Full Code of lbryio/spee.ch for AI

master 99c7b087d751 cached
442 files
537.4 KB
157.1k tokens
603 symbols
1 requests
Download .txt
Showing preview only (640K chars total). Download the full file or copy to clipboard to get everything.
Repository: lbryio/spee.ch
Branch: master
Commit: 99c7b087d751
Files: 442
Total size: 537.4 KB

Directory structure:
gitextract_v9i4r49y/

├── .babelrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── .nvmrc
├── .prettierrc.json
├── .sequelizerc
├── .travis.yml
├── LICENSE
├── README.md
├── changelog.md
├── cli/
│   ├── configure.js
│   ├── defaults/
│   │   ├── chainqueryConfig.json
│   │   ├── lbryConfig.json
│   │   ├── loggerConfig.json
│   │   ├── mysqlConfig.json
│   │   ├── siteConfig.json
│   │   └── slackConfig.json
│   └── questions/
│       ├── mysqlQuestions.js
│       └── siteQuestions.js
├── client/
│   ├── scss/
│   │   ├── _asset-blocked.scss
│   │   ├── _asset-display.scss
│   │   ├── _asset-preview.scss
│   │   ├── _body.scss
│   │   ├── _button-primary.scss
│   │   ├── _button-secondary.scss
│   │   ├── _button.scss
│   │   ├── _channel-claims-display.scss
│   │   ├── _claim-pending.scss
│   │   ├── _click-to-copy.scss
│   │   ├── _dropzone.scss
│   │   ├── _form-feedback.scss
│   │   ├── _form.scss
│   │   ├── _horizontal-split.scss
│   │   ├── _html.scss
│   │   ├── _input.scss
│   │   ├── _label.scss
│   │   ├── _link.scss
│   │   ├── _markdown.scss
│   │   ├── _media-queries.scss
│   │   ├── _nav-bar.scss
│   │   ├── _page-content.scss
│   │   ├── _page-layout-show-lite.scss
│   │   ├── _page-layout.scss
│   │   ├── _progress-bar.scss
│   │   ├── _publish-disabled-message.scss
│   │   ├── _publish-preview.scss
│   │   ├── _publish-status.scss
│   │   ├── _publish-url-input.scss
│   │   ├── _react-app.scss
│   │   ├── _reset.scss
│   │   ├── _row.scss
│   │   ├── _select.scss
│   │   ├── _share-buttons.scss
│   │   ├── _social-share-link.scss
│   │   ├── _space-around.scss
│   │   ├── _space-between.scss
│   │   ├── _text.scss
│   │   ├── _textarea.scss
│   │   ├── _tooltip.scss
│   │   ├── _variables.scss
│   │   ├── _video.scss
│   │   ├── all.scss
│   │   └── font/
│   │       ├── Lekton/
│   │       │   └── OFL.txt
│   │       └── _font.scss
│   └── src/
│       ├── actions/
│       │   ├── channel.js
│       │   ├── channelCreate.js
│       │   ├── index.js
│       │   ├── publish.js
│       │   └── show.js
│       ├── api/
│       │   ├── assetApi.js
│       │   ├── authApi.js
│       │   ├── channelApi.js
│       │   ├── fileApi.js
│       │   ├── homepageApi.js
│       │   └── specialAssetApi.js
│       ├── app.js
│       ├── channels/
│       │   └── publish.js
│       ├── components/
│       │   ├── AboutSpeechDetails/
│       │   │   └── index.jsx
│       │   ├── AboutSpeechOverview/
│       │   │   └── index.jsx
│       │   ├── ActiveStatusBar/
│       │   │   └── index.jsx
│       │   ├── AssetInfoFooter/
│       │   │   └── index.js
│       │   ├── AssetPreview/
│       │   │   └── index.jsx
│       │   ├── AssetShareButtons/
│       │   │   └── index.js
│       │   ├── ButtonPrimary/
│       │   │   └── index.jsx
│       │   ├── ButtonPrimaryJumbo/
│       │   │   └── index.jsx
│       │   ├── ButtonSecondary/
│       │   │   └── index.jsx
│       │   ├── ChannelAbout/
│       │   │   └── index.jsx
│       │   ├── ChannelCreateNameInput/
│       │   │   └── index.jsx
│       │   ├── ChannelCreatePasswordInput/
│       │   │   └── index.jsx
│       │   ├── ChannelInfoDisplay/
│       │   │   └── index.jsx
│       │   ├── ChannelLoginNameInput/
│       │   │   └── index.jsx
│       │   ├── ChannelLoginPasswordInput/
│       │   │   └── index.jsx
│       │   ├── ChannelSelectDropdown/
│       │   │   └── index.jsx
│       │   ├── ChooseAnonymousPublishRadio/
│       │   │   └── index.jsx
│       │   ├── ChooseChannelPublishRadio/
│       │   │   └── index.jsx
│       │   ├── ClickToCopy/
│       │   │   └── index.jsx
│       │   ├── DropzoneDropItDisplay/
│       │   │   └── index.jsx
│       │   ├── DropzoneInstructionsDisplay/
│       │   │   └── index.jsx
│       │   ├── DropzonePreviewImage/
│       │   │   └── index.jsx
│       │   ├── ErrorBoundary/
│       │   │   └── index.jsx
│       │   ├── ExpandingTextArea/
│       │   │   └── index.jsx
│       │   ├── FileViewer/
│       │   │   └── index.jsx
│       │   ├── FormFeedbackDisplay/
│       │   │   └── index.jsx
│       │   ├── GAListener/
│       │   │   └── index.jsx
│       │   ├── HorizontalSplit/
│       │   │   └── index.jsx
│       │   ├── InactiveStatusBar/
│       │   │   └── index.jsx
│       │   ├── Label/
│       │   │   └── index.jsx
│       │   ├── Logo/
│       │   │   └── index.jsx
│       │   ├── Memeify/
│       │   │   ├── EditableFontface/
│       │   │   │   └── index.js
│       │   │   ├── FontFaces/
│       │   │   │   ├── .gitkeep
│       │   │   │   ├── GreenMachine.js
│       │   │   │   ├── Inferno.js
│       │   │   │   ├── Lazer.js
│       │   │   │   ├── Neon.js
│       │   │   │   ├── OldBlue.js
│       │   │   │   ├── Outline.js
│       │   │   │   ├── RetroRainbow.js
│       │   │   │   ├── TheSpecial.js
│       │   │   │   └── VaporWave.js
│       │   │   ├── RichDraggable/
│       │   │   │   └── index.js
│       │   │   └── index.js
│       │   ├── NavBar/
│       │   │   └── index.jsx
│       │   ├── NavBarChannelOptionsDropdown/
│       │   │   └── index.jsx
│       │   ├── PageLayout/
│       │   │   └── index.jsx
│       │   ├── PageLayoutShowLite/
│       │   │   └── index.jsx
│       │   ├── ProgressBar/
│       │   │   └── index.jsx
│       │   ├── PublishDescriptionInput/
│       │   │   └── index.jsx
│       │   ├── PublishFinePrint/
│       │   │   └── index.jsx
│       │   ├── PublishLicenseInput/
│       │   │   └── index.jsx
│       │   ├── PublishLicenseUrlInput/
│       │   │   └── index.jsx
│       │   ├── PublishNsfwInput/
│       │   │   └── index.jsx
│       │   ├── PublishPreview/
│       │   │   └── index.jsx
│       │   ├── PublishUrlMiddleDisplay/
│       │   │   └── index.jsx
│       │   ├── Row/
│       │   │   └── index.jsx
│       │   ├── RowLabeled/
│       │   │   └── index.jsx
│       │   ├── SocialShareLink/
│       │   │   └── index.jsx
│       │   ├── SpaceAround/
│       │   │   └── index.jsx
│       │   ├── SpaceBetween/
│       │   │   └── index.jsx
│       │   └── VerticalSplit/
│       │       └── index.jsx
│       ├── constants/
│       │   ├── asset_display_states.js
│       │   ├── channel_action_types.js
│       │   ├── channel_create_action_types.js
│       │   ├── confirmation_messages.js
│       │   ├── publish_action_types.js
│       │   ├── publish_channel_select_states.js
│       │   ├── publish_claim_states.js
│       │   ├── publish_license_urls.js
│       │   ├── show_action_types.js
│       │   └── show_request_types.js
│       ├── containers/
│       │   ├── AssetBlocked/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── AssetDisplay/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── AssetInfo/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── AssetTitle/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelClaimsDisplay/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelCreateForm/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelLoginForm/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelSelect/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelTools/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── Dropzone/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── NavigationLinks/
│       │   │   ├── index.jsx
│       │   │   └── view.jsx
│       │   ├── PublishDetails/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishDisabledMessage/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishMetadataInputs/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishStatus/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishThumbnailInput/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishTitleInput/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishTool/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishUrlInput/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── SEO/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   └── SiteDescription/
│       │       ├── index.jsx
│       │       └── view.jsx
│       ├── index.js
│       ├── pages/
│       │   ├── AboutPage/
│       │   │   └── index.jsx
│       │   ├── ContentPageWrapper/
│       │   │   ├── index.jsx
│       │   │   └── view.jsx
│       │   ├── EditPage/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ErrorPage/
│       │   │   └── index.jsx
│       │   ├── FaqPage/
│       │   │   └── index.jsx
│       │   ├── FourOhFourPage/
│       │   │   └── index.jsx
│       │   ├── HomePage/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── LoginPage/
│       │   │   ├── index.jsx
│       │   │   └── view.jsx
│       │   ├── MultisitePage/
│       │   │   └── index.jsx
│       │   ├── PopularPage/
│       │   │   ├── index.jsx
│       │   │   └── view.jsx
│       │   ├── ShowAssetDetails/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ShowAssetLite/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ShowChannel/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   └── TosPage/
│       │       └── index.jsx
│       ├── reducers/
│       │   ├── channel.js
│       │   ├── channelCreate.js
│       │   ├── index.js
│       │   ├── publish.js
│       │   ├── show.js
│       │   └── site.js
│       ├── sagas/
│       │   ├── abandon.js
│       │   ├── checkForLoggedInChannel.js
│       │   ├── createChannel.js
│       │   ├── file.js
│       │   ├── index.js
│       │   ├── logoutChannel.js
│       │   ├── publish.js
│       │   ├── rootSaga.js
│       │   ├── show_asset.js
│       │   ├── show_channel.js
│       │   ├── show_special.js
│       │   ├── show_uri.js
│       │   ├── updateChannelAvailability.js
│       │   └── updateClaimAvailability.js
│       ├── selectors/
│       │   ├── channel.js
│       │   ├── channelCreate.js
│       │   ├── publish.js
│       │   ├── show.js
│       │   └── site.js
│       └── utils/
│           ├── createAssetMetaTags.js
│           ├── createBasicMetaTags.js
│           ├── createChannelMetaTags.js
│           ├── createGroupedList.js
│           ├── createMetaTags.js
│           ├── createMetaTagsArray.js
│           ├── createPageTitle.js
│           ├── createPermanentURI.js
│           ├── determineContentTypeFromExtension.js
│           ├── dynamicImport.js
│           ├── file.js
│           ├── oEmbed.js
│           ├── publish.js
│           ├── request.js
│           └── validate.js
├── customize.md
├── devConfig/
│   ├── sequelizeCliConfig.example.js
│   └── testingConfig.example.js
├── docs/
│   ├── settings.md
│   ├── setup/
│   │   ├── conf/
│   │   │   ├── caddy/
│   │   │   │   ├── Caddyfile.template
│   │   │   │   └── caddy.service
│   │   │   ├── lbrynet/
│   │   │   │   ├── lbrynet.service.example
│   │   │   │   └── lbrynet.service.template
│   │   │   ├── nginx/
│   │   │   │   ├── letsencrypt.conf
│   │   │   │   ├── myspeech
│   │   │   │   └── ssl.conf
│   │   │   └── speech/
│   │   │       ├── chainqueryConfig.json
│   │   │       └── speech.service.draft
│   │   └── scripts/
│   │       ├── firewall.sh
│   │       └── newuser.sh
│   └── ubuntuinstall.md
├── fullstart.md
├── lintstagedrc.json
├── nginx_example_config
├── package.json
├── public/
│   ├── bundle/
│   │   └── .gitkeep
│   └── robots.txt
├── server/
│   ├── chainquery/
│   │   ├── index.debug.js
│   │   ├── index.js
│   │   ├── models/
│   │   │   ├── AbnormalClaimModel.js
│   │   │   ├── AddressModel.js
│   │   │   ├── BlockModel.js
│   │   │   ├── ClaimModel.js
│   │   │   ├── InputModel.js
│   │   │   ├── OutputModel.js
│   │   │   ├── SupportModel.js
│   │   │   ├── TransactionAddressModel.js
│   │   │   └── TransactionModel.js
│   │   ├── queries/
│   │   │   ├── abnormalClaimQueries.js
│   │   │   ├── addressQueries.js
│   │   │   ├── blockQueries.js
│   │   │   ├── claimQueries.js
│   │   │   ├── inputQueries.js
│   │   │   ├── outputQueries.js
│   │   │   ├── supportQueries.js
│   │   │   ├── transactionAddressQueries.js
│   │   │   └── transactionQueries.js
│   │   └── tables/
│   │       ├── abnormalClaimTable.js
│   │       ├── addressTable.js
│   │       ├── blockTable.js
│   │       ├── claimTable.js
│   │       ├── inputTable.js
│   │       ├── outputTable.js
│   │       ├── supportTable.js
│   │       ├── transactionAddressTable.js
│   │       └── transactionTable.js
│   ├── controllers/
│   │   ├── api/
│   │   │   ├── blocked/
│   │   │   │   └── index.js
│   │   │   ├── channel/
│   │   │   │   ├── availability/
│   │   │   │   │   ├── checkChannelAvailability.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── claims/
│   │   │   │   │   ├── channelPagination.js
│   │   │   │   │   ├── getChannelClaims.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── data/
│   │   │   │   │   ├── getChannelData.js
│   │   │   │   │   └── index.js
│   │   │   │   └── shortId/
│   │   │   │       └── index.js
│   │   │   ├── claim/
│   │   │   │   ├── abandon/
│   │   │   │   │   └── index.js
│   │   │   │   ├── availability/
│   │   │   │   │   ├── checkClaimAvailability.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── data/
│   │   │   │   │   └── index.js
│   │   │   │   ├── get/
│   │   │   │   │   └── index.js
│   │   │   │   ├── list/
│   │   │   │   │   └── index.js
│   │   │   │   ├── longId/
│   │   │   │   │   └── index.js
│   │   │   │   ├── publish/
│   │   │   │   │   ├── authentication.js
│   │   │   │   │   ├── createPublishParams.js
│   │   │   │   │   ├── createThumbnailPublishParams.js
│   │   │   │   │   ├── deleteFile.js
│   │   │   │   │   ├── index.js
│   │   │   │   │   ├── parsePublishApiRequestBody.js
│   │   │   │   │   ├── parsePublishApiRequestBody.test.js
│   │   │   │   │   ├── parsePublishApiRequestFiles.js
│   │   │   │   │   ├── parsePublishApiRequestFiles.test.js
│   │   │   │   │   ├── publish.js
│   │   │   │   │   ├── validateFileForPublish.js
│   │   │   │   │   └── validateFileTypeAndSize.js
│   │   │   │   ├── resolve/
│   │   │   │   │   └── index.js
│   │   │   │   ├── shortId/
│   │   │   │   │   └── index.js
│   │   │   │   ├── update/
│   │   │   │   │   └── index.js
│   │   │   │   └── views/
│   │   │   │       └── index.js
│   │   │   ├── file/
│   │   │   │   └── availability/
│   │   │   │       └── index.js
│   │   │   ├── homepage/
│   │   │   │   └── data/
│   │   │   │       ├── getChannelData.js
│   │   │   │       └── index.js
│   │   │   ├── oEmbed/
│   │   │   │   ├── getOEmbedDataForAsset.js
│   │   │   │   ├── getOEmbedDataForChannel.js
│   │   │   │   ├── index.js
│   │   │   │   └── parseSpeechUrl.js
│   │   │   ├── special/
│   │   │   │   └── claims/
│   │   │   │       └── index.js
│   │   │   ├── tor/
│   │   │   │   └── index.js
│   │   │   └── user/
│   │   │       └── password/
│   │   │           └── index.js
│   │   ├── assets/
│   │   │   ├── constants/
│   │   │   │   └── request_types.js
│   │   │   ├── serveAsset/
│   │   │   │   └── index.js
│   │   │   ├── serveByClaim/
│   │   │   │   └── index.js
│   │   │   ├── serveByIdentifierAndClaim/
│   │   │   │   └── index.js
│   │   │   └── utils/
│   │   │       ├── determineRequestType.js
│   │   │       ├── flipClaimNameAndId.js
│   │   │       ├── getClaimIdAndServeAsset.js
│   │   │       ├── getLocalFileRecord.js
│   │   │       ├── logRequestData.js
│   │   │       ├── serveFile.js
│   │   │       └── transformImage.js
│   │   ├── auth/
│   │   │   ├── login/
│   │   │   │   └── index.js
│   │   │   ├── logout/
│   │   │   │   └── index.js
│   │   │   ├── signup/
│   │   │   │   └── index.js
│   │   │   └── user/
│   │   │       └── index.js
│   │   ├── pages/
│   │   │   ├── sendReactApp.js
│   │   │   └── sendVideoEmbedPage.js
│   │   └── utils/
│   │       ├── errorHandlers.js
│   │       ├── getClaimId.js
│   │       └── redirect.js
│   ├── index.js
│   ├── lbrynet/
│   │   ├── index.js
│   │   └── utils/
│   │       └── handleLbrynetResponse.js
│   ├── middleware/
│   │   ├── autoblockPublishMiddleware.js
│   │   ├── httpContextMiddleware.js
│   │   ├── logMetricsMiddleware.js
│   │   ├── multipartMiddleware.js
│   │   ├── requestLogger.js
│   │   └── torCheckMiddleware.js
│   ├── migrations/
│   │   ├── ChangeCertificateColumnTypes2.js
│   │   ├── ChangeClaimColumnTypes.js
│   │   └── File_AddHeightAndWidthColumn.js
│   ├── models/
│   │   ├── blocked.js
│   │   ├── certificate.js
│   │   ├── channel.js
│   │   ├── claim.js
│   │   ├── file.js
│   │   ├── index.js
│   │   ├── metrics.js
│   │   ├── tor.js
│   │   ├── trending.js
│   │   ├── user.js
│   │   ├── utils/
│   │   │   ├── createClaimRecordData.js
│   │   │   ├── createDatabaseIfNotExists.js
│   │   │   ├── createFileRecordData.js
│   │   │   ├── returnShortId.js
│   │   │   ├── returnShortId.test.js
│   │   │   └── trendingAnalysis.js
│   │   └── views.js
│   ├── render/
│   │   ├── handleShowRender.jsx
│   │   └── renderFullPage.js
│   ├── routes/
│   │   ├── api/
│   │   │   └── index.js
│   │   ├── assets/
│   │   │   └── index.js
│   │   ├── auth/
│   │   │   └── index.js
│   │   ├── fallback/
│   │   │   └── index.js
│   │   ├── index.js
│   │   └── pages/
│   │       └── index.js
│   ├── speechPassport/
│   │   ├── index.js
│   │   └── utils/
│   │       ├── deserializeUser.js
│   │       ├── local-login.js
│   │       ├── local-signup.js
│   │       └── serializeUser.js
│   ├── task-scripts/
│   │   ├── update-channel-names.js
│   │   └── update-password.js
│   ├── utils/
│   │   ├── awaitFileSize.js
│   │   ├── blockList.js
│   │   ├── configureLogging.js
│   │   ├── configureSlack.js
│   │   ├── createModuleAliases.js
│   │   ├── fetchClaimData.js
│   │   ├── getClaimData.js
│   │   ├── getMediaDimensions.js
│   │   ├── googleAnalytics.js
│   │   ├── imageProcessing.js
│   │   ├── isRequestLocal.js
│   │   ├── isValidQueryObj.js
│   │   ├── processTrending.js
│   │   └── videoProcessing.js
│   └── views/
│       ├── embed.handlebars
│       ├── layouts/
│       │   └── embed.handlebars
│       └── partials/
│           └── logo.handlebars
├── server.js
├── test/
│   ├── end-to-end/
│   │   └── end-to-end.test.js
│   └── module-alias-boilerplate.js
├── utils/
│   ├── checkForLocalConfig.js
│   ├── createCanonicalLink.js
│   ├── createModuleAliases.js
│   ├── isApprovedChannel.js
│   ├── lbryUri.js
│   └── validateFileForPublish.js
├── webpack/
│   ├── webpack.client.config.js
│   └── webpack.server.config.js
└── webpack.config.js

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

================================================
FILE: .babelrc
================================================
{
  "presets": ["@babel/env", "@babel/react"],
  "plugins": ["react-hot-loader/babel", "@babel/plugin-proposal-object-rest-spread"]
}


================================================
FILE: .eslintignore
================================================
client/build
node_modules/
public/bundle
server/render/build
server/bundle
test/
server/chainquery


================================================
FILE: .eslintrc
================================================
{
  "parser": "babel-eslint",
  "extends": ["standard", "standard-jsx"],
  "env": {
    "es6": true,
    "jest": true,
    "node": true,
    "browser": true
  },
  "globals": {
    "GENTLY": true
  },
  "rules": {
    "no-multi-spaces": 0,
    "new-cap": 0,
    "prefer-promise-reject-errors": 0,
    "no-unused-vars": 0,
    "standard/object-curly-even-spacing": 0,
    "handle-callback-err": 0,
    "one-var": 0,
    "object-curly-spacing": 0,
    "comma-dangle": ["error", "always-multiline"],
    "semi": ["error", "always", { "omitLastInOneLineBlock": true }],
    "key-spacing": [
      "error",
      {
        "multiLine": {
          "beforeColon": false,
          "afterColon": true
        },
        "align": {
          "beforeColon": false,
          "afterColon": true,
          "on": "colon",
          "mode": "strict"
        }
      }
    ]
  }
}


================================================
FILE: .gitignore
================================================
.DS_Store
*.log
.idea/

node_modules

client/build

site/

devConfig/sequelizeCliConfig.js
devConfig/testingConfig.js

server/bundle

public/bundle/bundle.js
public/bundle/bundle.js.map
public/bundle/Lekton-*
public/bundle/style.css

uploads

config/

deployment-config.json


================================================
FILE: .npmignore
================================================
client/src
server/render/src
.babelrc


================================================
FILE: .nvmrc
================================================
v8.12.0


================================================
FILE: .prettierrc.json
================================================
{
  "trailingComma": "es5",
  "printWidth": 100,
  "singleQuote": true
}


================================================
FILE: .sequelizerc
================================================
const path = require('path');

module.exports = {
  'config': path.resolve('devConfig', 'sequelizeCliConfig.js'),
  'models-path': path.resolve('server', 'models'),
  'seeders-path': path.resolve('server', 'seeders'),
  'migrations-path': path.resolve('server', 'migrations')
}


================================================
FILE: .travis.yml
================================================
dist: xenial
#addons:
#  apt:
#    sources:
#      - mysql-5.7-trusty
#    packages:
#      - mysql-server
#      - mysql-client
language: node_js
node_js:
  - "lts/dubnium"
cache:
  directories:
    - "node_modules"
#services:
#  - mysql

jobs:
  include:
    - stage: "Build"
      name: "Build and run test environment"

      before_install:
      #  - sudo mysql -e "use mysql; update user set authentication_string=PASSWORD('password') where User='root'; update user set plugin='mysql_native_password';FLUSH PRIVILEGES;"
      #  - sudo mysql_upgrade -u root -ppassword
      #  - sudo service mysql restart
      #  - mysql -u root -ppassword -e 'CREATE DATABASE IF NOT EXISTS lbry;'
      #  - mysql -u root -ppassword -e "CREATE USER 'lbry'@'localhost' IDENTIFIED BY 'lbry';"
      #  - mysql -u root -ppassword -e "GRANT ALL ON lbry.* TO 'lbry'@'localhost';"
      #  - sudo service mysql restart
        - dpkg --compare-versions `npm -v` ge 6.4.0 || npm i -g npm@^6.4.0

      install:
        - npm i

      script:
        - cp ./cli/defaults/* ./site/config/
        - |
          echo '{ "sessionKey": "session", "masterPassword": false }' > ./site/private/authConfig.json
      #  - npm run fix
        - npm run build
        - npm start &
        - sleep 10 # Attempt to collect output for 10 seconds


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2017-2020 LBRY Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
# Spee.ch

spee.ch provides a user-friendly, custom-designed, image and video hosting site backed by a decentralized network and
blockchain ([LBRY](https://lbry.tech/)). Via just a small set of config files, you can spin your an entire spee.ch site back up including assets.

**Please note: the spee.ch code base and setup instructions are no longer actively maintained now that we have lbry.tv. Proceed at your own caution. Setup will require dev ops skills.**

![App GIF](https://spee.ch/e/speechgif.gif)

For a completely open, unrestricted example of a spee.ch site, check out https://www.spee.ch.

For a closed, custom-hosted and branded example, check out https://lbry.theantimedia.com/.

## Installation

### Ubuntu Step-by-Step

[Step-by-step Ubuntu Install Guide](./docs/ubuntuinstall.md)

### Full Instructions

#### Get some information ready:

- mysqlusername
- mysqlpassword
- domainname or 'http://localhost:3000'
- speechport = 3000

#### Install and Set Up Dependencies

- Firewall open ports
  - 22
  - 80
  - 443
  - 3333
  - 4444
- [NodeJS](https://nodejs.org)
- [MySQL version 5.7 or higher](https://dev.mysql.com/doc/refman/8.0/en/installing.html)
  - mysqlusername or root
  - mysqlpassword
  - Requires mysql_native_password plugin
  ```
  mysql> `ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'yourpassword';`
  ```
- [lbrynet](https://github.com/lbryio/lbry) daemon
  - run this as a service exposing ports 3333 and 4444
  - _note_: once the daemon is running, issue commands in another terminal session (tmux) to retrieve an address for your wallet to recieve 5+ LBC credits (or join us in the [#speech discord channel](https://discord.gg/YjYbwhS) and we will send you a few)
    - `./lbrynet commands` gets a list of commands
    - `./lbrynet account_balance` gets your balance (initially 0.0)
    - `./lbrynet address_list` gets addresses you can use to recieve LBC
- [FFmpeg](https://www.ffmpeg.org/download.html)
- [ImageMagick](https://packages.ubuntu.com/xenial/graphics/imagemagick)
- Spee.ch (below)
- pm2 (optional) process manager such as pm2 to run speech server.js
- http proxy server e.g. caddy, nginx, or traefik, to forward 80/443 to speech port 3000
  - _note: even running on http://localhost, you must redirect http or https to port 3000_

#### Clone spee.ch

- release version for stable production

```
$ git clone -b release https://github.com/lbryio/spee.ch.git
```

- master version for development

```
$ git clone https://github.com/lbryio/spee.ch.git
```

- your own fork for customization

#### Change directory into your project

```
$ cd spee.ch
```

#### Install node dependencies

```
$ npm install
```

#### Create the config files using the built-in CLI

Make sure lbrynet is running in the background before proceeding.

_note: If you are opt to run a local chainquery, such as from [lbry-docker/chainquery](https://github.com/lbryio/lbry-docker/tree/master/chainquery) you will need to specify connection details at this time in:_ ~/spee.ch/docs/setup/conf/speech/chainqueryConfig.json

```
$ npm run configure
```

#### Build & start the app

```
$ npm run build

$ npm run start
```

#### View in browser

- Visit [http://localhost:3000](http://localhost:3000) in your browser

#### Customize your app

Check out the [customization guide](https://github.com/lbryio/spee.ch/blob/master/customize.md) to change your app's appearance and components

#### (optional) add custom components and update the styles

- Create custom components by creating React components in `site/custom/src/`
- Update or override the CSS by changing the files in `site/custom/scss`

#### (optional) install your own chainquery

Instructions are coming at [lbry-docker] to install your own chainquery instance using docker-compose. This will require 50GB of preferably SSD space and at least 10 minutes to download, possibly much longer.

## Settings

There are a number of settings available for customizing the behavior of your installation.  
[Here](https://github.com/lbryio/spee.ch/blob/master/docs/settings.md) is some documentation on them.

## API

#### /api/claim/publish

method: `POST`

example:

```
curl -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/api/claim/publish
```

Parameters:

- `name` (required, must be unique across the instance)
- `file` (required) (must be type .mp4, .jpeg, .jpg, .gif, or .png)
- `nsfw` (optional)
- `license` (optional)
- `title` (optional)
- `description` (optional)
- `thumbnail` URL to thumbnail image, for .mp4 uploads only (optional)
- `channelName` channel to publish too (optional)
- `channelPassword` password for channel to publish too (optional, but required if `channelName` is provided)

response:

```
{
    "success": <bool>,
    "message": <string>,
    "data": {
        "name": <string>,
        "claimId": <string>,
        "url": <string>,
        "showUrl": <string>,
        "serveUrl": <string>,
        "lbryTx": {
            "claim_address": <string>,
            "claim_id": <string>,
            "fee": <number>,
            "nout": <number>,
            "tx": <string>,
            "value": <number>
        }
    }
}
```

#### /api/claim/availability/:name

method: `GET`

example:

```
curl https://spee.ch/api/claim/availability/doitlive
```

response:

```
{
    "success": <bool>,  // `true` if spee.ch successfully checked the claim availability
    "data": <bool>, // `true` if claim is available, false if it is not available
    "message": <string> // human readable message of whether claim was available or not
}
```

## Contribute

### Stack

The spee.ch stack is MySQL, Express.js, Node.js, and React.js. Spee.ch also runs `lbrynet` on its server, and it uses the `lbrynet` API to make requests -- such as `publish`, `create_channel`, and `get` -- on the `LBRY` network.

Spee.ch also runs a sync tool, which decodes blocks from the `LBRY` blockchain as they are mined, and stores the information in MySQL. It stores all claims in the `Claims` table, and all channel claims in the `Certificates` table.

- server
  - [MySQL](https://www.mysql.com/)
  - [express](https://www.npmjs.com/package/express)
  - [node](https://nodejs.org/)
  - [lbry](https://github.com/lbryio/lbry)
  - [FFmpeg](https://www.ffmpeg.org/)
- client
  - [react](https://reactjs.org/)
  - redux
  - sagas
  - scss
  - handlebars

### Architecture

- `cli/` contains the code for the CLI tool. Running the tool will create `.json` config files and place them in the `site/config/` folder

  - `configure.js` is the entry point for the CLI tool
  - `cli/defaults/` holds default config files
  - `cli/questions/` holds the questions that the CLI tool asks to build the config files

- `client/` contains all of the client code

  - The client side of spee.ch uses `React` and `Redux`
  - `client/src/index.js` is the entry point for the client side js. It checks for preloaded state, creates the store, and places the `<App />` component in the document.
  - `client/src/app.js` holds the `<App />` component, which contains the routes for `react-router-dom`
  - `client/src/` contains all of the JSX code for the app. When the app is built, the content of this folder is transpiled into the `client/build/` folder.
    - The Redux code is broken up into `actions/` `reducers/` and `selectors/`
    - The React components are broken up into `containers/` (components that pull props directly from the Redux store), `components/` ('dumb' components), and `pages/`
    - spee.ch also uses sagas which are in the `sagas/` folders and `channels/`
  - `client/scss/` contains the CSS for the project \*

- `site/custom` is a folder which can be used to override the default components in `client/`

  - The folder structure mimics that of the `client/` folder
  - to customize spee.ch, place your own components and scss in the `site/custom/src/` and `site/custom/scss` folders.

- `server/` contains all of the server code

  - `index.js` is the entry point for the server. It creates the [express app](https://expressjs.com/), requires the routes, syncs the database, and starts the server listening on the `PORT` designated in the config files.
  - `server/routes/` contains all of the routes for the express app
  - `server/controllers/` contains all of the controllers for all of the routes
  - `server/models/` contains all of the models which the app uses to interact with the `MySQL` database.
    - Spee.ch uses the [sequelize](http://docs.sequelizejs.com/) ORM for communicating with the database.

- `tests/` holds the end-to-end tests for this project
  - Spee.ch uses `mocha` with the `chai` assertion library
  - unit tests are located inside the project in-line with the files being tested and are designated with a `xxxx.test.js` file name

### Tests

- This package uses `mocha` with `chai` for testing.
- Before running tests, create a `testingConfig.js` file in `devConfig/` by copying `testingConfig.example.js`
- To run tests:
  - To run all tests, including those that require LBC (like publishing), simply run `npm test`
  - To run only tests that do not require LBC, run `npm run test:no-lbc`

### URL formats

Spee.ch has a few types of URL formats that return different assets from the LBRY network. Below is a list of all possible URLs for the content on spee.ch. You can learn more about LBRY URLs [here](https://lbry.tech/resources/uri).

- retrieve the controlling `LBRY` claim:
  - https://spee.ch/`claim`
  - https://spee.ch/`claim`.`ext` (serve)
  - https://spee.ch/`claim`.`ext`&`querystring` (serve transformed)
- retrieve a specific `LBRY` claim:
  - https://spee.ch/`claim_id`/`claim`
  - https://spee.ch/`claim_id`/`claim`.`ext` (serve)
  - https://spee.ch/`claim_id`/`claim`.`ext`&`querystring` (serve transformed)
- retrieve all contents for the controlling `LBRY` channel
  - https://spee.ch/`@channel`
- a specific `LBRY` channel
  - https://spee.ch/`@channel`:`channel_id`
- retrieve a specific claim within the controlling `LBRY` channel
  - https://spee.ch/`@channel`/`claim`
  - https://spee.ch/`@channel`/`claim`.`ext` (serve)
  - https://spee.ch/`@channel`/`claim`.`ext`&`querystring` (serve)
- retrieve a specific claim within a specific `LBRY` channel
  - https://spee.ch/`@channel`:`channel_id`/`claim`
  - https://spee.ch/`@channel`:`channel_id`/`claim`.`ext` (serve)
  - https://spee.ch/`@channel`:`channel_id`/`claim`.`ext`&`querystring` (serve)
- `querystring` can include the following transformation values separated by `&`
  - h=`number` (defines height)
  - w=`number` (defines width)
  - t=`crop` or `stretch` (defines transformation - missing implies constrained proportions)

### Dependencies

Spee.ch depends on two other lbry technologies:

- [chainquery](https://github.com/lbryio/chainquery) - a normalized database of the blockchain data. We've provided credentials to use a public chainquery service. You can also install it on your own server to avoid being affected by the commons.
- [lbrynet](https://github.com/lbryio/lbry) - a daemon that handles your wallet and transactions.

### Bugs

If you find a bug or experience a problem, please report your issue here on GitHub and find us in the lbry discord!

## License

This project is MIT licensed. For the full license, see [LICENSE](LICENSE).

## Security

We take security seriously. Please contact security@lbry.com regarding any security issues. [Our GPG key is here](https://lbry.com/faq/gpg-key) if you need it.

## Contact

The primary contact for this project is [@jessopb](mailto:jessop@lbry.com).


================================================
FILE: changelog.md
================================================


================================================
FILE: cli/configure.js
================================================
const inquirer = require('inquirer');
const fs = require('fs');
const Path = require('path');
const axios = require('axios');
const ip = require('ip');
const pwGenerator = require('generate-password');

const mysqlQuestions = require(Path.resolve(__dirname, 'questions/mysqlQuestions.js'));
const siteQuestions = require(Path.resolve(__dirname, 'questions/siteQuestions.js'));

let primaryClaimAddress = '';
let thumbnailChannelDefault = '@thumbnails';
let thumbnailChannel = '';
let thumbnailChannelId = '';

const createConfigFile = (fileName, configObject, topSecret) => {
  // siteConfig.json , siteConfig
  const fileLocation = topSecret
    ? Path.resolve(__dirname, `../site/private/${fileName}`)
    : Path.resolve(__dirname, `../site/config/${fileName}`);

  const fileContents = JSON.stringify(configObject, null, 2);
  fs.writeFileSync(fileLocation, fileContents, 'utf-8');
  console.log(`Successfully created ${fileLocation}\n`);
};

// import existing configs or import the defaults
let mysqlConfig;
try {
  mysqlConfig = require('../site/config/mysqlConfig.json');
} catch (error) {
  mysqlConfig = require('./defaults/mysqlConfig.json');
}
const { database: mysqlDatabase, username: mysqlUsername, password: mysqlPassword } = mysqlConfig;

let siteConfig;
try {
  siteConfig = require('../site/config/siteConfig.json');
} catch (error) {
  siteConfig = require('./defaults/siteConfig.json');
}
const {
  details: { port, title, host },
  publishing: { uploadDirectory, channelClaimBidAmount: channelBid },
} = siteConfig;

let lbryConfig;
try {
  lbryConfig = require('../site/config/lbryConfig.json');
} catch (error) {
  lbryConfig = require('./defaults/lbryConfig.json');
}

let loggerConfig;
try {
  loggerConfig = require('../site/config/loggerConfig.json');
} catch (error) {
  loggerConfig = require('./defaults/loggerConfig.json');
}

let slackConfig;
try {
  slackConfig = require('../site/config/slackConfig.json');
} catch (error) {
  slackConfig = require('./defaults/slackConfig.json');
}

let chainqueryConfig;
try {
  chainqueryConfig = require('../site/config/chainqueryConfig.json');
} catch (error) {
  chainqueryConfig = require('./defaults/chainqueryConfig.json');
}

// authConfig
let randSessionKey = pwGenerator.generate({
  length: 20,
  numbers: true,
});

let randMasterPass = pwGenerator.generate({
  length: 20,
  numbers: true,
});

let authConfig;
try {
  authConfig = require('../site/private/authConfig.json');
} catch (error) {
  authConfig = {
    sessionKey: randSessionKey,
    masterPassword: randMasterPass,
  };
}

// ask user questions and create config files
inquirer
  .prompt(mysqlQuestions(mysqlDatabase, mysqlUsername, mysqlPassword))
  .then(results => {
    console.log('\nCreating mysql config file...');
    createConfigFile('mysqlConfig.json', results);
  })
  .then(() => {
    // check for lbrynet connection & retrieve a default address
    console.log('\nRetrieving your primary claim address from LBRY...');
    return axios
      .post('http://localhost:5279', {
        method: 'address_list',
      })
      .then(response => {
        if (response.data) {
          if (response.data.error) {
            throw new Error(response.data.error.message);
          }

          primaryClaimAddress = response.data.result[0];
          console.log('Primary claim address:', primaryClaimAddress, '!\n');
          siteConfig['publishing']['primaryClaimAddress'] = primaryClaimAddress;
          return;
        }
        throw new Error('No data received from lbrynet');
      })
      .catch(error => {
        throw error;
      });
  })
  .then(() => {
    console.log('\nChecking to see if a LBRY channel exists for thumbnails...');
    // see if a channel name already exists in the configs
    const { publishing } = siteConfig;
    thumbnailChannel = publishing.thumbnailChannel;
    thumbnailChannelId = publishing.thumbnailChannelId;
    console.log(`Thumbnail channel in configs: ${thumbnailChannel}#${thumbnailChannelId}.`);
    // see if channel exists in the wallet
    return axios
      .post('http://localhost:5279', {
        method: 'channel_list',
      })
      .then(response => {
        if (response.data) {
          if (response.data.error) {
            throw new Error(response.data.error.message);
          }

          const channelList = response.data.result || [];
          console.log('channels in this wallet:', channelList.length);
          for (let i = 0; i < channelList.length; i++) {
            if (channelList[i].name === thumbnailChannelDefault) {
              const foundChannel = channelList[i];
              console.log(`Found a thumbnail channel in wallet.`);
              if (foundChannel.is_mine === false) {
                console.log('Channel was not mine');
                continue;
              }
              console.log('name:', foundChannel.name);
              console.log('claim_id:', foundChannel.claim_id);
              if (
                foundChannel.name === thumbnailChannel &&
                foundChannel.claim_id === thumbnailChannelId
              ) {
                console.log('No update to existing thumbnail config required\n');
              } else {
                console.log(`Replacing thumbnail channel in config...`);
                siteConfig['publishing']['thumbnailChannel'] = foundChannel.name;
                siteConfig['publishing']['thumbnailChannelId'] = foundChannel.claim_id;
                console.log(`Successfully replaced channel in config.\n`);
              }
              return true;
            }
          }
          console.log(`Did not find a thumbnail channel that is mine in wallet.\n`);
          return false;
        }
        throw new Error('No data received from lbrynet');
      })
      .catch(error => {
        throw error;
      });
  })
  .then(thumbnailChannelAlreadyExists => {
    // exit if a channel already exists, skip this step
    if (thumbnailChannelAlreadyExists) {
      return;
    }
    // create thumbnail address
    console.log('\nCreating a LBRY channel to publish your thumbnails to...');
    return axios
      .post('http://localhost:5279', {
        method: 'channel_new',
        params: {
          name: thumbnailChannelDefault,
          bid: channelBid,
        },
      })
      .then(response => {
        if (response.data) {
          if (response.data.error) {
            throw new Error(response.data.error.message);
          }
          thumbnailChannel = thumbnailChannelDefault;
          thumbnailChannelId = response.data.result.claim_id;
          siteConfig['publishing']['thumbnailChannel'] = thumbnailChannel;
          siteConfig['publishing']['thumbnailChannelId'] = thumbnailChannelId;
          console.log(`Created channel: ${thumbnailChannel}#${thumbnailChannelId}\n`);
          return;
        }
        throw new Error('No data received from lbrynet');
      })
      .catch(error => {
        throw error;
      });
  })
  .then(() => {
    return inquirer.prompt(siteQuestions(port, title, host, uploadDirectory));
  })
  .then(results => {
    console.log('\nCreating site config file...');
    siteConfig['details']['port'] = results.port;
    siteConfig['details']['title'] = results.title;
    siteConfig['details']['host'] = results.host;
    siteConfig['details']['ipAddress'] = ip.address();
    siteConfig['publishing']['uploadDirectory'] = results.uploadDirectory;
  })
  .then(() => {
    // create the config files
    createConfigFile('siteConfig.json', siteConfig);
    createConfigFile('lbryConfig.json', lbryConfig);
    createConfigFile('loggerConfig.json', loggerConfig);
    createConfigFile('slackConfig.json', slackConfig);
    createConfigFile('chainqueryConfig.json', chainqueryConfig);
    createConfigFile('authConfig.json', authConfig, true);
  })
  .then(() => {
    console.log("\nYou're all done!");
    console.log(
      '\nIt\'s a good idea to BACK UP YOUR MASTER PASSWORD \nin "/site/private/authConfig.json" so that you don\'t lose \ncontrol of your channel.'
    );

    console.log(
      '\nNext step: run "npm run build" (or "npm run dev") to compiles, and "npm run start" to start your server!'
    );
    console.log(
      'If you want to change any settings, you can edit the files in the "/site" folder.'
    );
    process.exit(0);
  })
  .catch(error => {
    if (error.code === 'ECONNREFUSED') {
      console.log('Error: could not connect to LBRY.  Please make sure lbrynet daemon is running.');
      process.exit(1);
    } else {
      console.log(error);
      process.exit(1);
    }
  });


================================================
FILE: cli/defaults/chainqueryConfig.json
================================================
{
  "host": "public.chainquery.lbry.com",
  "port": "3306",
  "timeout": 30,
  "database": "chainquery",
  "username": "speechpublic",
  "password": "7uITJLwZRvHBZYS3JZDykD1-7hLVkVA1jDWfcgqi6QnC"
}


================================================
FILE: cli/defaults/lbryConfig.json
================================================
{
  "apiHost": "localhost",
  "apiPort": "5279",
  "getTimeout": 30
}


================================================
FILE: cli/defaults/loggerConfig.json
================================================
{
  "logLevel": "verbose"
}


================================================
FILE: cli/defaults/mysqlConfig.json
================================================
{
  "database": "lbry",
  "username": "root",
  "password": ""
}


================================================
FILE: cli/defaults/siteConfig.json
================================================
{
  "analytics": {
    "googleId": null
  },
  "assetDefaults": {
    "title": "Default Content Title",
    "description": "Default Content Description",
    "thumbnail": "https://spee.ch/0e5d4e8f4086e13f5b9ca3f9648f518e5f524402/speechflag.png"
  },
  "auth": {
    "sessionKey": "mysecretkeyword",
    "masterPassword": "myMasterPassword"
  },
  "details": {
    "port": 3000,
    "title": "My Site",
    "ipAddress": "",
    "host": "https://www.example.com",
    "description": "A decentralized hosting platform built on LBRY",
    "twitter": false,
    "blockListEndpoint": "https://api.lbry.com/file/list_blocked"
  },
  "publishing": {
    "primaryClaimAddress": null,
    "uploadDirectory": "/home/lbry/Uploads",
    "thumbnailChannel": null,
    "thumbnailChannelId": null,
    "additionalClaimAddresses": [],
    "disabled": false,
    "disabledMessage": "Default publishing disabled message",
    "closedRegistration": false,
    "serveOnlyApproved": false,
    "publishOnlyApproved": false,
    "approvedChannels": [],
    "publishingChannelWhitelist": [],
    "channelClaimBidAmount": "0.1",
    "fileClaimBidAmount": "0.01",
    "fileSizeLimits": {
      "image": 50000000,
      "video": 50000000,
      "audio": 50000000,
      "text": 50000000,
      "model": 50000000,
      "application": 50000000,
      "customByContentType": {
        "application/octet-stream": 50000000
      }
    }
  },
  "serving": {
    "dynamicFileSizing": {
      "enabled": true,
      "maxDimension": 2000
    },
    "markdownSettings": {
      "skipHtmlMain": true,
      "escapeHtmlMain": true,
      "skipHtmlDescriptions": true,
      "escapeHtmlDescriptions": true,
      "allowedTypesMain": [],
      "allowedTypesDescriptions": [],
      "allowedTypesExample": [
        "see react-markdown docs",
        "root",
        "text",
        "break",
        "paragraph",
        "emphasis",
        "strong",
        "thematicBreak",
        "blockquote",
        "delete",
        "link",
        "image",
        "linkReference",
        "imageReference",
        "table",
        "tableHead",
        "tableBody",
        "tableRow",
        "tableCell",
        "list",
        "listItem",
        "heading",
        "inlineCode",
        "code",
        "html",
        "parsedHtml"
      ]
    },
    "customFileExtensions": {
      "application/x-troff-man": "man",
      "application/x-troff-me": "me",
      "application/x-mif": "mif",
      "application/x-troff-ms": "ms",
      "application/x-troff": "roff",
      "application/x-python-code": "pyc",
      "text/x-python": "py",
      "application/x-pn-realaudio": "ram",
      "application/x-sgml": "sgm",
      "model/stl": "stl",
      "image/pict": "pct",
      "text/xul": "xul",
      "text/x-go": "go"
    }
  },
  "startup": {
    "performChecks": true,
    "performUpdates": true
  }
}


================================================
FILE: cli/defaults/slackConfig.json
================================================
{
  "slackWebHook": false,
  "slackErrorChannel": false,
  "slackInfoChannel": false
}

================================================
FILE: cli/questions/mysqlQuestions.js
================================================
const database = (defaultAnswer) => {
  return {
    type   : 'input',
    message: 'What is the name of the MySQL DATABASE to be used?',
    default: defaultAnswer,
    name   : 'database',
  };
};

const username = (defaultAnswer) => {
  return {
    type   : 'input',
    message: 'What is the USER NAME for your MySQL database?',
    default: defaultAnswer,
    name   : 'username',
  };
};

const password = (defaultAnswer) => {
  return {
    type   : 'input',
    message: 'What is the PASSWORD for your MySQL database?',
    default: defaultAnswer,
    name   : 'password',
  };
};

module.exports = (defaultDatabase, defaultUsername, defaultPassword) => {
  return [
    database(defaultDatabase),
    username(defaultUsername),
    password(defaultPassword),
  ];
};


================================================
FILE: cli/questions/siteQuestions.js
================================================
const makeDir = require('make-dir');

const port = (defaultAnswer) => {
  return {
    type   : 'input',
    message: 'Enter a PORT for your server to run on.',
    default: defaultAnswer,
    name   : 'port',
  };
};

const title = (defaultAnswer) => {
  return {
    type   : 'input',
    message: 'Enter a title for your site.',
    default: defaultAnswer,
    name   : 'title',
  };
};

const host = (defaultAnswer) => {
  return {
    type   : 'input',
    message: 'Enter your site\'s domain.',
    default: defaultAnswer,
    name   : 'host',
  };
};

const uploadDirectory = (defaultAnswer) => {
  return {
    type   : 'input',
    message: 'Enter a directory where uploads should be stored.',
    default: defaultAnswer,
    name   : 'uploadDirectory',
    validate (input) {
      // make sure the directory exists
      return new Promise((resolve, reject) => {
        console.log('\n\nCreating directory', input, '...');
        try {
          const dirPath = makeDir.sync(input);
          console.log('Successfully created directory at', dirPath, '\n');
        } catch (error) {
          console.log('Failed to create directory, please create directory manually.\n');
        }
        resolve(true);
      });
    },
  };
};

module.exports = (defaultPort, defaultTitle, defaultHost, defaultUploadDirectory) => {
  return [
    port(defaultPort),
    title(defaultTitle),
    host(defaultHost),
    uploadDirectory(defaultUploadDirectory),
  ];
};


================================================
FILE: client/scss/_asset-blocked.scss
================================================
.asset-blocked__image {
width: 100%;
}

.asset-blocked__text {
width: 100%;
}


================================================
FILE: client/scss/_asset-display.scss
================================================
.asset-main {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.asset-document {
  max-width: 1000px;
  padding: $thin-padding;
  height: fit-content;
  box-sizing: border-box;
}
.asset-display {
  height: fit-content;
  width: fit-content;
}

.asset-title {
  max-width: 1000px;
  padding-bottom: $thin-padding;
  text-align: center;
}

.asset-image, .asset-video {
  max-height: 75vh;
  max-width: 85vw;
  object-fit: contain;
  object-position: center;
  background: black;
}

/*below must die if this is intended to be shareable component! it also probably doesn't need to be*/

.visible-content {
  margin: 0;
  padding-bottom: 30px;
  position: relative;
  width: 100%;

  &.closed {
    box-shadow: none;

    &:after {
      box-shadow: none;
    }
  }

  &:after {
    box-shadow: 0px 2px 3px 2px $shadow-color;
    content: '';
    height: 0;
    position: absolute;
    top: 100%;
    width: 100%;
    z-index: 100;
  }
}


.vertical-split, .visible-content {
  flex           : 1 0 auto;
  display        : flex;
  flex-direction : column;
  justify-content: space-between;
  align-items    : center;
};

.collapse-content {
  flex-grow: 0;
  @media (max-width: $break-point-tablet) {
    max-width: 100%;
    width: 100%;
  }
}

.collapse-content.closed{
  display: none;
}

.collapse-button {
  background: none;
  border: none;
  display: block;
  margin: 15px auto 0;
  width: 25px;
  height: 25px;
  text-align: center;
  padding: 0px;

  @media (max-width: $break-point-tablet) {
    padding: 0;
  }

  svg {
    stroke: $primary-color;
    &.plus-icon {
      transform: rotate(0);
      transition: all 0.4s ease;
    }

  }

  &:hover {
    .plus-icon {
      transform: rotate(-180deg);
    }
  }
}

.asset-info {
  $asset-info-width: 1000px;
  max-width: $asset-info-width;
  margin: $primary-padding;
  width: 100%;

  @media (min-width: $break-point-tablet) {
    padding: $primary-padding;
  }
  @media (max-width: $break-point-tablet) {
    padding: $tertiary-padding;
  }

  @media (max-width: $break-point-mobile) {
    margin: $tertiary-padding;
  }
}

.asset-footer {
  border-top: $subtle-border;
  padding-top: $primary-padding;
  margin-top: $primary-padding;
  color: $grey;
  text-align: center;
}


================================================
FILE: client/scss/_asset-preview.scss
================================================
.asset-preview {
  display: flex;
  flex-direction: column;
  background: $card-color;
  color: $text-color;
  width: 240px;
  border: $subtle-border;
  height: 256px;
  &:hover {
    border-color: $highlight-border-color;
    color: $primary-color;
  }
}

.asset-preview__image {
  height  : 180px;
  width   : 240px;
  overflow: hidden;
  object-fit: cover;
  padding: 0;
  margin : 0;
  box-sizing: border-box;

}

.asset-preview__image-box {
  width  : 240px;
  height : 180px;
  padding: 0;
  margin : 0;
  box-sizing: border-box;
}

.asset-preview__label {
  padding: $thin-padding;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.asset-preview__label-text {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  box-sizing: border-box;
  font-size: $text-small;
  font-weight: bold;
  height: 54px;
}

.asset-preview__label-info {
  width: 100%;
  height: 15px;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  overflow: hidden;
  text-overflow: ellipsis;
  box-sizing: border-box;
  align-items: center;
}

.asset-preview__label-info-datum {
  display: flex;
  flex-direction: row;
  align-items: center;
  overflow: hidden;
  box-sizing: border-box;
  font-size: $text-small;
  max-width: 40%;
}

.asset-preview__label-info-datum svg{
  height: 1.2em;
  width: 1.2em;
  padding: 0;
  padding-right: $thin-padding;
  margin: 0;
}

.asset-preview__label-info-datum .svg-icon{
  padding: 0px;
  margin: 0;
  height: 15px;
}

.asset-preview__blocked {
  box-sizing: border-box;
  background: black;
  color: white;
  height: 64%;
  padding: $thin-padding;
  margin-bottom: $thin-padding;
}


================================================
FILE: client/scss/_body.scss
================================================
body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  word-wrap: break-word;
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: column;
  flex-direction: column;
}

================================================
FILE: client/scss/_button-primary.scss
================================================
.button--primary, .button--primary:focus, .button--primary:active {
  border-color: $primary-color;
  color: $primary-color;
  background-color: $background-color;
}

.button--primary:hover {
  color: $background-color;
  background-color: $primary-color;
}

.button--primary:active {
  $color: darken($primary-color, 10%);
  border-color: $color;
  background-color: $color;
}

================================================
FILE: client/scss/_button-secondary.scss
================================================
.button--secondary, .button--secondary:focus, .button--secondary:active  {
  border-bottom-color: $secondary-color;
  color: $secondary-color;
  background-color: $background-color;
}

.button--secondary:active {
  $color: darken($secondary-color, 10%);
  color: $color;
  border-bottom-color: $color;
}


================================================
FILE: client/scss/_button.scss
================================================
button {
  cursor: pointer;
  &:active
  {
    outline: 0;
  }
}

.button--primary, .button--secondary
{
  border-width: $button-border-width;
  border-style: $button-border-strength;
  border-color: transparent;
  padding: $thin-padding;
}


.button--jumbo, .button--jumbo:focus, .button--jumbo:active {
  width: $button-full-width;
  font-size: x-large;
}


================================================
FILE: client/scss/_channel-claims-display.scss
================================================
.channel-claims-display {
  width: 100%;
  display: grid;
  grid-gap: $tertiary-padding;
  align-content: space-around;
  @media (min-width: $break-point-x-large) {
    grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
  }
  @media (min-width: $break-point-large) and (max-width: $break-point-x-large){
      grid-template-columns: 1fr 1fr 1fr 1fr;
  }

  @media (min-width: $break-point-tablet) and (max-width: $break-point-large) {
    grid-template-columns: 1fr 1fr 1fr;
  }

  @media (min-width: $break-point-mobile) and (max-width: $break-point-tablet) {
    grid-template-columns: 1fr 1fr;
  }

  @media (max-width: $break-point-mobile) {
    grid-template-columns: 1fr;
  }
}


================================================
FILE: client/scss/_claim-pending.scss
================================================
.claim-pending {
  display: inline-block;
  position: absolute;
  top: 10px;
  left: 10px;
  padding: 5px;
  border-radius: 5px;
  border: 2px solid red;
  color: red;
  font-weight: bold;
  background-color: white;
}


================================================
FILE: client/scss/_click-to-copy.scss
================================================
.click-to-copy-wrap {
  display: flex;
  width: 100%;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
  border: 1px solid $subtle-border-color;
  border-radius: 6px;
  .click-to-copy {
    border: none;
    padding: 0.36em 0.5em;
    margin: 0;
    background-color: transparent;
    width: calc(100% - 1em - 2px);
    font-size: 14px;
    letter-spacing: -0.6px;
    line-height: 20px;
    letter-spacing: 0;
    font-family: monospace;
    border-right: 1px solid $subtle-border-color;
  }
  .icon-wrap {
    width: 30px;
    height: 30px;
    line-height: 34px;
    text-align: center;
    svg {
      stroke: $primary-color;
      width: 16px;
      height: 16px;
    }
  }
}


================================================
FILE: client/scss/_dropzone.scss
================================================
.dropzone-wrapper {
  // fill the parent flex container
  flex: 1 0 auto;
  // be a flex container for children
  display: flex;
  flex-direction: column;
  position: relative;
  width: 100%;
  box-sizing: border-box;
  padding: 0px;
}

.dropzone {
  border: 2px dashed $drop-zone-border-color;
  // fill the parent flex container
  flex: 1 0 auto;
  // be a flex container for children
  display: flex;
  padding: $thin-padding;
  -webkit-flex-direction: column;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  user-select: none;
}

.dropzone:hover, .dropzone--active {
  border: 2px dashed $drop-zone-border-hover;
  cursor: pointer;
}

.dropzone-dropit-display, .dropzone-instructions-display {
  padding: 1em;
  text-align: center;
}

.dropzone-dropit-display
{
  color: $primary-color;
}

.dropzone-preview-wrapper {
  position: relative;
  width: 100%;
  .dropzone-preview-overlay {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    display: flex;
    -webkit-flex-direction: column;
    flex-direction: column;
    justify-content: center;
  }
}

.dropzone-preview-image {
  display: block;
  width: 100%;
}

.dropzone-preview-memeify {
  margin-top: 3em;
}

.dropzone-memeify-button {
  background: $primary-color;
  color: #fff;
  cursor: pointer;
  font-size: .8em;
  padding: 3px 6px;
  position: absolute;
  right: 0;
  top: 0;
  z-index: 3;
}

.dropzone-memeify-saveMessage {
  padding-top: .25em;
  position: relative;
  top: .5em;
}

.dropzone-memeify-toolbar {
  /* TODO: Cleanup `!important` */
  background: $primary-color !important;
  left: -1em !important;
  right: -1em !important;
  top: -4em !important;
}

.dropzone-instructions-display__chooser-label {
  text-decoration: underline;
}


================================================
FILE: client/scss/_form-feedback.scss
================================================
.form-feedback {
  padding-top: $thin-padding;
  padding-bottom: $thin-padding;
}

.form-feedback--default {
  color: $secondary-color;
}

.form-feedback--success {
  color: $success-color;
}

.form-feedback--failure {
  color: $failure-color;
}


================================================
FILE: client/scss/_form.scss
================================================
.form-group {
  padding-bottom: $secondary-padding;
}

.form-title {
  padding-bottom: $secondary-padding;
}


================================================
FILE: client/scss/_horizontal-split.scss
================================================
.horizontal-split {
  max-width: $width-content-constrained;
  width: 100%;
  display       : flex;
  flex-direction : row;
  justify-content: center;
  box-sizing: border-box;

  &.horizontal-split--mobile-collapse {
    @media (max-width: $break-point-tablet) {
      flex-direction: column;
      .horizontal-split__column {
      }
      .horizontal-split__column--left {
        padding-top: $thin-padding;
      }
      .horizontal-split__column--right {
        padding-top: $thin-padding;
      }
    }
  }
};

.horizontal-split__column {
  display       : flex;
  flex: 1 1 auto;
  box-sizing: border-box;
  width: 100%;
}

.horizontal-split__column--left {
  padding: $tertiary-padding;

  @media (max-width: $break-point-tablet) {
    padding-left: 0px;
    padding-right: 0px;
  }
}

.horizontal-split__column--right {

  padding: $tertiary-padding;

  @media (max-width: $break-point-tablet) {
    padding-left: 0px;
    padding-right: 0px;
  }
}

@media (max-width: $break-point-tablet) {

  .horizontal-split__column {
    justify-content: space-between;
  };

  .column {
    width: 100%;
    padding-left: 0;
    padding-right: 0;
    padding-bottom: $secondary-padding;
  }
}


================================================
FILE: client/scss/_html.scss
================================================
html {
  margin: 0;
  padding: 0;
  height: 100%;
}


================================================
FILE: client/scss/_input.scss
================================================
input:-webkit-autofill {
  -webkit-box-shadow: 0 0 0px 1000px white inset;
}

input {
  margin: 0;
  padding: $input-padding;
  border: 0;
  background-color: $background-color;
  display: inline-block;
  color: $text-color
}

.input-slider {
  width: 100%
}

.input-checkbox {
  border: 1px solid black;
  background: white;
}

.input-file {
  width: 0.1px;
  height: 0.1px;
  opacity: 0;
  overflow: hidden;
  position: absolute;
  z-index: -1;
}

.input-radio {
  cursor: pointer;
}

// input area wrapper

.input-area {
  border-bottom: 1px solid $secondary-color;
}

.form-group {
  padding-bottom: $secondary-padding;
}

// modifiers

.input--full-width {
  width: $input-full-width;
}


================================================
FILE: client/scss/_label.scss
================================================
.label {
  padding-top: $thin-padding;
  padding-bottom: $thin-padding;
  display: inline-block;
  font-weight: bold;
  font-size: $text-medium;
  width: 100%;
  box-sizing: border-box;
}

.label-radio {
  padding-left: $thin-padding;
  padding-right: $thin-padding;
  cursor: pointer;
  font-weight: bold;
}

@media (max-width: $break-point-tablet ) {

  // note: bolding break point lines up with row-label break point
  .label, .label-radio {
    font-weight: bold;
  }

}


================================================
FILE: client/scss/_link.scss
================================================
a, a:visited {
  text-decoration: none;
}

.link--primary, .link--primary:visited {
  color: $primary-color;
  &:hover { text-decoration: underline; }
}

.link--nav {
  color: $text-color;
  &:hover {
    color: $primary-color;
  }
}


.link--nav-active {
  border-bottom: 2px solid $primary-color;
}


================================================
FILE: client/scss/_markdown.scss
================================================
.markdown-preview {

  margin: $tertiary-padding 0px;

  h1,
  h2,
  h3 {
    font-size: inherit;
    font-weight: inherit;
    margin: $tertiary-padding 0px;
  }

  h4, h5, h6 {
    font-size: $text-large;
    font-weight: 600;
    margin: $tertiary-padding 0px;
  }

  // Paragraphs
  p {
    font-size: 1.15rem;
    white-space: pre-line;
    margin: $tertiary-padding 0px;

    svg {
      width: 1rem;
      height: 1rem;

      margin-left: 0.2rem;
      position: relative;
      top: 1px;
    }
  }

  blockquote {
    background: $blockquote-background;
    padding: $tertiary-padding;
    min-width: 60%;
    margin: $tertiary-padding;
    p:first-child{
      margin-top: 0px;
    }
    p:last-child {
      margin-bottom: 0px;
    }

    div {
      display: none;
    }
  }

  // Strikethrough text
  del {
  }

  // Tables
  table {
    width: 100%;
    background-color: $base-color;
    border-spacing: 0;
    border: .5px solid $chrome-color;
    margin: $tertiary-padding 0px;

    tr {
      td,
      th,
      td:first-of-type,
      th:first-of-type,
      td:last-of-type,
      th:last-of-type {
        padding: $thin-padding $tertiary-padding;
        text-overflow: ellipsis;
      }
      td:last-of-type {
        text-align: right;
      }

      th {
        background: $chrome-color;
      }

    }
    tr:nth-child(even){
      background: $chrome-color;
    }
  }

  // Image
  img {
    margin-bottom: $tertiary-padding;
    margin-top: $tertiary-padding;
    padding: $secondary-padding;
    object-fit: scale-down;
    box-sizing: border-box;
    display: block;
    margin-left: auto;
    margin-right: auto;
    max-width: 90vw;
  }

  iframe {
    display: block;
    margin-left: auto;
    margin-right: auto;
    max-width: 90vw;
    margin-top: $tertiary-padding 0px;
    margin-bottom: $tertiary-padding 0px;
  }

  // Horizontal Rule
  hr {
    width: 100%;
    height: 1px;

    background-color: $base-color;
    margin-bottom: 2rem;
    position: relative;
    top: 1rem;

    html[data-theme='dark'] & {
      background-color: rgba($base-color, 0.2);
    }
  }

  // Code
  pre {
    white-space: normal;
  }

  code {
    margin-bottom: $tertiary-padding;
    padding: $tertiary-padding;

    background-color: $subtle-border-color;
    color: $text-color;
    display: block;
    font-family: Consolas, 'Lucida Console', 'Source Sans', monospace;
  }

  a {
    color: $primary-color;
    display: inline;
  }

  // Lists
  ul,
  ol {
    margin-bottom: $thin-padding;

    > li {
      list-style-position: outside;
    }
  }

  ul {
    list-style: initial;
  }

  li {
    margin-left: $primary-padding;

    p {
      display: inline-block;
    }
  }
}


================================================
FILE: client/scss/_media-queries.scss
================================================
@media (max-width: $break-point-x-large) {
  // hide site description in nav bar
  .site-description {
    display: none;
  }

}

================================================
FILE: client/scss/_nav-bar.scss
================================================
.nav-bar {
  box-sizing: border-box;
  padding: $thin-padding $primary-padding;
  background: $chrome-color;
  flex: 0 1 auto;
  width: 100%;
  border-bottom: $subtle-border;
  color: $primary-color;

  @media (max-width: $break-point-mobile) {
    margin-left: 15px;
    margin-right: 15px;
  }
  input {
    background: $chrome-color;

  }
  select {
    background: $chrome-color;
    color: $text-color;
  }
}

.nav-bar-link {
  padding: calc(1em - 2px);
  display: inline-block;
  font-size: $text-medium;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}

.nav-bar-logo {
  cursor: pointer;
}

@media (max-width: $break-point-tablet ) {
  .nav-bar-link {
    padding-top: calc(1em - 2px);
    padding-right: 1em;
    padding-bottom: calc(1em - 2px);
    padding-left: 1em;
  }

}

@media (max-width: $break-point-mobile ) {
  .nav-bar-link {
    padding-top: calc(0.5em - 2px);
    padding-right: 0.5em;
    padding-bottom: calc(0.5em - 2px);
    padding-left: 0.5em;
  }
}


================================================
FILE: client/scss/_page-content.scss
================================================
.page-content {
  margin: $primary-padding;
  // fill the parent flex container
  flex: 1 0 auto;
  // be a flex container for children
  display: flex;
  -webkit-flex-direction: column;
  flex-direction: column;
};


================================================
FILE: client/scss/_page-layout-show-lite.scss
================================================
.page-layout-show-lite {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  .content {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
  }
  .footer {
    flex: 0 1 auto;
  }
}


================================================
FILE: client/scss/_page-layout.scss
================================================
.page-layout {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 100%;
  .content {
    flex: 1 0 auto;
    display: flex;
    -webkit-flex-direction: column;
    flex-direction: column;
    width: 100%;
    align-items: center;
    box-sizing: border-box;
    background: $base-color;

    @media (min-width: $break-point-tablet) {
      padding: $primary-padding;
    }

    @media (max-width: $break-point-tablet) {
      padding: $tertiary-padding;
    }
  }
}


================================================
FILE: client/scss/_progress-bar.scss
================================================
.progress-bar__wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
}
.progress-bar--inactive {
  color: $grey;
}

.progress-bar--active {
  color: $primary-color;
}


================================================
FILE: client/scss/_publish-disabled-message.scss
================================================
.publish-disabled-message {
  // fill the parent flex container
  flex: 1 0 auto;
  // be a flex container for children
  display: flex;
  flex-direction: column;
  justify-content: center;
  .message {
    text-align: center;
  }
}


================================================
FILE: client/scss/_publish-preview.scss
================================================
.publish-form__title {
  max-width: $width-content-constrained;
  margin-left: auto;
  margin-right: auto;

  @media (max-width: $break-point-mobile) {
    font-size: .8em;
  }
}

.publish-preview-dim {
  opacity: 0.2;
}


================================================
FILE: client/scss/_publish-status.scss
================================================
.publish-status {
  // fill the parent flex container
  flex: 1 0 auto;
  // be a flex container for children
  display: flex;
  flex-direction: column;
  justify-content: center;
  .status {
    text-align: center;
  }
}


================================================
FILE: client/scss/_publish-url-input.scss
================================================
.publish-url-input {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: flex-start;
  align-items: baseline;
  border-bottom: solid 1px grey;
  .shrink {
    flex: 0 1 auto;
  };
  .fill {
    flex: 1 0 auto;
  };
}


.publish-url-text {
  margin: 0;
  padding: 0;
  color: $help-color;
}


================================================
FILE: client/scss/_react-app.scss
================================================
#react-app {
  flex: 1 0 auto;
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: column;
  flex-direction: column;
}


================================================
FILE: client/scss/_reset.scss
================================================
button, input, textarea, label, select, option {
  font-family: inherit;
  font-size: inherit;
}

================================================
FILE: client/scss/_row.scss
================================================
.row {
  margin-bottom: 1.2em;
}

.row-labeled {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  justify-content: flex-start;
  padding-bottom: $tertiary-padding;
}

.row-labeled-label {
  width: 100%;
  display: flex;
  align-items: center;
  flex: 1;
}
.row-labeled-content {
  align-self: center;
  width: 100%;
}

@media (max-width: $break-point-tablet ) {
  .row-labeled {
    flex-direction: column;
  }
  .row-labeled-label {
    width: 100%;
  }
  .row-labeled-content {
    width: 100%;
  }
}


================================================
FILE: client/scss/_select.scss
================================================
select {
  margin: 0;
  display: inline-block;
  background: $background-color;
  border: 0;
  color: $text-color;
}


================================================
FILE: client/scss/_share-buttons.scss
================================================
.share-buttons {
  display: flex;
  align-items: center;
  
  a {
    display: block;
    width: 30px;
    height: 30px;
    margin: 0 7px;
    border-radius: 100%;
    line-height: 30px;
    text-align: center;
    transition: all 0.2s ease;
    &.twitter {
      background:#4DC2FE;
      img {
        margin-top: 8px;
        margin-left: 2px;
      }
    }
    
    &.facebook {
      background: #5487DE;
      img {
        margin-top: 6px;
      }
    }
    
    &.tumblr {
      background: #274061;
      img {
        margin-top: 7px;
      }
    }
    
    &.reddit {
      background: #FF4500;
      img {
        margin-top: 7px;
      }
    }
    
    &:first-child{
      margin-left: 0px;
    }
    
    &:hover {
      background: $primary-color;
    }
  }
}


================================================
FILE: client/scss/_social-share-link.scss
================================================
.social-share-link {
  flex-wrap: wrap;
  margin-right: -0.5em;
  margin-left: -0.5em;
}

.social-share-link > a{
  padding-right:0.5em;
  padding-left:0.5em;
}

================================================
FILE: client/scss/_space-around.scss
================================================
.space-around {
  display: flex;
  justify-content: space-around;
  align-items: center;
}


================================================
FILE: client/scss/_space-between.scss
================================================
.space-between {
  display: flex;
  justify-content: space-between;
  align-items: center;
}


================================================
FILE: client/scss/_text.scss
================================================
// set defaults

h1, h2, h3, h4, p {
  margin: 0;
}

body {
  color: $text-color;
  font-family: 'Circular', serif;
  font-size: 14px;
}

body a {
  color: $primary-color;
}

h1 {
 font-size: $text-xx-large;
}

h2 {
  font-size: $text-x-large;
}

h3 {
  font-size: $text-large;
}

.text--extra-large {
  font-size: $text-xx-large;
}

.text--large {
  font-size: $text-large;
}

.text--medium {
  font-size: $text-medium;
}

.text--small {
  font-size: $text-small;
}

.text--extra-small {
  font-size: $text-x-small;
}

.text--secondary {
  color: $help-color;
}

.text--interactive {
  color: $primary-color;
}

.text--failure {
  color: $failure-color;
}

.text--success {
  color: $success-color;
}


================================================
FILE: client/scss/_textarea.scss
================================================
textarea {
  margin: 0;
  padding: $input-padding;
  display: inline-block;
  width: $input-full-width;
}


================================================
FILE: client/scss/_tooltip.scss
================================================
/* Tooltip container */
.tooltip {
  position: relative;
}
/* Tooltip text */
.tooltip > .tooltip-text {
  visibility: hidden;
  width: 15em;
  background-color: #9b9b9b;
  color: #fff;
  text-align: center;
  padding: 0.5em;
  /* Position the tooltip text */
  position: absolute;
  z-index: 1;
  bottom: 110%;
  left: 50%;
  margin-left: -8em; /* Use half of the width (120/2 = 60), to center the tooltip */
}
/* Show the tooltip text when you mouse over the tooltip container */
.tooltip:hover > .tooltip-text {
  visibility: visible;
}
/* arrow at bottom of tooltip text */
.tooltip > .tooltip-text::after {
  content: " ";
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: #9b9b9b transparent transparent transparent;
}


================================================
FILE: client/scss/_variables.scss
================================================
//backgrounds
$base-color: white; //default white
$card-color: white; //default white
$chrome-color: white; //default white (navbar)
$blockquote-background: #EEEEFF;
$background-color: $base-color;

//text colors
$primary-color: #005da0; //link default light blue #005da0
$secondary-color: $primary-color;
$text-color: #333;
$success-color: green;
$failure-color: red;
$grey: #9095A5;
$blockquote-text: $text-color;


//borders and highlights
$grey: #9095A5;
$help-color: $grey;
$subtle-border-color: #DDD;
$highlight-border-color: #777;
$shadow-color: rgba(169, 173, 186, 0.2);
$subtle-border: 1px dashed $subtle-border-color;
$grey-border: $subtle-border-color; //factor this out for all customers
$drop-zone-border-color: #9b9b9b; //default  #9b9b9b
$drop-zone-border-hover: #4156C5; //default  #4156C5

//padding
$primary-padding: 3em;
$secondary-padding: 2em;
$tertiary-padding: 1em;
$thin-padding: 0.3em;
$full-width-thin-padding: calc(100% - 0.6em);
$input-padding: 0.3em;

$width-content-constrained: 1000px;

$button-border-width: 1px;
$button-border-strength: solid;
$button-full-width: calc(100% - 2px);

$input-full-width: calc(100% - 0.6em);

//text sizes
$base-font-size: 14px;

$text-xx-large: 2.5em;
$text-x-large: 2.0em;
$text-large: 1.5em;
$text-medium: 1.2em;
$text-small: 0.9em;
$text-x-small: 0.8em;

//@media sizes
$break-point-xx-large: 1400px;
$break-point-x-large: 1290px;
$break-point-large: 1024px;
$break-point-tablet: 800px;
$break-point-mobile: 500px;
$break-point-phone: 300px;
$break-point-phone: 300px;


================================================
FILE: client/scss/_video.scss
================================================
video:-moz-full-screen {
  border:none;
  padding:0;
}
video:-webkit-full-screen {
  border:none;
  padding:0;
}
video:fullscreen {
  border:none;
  padding:0;
}


================================================
FILE: client/scss/all.scss
================================================
@import '~scss/_variables';
@import '~scss/_reset';
@import '~scss/font/_font.scss';
@import '~scss/_html';
@import '~scss/_body';
@import '~scss/_react-app';
@import '~scss/_text';
@import '~scss/_markdown';

@import '~scss/_link';
@import '~scss/_input';
@import '~scss/_select';
@import '~scss/_textarea';
@import '~scss/_video';
@import '~scss/_form';

@import '~scss/_asset-display';
@import '~scss/_asset-preview';
@import '~scss/_asset-blocked';
@import '~scss/_button';
@import '~scss/_button-primary';
@import '~scss/_button-secondary';
@import '~scss/_claim-pending';
@import '~scss/_click-to-copy';
@import '~scss/_form-feedback';
@import '~scss/_horizontal-split';
@import '~scss/_label';
@import '~scss/_nav-bar';
@import '~scss/_page-layout';
@import '~scss/_page-layout-show-lite';
@import '~scss/_page-content';
@import '~scss/_progress-bar';
@import '~scss/_publish-preview';
@import '~scss/_share-buttons';
@import '~scss/_space-between';
@import '~scss/_space-around';
@import '~scss/_row';
@import '~scss/_tooltip';
@import '~scss/_social-share-link';

@import '~scss/_channel-claims-display';
@import '~scss/_dropzone';
@import '~scss/_publish-url-input';
@import '~scss/_publish-status';
@import '~scss/_publish-disabled-message';

@import '~scss/_media-queries';


================================================
FILE: client/scss/font/Lekton/OFL.txt
================================================
Copyright (c) 2008-2010, Isia Urbino (http://www.isiaurbino.net)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.


================================================
FILE: client/scss/font/_font.scss
================================================
@font-face {
  font-family: 'Lekton';
  src: url('./font/Lekton/Lekton-Regular.ttf');
}

@font-face {
  font-family: 'Lekton';
  src: url('./font/Lekton/Lekton-Bold.ttf');
  font-weight: bold;
  font-style: normal;
}

@font-face {
  font-family: 'Lekton';
  src: url('./font/Lekton/Lekton-Italic.ttf');
  font-weight: normal;
  font-style: italic;
}


@font-face {
  font-family: 'Circular';
  src: url('./font/Circular/CircularStd-Book.ttf');
  font-weight: normal;
}

@font-face {
  font-family: 'Circular';
  src: url('./font/Circular/CircularStd-Bold.ttf');
  font-weight: bold;
}

================================================
FILE: client/src/actions/channel.js
================================================
import * as actions from '../constants/channel_action_types';

// export action creators

export function updateLoggedInChannel (name, shortId, longId) {
  return {
    type: actions.CHANNEL_UPDATE,
    data: {
      name,
      shortId,
      longId,
    },
  };
}

export function checkForLoggedInChannel () {
  return {
    type: actions.CHANNEL_LOGIN_CHECK,
  };
}

export function logOutChannel () {
  return {
    type: actions.CHANNEL_LOGOUT,
  };
}


================================================
FILE: client/src/actions/channelCreate.js
================================================
import * as actions from '../constants/channel_create_action_types';

// export action creators

export function updateChannelCreateName (name, value) {
  return {
    type: actions.CHANNEL_CREATE_UPDATE_NAME,
    data: {
      name,
      value,
    },
  };
}

export function updateChannelCreatePassword (name, value) {
  return {
    type: actions.CHANNEL_CREATE_UPDATE_PASSWORD,
    data: {
      name,
      value,
    },
  };
}

export function updateChannelCreateStatus (status) {
  return {
    type: actions.CHANNEL_CREATE_UPDATE_STATUS,
    data: status,
  };
}

export function updateChannelAvailability (channel) {
  return {
    type: actions.CHANNEL_AVAILABILITY,
    data: channel,
  };
}

export function createChannel () {
  return {
    type: actions.CHANNEL_CREATE,
  };
}


================================================
FILE: client/src/actions/index.js
================================================
// import { } from './channel';
// import { } from './publish';
import { onHandleShowPageUri } from './show';

export default {
  onHandleShowPageUri,
};


================================================
FILE: client/src/actions/publish.js
================================================
import * as actions from '../constants/publish_action_types';

// export action creators
export function selectFile (file) {
  return {
    type: actions.FILE_SELECTED,
    data: file,
  };
}

export function clearFile () {
  return {
    type: actions.FILE_CLEAR,
  };
}

export function setUpdateTrue () {
  return {
    type: actions.SET_UPDATE_TRUE,
  };
}

export function setHasChanged (status) {
  return {
    type: actions.SET_HAS_CHANGED,
    data: status,
  };
}

export function updateMetadata (name, value) {
  return {
    type: actions.METADATA_UPDATE,
    data: {
      name,
      value,
    },
  };
}

export function updateClaim (value) {
  return {
    type: actions.CLAIM_UPDATE,
    data: value,
  };
};

export function abandonClaim (data) {
  return {
    type: actions.ABANDON_CLAIM,
    data,
  };
};

export function setPublishInChannel (channel) {
  return {
    type: actions.SET_PUBLISH_IN_CHANNEL,
    channel,
  };
}

export function updatePublishStatus (status, message) {
  return {
    type: actions.PUBLISH_STATUS_UPDATE,
    data: {
      status,
      message,
    },
  };
}

export function updateError (name, value) {
  return {
    type: actions.ERROR_UPDATE,
    data: {
      name,
      value,
    },
  };
}

export function updateSelectedChannel (channelName) {
  return {
    type: actions.SELECTED_CHANNEL_UPDATE,
    data: channelName,
  };
}

export function toggleMetadataInputs (showMetadataInputs) {
  return {
    type: actions.TOGGLE_METADATA_INPUTS,
    data: showMetadataInputs,
  };
}

export function onNewThumbnail (file) {
  return {
    type: actions.THUMBNAIL_NEW,
    data: file,
  };
}

export function startPublish (history) {
  return {
    type: actions.PUBLISH_START,
    data: { history },
  };
}

export function validateClaim (claim) {
  return {
    type: actions.CLAIM_AVAILABILITY,
    data: claim,
  };
}


================================================
FILE: client/src/actions/show.js
================================================
import * as actions from '../constants/show_action_types';
import { ASSET_DETAILS, ASSET_LITE, CHANNEL, SPECIAL_ASSET } from '../constants/show_request_types';

// basic request parsing
export function onHandleShowPageUri(params, url) {
  return {
    type: actions.HANDLE_SHOW_URI,
    data: {
      ...params,
      url,
    },
  };
}

export function onHandleShowHomepage(params, url) {
  return {
    type: actions.HANDLE_SHOW_HOMEPAGE,
    data: {
      ...params,
      url,
    },
  };
}

export function onRequestError(error) {
  return {
    type: actions.REQUEST_ERROR,
    data: error,
  };
}

export function onNewChannelRequest(channelName, channelId) {
  const requestType = CHANNEL;
  const requestId = `cr#${channelName}#${channelId}`;
  return {
    type: actions.CHANNEL_REQUEST_NEW,
    data: { requestType, requestId, channelName, channelId },
  };
}

export function onNewSpecialAssetRequest(name) {
  const requestType = SPECIAL_ASSET;
  const requestId = `sar#${name}`;
  return {
    type: actions.SPECIAL_ASSET_REQUEST_NEW,
    data: { requestType, requestId, name, channelName: name, channelId: name },
  };
}

export function onNewAssetRequest(name, id, channelName, channelId, extension) {
  const requestType = extension ? ASSET_LITE : ASSET_DETAILS;
  const requestId = `ar#${name}#${id}#${channelName}#${channelId}`;
  return {
    type: actions.ASSET_REQUEST_NEW,
    data: {
      requestType,
      requestId,
      name,
      modifier: {
        id,
        channel: {
          name: channelName,
          id: channelId,
        },
      },
    },
  };
}

export function onRequestUpdate(requestType, requestId) {
  return {
    type: actions.REQUEST_UPDATE,
    data: {
      requestType,
      requestId,
    },
  };
}

export function addRequestToRequestList(id, error, key) {
  return {
    type: actions.REQUEST_LIST_ADD,
    data: { id, error, key },
  };
}

// asset actions

export function addAssetToAssetList(id, error, name, claimId, shortId, claimData, claimViews) {
  return {
    type: actions.ASSET_ADD,
    data: { id, error, name, claimId, shortId, claimData, claimViews },
  };
}

export function updateAssetViewsInList(id, claimId, claimViews) {
  return {
    type: actions.ASSET_VIEWS_UPDATE,
    data: { id, claimId, claimViews },
  };
}

export function removeAsset(data) {
  return {
    type: actions.ASSET_REMOVE,
    data,
  };
}

// channel actions

export function addNewChannelToChannelList(id, name, shortId, longId, claimsData) {
  return {
    type: actions.CHANNEL_ADD,
    data: {
      id,
      name,
      shortId,
      longId,
      claimsData,
    },
  };
}

export function onUpdateChannelClaims(channelKey, name, longId, page) {
  return {
    type: actions.CHANNEL_CLAIMS_UPDATE_ASYNC,
    data: { channelKey, name, longId, page },
  };
}

export function updateChannelClaims(channelListId, claimsData) {
  return {
    type: actions.CHANNEL_CLAIMS_UPDATE_SUCCEEDED,
    data: { channelListId, claimsData },
  };
}

// display a file

export function fileRequested(name, claimId) {
  return {
    type: actions.FILE_REQUESTED,
    data: { name, claimId },
  };
}

export function updateFileAvailability(status) {
  return {
    type: actions.FILE_AVAILABILITY_UPDATE,
    data: status,
  };
}

export function updateDisplayAssetError(error) {
  return {
    type: actions.DISPLAY_ASSET_ERROR,
    data: error,
  };
}

// viewer settings
export function toggleDetailsExpanded(isExpanded) {
  return {
    type: actions.TOGGLE_DETAILS_EXPANDED,
    data: isExpanded,
  };
}


================================================
FILE: client/src/api/assetApi.js
================================================
import Request from '../utils/request';

export function getLongClaimId(host, name, modifier) {
  let body = {};
  // create request params
  if (modifier) {
    if (modifier.id) {
      body['claimId'] = modifier.id;
    } else {
      body['channelName'] = modifier.channel.name;
      body['channelClaimId'] = modifier.channel.id;
    }
  }
  body['claimName'] = name;
  const params = {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  };
  // create url
  const url = `${host}/api/claim/long-id`;
  // return the request promise
  return Request(url, params);
}

export function getShortId(host, name, claimId) {
  const url = `${host}/api/claim/short-id/${claimId}/${name}`;
  return Request(url);
}

export function getClaimData(host, name, claimId) {
  const url = `${host}/api/claim/data/${name}/${claimId}`;
  return Request(url);
}

export function checkClaimAvailability(claim) {
  const url = `/api/claim/availability/${claim}`;
  return Request(url);
}

export function getClaimViews(claimId) {
  const url = `/api/claim/views/${claimId}`;
  return Request(url);
}

export function doAbandonClaim(outpoint) {
  const params = {
    method: 'POST',
    body: JSON.stringify({ outpoint }),
    headers: new Headers({
      'Content-Type': 'application/json',
    }),
    credentials: 'include',
  };
  return Request('/api/claim/abandon', params);
}


================================================
FILE: client/src/api/authApi.js
================================================
import Request from '../utils/request';

export function checkForLoggedInChannelApi () {
  const url = `/user`;
  const params = {credentials: 'include'};
  return Request(url, params);
}

export function channelLogoutApi () {
  const url = `/logout`;
  const params = {credentials: 'include'};
  return Request(url, params);
}


================================================
FILE: client/src/api/channelApi.js
================================================
import Request from '../utils/request';

export function getChannelData (host, name, id) {
  if (!id) id = 'none';
  const url = `${host}/api/channel/data/${name}/${id}`;
  return Request(url);
}

export function getChannelClaims (host, name, longId, page) {
  if (!page) page = 1;
  const url = `${host}/api/channel/claims/${name}/${longId}/${page}`;
  return Request(url);
}

export function checkChannelAvailability (channel) {
  const url = `/api/channel/availability/${channel}`;
  return Request(url);
}

export function makeCreateChannelRequest (username, password) {
  const params = {
    method : 'POST',
    body   : JSON.stringify({username, password}),
    headers: new Headers({
      'Content-Type': 'application/json',
    }),
    credentials: 'include',
  };
  return Request('/signup', params);
}


================================================
FILE: client/src/api/fileApi.js
================================================
import Request from '../utils/request';

export function checkFileAvailability (claimId, host, name) {
  const url = `${host}/api/file/availability/${name}/${claimId}`;
  return Request(url);
}

export function triggerClaimGet (claimId, host, name) {
  const url = `${host}/api/claim/get/${name}/${claimId}`;
  return Request(url);
}


================================================
FILE: client/src/api/homepageApi.js
================================================
import Request from '../utils/request';

export function getHomepageChannelsData (host, name, id) {
  const url = `${host}/api/homepage/data/channels`;
  return Request(url);
}


================================================
FILE: client/src/api/specialAssetApi.js
================================================
import Request from '../utils/request';

export function getSpecialAssetClaims (host, name, page) {
  if (!page) page = 1;
  const url = `${host}/api/special/${name}/${page}`;
  return Request(url);
}


================================================
FILE: client/src/app.js
================================================
import React from 'react';
import { hot } from 'react-hot-loader/root'
import { Route, Switch } from 'react-router-dom';

import HomePage  from '@pages/HomePage';
import AboutPage from '@pages/AboutPage';
import TosPage from '@pages/TosPage';
import FaqPage from '@pages/FaqPage';
import LoginPage from '@pages/LoginPage';
import ContentPageWrapper from '@pages/ContentPageWrapper';
import FourOhFourPage from '@pages/FourOhFourPage';
import MultisitePage from '@pages/MultisitePage';
import PopularPage from '@pages/PopularPage';
import EditPage from '@pages/EditPage';

const App = () => {
  return (
    <Switch>
      <Route exact path='/' component={AboutPage} />
      <Route exact path='/about' component={AboutPage} />
      <Route exact path='/tos' component={TosPage} />
      <Route exact path='/faq' component={FaqPage} />
      <Route exact path='/login' component={LoginPage} />
      <Route exact path='/multisite' component={MultisitePage} />
      <Route exact path='/popular' component={PopularPage} />
      <Route exact path='/edit/:identifier/:claim' component={EditPage} />
      <Route exact path='/:identifier/:claim' component={ContentPageWrapper} />
      <Route exact path='/:claim' component={ContentPageWrapper} />
      <Route component={FourOhFourPage} />
    </Switch>
  );
};

export default hot(App);


================================================
FILE: client/src/channels/publish.js
================================================
import {buffers, END, eventChannel} from 'redux-saga';

export const makePublishRequestChannel = (fd, isUpdate) => {
  return eventChannel(emitter => {
    const uri = `/api/claim/${isUpdate ? 'update' : 'publish'}`;
    const xhr = new XMLHttpRequest();
    // add event listeners
    const onLoadStart = () => {
      emitter({loadStart: true});
    };
    const onProgress = (event) => {
      if (event.lengthComputable) {
        const percentage = Math.round((event.loaded * 100) / event.total);
        emitter({progress: percentage});
      }
    };
    const onLoad = () => {
      emitter({load: true});
    };
    xhr.upload.addEventListener('loadstart', onLoadStart);
    xhr.upload.addEventListener('progress', onProgress);
    xhr.upload.addEventListener('load', onLoad);
    // set state change handler
    xhr.onreadystatechange = () => {
      if (xhr.readyState === XMLHttpRequest.DONE) {
        switch (xhr.status) {
          case 413:
            emitter({error: new Error("Unfortunately it appears this web server " +
              "has been misconfigured, please inform the service administrators " +
              "that they must set their nginx/apache request size maximums higher " +
              "than their file size limits.")});
            emitter(END);
            break;
          case 200:
            var response = JSON.parse(xhr.response);
            if (response.success) {
              emitter({success: response});
              emitter(END);
            } else {
              emitter({error: new Error(response.message)});
              emitter(END);
            }
            break;
          default:
            emitter({error: new Error("Received an unexpected response from " +
              "server: " + xhr.status)});
            emitter(END);
        }
      }
    };
    // open and send
    xhr.open('POST', uri, true);
    xhr.send(fd);
    // clean up
    return () => {
      xhr.upload.removeEventListener('loadstart', onLoadStart);
      xhr.upload.removeEventListener('progress', onProgress);
      xhr.upload.removeEventListener('load', onLoad);
      xhr.onreadystatechange = null;
      xhr.abort();
    };
  }, buffers.sliding(2));
};


================================================
FILE: client/src/components/AboutSpeechDetails/index.jsx
================================================
import React from 'react';
import Row from '@components/Row';

const AboutSpeechDetails = () => {
  return (
    <div>
      <Row>
        <p className={'text--large'}>
          Spee.ch's journey may be on hold, but LBRY is still on mission. We'd like to thank all of our testers and early adopters for helping us explore this use case.
          We're really excited about <a className='link--primary' href='https://lbry.tv' target='_blank'>lbry.tv</a> and can't wait to see you over there for a fully featured experience.
        </p>
      </Row>
    </div>
  );
};

export default AboutSpeechDetails;


================================================
FILE: client/src/components/AboutSpeechOverview/index.jsx
================================================
import React from 'react';
import Row from '@components/Row';

const AboutSpeechOverview = () => {
  return (
    <div>
      <Row>
        <p className={'text--extra-large'}>Lbry is no longer supporting Spee.ch. However, we're excited to show you <a className='link--primary' href='https://lbry.tv' target='_blank'>lbry.tv</a>!</p>
      </Row>
      <Row>
        <div className={'text--large'}>
          <a className='link--primary' target='_blank' href='https://twitter.com/lbry'>TWITTER</a><br/>
          <a className='link--primary' target='_blank' href='https://github.com/lbryio/'>GITHUB</a><br/>
          <a className='link--primary' target='_blank' href='https://discord.gg/YjYbwhS'>DISCORD CHANNEL</a><br/>
        </div>
      </Row>
    </div>
  );
};

export default AboutSpeechOverview;


================================================
FILE: client/src/components/ActiveStatusBar/index.jsx
================================================
import React from 'react';

const ActiveStatusBar = () => {
  return <span className='progress-bar--active'>| </span>;
};

export default ActiveStatusBar;


================================================
FILE: client/src/components/AssetInfoFooter/index.js
================================================
import React from 'react';
import Row from '@components/Row';

const AssetInfoFooter = ({ assetUrl, name }) => {
  return (
    <div className="asset-footer">
      <p>
        Hosted via the{' '}
        <a className={'link--primary'} href={'https://lbry.com/get'} target={'_blank'}>
          LBRY
        </a>{' '}
        blockchain
      </p>
    </div>
  );
};

export default AssetInfoFooter;


================================================
FILE: client/src/components/AssetPreview/index.jsx
================================================
import React from 'react';
import { Link } from 'react-router-dom';
import createCanonicalLink from '@globalutils/createCanonicalLink';
import * as Icon from 'react-feather';
import Img from 'react-image';

const AssetPreview = ({ defaultThumbnail, claimData }) => {
  const {name, fileExt, contentType, thumbnail, title, blocked, transactionTime = 0} = claimData;
  const showUrl = createCanonicalLink({asset: {...claimData}});
  const embedUrl = `${showUrl}.${fileExt}`;
  const ago = Date.now() / 1000 - transactionTime;
  const dayInSeconds = 60 * 60 * 24;
  const monthInSeconds = dayInSeconds * 30;
  let when;

  if (ago < dayInSeconds || transactionTime < 1) {
    when = 'Just today';
  } else if (ago < monthInSeconds) {
    when = `${Math.floor(ago / dayInSeconds)} d ago`;
  } else {
    when = `${Math.floor(ago / monthInSeconds)} mo ago`;
  }
  /*
  we'll be assigning media icon based on supported type / mime types
  */
  const media = contentType.split('/')[0];
  /*
  make sure thumb has the right url
   */
  const thumb = media === 'image' ? embedUrl : thumbnail;
  /*
  This blocked section shouldn't be necessary after pagination is reworked,
  though it might be useful for channel_mine situations.
  */

  if (blocked) {
    return (
      <div className='asset-preview'>
        <div className='asset-preview__blocked'>
          <p>Error 451</p>
          <p>This content is blocked for legal reasons.</p>
        </div>
        <div className={'asset-preview__label'}>
          <div className={'asset-preview__label-text'}>
            <p className='asset-preview__title text--medium'>Blocked Content</p>
          </div>
        </div>
      </div>
    );
  } else {
    return (
      <Link to={showUrl} className='asset-preview'>
        <div className='asset-preview__image-box'>
          <Img
            src={[
              thumb,
              defaultThumbnail,
              '/assets/img/default_thumb.jpg',
            ]}
            alt={name}
            className={'asset-preview__image'}
          />
        </div>

        <div className={'asset-preview__label'}>

          <div className={'asset-preview__label-text'}>
            <p>{title}</p>
          </div>
          <div className={'asset-preview__label-info '}>
            <div className={'asset-preview__label-info-datum'}>
              <div className={'svg-icon'}>
                { media === 'image' && <Icon.Image />}
                { media === 'text' && <Icon.FileText />}
                { media === 'video' && contentType === 'video/mp4' && <Icon.Video />}
                { media !== 'image' && media !== 'text' && contentType !== 'video/mp4' && <Icon.File />}
              </div>
              <div>{fileExt}</div>
            </div>

            <div className={'asset-preview__label-info-datum'}>
              <div>{when}</div>
            </div>
          </div>
        </div>
      </Link>
    );
  }
};

export default AssetPreview;


================================================
FILE: client/src/components/AssetShareButtons/index.js
================================================
import React from 'react';
import SocialShareLink from '@components/SocialShareLink';

const AssetShareButtons = ({ assetUrl, name }) => {
  return (
    <SocialShareLink >
      <a
        className='link--primary twitter'
        target='_blank'
        href={`https://twitter.com/intent/tweet?text=${assetUrl}`}
      >
        <img src='/assets/img/icn_twitter.svg' />
      </a>
      <a
        className='link--primary facebook'
        target='_blank'
        href={`https://www.facebook.com/sharer/sharer.php?u=${assetUrl}`}
      >
        <img src='/assets/img/icn_facebook.svg' />
      </a>
      <a
        className='link--primary tumblr'
        target='_blank'
        href={`https://tumblr.com/widgets/share/tool?canonicalUrl=${assetUrl}`}
      >
        <img src='/assets/img/icn_tumblr.svg' />
      </a>
      <a
        className='link--primary reddit'
        target='_blank'
        href={`https://www.reddit.com/submit?url=${assetUrl}&title=${name}`}
      >
        <img src='/assets/img/icn_reddit.svg' />
      </a>
    </SocialShareLink>
  );
};
//
// Additional icons disabled. If you want to add additional icons, you have to solve
// https://github.com/lbryio/spee.ch/issues/687
//
// <a
//   className='link--primary'
//   target='_blank'
//   href={`https://sharetomastodon.github.io/?title=${name}&url=${assetUrl}`}
// >
//   mastodon
// </a>
// <a
//   className='link--primary'
//   target='_blank'
//   href={`https://share.diasporafoundation.org/?title=${name}&url=${assetUrl}`}
// >
//   diaspora
// </a>

export default AssetShareButtons;


================================================
FILE: client/src/components/ButtonPrimary/index.jsx
================================================
import React from 'react';

const ButtonPrimary  = ({ value, onClickHandler, type = 'button' }) => {
  return (
    <button
      type={type}
      className={'button button--primary'}
      onClick={onClickHandler}
    >
      {value}
    </button>
  );
};

export default ButtonPrimary;


================================================
FILE: client/src/components/ButtonPrimaryJumbo/index.jsx
================================================
import React from 'react';

const ButtonPrimaryJumbo  = ({ value, onClickHandler }) => {
  return (
    <button
      className={'button button--primary button--jumbo'}
      onClick={onClickHandler}
    >
      {value}
    </button>
  );
};

export default ButtonPrimaryJumbo;


================================================
FILE: client/src/components/ButtonSecondary/index.jsx
================================================
import React from 'react';

const ButtonPrimary  = ({ value, onClickHandler }) => {
  return (
    <button
      className={'button button--secondary'}
      onClick={onClickHandler}
    >
      {value}
    </button>
  );
};

export default ButtonPrimary;


================================================
FILE: client/src/components/ChannelAbout/index.jsx
================================================
import React from 'react';

const ChannelAbout = () => {
  return (
    <div className={'text--large'}>
      <p>Channels allow you to publish and group content under an identity. You can create a channel for yourself, or share one with like-minded friends.</p>
      <p>You can create 1 channel, or 100, so whether you're <a className='link--primary' target='_blank' href='/@catalonia2017:43dcf47163caa21d8404d9fe9b30f78ef3e146a8'>documenting important events</a>, or making a public repository for <a className='link--primary' target='_blank' href='/@catGifs'>cat gifs</a> (password: '1234'), try creating a channel for it!</p>
    </div>
  );
};

export default ChannelAbout;


================================================
FILE: client/src/components/ChannelCreateNameInput/index.jsx
================================================
import React from 'react';
import Label from '@components/Label';
import RowLabeled from '@components/RowLabeled';

const ChannelCreateNameInput  = ({ value, error, handleNameInput }) => {
  return (
    <RowLabeled
      label={
        <Label value={'Name:'} />
      }
      content={
        <div className='input-area'>
          <span>@</span>
          <input
            type='text'
            name='channel'
            className='input-text'
            placeholder='exampleChannelName'
            value={value}
            onChange={handleNameInput}
          />
          { (value && !error) && (
            <span className='info-message--success span--absolute'>
              {'\u2713'}
            </span>
          )}
          { error && (
            <span className='info-message--failure span--absolute'>
              {'\u2716'}
            </span>
          )}
        </div>
      }
    />
  );
};

export default ChannelCreateNameInput;


================================================
FILE: client/src/components/ChannelCreatePasswordInput/index.jsx
================================================
import React from 'react';
import Label from '@components/Label';
import RowLabeled from '@components/RowLabeled';

const ChannelCreatePasswordInput  = ({ value, handlePasswordInput }) => {
  return (
    <RowLabeled
      label={
        <Label value={'Password:'} />
      }
      content={
        <div className='input-area'>
          <input
            type='password'
            name='password'
            className='input-text'
            placeholder=''
            value={value}
            onChange={handlePasswordInput} />
        </div>
      }
    />
  );
};

export default ChannelCreatePasswordInput;


================================================
FILE: client/src/components/ChannelInfoDisplay/index.jsx
================================================
import React from 'react';
// TODO: factor out longId OR implement tooltip display
const ChannelInfoDisplay = ({name, longId, shortId}) => {
  return (
    <div>
      <h2>{name}:{shortId}</h2>
    </div>
  );
};

export default ChannelInfoDisplay;


================================================
FILE: client/src/components/ChannelLoginNameInput/index.jsx
================================================
import React from 'react';
import RowLabeled from '@components/RowLabeled';
import Label from '@components/Label';

const ChannelLoginNameInput  = ({ channelName, handleInput }) => {
  return (
    <RowLabeled
      label={
        <Label value={'Name:'} />
      }
      content={
        <div className='input-area'>
          <span>@</span>
          <input
            type='text'
            id='channel-login-name-input'
            className='input-text'
            name='name'
            placeholder='Your Channel Name'
            value={channelName}
            onChange={handleInput}
          />
        </div>
      }
    />
  );
};

export default ChannelLoginNameInput;


================================================
FILE: client/src/components/ChannelLoginPasswordInput/index.jsx
================================================
import React from 'react';
import RowLabeled from '@components/RowLabeled';
import Label from '@components/Label';

const ChannelLoginPasswordInput  = ({ channelPassword, handleInput }) => {
  return (
    <RowLabeled
      label={
        <Label value={'Password:'} />
      }
      content={
        <div className='input-area'>
          <input
            type='password'
            id='channel-login-password-input'
            name='password'
            className='input-text'
            placeholder=''
            value={channelPassword}
            onChange={handleInput}
          />
        </div>
      }
    />
  );
};

export default ChannelLoginPasswordInput;


================================================
FILE: client/src/components/ChannelSelectDropdown/index.jsx
================================================
import React from 'react';
import { LOGIN, CREATE } from '../../constants/publish_channel_select_states';

const ChannelSelectDropdown = ({ selectedChannel, handleSelection, loggedInChannelName }) => {
  return (
    <select
      id='channel-name-select'
      value={selectedChannel}
      onChange={handleSelection}>
      { loggedInChannelName && (
        <option value={loggedInChannelName} >{loggedInChannelName}</option>
      )}
      <option value={LOGIN}>Existing</option>
      <option value={CREATE}>New</option>
    </select>
  );
};

export default ChannelSelectDropdown;


================================================
FILE: client/src/components/ChooseAnonymousPublishRadio/index.jsx
================================================
import React from 'react';

const ChooseAnonymousPublishRadio = ({ publishInChannel, toggleAnonymousPublish }) => {
  return (
    <div>
      <input
        type='radio'
        name='anonymous-or-channel'
        id='anonymous-radio'
        className='input-radio'
        value='anonymous'
        checked={!publishInChannel}
        onChange={toggleAnonymousPublish}
      />
      <label
        className='label-radio'
        htmlFor='anonymous-radio'
      >
        Anonymous
      </label>
    </div>
  );
};

export default ChooseAnonymousPublishRadio;


================================================
FILE: client/src/components/ChooseChannelPublishRadio/index.jsx
================================================
import React from 'react';

const ChooseChannelPublishRadio = ({ publishInChannel, toggleAnonymousPublish }) => {
  return (
    <div>
      <input
        type='radio'
        name='anonymous-or-channel'
        id='channel-radio'
        className='input-radio'
        value='in a channel'
        checked={publishInChannel}
        onChange={toggleAnonymousPublish}
      />
      <label
        className='label-radio'
        htmlFor='channel-radio'
      >
        In a channel
      </label>
    </div>
  );
};

export default ChooseChannelPublishRadio;


================================================
FILE: client/src/components/ClickToCopy/index.jsx
================================================
import React from 'react';
import * as Icon from 'react-feather';

class ClickToCopy extends React.Component {
  constructor (props) {
    super(props);
    this.copyToClipboard = this.copyToClipboard.bind(this);
  }
  copyToClipboard () {
    const elementToCopy = this.props.id;
    const element = document.getElementById(elementToCopy);
    console.log(elementToCopy);
    element.select();
    try {
      document.execCommand('copy');
    } catch (err) {
      this.setState({error: 'Oops, unable to copy'});
    }
  }
  render () {
    const {id, value} = this.props;
    return (
      <div
        className='click-to-copy-wrap'
        onClick={this.copyToClipboard}
      >
        <input
          id={id}
          value={value}
          type='text'
          className='click-to-copy'
          readOnly
          spellCheck='false'
        />
        <div className='icon-wrap'>
          <Icon.Copy />
        </div>
      </div>
    );
  }
}

export default ClickToCopy;


================================================
FILE: client/src/components/DropzoneDropItDisplay/index.jsx
================================================
import React from 'react';

const DropzoneDropItDisplay = () => {
  return (
    <div className={'dropzone-dropit-display'}>
      Drop it.
    </div>
  );
};

export default DropzoneDropItDisplay;


================================================
FILE: client/src/components/DropzoneInstructionsDisplay/index.jsx
================================================
import React from 'react';
import FormFeedbackDisplay from '@components/FormFeedbackDisplay';
import Row from '@components/Row';

const DropzoneInstructionsDisplay = ({fileError, message}) => {
  if (!message) {
    message = 'Drag & drop image or video here to publish';
  }
  return (
    <div className={'dropzone-instructions-display'}>
      <Row>
        <span className={'text--large'}>{message}</span>
      </Row>
      <Row>
        <span className={'text--small text--secondary'}>OR</span>
      </Row>
      { fileError ? (
        <div>
          <Row>
            <span className={'text--large dropzone-instructions-display__chooser-label'}>CHOOSE FILE</span>
          </Row>
          <FormFeedbackDisplay
            errorMessage={fileError}
            defaultMessage={false}
          />
        </div>
      ) : (
        <span className={'text--large dropzone-instructions-display__chooser-label'}>CHOOSE FILE</span>
      )}
    </div>
  );
};

export default DropzoneInstructionsDisplay;


================================================
FILE: client/src/components/DropzonePreviewImage/index.jsx
================================================
import React from 'react';
import PropTypes from 'prop-types';

class PublishPreview extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      imgSource            : '',
      defaultVideoThumbnail: '/assets/img/video_thumb_default.png',
      defaultThumbnail     : '/assets/img/Speech_Logo_Main@OG-02.jpg',
    };
  }
  componentDidMount () {
    const { isUpdate, sourceUrl, file } = this.props;
    if (isUpdate && sourceUrl) {
      this.setState({ imgSource: sourceUrl });
    } else {
      this.setPreviewImageSource(file);
    }
  }
  componentWillReceiveProps (newProps) {
    if (newProps.file !== this.props.file) {
      this.setPreviewImageSource(newProps.file);
    }
    if (newProps.thumbnail !== this.props.thumbnail) {
      if (newProps.thumbnail) {
        this.setPreviewImageSourceFromFile(newProps.thumbnail);
      } else {
        this.setState({imgSource: this.state.defaultThumbnail});
      }
    }
  }
  setPreviewImageSourceFromFile (file) {
    const previewReader = new FileReader();
    previewReader.readAsDataURL(file);
    previewReader.onloadend = () => {
      this.setState({imgSource: previewReader.result});
    };
  }
  setPreviewImageSource (file) {
    if (this.props.thumbnail) {
      this.setPreviewImageSourceFromFile(this.props.thumbnail);
    } else if (file.type.substr(0, file.type.indexOf('/')) === 'image'){
      this.setPreviewImageSourceFromFile(file);
    } else if (file.type === 'video'){
      this.setState({imgSource: this.state.defaultVideoThumbnail});
    } else {
      this.setState({imgSource: this.state.defaultThumbnail});
    }
  }
  render () {
    return (
      <img
        src={this.state.imgSource}
        className={'dropzone-preview-image ' + (this.props.dimPreview ? 'publish-preview-dim' : '')}
        alt='publish preview'
      />
    );
  }
};

PublishPreview.propTypes = {
  dimPreview: PropTypes.bool.isRequired,
  file      : PropTypes.object,
  thumbnail : PropTypes.object,
  isUpdate  : PropTypes.bool,
  sourceUrl : PropTypes.string,
};

export default PublishPreview;


================================================
FILE: client/src/components/ErrorBoundary/index.jsx
================================================
import React from 'react';
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    console.log('Error occurred while rendering markdown')
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return (<p>A component was prevented from crashing the App.</p>);
    }
    return this.props.children;
  }
}

export default ErrorBoundary;


================================================
FILE: client/src/components/ExpandingTextArea/index.jsx
================================================
import React, { Component } from 'react';
import PropTypes from 'prop-types';

class ExpandingTextarea extends Component {
  constructor (props) {
    super(props);
    this._handleChange = this._handleChange.bind(this);
  }
  componentDidMount () {
    this.adjustTextarea({});
  }
  _handleChange (event) {
    const { onChange } = this.props;
    if (onChange) onChange(event);
    this.adjustTextarea(event);
  }
  adjustTextarea ({ target = this.el }) {
    target.style.height = 0;
    target.style.height = `${target.scrollHeight}px`;
  }
  render () {
    const { ...rest } = this.props;
    return (
      <textarea
        {...rest}
        ref={x => this.el = x}
        onChange={this._handleChange}
      />
    );
  }
}

ExpandingTextarea.propTypes = {
  onChange: PropTypes.func,
};

export default ExpandingTextarea;


================================================
FILE: client/src/components/FileViewer/index.jsx
================================================
import React from 'react';
import ReactMarkdown from 'react-markdown/with-html';
import { serving } from '@config/siteConfig.json';
import ErrorBoundary from '@components/ErrorBoundary';
const { markdownSettings: { escapeHtmlMain, skipHtmlMain, allowedTypesMain } } = serving;
class FileViewer extends React.Component {

  constructor (props) {
    super(props);
    /*
      Prevent memory leak by closing fetch before unmount
     */
    this.abortController = new AbortController();
    this.abortSignal = this.abortController.signal;
    this.state = {
      fileLoaded: false,
      fileText  : '',
    };
  }

  componentDidMount () {
    const {sourceUrl} = this.props;
    const signal = this.abortSignal;
    fetch(sourceUrl, { signal })
      .then(response => response.text())
      .then((text) => {
        this.setState({fileText: text});
        this.setState({fileLoaded: true});
        return true;
      })
      .catch(e => { console.log('fetch aborted on unmount ', e) });
  }

  componentWillUnmount () {
    this.abortController.abort();
  }

  render () {
    return (
      <div className={'markdown'}>
        {
          this.state.fileLoaded &&
            <ErrorBoundary>
              <ReactMarkdown className={'markdown-preview'} source={this.state.fileText} skipHtml={skipHtmlMain} allowedTypes={allowedTypesMain} escapeHtml={escapeHtmlMain} />
            </ErrorBoundary>
        }
        {
          !this.state.fileLoaded &&
          <p>Loading your file...</p>
        }
      </div>
    );
  }
}

export default FileViewer;


================================================
FILE: client/src/components/FormFeedbackDisplay/index.jsx
================================================
import React from 'react';

const FormFeedbackDisplay = ({ errorMessage, defaultMessage }) => {
  return (errorMessage || defaultMessage) ? (
    <div className={'form-feedback'}>
      { errorMessage ? (
        <span className={'text--small text--failure'}>{errorMessage}</span>
      ) : (
        <div>
          { defaultMessage ? (
            <span className={'text--small text--secondary'}>{defaultMessage}</span>
          ) : (
            <span className={'text--small'}>&nbsp;</span>
          )}
        </div>
      )}
    </div>
  ) : null;
};

export default FormFeedbackDisplay;


================================================
FILE: client/src/components/GAListener/index.jsx
================================================
import React from 'react';
import GoogleAnalytics from 'react-ga';
import { withRouter } from 'react-router-dom';

import siteConfig from '@config/siteConfig.json';

let googleId = null;

if (siteConfig && siteConfig.analytics) {
  ({ googleId } = siteConfig.analytics);
}

if (googleId) {
  GoogleAnalytics.initialize(googleId);
}

class GAListener extends React.Component {
  componentDidMount () {
    this.sendPageView(this.props.history.location);
    this.props.history.listen(this.sendPageView);
  }

  sendPageView (location) {
    if (googleId) {
      GoogleAnalytics.set({ page: location.pathname });
      GoogleAnalytics.pageview(location.pathname);
    }
  }

  render () {
    return this.props.children;
  }
}

export default withRouter(GAListener);


================================================
FILE: client/src/components/HorizontalSplit/index.jsx
================================================
import React from 'react';

class HorizontalSplit extends React.Component {
  render () {
    const { leftSide, rightSide, collapseOnMobile } = this.props;

    let className = 'horizontal-split';
    if (collapseOnMobile) {
      className += " horizontal-split--mobile-collapse";
    }

    // If there is no left side, move the right side to the left
    // This is mostly for content with no description
    // It doesn't need to be on the right side with nothing next to it.
    const leftComponent = leftSide || rightSide;
    const rightComponent = leftSide ? rightSide : null;

    return (
      <div className={className}>
        <div className={'horizontal-split__column horizontal-split__column--left'}>
          {leftComponent}
        </div>
        <div className={'horizontal-split__column horizontal-split__column--right'}>
          {rightComponent}
        </div>
      </div>
    );
  }
}

export default HorizontalSplit;


================================================
FILE: client/src/components/InactiveStatusBar/index.jsx
================================================
import React from 'react';

const InactiveStatusBar = () => {
  return <span className='progress-bar--inactive'>| </span>;
};

export default InactiveStatusBar;


================================================
FILE: client/src/components/Label/index.jsx
================================================
import React from 'react';

const Label = ({ value }) => {
  return (
    <label
      className='label'
    >
      {value}
    </label>
  );
};
export default Label;


================================================
FILE: client/src/components/Logo/index.jsx
================================================
import React from 'react';
import { Link } from 'react-router-dom';

function Logo () {
  return (
    <svg version='1.1' id='Layer_1' x='0px' y='0px' height='24px' viewBox='0 0 80 31' enableBackground='new 0 0 80 31' className='nav-bar-logo'>
      <Link to='/'>
        <title>Logo</title>
        <desc>Spee.ch logo</desc>
        <g id='About'>
          <g id='Publish-Form-V2-_x28_filled_x29_' transform='translate(-42.000000, -23.000000)'>
            <g id='Group-17' transform='translate(42.000000, 22.000000)'>
              <text transform='matrix(1 0 0 1 0 20)' fontSize='25' fontFamily='Roboto'>Spee&lt;h</text>
              <g id='Group-16' transform='translate(0.000000, 30.000000)'>
                <path id='Line-8' fill='none' stroke='#09F911' strokeWidth='1' strokeLinecap='square' d='M0.5,1.5h15' />
                <path id='Line-8-Copy' fill='none' stroke='#029D74' strokeWidth='1' strokeLinecap='square' d='M16.5,1.5h15' />
                <path id='Line-8-Copy-2' fill='none' stroke='#E35BD8' strokeWidth='1' strokeLinecap='square' d='M32.5,1.5h15' />
                <path id='Line-8-Copy-3' fill='none' stroke='#4156C5' strokeWidth='1' strokeLinecap='square' d='M48.5,1.5h15' />
                <path id='Line-8-Copy-4' fill='none' stroke='#635688' strokeWidth='1' strokeLinecap='square' d='M64.5,1.5h15' />
              </g>
            </g>
          </g>
        </g>
      </Link>
    </svg>
  );
};

export default Logo;


================================================
FILE: client/src/components/Memeify/EditableFontface/index.js
================================================
import React, { Component } from 'react';

const DEFAULT_TEXT_RENDER = (text) => text;

export default class EditableFontface extends Component {
  constructor(props) {
    super(props);

    this.state = {
      blinkSelection: props.blinkSelection == false ? false : true,
      value: props.value,
    };

    this.textInput = React.createRef();
  }

  componentDidMount() {
    const textInput = this.textInput.current;

    if(textInput) {
      textInput.focus();
    }
  }

  render() {
    const me = this;

    const {
      blinkSelection,
      value
    } = me.state;

    const {
      editable = true,
      fontFace,
      preview,
    } = me.props;

    const textRender = fontFace.textRender || DEFAULT_TEXT_RENDER;

    const textStyles = Object.assign({
      ...(blinkSelection ? {
        animation: 'textBlink 1s infinite',
      } : {}),
      minHeight: '20px',
      WebkitFontSmoothing: 'antialiased',
      MozOsxFontSmoothing: 'grayscale',
    }, fontFace.text, preview ? fontFace.previewOverrides : {});

    const fontInput = (editable === true) ? (
      <input ref={this.textInput} type="text" onKeyPress={(e) => me.onKeyPress(e)} onChange={(e) => me.onChange(e)} style={{
        ...{
          bottom: 0,
          opacity: 0,
          padding: 0,
          left: 0,
          position: 'absolute',
          top: 0,
          width: '100%',
          zIndex: 1,
        },
        ...(fontFace.editorStyle || {}),
      }} />
    ) : null;

    return (
      <div style={{ position: 'relative', ...(fontFace.container || {}) }}>
        <style scoped>{'@keyframes textBlink { 0% { opacity: 1 } 30% { opacity: 0.6 } 60% { opacity: 1 } }'}</style>
        {fontInput}
        <div ref={me.state.fontRender} style={textStyles} title={value}>{textRender(value)}</div>
      </div>
    );
  }

  onKeyPress(e) {
    this.setState({
      blinkSelection: false,
      value: e.target.value
    });
  }

  onChange(e) {
    this.setState({
      blinkSelection: false,
      value: e.target.value
    });
  }
};

export const PRESETS = {
  'Green Machine': require('../FontFaces/GreenMachine').default,
  'Inferno': require('../FontFaces/Inferno').default,
  'Lazer': require('../FontFaces/Lazer').default,
  'Neon': require('../FontFaces/Neon').default,
  'Old Blue': require('../FontFaces/OldBlue').default,
  'Outline': require('../FontFaces/Outline').default,
  'Retro Rainbow': require('../FontFaces/RetroRainbow').default,
  'The Special': require('../FontFaces/TheSpecial').default,
  'Vapor Wave': require('../FontFaces/VaporWave').default,
}


================================================
FILE: client/src/components/Memeify/FontFaces/.gitkeep
================================================


================================================
FILE: client/src/components/Memeify/FontFaces/GreenMachine.js
================================================
export default {
  editorStyle: {
    fontFamily: 'courier, Courier New',
    fontWeight: 'bold',
    fontSize: '2em',
  },
  text: {
    color: '#00b700',
    fontFamily: 'courier, Courier New',
    fontSize: '2rem',
    fontWeight: 'bold',
    textShadow: '1px 1px 2px #003605',
  },
  previewOverrides: {
    fontSize: '1.6em',
  },
};


================================================
FILE: client/src/components/Memeify/FontFaces/Inferno.js
================================================
export default {
  editorStyle: {
    fontFamily: 'helvetica, Helvetica Nue',
    fontWeight: 'bold',
    fontSize: '2em',
  },
  text: {
    fontFamily: 'helvetica, Helvetica Nue',
    fontWeight: 'bold',
    fontSize: '2em',
    color: '#fff',
    textShadow: '0px 0px 3px #c20000, 0px 0px 3px #e0f2ff, 0px 0px 5px #532761, 0px 0px 20px #670606, 0 0 10px rgba(0, 0, 0, .8), 0 0 10px #fefcc9, 5px -5px 15px #feec85, -10px -10px 20px #ffae34, 10px -20px 25px #ec760c, -10px -30px 30px #cd4606, 0 -40px 35px #973716, 5px -45px 40px #451b0e, 0 -2px 15px rgba(255, 200, 160, .5)',
  },
  previewOverrides: {
    fontSize: '1.5em',
    overflow: 'hidden',
    padding: '0 1rem 0 1rem',
  },
};


================================================
FILE: client/src/components/Memeify/FontFaces/Lazer.js
================================================
export default {
  editorStyle: {
    fontFamily: 'helvetica, Helvetica Nue',
    fontWeight: 'bold',
    fontSize: '2em',
    textTransform: 'uppercase',
    whiteSpace: 'nowrap',
  },
  text: {
    fontFamily: 'helvetica, Helvetica Nue',
    fontWeight: 'bold',
    backgroundImage: 'linear-gradient(180deg, #249bff 0%, #e1f8ff 44%, #3a006b 44%, #ff57d6 100%)',
    backgroundClip: 'text',
    fontSize: '2em',
    color: 'transparent',
    filter: 'drop-shadow(0 0 .05rem black)',
    textTransform: 'uppercase',
    whiteSpace: 'nowrap',
    WebkitBackgroundClip: 'text',
    WebkitTextStroke: '0.03em rgba(255, 255, 255, 0.6)',
  },
  previewOverrides: {
    fontSize: '1.8em',
  },
};


================================================
FILE: client/src/components/Memeify/FontFaces/Neon.js
================================================
export default {
  editorStyle: {
    fontFamily: 'Helvetica, Arial',
    fontWeight: 'bold',
    fontSize: '2em',
    letterSpacing: '.1em',
  },
  text: {
    color: '#fff',
    fontFamily: 'Helvetica, Arial',
    fontSize: '2rem',
    fontWeight: 'bold',
    letterSpacing: '.1em',
    textShadow: '0 0 0.05em #fff, 0 0 0.1em #fff, 0 0 0.2em #fff, 0 0 .2em #ff1de2, 0 0 .4em #ff26e3, 0 0 .5em #ff00de, 0 0 1em #ff61eb, 0 0 1.5em #ff7cee',
  },
  previewOverrides: {
    fontSize: '1.8em',
    padding: '0 1rem 0 1rem',
  },
};


================================================
FILE: client/src/components/Memeify/FontFaces/OldBlue.js
================================================
import React from 'react';

const charToFullWidth = char => {
	const c = char.charCodeAt( 0 )
	return c >= 33 && c <= 126
		? String.fromCharCode( ( c - 33 ) + 65281 )
		: char
}

export default {
  container: {},
  editorStyle: {},
  text: {
    fontFamily: 'Segoe UI,Helvetica,Arial',
  },
	previewOverrides: {
		height: '2.6rem',
		overflow: 'hidden',
	},
  textRender: (text) => {
    const id = `curve-${text.replace(/[^A-Za-z0-9]/g, '')}-oceanwave`
    return (
      <svg viewBox="0 0 500 50" style={{ height: '4em', fontFamily: 'Arial', fontWeight: 'bold' }}>
				<path id={id} fill="transparent" d="M 0 50 Q 50 0 100 50 Q 150 100 200 50 Q 250 0 300 50 Q 350 100 400 50 Q 450 0 500 50 Q 550 100 600 50 " transform="scale(1 0.5) translate(0 15)" />
        <text x="10" style={{ fill: '#4dc2fe', fontWeight: 900, letterSpacing: '-0.15em', textShadow: '0.15em -0.1em #1c55a0' }}>
          <textPath xlinkHref={`#${id}`}>
            {text}
          </textPath>
        </text>
				<text x="10" style={{ fill: 'transparent', stroke: '#1c55a0', strokeWidth: '.012em', fontWeight: 900, letterSpacing: '-0.15em' }}>
          <textPath xlinkHref={`#${id}`}>
            {text}
          </textPath>
        </text>
      </svg>
    );
  },
};


================================================
FILE: client/src/components/Memeify/FontFaces/Outline.js
================================================
export default {
  editorStyle: {
    fontFamily: 'arial',
    fontWeight: 'bold',
    fontSize: '2em',
  },
  text: {
    color: '#fff',
    fontFamily: 'arial',
    fontSize: '2rem',
    fontWeight: 'bold',
    textShadow: '2px 2px .1px #000, -1px -1px .1px #000, 1px -1px .1px #000, -1px 1px .1px #000, 1px 1px .1px #000',
  },
  previewOverrides: {
    fontSize: '1.6rem',
    padding: '0 .04rem',
  },
};


================================================
FILE: client/src/components/Memeify/FontFaces/RetroRainbow.js
================================================
export default {
  editorStyle: {
    fontFamily: 'Arial, sans-serif',
    fontWeight: 'bold',
    fontSize: '1.2em',
    transform: 'scale(1, 1.5)',
  },
  text: {
    fontFamily: 'Arial, sans-serif',
    fontWeight: 'bold',
    backgroundImage: 'linear-gradient(to right, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af)',
    backgroundClip: 'text',
    fontSize: '1.2em',
    transform: 'scale(1, 1.5)',
    color: 'transparent',
    paddingBottom: '.25em',
    paddingTop: '.1em',
    WebkitBackgroundClip: 'text',
  },
};


================================================
FILE: client/src/components/Memeify/FontFaces/TheSpecial.js
================================================
import React from 'react';

export default {
  editorStyle: {
    fontFamily: 'Arial, sans-serif',
    fontWeight: 'bold',
    fontSize: '1.4em',
  },
  text: {
    fontFamily: 'Arial, sans-serif',
    fontWeight: 'bold',
    backgroundImage: 'linear-gradient(to right, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af)',
    backgroundClip: 'text',
    fontSize: '1.4em',
    color: 'transparent',
    paddingBottom: '.25em',
    paddingTop: '.1em',
    WebkitBackgroundClip: 'text',
  },
  textRender: (text) => {
    text = text
      .replace(/love [^\s.!$]+/g, 'love LBRY')
      .replace(/LBRY/g, 'amazing LBRY')
      .replace(/julie/gi, 'super Julie')
      .replace(/tom/gi, 'amazing Tom')
      .replace(/(btc|bch)/gi, 'LBC')
      .replace(/\w+ is \w+/gi, 'LBRY is amazing');

    return text.split(/chris[\d\w]*/gi).reduce((result, value, index) => {
      if(index !== 0) {
        result.push(<img key={`font_glyph_${index}`} style={{ height: '.9em', position: 'relative', top: '.1em' }} src={THE_FACE} />);
      }
      result.push(<span key={`${value}_${index}`} title={value}>{value}</span>)

      return result;
    }, [])
  },
};

const THE_FACE = 'data:image/png;base64,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';


================================================
FILE: client/src/components/Memeify/FontFaces/VaporWave.js
================================================
import React from 'react';

const charToFullWidth = char => {
	const c = char.charCodeAt( 0 )
	return c >= 33 && c <= 126
		? String.fromCharCode( ( c - 33 ) + 65281 )
		: char
}

export default {
  container: {
		overflow: 'hidden',
	},
  editorStyle: {},
  text: {
    fontFamily: 'Segoe UI,Helvetica,Arial',
  },
	previewOverrides: {
		transform: 'rotate(39deg)',
    height: '7rem',
    paddingLeft: '2rem',
	  margin: '-2rem 0',
	},
  textRender: (text) => {
    const formattedText = text.toLowerCase().split('').map((char) => {
      const c = char.charCodeAt( 0 )
      return (c >= 33 && c <= 126) ? String.fromCharCode(c + 65248) : char
    }).join('');

    // TODO: Inline the path
    const id = `curve-${text.replace(/[^A-Za-z0-9]/g, '')}-oceanwave`
    return (
      <svg viewBox="0 0 500 160" style={{ height: '10em' }}>
        <path id={id} fill="transparent" d="M6,150C49.63,93,105.79,36.65,156.2,47.55,207.89,58.74,213,131.91,264,150c40.67,14.43,108.57-6.91,229-145" />
        <text x="10">
          <textPath xlinkHref={`#${id}`}>
            {formattedText}
          </textPath>
        </text>
      </svg>
    );
  },
};


================================================
FILE: client/src/components/Memeify/RichDraggable/index.js
================================================
import React, { Component } from 'react';
import Draggable from 'react-draggable';

let body;
try {
  body = document.body;
} catch(e) {}

export default class RichDraggable extends Component {
  constructor(props) {
    super(props);

    this.contents = React.createRef();
    this.state = {
      height: 0,
      width: 0,
    };
  }

  componentDidMount() {
    const height = this.contents.current.offsetHeight;
    const width = this.contents.current.offsetWidth;

    this.setState({
      height,
      width,
    });
  }

  render() {
    const me = this;

    const {
      props,
      state,
    } = me;

    const {
      height: bottom,
      width: right,
    } = props.bounds;

    const bounds = {
      //top: 0,
      //left: 0,
      right: right - state.width,
      bottom: bottom - state.height,
    };

    return (
      <Draggable {...props} bounds={bounds} offsetParent={body} cancel=".no-drag">
        <div ref={me.contents} style={{ border: '4px dashed rgba(0, 0, 0, .7)', cursor: 'move', position: 'absolute' }} className="creatifyDecor">
          <div style={{ border: '4px dashed rgba(255, 255, 255, .8)', margin: '-5px -3px -3px -5px', padding: '15px' }} className="creatifyDecor">
            <div className="no-drag" style={{ position: 'relative', cursor: 'auto' }}>
              {props.children}
            </div>
          </div>
        </div>
      </Draggable>
    );
  }
};


================================================
FILE: client/src/components/Memeify/index.js
================================================
import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

import React, { Component } from 'react';
import Select from 'react-select'

import RichDraggable from './RichDraggable';
import EditableFontface, { PRESETS as FontPresets } from './EditableFontface';

import {
  faFont,
  faMinusCircle,
  faPlusCircle,
} from '@fortawesome/free-solid-svg-icons';

const getRasterizedCanvas = (contents, width, height) => {
  return new Promise((resolve) => {
    // Parse to xHTML for SVG/foreignObject rendering
    contents = new XMLSerializer().serializeToString(
      new DOMParser().parseFromString(contents, 'text/html')
    );

    // Resolves a bug in Chrome where it renders correctly, but
    // replaces the inline styles with an invalid `background-clip`.
    if(/Chrome/.test(navigator.userAgent)) {
      contents = contents.replace(/background\-clip:(\s*text\s*)[;$]/g,
        (match, group) => (`-webkit-background-clip:text;${match}`)
      );
    }

    // Fix busted SVG images in Safari
    if(/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
      contents = contents.replace(/\<img\s/g, '<xhtml:img ');
    }

    // Attempt to match font kerning with the DOM.
    const kerningAndPadding = '<style>svg{font-kerning:normal}body{padding:0;margin:0}</style>';
    let svgContents = `<svg xmlns="http://www.w3.org/2000/svg" width="${width * 2}" height="${height * 2}">
<foreignObject x="0" y="0" width="${width * 2}" height="${height * 2}" externalResourcesRequired="true">
<html xmlns="http://www.w3.org/1999/xhtml"><head>${kerningAndPadding}</head><body>${contents}</body></html>
</foreignObject></svg>`;

    const pixelRatio = 2;

    let img = document.createElement('img');
    let canvas = document.createElement('canvas');

    img.height = canvas.height = height * pixelRatio;
    img.width = canvas.width = width * pixelRatio;
    canvas.style.height = `${height}px`;
    canvas.style.width = `${width}px`;

    let shadowNode = document.createElement('div');
    Object.assign(shadowNode.style, {
      height: 0,
      overflow: 'hidden',
      width: 0,
    });
    document.body.appendChild(shadowNode);

    shadowNode.appendChild(img);

    var svg64 = btoa(unescape(encodeURIComponent(svgContents)));
    var b64Start = 'data:image/svg+xml;base64,';
    var image64 = b64Start + svg64;
    img.addEventListener('load', () => {
      window.requestAnimationFrame(() => {
        // We still can't trust Firefox's %$%&* engine, add another 5ms timeout
        // `background-clip: text` is very broken and does not always render in time.
        setTimeout(() => {
          let context = canvas.getContext('2d', { alpha: false });
          context.clearRect(0, 0, canvas.width, canvas.height);
          context.fillStyle = 'white';
          context.imageSmoothingEnabled = false;
          context.scale(pixelRatio, pixelRatio);
          context.fillRect(0, 0, canvas.width, canvas.height);
          context.drawImage(img, 0, 0);

          document.body.removeChild(shadowNode);

          resolve(canvas);
        }, 10);
      });
    });
    img.src = image64;
  });
};

export default class Memeify extends Component {
  constructor(props) {
    super(props);

    const fontKeys = Object.keys(FontPresets);

    this.contents = React.createRef();

    const fontOptions = fontKeys.map(
      (fontName) => (
        {
          value: fontName,
          label: (
            <div style={{ maxHeight: '150px', maxWidth: '100%', fontSize: '16px' }}>
              <EditableFontface key={fontName} fontFace={FontPresets[fontName]} preview={true} value={fontName} editable={false} blinkSelection={false} />
            </div>
          ),
          fontName,
        }
      )
    );

    this.state = {
      activeElement: false,
      bounds: {},
      fontName: fontKeys[0],
      elements: [],
      fontOptions,
    };
  }

  componentDidMount() {
    // TODO: Fix bounds
    /*
    const bounds = this.contents.current.getBoundingClientRect();

    this.setState({
      bounds,
    });

    console.log({
      bounds
    })
    */
  }

  setActiveElement(activeElement) {
    this.setState({ activeElement });
  }

  addElement() {
    const {
      state
    } = this;

    const newElementKey = `element-${state.elements.length}-${Date.now()}`;

    const newElement = (
      <RichDraggable key={newElementKey} bounds={state.bounds} onStart={() => this.setActiveElement(newElement)}>
        <EditableFontface fontFace={FontPresets[state.fontName]} value="Start Typing!" />
      </RichDraggable>
    );

    this.setState({
      elements: [...state.elements, newElement],
      activeElement: newElement,
    });
  }

  removeElement() {
    const {
      state
    } = this;

    const activeElementIndex = state.elements.indexOf(state.activeElement);

    if(state.elements.length === 0 || activeElementIndex === -1) {
      return;
    }

    const elements = [...state.elements];
    elements.splice(activeElementIndex, 1)

    this.setState({
      activeElement: false,
      elements,
    });
  }

  async onSave() {
    const renderedCanvas = await this.renderContentsToCanvas();

    if(this.props.onSave) {
      this.props.onSave(renderedCanvas);
    }
  }

  async renderContentsToCanvas() {
    const me = this;

    const contentsElement = me.contents.current;
    let contents = contentsElement.outerHTML;

    // Cheap border/handles removal
    contents = `<style>.creatifyDecor{border-color:transparent!important;background-color:transparent!important}</style>` + contents;

    const contentsWidth = contentsElement.offsetWidth;
    const contentsHeight = contentsElement.offsetHeight;

    // Fix the dimensions, fixes when flex is used.
    contents = `<div style="height:${contentsHeight}px;width:${contentsWidth}px">${contents}</div>`;

    return await getRasterizedCanvas(contents, contentsWidth, contentsHeight);
  }

  render() {
    const me = this;
    const {
      props,
      state,
    } = this;

    // TODO: Abstract into separate package & use CSS Modules.
    const spacerCss = { width: '.3em' };
    return (
      <div style={{ position: 'relative', flex: props.flex === true ? 1 : props.flex, display: props.flex ? 'flex' : 'block' }}>
        <div className={props.toolbarClassName} style={{ alignItems: 'center', color: '#fff', display: 'flex', padding: '.3em', position: 'absolute', top: 0, left: 0, right: 0, background: '#333', flexDirection: 'row', zIndex: 2 }}>
          <FontAwesomeIcon icon={faPlusCircle} size="2x" onClick={() => this.addElement()} />
          <div style={spacerCss} />
          <FontAwesomeIcon icon={faMinusCircle} size="2x" onClick={() => this.removeElement()} />
          <div style={spacerCss} />
          <div style={{ flex: 1 }}>
            <Select style={{ flex: 1 }} isSearchable={false} options={state.fontOptions} onChange={(option) => this.setFont(option.fontName)} />
          </div>
          <div style={spacerCss} />
          <div onClick={() => this.onSave()} style={{ alignItems: 'center', alignSelf: 'stretch', border: '1px solid #fff', borderRadius: '4px', color: '#fff', display: 'flex', padding: '0 0.6em' }}>
            <span>Save</span>
          </div>
        </div>
        <div ref={me.contents} style={{ fontSize: '22px', overflow: 'hidden', transform: 'translateZ(0)', flex: 1 }}>
          {state.elements}
          {props.children}
        </div>
      </div>
    );
  }

  setFont(fontName) {
   this.setState({
     fontName,
   });
  }
};


================================================
FILE: client/src/components/NavBar/index.jsx
================================================
import React from 'react';
import SpaceBetween from '@components/SpaceBetween';
import Logo from '@components/Logo';
import SiteDescription from '@containers/SiteDescription';
import NavigationLinks from '@containers/NavigationLinks';

class NavBar extends React.Component {
  render () {
    return (
      <div className={'nav-bar'}>
        <SpaceBetween >
          <Logo />
          <NavigationLinks />
        </SpaceBetween>
      </div>
    );
  }
}

export default NavBar;


================================================
FILE: client/src/components/NavBarChannelOptionsDropdown/index.jsx
================================================
import React from 'react';

function NavBarChannelDropdown ({ channelName, handleSelection, defaultSelection, VIEW, LOGOUT }) {
  return (
    <div className={'nav-bar-link link--nav'}>
      <select
        type='text'
        id='nav-bar-channel-select'
        onChange={handleSelection}
        value={defaultSelection}
      >
        <option id='nav-bar-channel-select-channel-option'>{channelName}</option>
        <option value={VIEW}>View</option>
        <option value={LOGOUT}>Logout</option>
      </select>
    </div>
  );
}

export default NavBarChannelDropdown;


================================================
FILE: client/src/components/PageLayout/index.jsx
================================================
import React from 'react';

import SEO from '@containers/SEO';
import NavBar from '@components/NavBar';

class PageLayout extends React.Component {
  render () {
    return (
      <div className={'page-layout'}>
        <SEO
          pageTitle={this.props.pageTitle}
          pageUri={this.props.pageUri}
          asset={this.props.asset}
          channel={this.props.channel}
        />
        <NavBar />
        <div className={'content'}>
          {this.props.children}
        </div>
      </div>
    );
  }
}

export default PageLayout;


================================================
FILE: client/src/components/PageLayoutShowLite/index.jsx
================================================
import React from 'react';

import SEO from '@containers/SEO';

class PageLayoutShowLite extends React.Component {
  shouldComponentUpdate () {
    return false;
  }
  render () {
    return (
      <div className={'page-layout-show-lite'}>
        <SEO pageTitle={this.props.pageTitle} asset={this.props.asset} />
        <div className={'content'}>
          {this.props.children}
        </div>
      </div>
    );
  }
}

export default PageLayoutShowLite;


================================================
FILE: client/src/components/ProgressBar/index.jsx
================================================
import React from 'react';
import PropTypes from 'prop-types';
import ActiveStatusBar from '../ActiveStatusBar';
import InactiveStatusBar from '../InactiveStatusBar';

class ProgressBar extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      bars       : [],
      index      : 0,
      incrementer: 1,
    };
    this.createBars = this.createBars.bind(this);
    this.startProgressBar = this.startProgressBar.bind(this);
    this.updateProgressBar = this.updateProgressBar.bind(this);
    this.stopProgressBar = this.stopProgressBar.bind(this);
  }
  componentDidMount () {
    this.createBars();
    this.startProgressBar();
  }
  componentWillUnmount () {
    this.stopProgressBar();
  }
  createBars () {
    const bars = [];
    for (let i = 0; i <= this.props.size; i++) {
      bars.push({isActive: false});
    }
    this.setState({ bars });
  }
  startProgressBar () {
    this.updateInterval = setInterval(this.updateProgressBar.bind(this), 300);
  };
  updateProgressBar () {
    let index = this.state.index;
    let incrementer = this.state.incrementer;
    let bars = this.state.bars;
    // flip incrementer if necessary, to stay in bounds
    if ((index < 0) || (index > this.props.size)) {
      incrementer = incrementer * -1;
      index += incrementer;
    }
    // update the indexed bar
    if (incrementer > 0) {
      bars[index].isActive = true;
    } else {
      bars[index].isActive = false;
    };
    // increment index
    index += incrementer;
    // update state
    this.setState({
      bars,
      incrementer,
      index,
    });
  };
  stopProgressBar () {
    clearInterval(this.updateInterval);
  };
  render () {
    return (
      <div className="progress-bar__wrapper">
        {this.state.bars.map((bar, index) => bar.isActive ? <ActiveStatusBar key={index} /> : <InactiveStatusBar key={index}/>)}
      </div>
    );
  }
};

ProgressBar.propTypes = {
  size: PropTypes.number.isRequired,
};

export default ProgressBar;


================================================
FILE: client/src/components/PublishDescriptionInput/index.jsx
================================================
import React from 'react';
import RowLabeled from '@components/RowLabeled';
import Label from '@components/Label';
import ExpandingTextArea from '@components/ExpandingTextArea';

const PublishDescriptionInput = ({ description, handleInput }) => {
  return (
    <RowLabeled
      label={
        <Label value={'Description:'} />
      }
      content={
        <ExpandingTextArea
          id='publish-description'
          className='textarea textarea--primary textarea--full-width'
          rows={1}
          maxLength={2000}
          style={{ maxHeight: 200 }}
          name='description'
          placeholder='Optional description'
          value={description}
          onChange={handleInput}
        />
      }
    />
  );
};

export default PublishDescriptionInput;


================================================
FILE: client/src/components/PublishFinePrint/index.jsx
================================================
import React from 'react';

const PublishFinePrint  = () => {
  return (
    <p className={'text--extra-small text--secondary'}>
      By clicking 'Publish', you affirm that you have the rights to publish this content to the LBRY network, and that you understand the properties of publishing it to a decentralized, user-controlled network. <a className='link--primary' target='_blank' href='https://lbry.com/learn'>Read more.</a>
    </p>
  );
};

export default PublishFinePrint;


================================================
FILE: client/src/components/PublishLicenseInput/index.jsx
================================================
import React from 'react';
import RowLabeled from '@components/RowLabeled';
import Label from '@components/Label';
import { LICENSES } from '@clientConstants/publish_license_urls';

const PublishLicenseInput = ({ handleSelect, license }) => {
  return (
    <RowLabeled
      label={
        <Label value={'License'} />
      }
      content={
        <select
          type='text'
          name='license'
          id='publish-license'
          value={license}
          onChange={handleSelect}
        >
          <option value=''>Unspecified</option>
          {
            LICENSES.map(function(item, i){
              return <option key={item + 'license key'} value={item}>{item}</option>;
            })
          }
        </select>
      }
    />
  );
};

export default PublishLicenseInput;


================================================
FILE: client/src/components/PublishLicenseUrlInput/index.jsx
================================================
import React from 'react';
import RowLabeled from '@components/RowLabeled';
import Label from '@components/Label';
import { CC_LICENSES } from '@clientConstants/publish_license_urls';

const PublishLicenseUrlInput = ({ handleSelect, licenseUrl }) => {
  return (
    <RowLabeled
      label={
        <Label value={'License Url'} />
      }
      content={
        <select
          type='text'
          name='licenseUrl'
          id='publish-license-url'
          value={licenseUrl}
          onChange={handleSelect}
        >
          <option value=''>Unspecified</option>
          {
            CC_LICENSES.map(function(item, i){
              return <option key={item.url} value={item.url}>{item.value}</option>
            })
          }
        </select>
      }
    />
  );
};

export default PublishLicenseUrlInput;


================================================
FILE: client/src/components/PublishNsfwInput/index.jsx
================================================
import React from 'react';
import RowLabeled from '@components/RowLabeled';
import Label from '@components/Label';

const PublishNsfwInput = ({ nsfw, handleInput }) => {
  return (
    <RowLabeled
      label={
        <Label value={'Mature:'} />
      }
      content={
        <input
          className='input-checkbox'
          type='checkbox'
          id='publish-nsfw'
          name='nsfw'
          checked={nsfw}
          onChange={handleInput}
        />
      }
    />
  );
};

export default PublishNsfwInput;


================================================
FILE: client/src/components/PublishPreview/index.jsx
================================================
import React from 'react';
import HorizontalSplit from '@components/HorizontalSplit';
import Dropzone from '@containers/Dropzone';
import PublishDetails from '@containers/PublishDetails';
import PublishTitleInput from '@containers/PublishTitleInput';
import Row from '@components/Row';

// this class seems more like PublishForm and should probably be renamed

class PublishPreview extends React.Component {
  render () {
    const { isUpdate, uri } = this.props;
    return (
      <div className={'publish-form'}>
        <div className={'publish-form__title'}>
          <Row>
            {isUpdate && uri && (<p className='text--secondary'>{`Editing ${uri}`}</p>)}
            <PublishTitleInput />
          </Row>
        </div>
        <HorizontalSplit
          collapseOnMobile
          leftSide={<Dropzone />}
          rightSide={<PublishDetails />}
        />
      </div>
    );
  }
};

export default PublishPreview;


================================================
FILE: client/src/components/PublishUrlMiddleDisplay/index.jsx
================================================
import React from 'react';
import PropTypes from 'prop-types';

function UrlMiddle ({publishInChannel, selectedChannel, loggedInChannelName, loggedInChannelShortId}) {
  if (publishInChannel) {
    if (selectedChannel === loggedInChannelName) {
      return <span id='url-channel' className='publish-url-text'>{loggedInChannelName}:{loggedInChannelShortId} /</span>;
    }
    return <span id='url-channel-placeholder' className='publish-url-text tooltip'>@channel<span
      className='tooltip-text'>Select a channel below</span> /</span>;
  }
  return (
    <span id='url-no-channel-placeholder' className='publish-url-text tooltip'>xyz<span className='tooltip-text'>This will be a random id</span> /</span>
  );
}

UrlMiddle.propTypes = {
  publishInChannel      : PropTypes.bool.isRequired,
  loggedInChannelName   : PropTypes.string,
  loggedInChannelShortId: PropTypes.string,
};

export default UrlMiddle;


================================================
FILE: client/src/components/Row/index.jsx
================================================
import React from 'react';

class Row extends React.Component {
  render () {
    return (
      <div className={'row'}>
        {this.props.children}
      </div>
    );
  }
}

export default Row;


================================================
FILE: client/src/components/RowLabeled/index.jsx
================================================
import React from 'react';

class RowLabeled extends React.Component {
  render () {
    return (
      <div className={'row-labeled'}>
        <div className={'row-labeled-label'}>{this.props.label}</div>
        <div className={'row-labeled-content'}>{this.props.content}</div>
      </div>
    );
  }
}

export default RowLabeled;


================================================
FILE: client/src/components/SocialShareLink/index.jsx
================================================
import React from 'react';

class SocialShareLink extends React.Component {
  render () {
    return (
      <div className={'space-between social-share-link'}>
        {this.props.children}
      </div>
    );
  }
}

export default SocialShareLink;


================================================
FILE: client/src/components/SpaceAround/index.jsx
================================================
import React from 'react';

class SpaceAround extends React.Component {
  render () {
    return (
      <div className={'space-around'}>
        {this.props.children}
      </div>
    );
  }
}

export default SpaceAround;


================================================
FILE: client/src/components/SpaceBetween/index.jsx
================================================
import React from 'react';

class SpaceBetween extends React.Component {
  render () {
    return (
      <div className={'space-between'}>
        {this.props.children}
      </div>
    );
  }
}

export default SpaceBetween;


================================================
FILE: client/src/components/VerticalSplit/index.jsx
================================================
import React from 'react';

class VerticalSplit extends React.Component {
  render () {
    return (
      <div className={'vertical-split'}>
        {this.props.top}
        {this.props.bottom}
      </div>
    );
  }
}

export default VerticalSplit;


================================================
FILE: client/src/constants/asset_display_states.js
================================================
export const LOCAL_CHECK = 'LOCAL_CHECK';
export const UNAVAILABLE = 'UNAVAILABLE';
export const ERROR = 'ERROR';
export const AVAILABLE = 'AVAILABLE';


================================================
FILE: client/src/constants/channel_action_types.js
================================================
export const CHANNEL_UPDATE = 'CHANNEL_UPDATE';
export const CHANNEL_LOGIN_CHECK = 'CHANNEL_LOGIN_CHECK';
export const CHANNEL_LOGOUT = 'CHANNEL_LOGOUT';


================================================
FILE: client/src/constants/channel_create_action_types.js
================================================
export const CHANNEL_CREATE_UPDATE_NAME = 'CHANNEL_CREATE_UPDATE_NAME';
export const CHANNEL_CREATE_UPDATE_PASSWORD = 'CHANNEL_CREATE_UPDATE_PASSWORD';
export const CHANNEL_CREATE_UPDATE_STATUS = 'CHANNEL_CREATE_UPDATE_STATUS';
export const CHANNEL_AVAILABILITY = 'CHANNEL_AVAILABILITY';
export const CHANNEL_CREATE = 'CHANNEL_CREATE';


================================================
FILE: client/src/constants/confirmation_messages.js
================================================
export const SAVE = 'Everything not saved will be lost. Are you sure you want to leave this page?';


================================================
FILE: client/src/constants/publish_action_types.js
================================================
export const FILE_SELECTED = 'FILE_SELECTED';
export const FILE_CLEAR = 'FILE_CLEAR';
export const METADATA_UPDATE = 'METADATA_UPDATE';
export const CLAIM_UPDATE = 'CLAIM_UPDATE';
export const SET_PUBLISH_IN_CHANNEL = 'SET_PUBLISH_IN_CHANNEL';
export const PUBLISH_STATUS_UPDATE = 'PUBLISH_STATUS_UPDATE';
export const ERROR_UPDATE = 'ERROR_UPDATE';
export const SELECTED_CHANNEL_UPDATE = 'SELECTED_CHANNEL_UPDATE';
export const TOGGLE_METADATA_INPUTS = 'TOGGLE_METADATA_INPUTS';
export const THUMBNAIL_NEW = 'THUMBNAIL_NEW';
export const PUBLISH_START = 'PUBLISH_START';
export const CLAIM_AVAILABILITY = 'CLAIM_AVAILABILITY';
export const SET_UPDATE_TRUE = 'SET_UPDATE_TRUE';
export const ABANDON_CLAIM = 'ABANDON_CLAIM';
export const SET_HAS_CHANGED = 'SET_HAS_CHANGED';


================================================
FILE: client/src/constants/publish_channel_select_states.js
================================================
export const LOGIN = 'Existing';
export const CREATE = 'New';


================================================
FILE: client/src/constants/publish_claim_states.js
================================================
export const LOAD_START = 'LOAD_START';
export const LOADING = 'LOADING';
export const PUBLISHING = 'PUBLISHING';
export const SUCCEEDED = 'SUCCEEDED';
export const FAILED = 'FAILED';
export const ABANDONING = 'ABANDONING';


================================================
FILE: client/src/constants/publish_license_urls.js
================================================
export const CC_LICENSES = [
  {
    value: 'CC Attr. 4.0 Int',
    url: 'https://creativecommons.org/licenses/by/4.0/legalcode',
  },
  {
    value: 'CC Attr-ShareAlike 4.0 Int',
    url: 'https://creativecommons.org/licenses/by-sa/4.0/legalcode',
  },
  {
    value: 'CC Attr-NoDerivatives 4.0 Int',
    url: 'https://creativecommons.org/licenses/by-nd/4.0/legalcode',
  },
  {
    value: 'CC Attr-NonComm 4.0 Int',
    url: 'https://creativecommons.org/licenses/by-nc/4.0/legalcode',
  },
  {
    value: 'CC Attr-NonComm-ShareAlike 4.0 Int',
    url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode',
  },
  {
    value: 'CC Attr-NonComm-NoDerivatives 4.0 Int',
    url: 'https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode',
  },
];

export const LICENSES = ['Public Domain', 'Other', 'Copyright', 'Creative Commons'];

export const PUBLIC_DOMAIN = 'Public Domain';
export const OTHER = 'other';
export const COPYRIGHT = 'copyright';
export const CREATIVE_COMMONS = 'Creative Commons';


================================================
FILE: client/src/constants/show_action_types.js
================================================
// request actions
export const HANDLE_SHOW_URI = 'HANDLE_SHOW_URI';
export const HANDLE_SHOW_HOMEPAGE = 'HANDLE_SHOW_HOMEPAGE';
export const REQUEST_ERROR = 'REQUEST_ERROR';
export const REQUEST_UPDATE = 'REQUEST_UPDATE';
export const ASSET_REQUEST_NEW = 'ASSET_REQUEST_NEW';
export const CHANNEL_REQUEST_NEW = 'CHANNEL_REQUEST_NEW';
export const SPECIAL_ASSET_REQUEST_NEW = 'SPECIAL_ASSET_REQUEST_NEW';
export const REQUEST_LIST_ADD = 'REQUEST_LIST_ADD';

// asset actions
export const ASSET_ADD = 'ASSET_ADD';
export const ASSET_VIEWS_UPDATE = 'ASSET_VIEWS_UPDATE';
export const ASSET_UPDATE_CLAIMDATA = 'ASSET_UPDATE_CLAIMDATA';
export const ASSET_REMOVE = 'ASSET_REMOVE';

// channel actions
export const CHANNEL_ADD = 'CHANNEL_ADD';

export const CHANNEL_CLAIMS_UPDATE_ASYNC = 'CHANNEL_CLAIMS_UPDATE_ASYNC';
export const CHANNEL_CLAIMS_UPDATE_SUCCEEDED = 'CHANNEL_CLAIMS_UPDATE_SUCCEEDED';

// asset/file display actions
export const FILE_REQUESTED = 'FILE_REQUESTED';
export const FILE_AVAILABILITY_UPDATE = 'FILE_AVAILABILITY_UPDATE';
export const DISPLAY_ASSET_ERROR = 'DISPLAY_ASSET_ERROR';
export const TOGGLE_DETAILS_EXPANDED = 'TOGGLE_DETAILS_EXPANDED';


================================================
FILE: client/src/constants/show_request_types.js
================================================
export const CHANNEL = 'CHANNEL';
export const ASSET_LITE = 'ASSET_LITE';
export const ASSET_DETAILS = 'ASSET_DETAILS';
export const SPECIAL_ASSET = 'SPECIAL_ASSET';


================================================
FILE: client/src/containers/AssetBlocked/index.js
================================================
import { connect } from 'react-redux';
import View from './view';
import { selectAsset } from '../../selectors/show';

const mapStateToProps = (props) => {
  const {show} = props;
  const asset = selectAsset(show);
  return {
    asset,
  };
};

export default connect(mapStateToProps, null)(View);


================================================
FILE: client/src/containers/AssetBlocked/view.jsx
================================================
import React from 'react';
import createCanonicalLink from '@globalutils/createCanonicalLink';
import HorizontalSplit from '@components/HorizontalSplit';

class BlockedLeft extends React.PureComponent {
  render () {
    return (
      <div>
        <img className={'asset-blocked__image'} src={'/assets/img/451sign.svg'} alt={'451 image'} />
      </div>
    );
  }
}

class BlockedRight extends React.PureComponent {
  render () {
    return (
      <div className={'asset-blocked__text'} >
        <p>In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications.</p>
        <p><a href={'https://lbry.com/faq/dmca'} >Click here</a> for more information.</p>
      </div>
    );
  }
}

class AssetBlocked extends React.Component {
  componentDidMount () {
    /*
    This function and fetch exists to send the browser the appropriate 451 error.
     */
    const { asset } = this.props;
    const { claimData: { contentType, outpoint } } = asset;
    let fileExt;
    if (typeof contentType === 'string') {
      fileExt = contentType.split('/')[1] || 'jpg';
    }
    const sourceUrl = `${createCanonicalLink({ asset: asset.claimData })}.${fileExt}?${outpoint}`;
    fetch(sourceUrl)
      .catch();
  }

  render () {
    return (
      <div>
        <HorizontalSplit
          collapseOnMobile
          leftSide={<BlockedLeft />}
          rightSide={<BlockedRight />}
        />
      </div>
    );
  }
}

export default AssetBlocked;


================================================
FILE: client/src/containers/AssetDisplay/index.js
================================================
import { connect } from 'react-redux';
import View from './view';
import { fileRequested } from '../../actions/show';
import { selectAsset } from '../../selectors/show';

const mapStateToProps = (props) => {
  const {show} = props;
  // select error and status
  const error  = show.displayAsset.error;
  const status = show.displayAsset.status;
  // select asset
  const asset = selectAsset(show);
  //  return props
  return {
    error,
    status,
    asset,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    onFileRequest: (name, claimId) => {
      dispatch(fileRequested(name, claimId));
    },
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(View);


================================================
FILE: client/src/containers/AssetDisplay/view.jsx
================================================
import React from 'react';
import Row from '@components/Row';
import ProgressBar from '@components/ProgressBar';
import { LOCAL_CHECK, UNAVAILABLE, ERROR, AVAILABLE } from '../../constants/asset_display_states';
import createCanonicalLink from '@globalutils/createCanonicalLink';
import FileViewer from '@components/FileViewer';
import isBot from 'isbot';
import Img from 'react-image';

class AvailableContent extends React.Component {
  render () {
    const {contentType, sourceUrl, name, thumbnail} = this.props;
    switch (contentType) {
      case 'image/jpeg':
      case 'image/jpg':
      case 'image/png':
      case 'image/gif':
      case 'image/svg+xml':
        return (
          <Img
            src={[
              sourceUrl,
              '/assets/img/default_thumb.jpg',
            ]}
            alt={name}
            className={'asset-image'}
          />
        );
      case 'video/mp4':
        return (
          <video
            className='asset-video'
            controls poster={!!thumbnail && thumbnail || '/assets/img/default_thumb.jpg'}
          >
            <source
              src={!!sourceUrl && sourceUrl}
            />
            <p>Your browser does not support the <code>video</code> element.</p>
          </video>
        );
      case 'text/markdown':

        return (
          (isBot(window.navigator.userAgent))
            ? (
              <img
                className='asset-image'
                src={'/assets/img/default_thumb.jpg'}
                alt={'markdown available on page load'}
              />
            )
            : <div className={'asset-document'}><FileViewer sourceUrl={!!sourceUrl && sourceUrl}/></div>
        );
      default:
        return (
          <Img
            src={[
              thumbnail,
              '/assets/img/default_thumb.jpg',
            ]}
            alt={name}
            className={'asset-image'}
          />
        );
    }
  }
}

class AssetDisplay extends React.Component {
  componentDidMount () {
    const { asset: { claimData: { name, claimId } } } = this.props;
    this.props.onFileRequest(name, claimId);
  }
  render () {
    const { status, error, asset } = this.props;
    const { name, claimData: { claimId, contentType, thumbnail, outpoint, pending } } = asset;
    // the outpoint is added to force the browser to re-download the asset after an update
    // issue: https://github.com/lbryio/spee.ch/issues/607
    let fileExt;
    if (typeof contentType === 'string') {
      fileExt = contentType.split('/')[1] || 'jpg';
    }
    const sourceUrl = `${createCanonicalLink({ asset: asset.claimData })}.${fileExt}?outpoint=${outpoint}`;
    return (
      <div className={'asset-display'}>
        {(status === LOCAL_CHECK) &&
        <div>
          <p>Checking to see if Spee.ch has your asset locally...</p>
        </div>
        }
        {(status === UNAVAILABLE) &&
        <div>
          <p>Sit tight, we're searching the LBRY blockchain for your asset!</p>
          <ProgressBar size={12} />
          <p>Curious what magic is happening here? <a className='link--primary' target='blank' href='https://lbry.com/faq/what-is-lbry'>Learn more.</a></p>
        </div>
        }
        {(status === ERROR) && (
          pending ? (
            <div>
              <p>This content is pending confirmation on the LBRY blockchain. It should be available in the next few minutes, please check back or refresh.</p>
            </div>
          ) : (
            <div>
              <Row>
                <p>Unfortunately, we couldn't download your asset from LBRY.  You can help us out by sharing the following error message in the <a className='link--primary' href='https://chat.lbry.com' target='_blank'>LBRY discord</a>.</p>
              </Row>
              <Row>
                <p id='error-message'><i>{error}</i></p>
              </Row>
            </div>
          )
        )}
        {(status === AVAILABLE) &&
        <AvailableContent
          contentType={contentType}
          sourceUrl={sourceUrl}
          name={name}
          thumbnail={thumbnail}
        />
        }
      </div>
    );
  }
}

export default AssetDisplay;


================================================
FILE: client/src/containers/AssetInfo/index.js
================================================
import { connect } from 'react-redux';
import View from './view';
import { selectAsset } from '../../selectors/show';

const mapStateToProps = (props) => {
  const {show} = props;
  // select asset
  const asset = selectAsset(show);
  const editable = Boolean(
    asset &&
    asset.claimData &&
    asset.claimData.channelName &&
    props.channel.loggedInChannel.name === asset.claimData.channelName
  );
  //  return props
  return {
    asset,
    editable,
  };
};

export default connect(mapStateToProps, null)(View);


================================================
FILE: client/src/containers/AssetInfo/view.jsx
================================================
import React from 'react';
import { Link } from 'react-router-dom';
import Label from '@components/Label';
import RowLabeled from '@components/RowLabeled';
import SpaceBetween from '@components/SpaceBetween';
import AssetShareButtons from '@components/AssetShareButtons';
import ClickToCopy from '@components/ClickToCopy';
import siteConfig from '@config/siteConfig.json';
import createCanonicalLink from '@globalutils/createCanonicalLink';
import AssetInfoFooter from '@components/AssetInfoFooter/index';
import { createPermanentURI } from '@clientutils/createPermanentURI';
import ReactMarkdown from 'react-markdown';

const { details: { host } } = siteConfig;
const { serving } = siteConfig;
const { markdownSettings: { escapeHtmlDescriptions, skipHtmlDescriptions, allowedTypesDescriptions } } = serving;
class AssetInfo extends React.Component {
  render () {
    const { editable, asset } = this.props;
    const { claimViews, claimData } = asset;
    const {
      channelName,
      claimId,
      channelShortId,
      description,
      name,
      fileExt,
      contentType,
      host,
      certificateId,
      license,
      licenseUrl,
      transactionTime
    } = claimData;

    const canonicalUrl = createCanonicalLink({ asset: { ...claimData, shortId: asset.shortId }});
    const assetCanonicalUrl = `${host}${canonicalUrl}`;
    // Todo Issue #882 centralize all this media type detection
    // Todo get markdown settings from siteConfig
    const embedable = contentType.split('/')[0] === 'image' || contentType === 'video/mp4';

    let channelCanonicalUrl;
    if (channelName) {
      const channel = {
        name   : channelName,
        shortId: channelShortId,
      };
      channelCanonicalUrl = `${createCanonicalLink({channel})}`;
    }
    return (
      <div className='asset-info'>
        { description && (
          <RowLabeled
            label={<Label value={'Description'} />}
            content={
              <div className='asset-info__description'>
                <ReactMarkdown
                  className={'markdown-preview'}
                  escapeHtml={escapeHtmlDescriptions}
                  skipHtml={skipHtmlDescriptions}
                  allowedTypes={allowedTypesDescriptions}
                  source={description}
                />
              </div>
            }
          />
        )}
        {editable && (
          <RowLabeled
            label={<Label value={'Edit'} />}
            content={<Link className='link--primary' to={`/edit${canonicalUrl}`}>{name}</Link>}
          />
        )}
        {channelName && (

          <RowLabeled
            label={
              <Label value={'Channel'} />
            }
            content={
              <span className='text'>
                <Link className='link--primary' to={channelCanonicalUrl}>{channelName}</Link>
              </span>
            }
          />
        )}
        <SpaceBetween>
          {claimViews ? (
            <RowLabeled
              label={
                <Label value={'Views'} />
              }
              content={
                <span className='text'>
                  {claimViews}
                </span>
              }
            />
          ) : null}
          {license && (
            <RowLabeled
              label={
                <Label value={'License'} />
              }
              content={
                <div className='text'>
                  {licenseUrl ? (
                    <a className={'link--primary'} href={licenseUrl} target={'_blank'}>{license}</a>
                  ) : (
                    <span>{license}</span> )}
                </div>
              }
            />
          )}
        </SpaceBetween>
        <RowLabeled
          label={
            <Label value={'Share'} />
          }
          content={
            <AssetShareButtons
              name={name}
              assetUrl={assetCanonicalUrl}
            />
          }
        />

        <RowLabeled
          label={
            <Label value={'Link'} />
          }
          content={
            <ClickToCopy
              id={'short-link'}
              value={assetCanonicalUrl}
            />
          }
        />
        {embedable && (
          <RowLabeled
            label={
              <Label value={'Embed'} />
            }
            content={
              <div>
                {(contentType === 'video/mp4') ? (
                  <ClickToCopy
                    id={'embed-text-video'}
                    value={`<iframe src="${host}/video-embed${canonicalUrl}" allowfullscreen="true" style="border:0"></iframe>`}
                  />
                ) : (
                  <ClickToCopy
                    id={'embed-text-image'}
                    value={`<img alt="${name}" src="${assetCanonicalUrl}.${fileExt}" />`}
                  />
                )}
              </div>
            }
          />
        )}
        <RowLabeled
          label={
            <Label value={'LBRY URI'} />
          }
          content={
            <ClickToCopy
              id={'lbry-permanent-url'}
              value={`${createPermanentURI(asset)}`}
            />
          }
        />

        <SpaceBetween>
          <a
            className='link--primary'
            href={`${assetCanonicalUrl}.${fileExt}`}
          >
            Direct Link
          </a>
          <a
            className={'link--primary'}
            href={`${assetCanonicalUrl}.${fileExt}`}
            download={`${name}.${fileExt}`}
          >
            Download
          </a>
          <a
            className={'link--primary'}
            href={`https://open.lbry.com/${createPermanentURI(asset)}`}
            download={name}
          >
            LBRY URL
          </a>
          <a
            className={'link--primary'}
            target='_blank'
           href={`https://lbry.com/dmca/${claimId}`}
          >
            Report
          </a>
        </SpaceBetween>
        <AssetInfoFooter />
      </div>
    );
  }
};

export default AssetInfo;


================================================
FILE: client/src/containers/AssetTitle/index.js
================================================
import { connect } from 'react-redux';
import View from './view';
import { selectAsset } from '../../selectors/show';

const mapStateToProps = (props) => {
  const { claimData: { title } } = selectAsset(props.show);
  return {
    title,
  };
};

export default connect(mapStateToProps, null)(View);


================================================
FILE: client/src/containers/AssetTitle/view.jsx
================================================
import React from 'react';

const AssetTitle = ({ title }) => {
  return (
    <h2 className='asset-title'>{title}</h2>
  );
};

export default AssetTitle;


================================================
FILE: client/src/containers/ChannelClaimsDisplay/index.js
================================================
import { connect } from 'react-redux';
import { onUpdateChannelClaims } from '../../actions/show';
import View from './view';

const mapStateToProps = ({ show, site: { defaultThumbnail } }) => {
  // select channel key
  const request = show.requestList[show.request.id];
  const channelKey = request.key;
  // select channel claims
  const channel = show.channelList[channelKey] || null;
  // return props
  return {
    channelKey,
    channel,
    defaultThumbnail,
  };
};

const mapDispatchToProps = {
  onUpdateChannelClaims,
};

export default connect(mapStateToProps, mapDispatchToProps)(View);


================================================
FILE: client/src/containers/ChannelClaimsDisplay/view.jsx
================================================
import React from 'react';
import AssetPreview from '@components/AssetPreview';
import Row from '@components/Row';
import ButtonSecondary from '@components/ButtonSecondary';
import { createGroupedList } from '../../utils/createGroupedList.js';

class ChannelClaimsDisplay extends React.Component {
  constructor (props) {
    super(props);
    this.showNextResultsPage = this.showNextResultsPage.bind(this);
    this.showPreviousResultsPage = this.showPreviousResultsPage.bind(this);
  }
  showPreviousResultsPage () {
    const { channel: { claimsData: { currentPage } } } = this.props;
    const previousPage = parseInt(currentPage) - 1;
    this.showNewPage(previousPage);
  }
  showNextResultsPage () {
    const { channel: { claimsData: { currentPage } } } = this.props;
    const nextPage = parseInt(currentPage) + 1;
    this.showNewPage(nextPage);
  }
  showNewPage (page) {
    const { channelKey, channel: { name, longId } } = this.props;
    this.props.onUpdateChannelClaims(channelKey, name, longId, page);
  }
  render () {
    const {channel: {claimsData: {claims, currentPage, totalPages}}, defaultThumbnail} = this.props;
    if (claims.length > 0) {
      return (
        <div>
          <div>
            <div className={'channel-claims-display'}>
              {claims.map(claim => (
                <AssetPreview
                  defaultThumbnail={defaultThumbnail}
                  claimData={claim}
                  key={claim.claimId}
                />
              ))}
            </div>
          </div>
          <Row>
            {(currentPage > 1) &&
            <ButtonSecondary
              value={'Previous Page'}
              onClickHandler={this.showPreviousResultsPage}
            />
            }
            {(currentPage < totalPages) &&
            <ButtonSecondary
              value={'Next Page'}
              onClickHandler={this.showNextResultsPage}
            />
            }
          </Row>
        </div>
      );
    } else {
      return (
        <p>There are no claims in this channel</p>
      );
    }
  }
}

export default ChannelClaimsDisplay;


================================================
FILE: client/src/containers/ChannelCreateForm/index.js
================================================
import { connect } from 'react-redux';
import View from './view';
import {
  updateChannelAvailability,
  updateChannelCreateName,
  updateChannelCreatePassword,
  createChannel,
} from '../../actions/channelCreate';

const mapStateToProps = ({channelCreate: { name, password, error, status }}) => {
  return {
    name,
    password,
    error,
    status,
  };
};

const mapDispatchToProps = {
  updateChannelAvailability,
  updateChannelCreateName,
  updateChannelCreatePassword,
  createChannel,
};

export default connect(mapStateToProps, mapDispatchToProps)(View);


================================================
FILE: client/src/containers/ChannelCreateForm/view.jsx
================================================
import React from 'react';
import ChannelCreateNameInput from '@components/ChannelCreateNameInput';
import ChannelCreatePasswordInput from '@components/ChannelCreatePasswordInput';
import ButtonPrimary from '@components/ButtonPrimary';
import FormFeedbackDisplay from '@components/FormFeedbackDisplay';
import ProgressBar from '@components/ProgressBar';

class ChannelCreateForm extends React.Component {
  constructor (props) {
    super(props);
    this.handleNameInput = this.handleNameInput.bind(this);
    this.handlePasswordInput = this.handlePasswordInput.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  cleanseNameInput (input) {
    input = input.replace(/\s+/g, '-'); // replace spaces with dashes
    input = input.replace(/[^A-Za-z0-9-]/g, '');  // remove all characters that are not A-Z, a-z, 0-9, or '-'
    return input;
  }
  cleansePasswordInput (input) {
    input = input.replace(/\s+/g, ''); // replace spaces
    return input;
  }
  handleNameInput (event) {
    let value = this.cleanseNameInput(event.target.value);
    if (!value) {
      this.props.updateChannelCreateName('error', 'Please enter a channel name');
    } else {
      this.props.updateChannelAvailability(value);
    }
    this.props.updateChannelCreateName('value', value);
  }
  handlePasswordInput (event) {
    let value = this.cleansePasswordInput(event.target.value);
    if (!value) {
      this.props.updateChannelCreatePassword('error', 'Please enter a password');
    } else {
      this.props.updateChannelCreatePassword('error', null);
    }
    this.props.updateChannelCreatePassword('value', value);
  }
  handleSubmit (event) {
    console.log('handling submit');
    event.preventDefault();
    this.props.createChannel();
  }
  returnErrors () {
    if (this.props.name.error) {
      return this.props.name.error;
    }
    if (this.props.password.error) {
      return this.props.password.error;
    }
    return null;
  }
  render () {
    const { name, password, status } = this.props;
    const formError = this.returnErrors();
    return (
      <div>
        { !status ? (
          <form className="form-group" onSubmit={this.handleSubmit}>
            <ChannelCreateNameInput
              value={name.value}
              error={name.error}
              handleNameInput={this.handleNameInput}
            />
            <ChannelCreatePasswordInput
              value={password.value}
              handlePasswordInput={this.handlePasswordInput}
            />
            <FormFeedbackDisplay errorMessage={formError} />
            <ButtonPrimary
              type={'submit'}
              value={'Create Channel'}
              onClickHandler={this.handleSubmit}
            />
          </form>
        ) : (
          <div>
            <span className={'text--small text--secondary'}>{status}</span>
            <ProgressBar size={12} />
          </div>
        )}
      </div>
    );
  }
}

export default ChannelCreateForm;


================================================
FILE: client/src/containers/ChannelLoginForm/index.js
================================================
import { connect } from 'react-redux';
import { updateLoggedInChannel } from '../../actions/channel';
import { updateSelectedChannel } from '../../actions/publish';
import View from './view';

const mapDispatchToProps = dispatch => {
  return {
    onChannelLogin: (name, shortId, longId) => {
      dispatch(updateLoggedInChannel(name, shortId, longId));
      dispatch(updateSelectedChannel(name));
    },
  };
};

export default connect(null, mapDispatchToProps)(View);


================================================
FILE: client/src/containers/ChannelLoginForm/view.jsx
================================================
import React from 'react';
import request from '../../utils/request';
import FormFeedbackDisplay from '@components/FormFeedbackDisplay';
import ChannelLoginNameInput from '@components/ChannelLoginNameInput';
import ChannelLoginPasswordInput from '@components/ChannelLoginPasswordInput';
import ButtonPrimary from '@components/ButtonPrimary';

class ChannelLoginForm extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      error   : null,
      name    : '',
      password: '',
    };
    this.handleInput = this.handleInput.bind(this);
    this.loginToChannel = this.loginToChannel.bind(this);
  }
  handleInput (event) {
    const name = event.target.name;
    const value = event.target.value;
    this.setState({[name]: value});
  }
  loginToChannel (event) {
    event.preventDefault();
    const params = {
      method : 'POST',
      body   : JSON.stringify({username: this.state.name, password: this.state.password}),
      headers: new Headers({
        'Content-Type': 'application/json',
      }),
      credentials: 'include',
    };
    request('auth', params)
      .then(({success, channelName, shortChannelId, channelClaimId, message}) => {
        if (success) {
          this.props.onChannelLogin(channelName, shortChannelId, channelClaimId);
        } else {
          this.setState({'error': message});
        };
      })
      .catch(error => {
        if (error.message) {
          this.setState({'error': error.message});
        } else {
          this.setState({'error': error});
        }
      });
  }
  render () {
    return (
      <form className="form-group" onSubmit={this.loginToChannel}>
        <ChannelLoginNameInput
          channelName={this.state.channelName}
          handleInput={this.handleInput}
        />
        <ChannelLoginPasswordInput
          channelPassword={this.state.channelPassword}
          handleInput={this.handleInput}
        />
        <FormFeedbackDisplay errorMessage={this.state.error} />
        <ButtonPrimary
          type={'submit'}
          value={'Authenticate'}
          onClickHandler={this.loginToChannel}
        />
      </form>
    );
  }
}

export default ChannelLoginForm;


================================================
FILE: client/src/containers/ChannelSelect/index.js
================================================
import {connect} from 'react-redux';
import {setPublishInChannel, updateSelectedChannel, updateError} from '../../actions/publish';
// import isApprovedChannel from '../../../../utils/isApprovedChannel';
import View from './view';

const mapStateToProps = ({ publish, site, channel: { loggedInChannel: { name, shortId, longId } } }) => {
  return {
    // isApprovedChannel  : isApprovedChannel({ longId }, site.approvedChannels),
    publishOnlyApproved: site.publishOnlyApproved,
    // closedRegistration : site.closedRegistration,
    loggedInChannelName: name,
    publishInChannel   : publish.publishInChannel,
    selectedChannel    : publish.selectedChannel,
    channelError       : publish.error.channel,
    longId,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    onPublishInChannelChange: (value) => {
      dispatch(updateError('channel', null));
      dispatch(setPublishInChannel(value));
    },
    onChannelSelect: (value) => {
      dispatch(updateError('channel', null));
      dispatch(updateSelectedChannel(value));
    },
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(View);


================================================
FILE: client/src/containers/ChannelSelect/view.jsx
================================================
import React from 'react';
import ChannelLoginForm from '@containers/ChannelLoginForm';
import ChannelCreateForm from '@containers/ChannelCreateForm';
import { LOGIN, CREATE } from '../../constants/publish_channel_select_states';
import RowLabeled from '@components/RowLabeled';
import ChooseAnonymousPublishRadio from '@components/ChooseAnonymousPublishRadio';
import ChooseChannelPublishRadio from '@components/ChooseChannelPublishRadio';
import FormFeedbackDisplay from '@components/FormFeedbackDisplay';
import Label from '@components/Label';
import ChannelSelectDropdown from '@components/ChannelSelectDropdown';

class ChannelSelect extends React.Component {
  constructor (props) {
    super(props);
    this.toggleAnonymousPublish = this.toggleAnonymousPublish.bind(this);
    this.handleSelection = this.handleSelection.bind(this);
  }
  componentWillMount () {
    const { loggedInChannelName, onChannelSelect, publishOnlyApproved, onPublishInChannelChange } = this.props;
    if (loggedInChannelName) {
      onChannelSelect(loggedInChannelName);
    }
    if (publishOnlyApproved) {
      onPublishInChannelChange(true);
    }
  }
  toggleAnonymousPublish (event) {
    const value = event.target.value;
    if (value === 'anonymous') {
      this.props.onPublishInChannelChange(false);
    } else {
      this.props.onPublishInChannelChange(true);
    }
  }
  handleSelection (event) {
    const selectedOption = event.target.selectedOptions[0].value;
    this.props.onChannelSelect(selectedOption);
  }
  render () {
    const { publishInChannel, channelError, selectedChannel, loggedInChannelName, publishOnlyApproved } = this.props;
    if (publishOnlyApproved) {
      return (
        <React.Fragment>
          <RowLabeled
            label={<Label value={'Channel:'} />}
            content={<span>{loggedInChannelName}</span>}
          />
        </React.Fragment>
      );
    }
    return (
      <React.Fragment>
        <RowLabeled
          label={
            <ChooseAnonymousPublishRadio
              publishInChannel={publishInChannel}
              toggleAnonymousPublish={this.toggleAnonymousPublish}
            />
          }
          content={
            <ChooseChannelPublishRadio
              publishInChannel={publishInChannel}
              toggleAnonymousPublish={this.toggleAnonymousPublish}
            />
          }
        />
        <FormFe
Download .txt
gitextract_v9i4r49y/

├── .babelrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── .nvmrc
├── .prettierrc.json
├── .sequelizerc
├── .travis.yml
├── LICENSE
├── README.md
├── changelog.md
├── cli/
│   ├── configure.js
│   ├── defaults/
│   │   ├── chainqueryConfig.json
│   │   ├── lbryConfig.json
│   │   ├── loggerConfig.json
│   │   ├── mysqlConfig.json
│   │   ├── siteConfig.json
│   │   └── slackConfig.json
│   └── questions/
│       ├── mysqlQuestions.js
│       └── siteQuestions.js
├── client/
│   ├── scss/
│   │   ├── _asset-blocked.scss
│   │   ├── _asset-display.scss
│   │   ├── _asset-preview.scss
│   │   ├── _body.scss
│   │   ├── _button-primary.scss
│   │   ├── _button-secondary.scss
│   │   ├── _button.scss
│   │   ├── _channel-claims-display.scss
│   │   ├── _claim-pending.scss
│   │   ├── _click-to-copy.scss
│   │   ├── _dropzone.scss
│   │   ├── _form-feedback.scss
│   │   ├── _form.scss
│   │   ├── _horizontal-split.scss
│   │   ├── _html.scss
│   │   ├── _input.scss
│   │   ├── _label.scss
│   │   ├── _link.scss
│   │   ├── _markdown.scss
│   │   ├── _media-queries.scss
│   │   ├── _nav-bar.scss
│   │   ├── _page-content.scss
│   │   ├── _page-layout-show-lite.scss
│   │   ├── _page-layout.scss
│   │   ├── _progress-bar.scss
│   │   ├── _publish-disabled-message.scss
│   │   ├── _publish-preview.scss
│   │   ├── _publish-status.scss
│   │   ├── _publish-url-input.scss
│   │   ├── _react-app.scss
│   │   ├── _reset.scss
│   │   ├── _row.scss
│   │   ├── _select.scss
│   │   ├── _share-buttons.scss
│   │   ├── _social-share-link.scss
│   │   ├── _space-around.scss
│   │   ├── _space-between.scss
│   │   ├── _text.scss
│   │   ├── _textarea.scss
│   │   ├── _tooltip.scss
│   │   ├── _variables.scss
│   │   ├── _video.scss
│   │   ├── all.scss
│   │   └── font/
│   │       ├── Lekton/
│   │       │   └── OFL.txt
│   │       └── _font.scss
│   └── src/
│       ├── actions/
│       │   ├── channel.js
│       │   ├── channelCreate.js
│       │   ├── index.js
│       │   ├── publish.js
│       │   └── show.js
│       ├── api/
│       │   ├── assetApi.js
│       │   ├── authApi.js
│       │   ├── channelApi.js
│       │   ├── fileApi.js
│       │   ├── homepageApi.js
│       │   └── specialAssetApi.js
│       ├── app.js
│       ├── channels/
│       │   └── publish.js
│       ├── components/
│       │   ├── AboutSpeechDetails/
│       │   │   └── index.jsx
│       │   ├── AboutSpeechOverview/
│       │   │   └── index.jsx
│       │   ├── ActiveStatusBar/
│       │   │   └── index.jsx
│       │   ├── AssetInfoFooter/
│       │   │   └── index.js
│       │   ├── AssetPreview/
│       │   │   └── index.jsx
│       │   ├── AssetShareButtons/
│       │   │   └── index.js
│       │   ├── ButtonPrimary/
│       │   │   └── index.jsx
│       │   ├── ButtonPrimaryJumbo/
│       │   │   └── index.jsx
│       │   ├── ButtonSecondary/
│       │   │   └── index.jsx
│       │   ├── ChannelAbout/
│       │   │   └── index.jsx
│       │   ├── ChannelCreateNameInput/
│       │   │   └── index.jsx
│       │   ├── ChannelCreatePasswordInput/
│       │   │   └── index.jsx
│       │   ├── ChannelInfoDisplay/
│       │   │   └── index.jsx
│       │   ├── ChannelLoginNameInput/
│       │   │   └── index.jsx
│       │   ├── ChannelLoginPasswordInput/
│       │   │   └── index.jsx
│       │   ├── ChannelSelectDropdown/
│       │   │   └── index.jsx
│       │   ├── ChooseAnonymousPublishRadio/
│       │   │   └── index.jsx
│       │   ├── ChooseChannelPublishRadio/
│       │   │   └── index.jsx
│       │   ├── ClickToCopy/
│       │   │   └── index.jsx
│       │   ├── DropzoneDropItDisplay/
│       │   │   └── index.jsx
│       │   ├── DropzoneInstructionsDisplay/
│       │   │   └── index.jsx
│       │   ├── DropzonePreviewImage/
│       │   │   └── index.jsx
│       │   ├── ErrorBoundary/
│       │   │   └── index.jsx
│       │   ├── ExpandingTextArea/
│       │   │   └── index.jsx
│       │   ├── FileViewer/
│       │   │   └── index.jsx
│       │   ├── FormFeedbackDisplay/
│       │   │   └── index.jsx
│       │   ├── GAListener/
│       │   │   └── index.jsx
│       │   ├── HorizontalSplit/
│       │   │   └── index.jsx
│       │   ├── InactiveStatusBar/
│       │   │   └── index.jsx
│       │   ├── Label/
│       │   │   └── index.jsx
│       │   ├── Logo/
│       │   │   └── index.jsx
│       │   ├── Memeify/
│       │   │   ├── EditableFontface/
│       │   │   │   └── index.js
│       │   │   ├── FontFaces/
│       │   │   │   ├── .gitkeep
│       │   │   │   ├── GreenMachine.js
│       │   │   │   ├── Inferno.js
│       │   │   │   ├── Lazer.js
│       │   │   │   ├── Neon.js
│       │   │   │   ├── OldBlue.js
│       │   │   │   ├── Outline.js
│       │   │   │   ├── RetroRainbow.js
│       │   │   │   ├── TheSpecial.js
│       │   │   │   └── VaporWave.js
│       │   │   ├── RichDraggable/
│       │   │   │   └── index.js
│       │   │   └── index.js
│       │   ├── NavBar/
│       │   │   └── index.jsx
│       │   ├── NavBarChannelOptionsDropdown/
│       │   │   └── index.jsx
│       │   ├── PageLayout/
│       │   │   └── index.jsx
│       │   ├── PageLayoutShowLite/
│       │   │   └── index.jsx
│       │   ├── ProgressBar/
│       │   │   └── index.jsx
│       │   ├── PublishDescriptionInput/
│       │   │   └── index.jsx
│       │   ├── PublishFinePrint/
│       │   │   └── index.jsx
│       │   ├── PublishLicenseInput/
│       │   │   └── index.jsx
│       │   ├── PublishLicenseUrlInput/
│       │   │   └── index.jsx
│       │   ├── PublishNsfwInput/
│       │   │   └── index.jsx
│       │   ├── PublishPreview/
│       │   │   └── index.jsx
│       │   ├── PublishUrlMiddleDisplay/
│       │   │   └── index.jsx
│       │   ├── Row/
│       │   │   └── index.jsx
│       │   ├── RowLabeled/
│       │   │   └── index.jsx
│       │   ├── SocialShareLink/
│       │   │   └── index.jsx
│       │   ├── SpaceAround/
│       │   │   └── index.jsx
│       │   ├── SpaceBetween/
│       │   │   └── index.jsx
│       │   └── VerticalSplit/
│       │       └── index.jsx
│       ├── constants/
│       │   ├── asset_display_states.js
│       │   ├── channel_action_types.js
│       │   ├── channel_create_action_types.js
│       │   ├── confirmation_messages.js
│       │   ├── publish_action_types.js
│       │   ├── publish_channel_select_states.js
│       │   ├── publish_claim_states.js
│       │   ├── publish_license_urls.js
│       │   ├── show_action_types.js
│       │   └── show_request_types.js
│       ├── containers/
│       │   ├── AssetBlocked/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── AssetDisplay/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── AssetInfo/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── AssetTitle/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelClaimsDisplay/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelCreateForm/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelLoginForm/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelSelect/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ChannelTools/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── Dropzone/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── NavigationLinks/
│       │   │   ├── index.jsx
│       │   │   └── view.jsx
│       │   ├── PublishDetails/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishDisabledMessage/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishMetadataInputs/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishStatus/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishThumbnailInput/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishTitleInput/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishTool/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── PublishUrlInput/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── SEO/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   └── SiteDescription/
│       │       ├── index.jsx
│       │       └── view.jsx
│       ├── index.js
│       ├── pages/
│       │   ├── AboutPage/
│       │   │   └── index.jsx
│       │   ├── ContentPageWrapper/
│       │   │   ├── index.jsx
│       │   │   └── view.jsx
│       │   ├── EditPage/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ErrorPage/
│       │   │   └── index.jsx
│       │   ├── FaqPage/
│       │   │   └── index.jsx
│       │   ├── FourOhFourPage/
│       │   │   └── index.jsx
│       │   ├── HomePage/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── LoginPage/
│       │   │   ├── index.jsx
│       │   │   └── view.jsx
│       │   ├── MultisitePage/
│       │   │   └── index.jsx
│       │   ├── PopularPage/
│       │   │   ├── index.jsx
│       │   │   └── view.jsx
│       │   ├── ShowAssetDetails/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ShowAssetLite/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   ├── ShowChannel/
│       │   │   ├── index.js
│       │   │   └── view.jsx
│       │   └── TosPage/
│       │       └── index.jsx
│       ├── reducers/
│       │   ├── channel.js
│       │   ├── channelCreate.js
│       │   ├── index.js
│       │   ├── publish.js
│       │   ├── show.js
│       │   └── site.js
│       ├── sagas/
│       │   ├── abandon.js
│       │   ├── checkForLoggedInChannel.js
│       │   ├── createChannel.js
│       │   ├── file.js
│       │   ├── index.js
│       │   ├── logoutChannel.js
│       │   ├── publish.js
│       │   ├── rootSaga.js
│       │   ├── show_asset.js
│       │   ├── show_channel.js
│       │   ├── show_special.js
│       │   ├── show_uri.js
│       │   ├── updateChannelAvailability.js
│       │   └── updateClaimAvailability.js
│       ├── selectors/
│       │   ├── channel.js
│       │   ├── channelCreate.js
│       │   ├── publish.js
│       │   ├── show.js
│       │   └── site.js
│       └── utils/
│           ├── createAssetMetaTags.js
│           ├── createBasicMetaTags.js
│           ├── createChannelMetaTags.js
│           ├── createGroupedList.js
│           ├── createMetaTags.js
│           ├── createMetaTagsArray.js
│           ├── createPageTitle.js
│           ├── createPermanentURI.js
│           ├── determineContentTypeFromExtension.js
│           ├── dynamicImport.js
│           ├── file.js
│           ├── oEmbed.js
│           ├── publish.js
│           ├── request.js
│           └── validate.js
├── customize.md
├── devConfig/
│   ├── sequelizeCliConfig.example.js
│   └── testingConfig.example.js
├── docs/
│   ├── settings.md
│   ├── setup/
│   │   ├── conf/
│   │   │   ├── caddy/
│   │   │   │   ├── Caddyfile.template
│   │   │   │   └── caddy.service
│   │   │   ├── lbrynet/
│   │   │   │   ├── lbrynet.service.example
│   │   │   │   └── lbrynet.service.template
│   │   │   ├── nginx/
│   │   │   │   ├── letsencrypt.conf
│   │   │   │   ├── myspeech
│   │   │   │   └── ssl.conf
│   │   │   └── speech/
│   │   │       ├── chainqueryConfig.json
│   │   │       └── speech.service.draft
│   │   └── scripts/
│   │       ├── firewall.sh
│   │       └── newuser.sh
│   └── ubuntuinstall.md
├── fullstart.md
├── lintstagedrc.json
├── nginx_example_config
├── package.json
├── public/
│   ├── bundle/
│   │   └── .gitkeep
│   └── robots.txt
├── server/
│   ├── chainquery/
│   │   ├── index.debug.js
│   │   ├── index.js
│   │   ├── models/
│   │   │   ├── AbnormalClaimModel.js
│   │   │   ├── AddressModel.js
│   │   │   ├── BlockModel.js
│   │   │   ├── ClaimModel.js
│   │   │   ├── InputModel.js
│   │   │   ├── OutputModel.js
│   │   │   ├── SupportModel.js
│   │   │   ├── TransactionAddressModel.js
│   │   │   └── TransactionModel.js
│   │   ├── queries/
│   │   │   ├── abnormalClaimQueries.js
│   │   │   ├── addressQueries.js
│   │   │   ├── blockQueries.js
│   │   │   ├── claimQueries.js
│   │   │   ├── inputQueries.js
│   │   │   ├── outputQueries.js
│   │   │   ├── supportQueries.js
│   │   │   ├── transactionAddressQueries.js
│   │   │   └── transactionQueries.js
│   │   └── tables/
│   │       ├── abnormalClaimTable.js
│   │       ├── addressTable.js
│   │       ├── blockTable.js
│   │       ├── claimTable.js
│   │       ├── inputTable.js
│   │       ├── outputTable.js
│   │       ├── supportTable.js
│   │       ├── transactionAddressTable.js
│   │       └── transactionTable.js
│   ├── controllers/
│   │   ├── api/
│   │   │   ├── blocked/
│   │   │   │   └── index.js
│   │   │   ├── channel/
│   │   │   │   ├── availability/
│   │   │   │   │   ├── checkChannelAvailability.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── claims/
│   │   │   │   │   ├── channelPagination.js
│   │   │   │   │   ├── getChannelClaims.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── data/
│   │   │   │   │   ├── getChannelData.js
│   │   │   │   │   └── index.js
│   │   │   │   └── shortId/
│   │   │   │       └── index.js
│   │   │   ├── claim/
│   │   │   │   ├── abandon/
│   │   │   │   │   └── index.js
│   │   │   │   ├── availability/
│   │   │   │   │   ├── checkClaimAvailability.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── data/
│   │   │   │   │   └── index.js
│   │   │   │   ├── get/
│   │   │   │   │   └── index.js
│   │   │   │   ├── list/
│   │   │   │   │   └── index.js
│   │   │   │   ├── longId/
│   │   │   │   │   └── index.js
│   │   │   │   ├── publish/
│   │   │   │   │   ├── authentication.js
│   │   │   │   │   ├── createPublishParams.js
│   │   │   │   │   ├── createThumbnailPublishParams.js
│   │   │   │   │   ├── deleteFile.js
│   │   │   │   │   ├── index.js
│   │   │   │   │   ├── parsePublishApiRequestBody.js
│   │   │   │   │   ├── parsePublishApiRequestBody.test.js
│   │   │   │   │   ├── parsePublishApiRequestFiles.js
│   │   │   │   │   ├── parsePublishApiRequestFiles.test.js
│   │   │   │   │   ├── publish.js
│   │   │   │   │   ├── validateFileForPublish.js
│   │   │   │   │   └── validateFileTypeAndSize.js
│   │   │   │   ├── resolve/
│   │   │   │   │   └── index.js
│   │   │   │   ├── shortId/
│   │   │   │   │   └── index.js
│   │   │   │   ├── update/
│   │   │   │   │   └── index.js
│   │   │   │   └── views/
│   │   │   │       └── index.js
│   │   │   ├── file/
│   │   │   │   └── availability/
│   │   │   │       └── index.js
│   │   │   ├── homepage/
│   │   │   │   └── data/
│   │   │   │       ├── getChannelData.js
│   │   │   │       └── index.js
│   │   │   ├── oEmbed/
│   │   │   │   ├── getOEmbedDataForAsset.js
│   │   │   │   ├── getOEmbedDataForChannel.js
│   │   │   │   ├── index.js
│   │   │   │   └── parseSpeechUrl.js
│   │   │   ├── special/
│   │   │   │   └── claims/
│   │   │   │       └── index.js
│   │   │   ├── tor/
│   │   │   │   └── index.js
│   │   │   └── user/
│   │   │       └── password/
│   │   │           └── index.js
│   │   ├── assets/
│   │   │   ├── constants/
│   │   │   │   └── request_types.js
│   │   │   ├── serveAsset/
│   │   │   │   └── index.js
│   │   │   ├── serveByClaim/
│   │   │   │   └── index.js
│   │   │   ├── serveByIdentifierAndClaim/
│   │   │   │   └── index.js
│   │   │   └── utils/
│   │   │       ├── determineRequestType.js
│   │   │       ├── flipClaimNameAndId.js
│   │   │       ├── getClaimIdAndServeAsset.js
│   │   │       ├── getLocalFileRecord.js
│   │   │       ├── logRequestData.js
│   │   │       ├── serveFile.js
│   │   │       └── transformImage.js
│   │   ├── auth/
│   │   │   ├── login/
│   │   │   │   └── index.js
│   │   │   ├── logout/
│   │   │   │   └── index.js
│   │   │   ├── signup/
│   │   │   │   └── index.js
│   │   │   └── user/
│   │   │       └── index.js
│   │   ├── pages/
│   │   │   ├── sendReactApp.js
│   │   │   └── sendVideoEmbedPage.js
│   │   └── utils/
│   │       ├── errorHandlers.js
│   │       ├── getClaimId.js
│   │       └── redirect.js
│   ├── index.js
│   ├── lbrynet/
│   │   ├── index.js
│   │   └── utils/
│   │       └── handleLbrynetResponse.js
│   ├── middleware/
│   │   ├── autoblockPublishMiddleware.js
│   │   ├── httpContextMiddleware.js
│   │   ├── logMetricsMiddleware.js
│   │   ├── multipartMiddleware.js
│   │   ├── requestLogger.js
│   │   └── torCheckMiddleware.js
│   ├── migrations/
│   │   ├── ChangeCertificateColumnTypes2.js
│   │   ├── ChangeClaimColumnTypes.js
│   │   └── File_AddHeightAndWidthColumn.js
│   ├── models/
│   │   ├── blocked.js
│   │   ├── certificate.js
│   │   ├── channel.js
│   │   ├── claim.js
│   │   ├── file.js
│   │   ├── index.js
│   │   ├── metrics.js
│   │   ├── tor.js
│   │   ├── trending.js
│   │   ├── user.js
│   │   ├── utils/
│   │   │   ├── createClaimRecordData.js
│   │   │   ├── createDatabaseIfNotExists.js
│   │   │   ├── createFileRecordData.js
│   │   │   ├── returnShortId.js
│   │   │   ├── returnShortId.test.js
│   │   │   └── trendingAnalysis.js
│   │   └── views.js
│   ├── render/
│   │   ├── handleShowRender.jsx
│   │   └── renderFullPage.js
│   ├── routes/
│   │   ├── api/
│   │   │   └── index.js
│   │   ├── assets/
│   │   │   └── index.js
│   │   ├── auth/
│   │   │   └── index.js
│   │   ├── fallback/
│   │   │   └── index.js
│   │   ├── index.js
│   │   └── pages/
│   │       └── index.js
│   ├── speechPassport/
│   │   ├── index.js
│   │   └── utils/
│   │       ├── deserializeUser.js
│   │       ├── local-login.js
│   │       ├── local-signup.js
│   │       └── serializeUser.js
│   ├── task-scripts/
│   │   ├── update-channel-names.js
│   │   └── update-password.js
│   ├── utils/
│   │   ├── awaitFileSize.js
│   │   ├── blockList.js
│   │   ├── configureLogging.js
│   │   ├── configureSlack.js
│   │   ├── createModuleAliases.js
│   │   ├── fetchClaimData.js
│   │   ├── getClaimData.js
│   │   ├── getMediaDimensions.js
│   │   ├── googleAnalytics.js
│   │   ├── imageProcessing.js
│   │   ├── isRequestLocal.js
│   │   ├── isValidQueryObj.js
│   │   ├── processTrending.js
│   │   └── videoProcessing.js
│   └── views/
│       ├── embed.handlebars
│       ├── layouts/
│       │   └── embed.handlebars
│       └── partials/
│           └── logo.handlebars
├── server.js
├── test/
│   ├── end-to-end/
│   │   └── end-to-end.test.js
│   └── module-alias-boilerplate.js
├── utils/
│   ├── checkForLocalConfig.js
│   ├── createCanonicalLink.js
│   ├── createModuleAliases.js
│   ├── isApprovedChannel.js
│   ├── lbryUri.js
│   └── validateFileForPublish.js
├── webpack/
│   ├── webpack.client.config.js
│   └── webpack.server.config.js
└── webpack.config.js
Download .txt
SYMBOL INDEX (603 symbols across 140 files)

FILE: cli/questions/siteQuestions.js
  method validate (line 36) | validate (input) {

FILE: client/src/actions/channel.js
  function updateLoggedInChannel (line 5) | function updateLoggedInChannel (name, shortId, longId) {
  function checkForLoggedInChannel (line 16) | function checkForLoggedInChannel () {
  function logOutChannel (line 22) | function logOutChannel () {

FILE: client/src/actions/channelCreate.js
  function updateChannelCreateName (line 5) | function updateChannelCreateName (name, value) {
  function updateChannelCreatePassword (line 15) | function updateChannelCreatePassword (name, value) {
  function updateChannelCreateStatus (line 25) | function updateChannelCreateStatus (status) {
  function updateChannelAvailability (line 32) | function updateChannelAvailability (channel) {
  function createChannel (line 39) | function createChannel () {

FILE: client/src/actions/publish.js
  function selectFile (line 4) | function selectFile (file) {
  function clearFile (line 11) | function clearFile () {
  function setUpdateTrue (line 17) | function setUpdateTrue () {
  function setHasChanged (line 23) | function setHasChanged (status) {
  function updateMetadata (line 30) | function updateMetadata (name, value) {
  function updateClaim (line 40) | function updateClaim (value) {
  function abandonClaim (line 47) | function abandonClaim (data) {
  function setPublishInChannel (line 54) | function setPublishInChannel (channel) {
  function updatePublishStatus (line 61) | function updatePublishStatus (status, message) {
  function updateError (line 71) | function updateError (name, value) {
  function updateSelectedChannel (line 81) | function updateSelectedChannel (channelName) {
  function toggleMetadataInputs (line 88) | function toggleMetadataInputs (showMetadataInputs) {
  function onNewThumbnail (line 95) | function onNewThumbnail (file) {
  function startPublish (line 102) | function startPublish (history) {
  function validateClaim (line 109) | function validateClaim (claim) {

FILE: client/src/actions/show.js
  function onHandleShowPageUri (line 5) | function onHandleShowPageUri(params, url) {
  function onHandleShowHomepage (line 15) | function onHandleShowHomepage(params, url) {
  function onRequestError (line 25) | function onRequestError(error) {
  function onNewChannelRequest (line 32) | function onNewChannelRequest(channelName, channelId) {
  function onNewSpecialAssetRequest (line 41) | function onNewSpecialAssetRequest(name) {
  function onNewAssetRequest (line 50) | function onNewAssetRequest(name, id, channelName, channelId, extension) {
  function onRequestUpdate (line 70) | function onRequestUpdate(requestType, requestId) {
  function addRequestToRequestList (line 80) | function addRequestToRequestList(id, error, key) {
  function addAssetToAssetList (line 89) | function addAssetToAssetList(id, error, name, claimId, shortId, claimDat...
  function updateAssetViewsInList (line 96) | function updateAssetViewsInList(id, claimId, claimViews) {
  function removeAsset (line 103) | function removeAsset(data) {
  function addNewChannelToChannelList (line 112) | function addNewChannelToChannelList(id, name, shortId, longId, claimsDat...
  function onUpdateChannelClaims (line 125) | function onUpdateChannelClaims(channelKey, name, longId, page) {
  function updateChannelClaims (line 132) | function updateChannelClaims(channelListId, claimsData) {
  function fileRequested (line 141) | function fileRequested(name, claimId) {
  function updateFileAvailability (line 148) | function updateFileAvailability(status) {
  function updateDisplayAssetError (line 155) | function updateDisplayAssetError(error) {
  function toggleDetailsExpanded (line 163) | function toggleDetailsExpanded(isExpanded) {

FILE: client/src/api/assetApi.js
  function getLongClaimId (line 3) | function getLongClaimId(host, name, modifier) {
  function getShortId (line 26) | function getShortId(host, name, claimId) {
  function getClaimData (line 31) | function getClaimData(host, name, claimId) {
  function checkClaimAvailability (line 36) | function checkClaimAvailability(claim) {
  function getClaimViews (line 41) | function getClaimViews(claimId) {
  function doAbandonClaim (line 46) | function doAbandonClaim(outpoint) {

FILE: client/src/api/authApi.js
  function checkForLoggedInChannelApi (line 3) | function checkForLoggedInChannelApi () {
  function channelLogoutApi (line 9) | function channelLogoutApi () {

FILE: client/src/api/channelApi.js
  function getChannelData (line 3) | function getChannelData (host, name, id) {
  function getChannelClaims (line 9) | function getChannelClaims (host, name, longId, page) {
  function checkChannelAvailability (line 15) | function checkChannelAvailability (channel) {
  function makeCreateChannelRequest (line 20) | function makeCreateChannelRequest (username, password) {

FILE: client/src/api/fileApi.js
  function checkFileAvailability (line 3) | function checkFileAvailability (claimId, host, name) {
  function triggerClaimGet (line 8) | function triggerClaimGet (claimId, host, name) {

FILE: client/src/api/homepageApi.js
  function getHomepageChannelsData (line 3) | function getHomepageChannelsData (host, name, id) {

FILE: client/src/api/specialAssetApi.js
  function getSpecialAssetClaims (line 3) | function getSpecialAssetClaims (host, name, page) {

FILE: client/src/components/ClickToCopy/index.jsx
  class ClickToCopy (line 4) | class ClickToCopy extends React.Component {
    method constructor (line 5) | constructor (props) {
    method copyToClipboard (line 9) | copyToClipboard () {
    method render (line 20) | render () {

FILE: client/src/components/DropzonePreviewImage/index.jsx
  class PublishPreview (line 4) | class PublishPreview extends React.Component {
    method constructor (line 5) | constructor (props) {
    method componentDidMount (line 13) | componentDidMount () {
    method componentWillReceiveProps (line 21) | componentWillReceiveProps (newProps) {
    method setPreviewImageSourceFromFile (line 33) | setPreviewImageSourceFromFile (file) {
    method setPreviewImageSource (line 40) | setPreviewImageSource (file) {
    method render (line 51) | render () {

FILE: client/src/components/ErrorBoundary/index.jsx
  class ErrorBoundary (line 2) | class ErrorBoundary extends React.Component {
    method constructor (line 3) | constructor(props) {
    method componentDidCatch (line 8) | componentDidCatch(error, info) {
    method render (line 15) | render() {

FILE: client/src/components/ExpandingTextArea/index.jsx
  class ExpandingTextarea (line 4) | class ExpandingTextarea extends Component {
    method constructor (line 5) | constructor (props) {
    method componentDidMount (line 9) | componentDidMount () {
    method _handleChange (line 12) | _handleChange (event) {
    method adjustTextarea (line 17) | adjustTextarea ({ target = this.el }) {
    method render (line 21) | render () {

FILE: client/src/components/FileViewer/index.jsx
  class FileViewer (line 6) | class FileViewer extends React.Component {
    method constructor (line 8) | constructor (props) {
    method componentDidMount (line 21) | componentDidMount () {
    method componentWillUnmount (line 34) | componentWillUnmount () {
    method render (line 38) | render () {

FILE: client/src/components/GAListener/index.jsx
  class GAListener (line 17) | class GAListener extends React.Component {
    method componentDidMount (line 18) | componentDidMount () {
    method sendPageView (line 23) | sendPageView (location) {
    method render (line 30) | render () {

FILE: client/src/components/HorizontalSplit/index.jsx
  class HorizontalSplit (line 3) | class HorizontalSplit extends React.Component {
    method render (line 4) | render () {

FILE: client/src/components/Logo/index.jsx
  function Logo (line 4) | function Logo () {

FILE: client/src/components/Memeify/EditableFontface/index.js
  class EditableFontface (line 5) | class EditableFontface extends Component {
    method constructor (line 6) | constructor(props) {
    method componentDidMount (line 17) | componentDidMount() {
    method render (line 25) | render() {
    method onKeyPress (line 75) | onKeyPress(e) {
    method onChange (line 82) | onChange(e) {
  constant PRESETS (line 90) | const PRESETS = {

FILE: client/src/components/Memeify/FontFaces/TheSpecial.js
  constant THE_FACE (line 40) | const THE_FACE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJ4AAADM...

FILE: client/src/components/Memeify/RichDraggable/index.js
  class RichDraggable (line 9) | class RichDraggable extends Component {
    method constructor (line 10) | constructor(props) {
    method componentDidMount (line 20) | componentDidMount() {
    method render (line 30) | render() {

FILE: client/src/components/Memeify/index.js
  class Memeify (line 89) | class Memeify extends Component {
    method constructor (line 90) | constructor(props) {
    method componentDidMount (line 120) | componentDidMount() {
    method setActiveElement (line 135) | setActiveElement(activeElement) {
    method addElement (line 139) | addElement() {
    method removeElement (line 158) | removeElement() {
    method onSave (line 178) | async onSave() {
    method renderContentsToCanvas (line 186) | async renderContentsToCanvas() {
    method render (line 204) | render() {
    method setFont (line 236) | setFont(fontName) {

FILE: client/src/components/NavBar/index.jsx
  class NavBar (line 7) | class NavBar extends React.Component {
    method render (line 8) | render () {

FILE: client/src/components/NavBarChannelOptionsDropdown/index.jsx
  function NavBarChannelDropdown (line 3) | function NavBarChannelDropdown ({ channelName, handleSelection, defaultS...

FILE: client/src/components/PageLayout/index.jsx
  class PageLayout (line 6) | class PageLayout extends React.Component {
    method render (line 7) | render () {

FILE: client/src/components/PageLayoutShowLite/index.jsx
  class PageLayoutShowLite (line 5) | class PageLayoutShowLite extends React.Component {
    method shouldComponentUpdate (line 6) | shouldComponentUpdate () {
    method render (line 9) | render () {

FILE: client/src/components/ProgressBar/index.jsx
  class ProgressBar (line 6) | class ProgressBar extends React.Component {
    method constructor (line 7) | constructor (props) {
    method componentDidMount (line 19) | componentDidMount () {
    method componentWillUnmount (line 23) | componentWillUnmount () {
    method createBars (line 26) | createBars () {
    method startProgressBar (line 33) | startProgressBar () {
    method updateProgressBar (line 36) | updateProgressBar () {
    method stopProgressBar (line 60) | stopProgressBar () {
    method render (line 63) | render () {

FILE: client/src/components/PublishPreview/index.jsx
  class PublishPreview (line 10) | class PublishPreview extends React.Component {
    method render (line 11) | render () {

FILE: client/src/components/PublishUrlMiddleDisplay/index.jsx
  function UrlMiddle (line 4) | function UrlMiddle ({publishInChannel, selectedChannel, loggedInChannelN...

FILE: client/src/components/Row/index.jsx
  class Row (line 3) | class Row extends React.Component {
    method render (line 4) | render () {

FILE: client/src/components/RowLabeled/index.jsx
  class RowLabeled (line 3) | class RowLabeled extends React.Component {
    method render (line 4) | render () {

FILE: client/src/components/SocialShareLink/index.jsx
  class SocialShareLink (line 3) | class SocialShareLink extends React.Component {
    method render (line 4) | render () {

FILE: client/src/components/SpaceAround/index.jsx
  class SpaceAround (line 3) | class SpaceAround extends React.Component {
    method render (line 4) | render () {

FILE: client/src/components/SpaceBetween/index.jsx
  class SpaceBetween (line 3) | class SpaceBetween extends React.Component {
    method render (line 4) | render () {

FILE: client/src/components/VerticalSplit/index.jsx
  class VerticalSplit (line 3) | class VerticalSplit extends React.Component {
    method render (line 4) | render () {

FILE: client/src/constants/asset_display_states.js
  constant LOCAL_CHECK (line 1) | const LOCAL_CHECK = 'LOCAL_CHECK';
  constant UNAVAILABLE (line 2) | const UNAVAILABLE = 'UNAVAILABLE';
  constant ERROR (line 3) | const ERROR = 'ERROR';
  constant AVAILABLE (line 4) | const AVAILABLE = 'AVAILABLE';

FILE: client/src/constants/channel_action_types.js
  constant CHANNEL_UPDATE (line 1) | const CHANNEL_UPDATE = 'CHANNEL_UPDATE';
  constant CHANNEL_LOGIN_CHECK (line 2) | const CHANNEL_LOGIN_CHECK = 'CHANNEL_LOGIN_CHECK';
  constant CHANNEL_LOGOUT (line 3) | const CHANNEL_LOGOUT = 'CHANNEL_LOGOUT';

FILE: client/src/constants/channel_create_action_types.js
  constant CHANNEL_CREATE_UPDATE_NAME (line 1) | const CHANNEL_CREATE_UPDATE_NAME = 'CHANNEL_CREATE_UPDATE_NAME';
  constant CHANNEL_CREATE_UPDATE_PASSWORD (line 2) | const CHANNEL_CREATE_UPDATE_PASSWORD = 'CHANNEL_CREATE_UPDATE_PASSWORD';
  constant CHANNEL_CREATE_UPDATE_STATUS (line 3) | const CHANNEL_CREATE_UPDATE_STATUS = 'CHANNEL_CREATE_UPDATE_STATUS';
  constant CHANNEL_AVAILABILITY (line 4) | const CHANNEL_AVAILABILITY = 'CHANNEL_AVAILABILITY';
  constant CHANNEL_CREATE (line 5) | const CHANNEL_CREATE = 'CHANNEL_CREATE';

FILE: client/src/constants/confirmation_messages.js
  constant SAVE (line 1) | const SAVE = 'Everything not saved will be lost. Are you sure you want t...

FILE: client/src/constants/publish_action_types.js
  constant FILE_SELECTED (line 1) | const FILE_SELECTED = 'FILE_SELECTED';
  constant FILE_CLEAR (line 2) | const FILE_CLEAR = 'FILE_CLEAR';
  constant METADATA_UPDATE (line 3) | const METADATA_UPDATE = 'METADATA_UPDATE';
  constant CLAIM_UPDATE (line 4) | const CLAIM_UPDATE = 'CLAIM_UPDATE';
  constant SET_PUBLISH_IN_CHANNEL (line 5) | const SET_PUBLISH_IN_CHANNEL = 'SET_PUBLISH_IN_CHANNEL';
  constant PUBLISH_STATUS_UPDATE (line 6) | const PUBLISH_STATUS_UPDATE = 'PUBLISH_STATUS_UPDATE';
  constant ERROR_UPDATE (line 7) | const ERROR_UPDATE = 'ERROR_UPDATE';
  constant SELECTED_CHANNEL_UPDATE (line 8) | const SELECTED_CHANNEL_UPDATE = 'SELECTED_CHANNEL_UPDATE';
  constant TOGGLE_METADATA_INPUTS (line 9) | const TOGGLE_METADATA_INPUTS = 'TOGGLE_METADATA_INPUTS';
  constant THUMBNAIL_NEW (line 10) | const THUMBNAIL_NEW = 'THUMBNAIL_NEW';
  constant PUBLISH_START (line 11) | const PUBLISH_START = 'PUBLISH_START';
  constant CLAIM_AVAILABILITY (line 12) | const CLAIM_AVAILABILITY = 'CLAIM_AVAILABILITY';
  constant SET_UPDATE_TRUE (line 13) | const SET_UPDATE_TRUE = 'SET_UPDATE_TRUE';
  constant ABANDON_CLAIM (line 14) | const ABANDON_CLAIM = 'ABANDON_CLAIM';
  constant SET_HAS_CHANGED (line 15) | const SET_HAS_CHANGED = 'SET_HAS_CHANGED';

FILE: client/src/constants/publish_channel_select_states.js
  constant LOGIN (line 1) | const LOGIN = 'Existing';
  constant CREATE (line 2) | const CREATE = 'New';

FILE: client/src/constants/publish_claim_states.js
  constant LOAD_START (line 1) | const LOAD_START = 'LOAD_START';
  constant LOADING (line 2) | const LOADING = 'LOADING';
  constant PUBLISHING (line 3) | const PUBLISHING = 'PUBLISHING';
  constant SUCCEEDED (line 4) | const SUCCEEDED = 'SUCCEEDED';
  constant FAILED (line 5) | const FAILED = 'FAILED';
  constant ABANDONING (line 6) | const ABANDONING = 'ABANDONING';

FILE: client/src/constants/publish_license_urls.js
  constant CC_LICENSES (line 1) | const CC_LICENSES = [
  constant LICENSES (line 28) | const LICENSES = ['Public Domain', 'Other', 'Copyright', 'Creative Commo...
  constant PUBLIC_DOMAIN (line 30) | const PUBLIC_DOMAIN = 'Public Domain';
  constant OTHER (line 31) | const OTHER = 'other';
  constant COPYRIGHT (line 32) | const COPYRIGHT = 'copyright';
  constant CREATIVE_COMMONS (line 33) | const CREATIVE_COMMONS = 'Creative Commons';

FILE: client/src/constants/show_action_types.js
  constant HANDLE_SHOW_URI (line 2) | const HANDLE_SHOW_URI = 'HANDLE_SHOW_URI';
  constant HANDLE_SHOW_HOMEPAGE (line 3) | const HANDLE_SHOW_HOMEPAGE = 'HANDLE_SHOW_HOMEPAGE';
  constant REQUEST_ERROR (line 4) | const REQUEST_ERROR = 'REQUEST_ERROR';
  constant REQUEST_UPDATE (line 5) | const REQUEST_UPDATE = 'REQUEST_UPDATE';
  constant ASSET_REQUEST_NEW (line 6) | const ASSET_REQUEST_NEW = 'ASSET_REQUEST_NEW';
  constant CHANNEL_REQUEST_NEW (line 7) | const CHANNEL_REQUEST_NEW = 'CHANNEL_REQUEST_NEW';
  constant SPECIAL_ASSET_REQUEST_NEW (line 8) | const SPECIAL_ASSET_REQUEST_NEW = 'SPECIAL_ASSET_REQUEST_NEW';
  constant REQUEST_LIST_ADD (line 9) | const REQUEST_LIST_ADD = 'REQUEST_LIST_ADD';
  constant ASSET_ADD (line 12) | const ASSET_ADD = 'ASSET_ADD';
  constant ASSET_VIEWS_UPDATE (line 13) | const ASSET_VIEWS_UPDATE = 'ASSET_VIEWS_UPDATE';
  constant ASSET_UPDATE_CLAIMDATA (line 14) | const ASSET_UPDATE_CLAIMDATA = 'ASSET_UPDATE_CLAIMDATA';
  constant ASSET_REMOVE (line 15) | const ASSET_REMOVE = 'ASSET_REMOVE';
  constant CHANNEL_ADD (line 18) | const CHANNEL_ADD = 'CHANNEL_ADD';
  constant CHANNEL_CLAIMS_UPDATE_ASYNC (line 20) | const CHANNEL_CLAIMS_UPDATE_ASYNC = 'CHANNEL_CLAIMS_UPDATE_ASYNC';
  constant CHANNEL_CLAIMS_UPDATE_SUCCEEDED (line 21) | const CHANNEL_CLAIMS_UPDATE_SUCCEEDED = 'CHANNEL_CLAIMS_UPDATE_SUCCEEDED';
  constant FILE_REQUESTED (line 24) | const FILE_REQUESTED = 'FILE_REQUESTED';
  constant FILE_AVAILABILITY_UPDATE (line 25) | const FILE_AVAILABILITY_UPDATE = 'FILE_AVAILABILITY_UPDATE';
  constant DISPLAY_ASSET_ERROR (line 26) | const DISPLAY_ASSET_ERROR = 'DISPLAY_ASSET_ERROR';
  constant TOGGLE_DETAILS_EXPANDED (line 27) | const TOGGLE_DETAILS_EXPANDED = 'TOGGLE_DETAILS_EXPANDED';

FILE: client/src/constants/show_request_types.js
  constant CHANNEL (line 1) | const CHANNEL = 'CHANNEL';
  constant ASSET_LITE (line 2) | const ASSET_LITE = 'ASSET_LITE';
  constant ASSET_DETAILS (line 3) | const ASSET_DETAILS = 'ASSET_DETAILS';
  constant SPECIAL_ASSET (line 4) | const SPECIAL_ASSET = 'SPECIAL_ASSET';

FILE: client/src/containers/AssetBlocked/view.jsx
  class BlockedLeft (line 5) | class BlockedLeft extends React.PureComponent {
    method render (line 6) | render () {
  class BlockedRight (line 15) | class BlockedRight extends React.PureComponent {
    method render (line 16) | render () {
  class AssetBlocked (line 26) | class AssetBlocked extends React.Component {
    method componentDidMount (line 27) | componentDidMount () {
    method render (line 42) | render () {

FILE: client/src/containers/AssetDisplay/view.jsx
  class AvailableContent (line 10) | class AvailableContent extends React.Component {
    method render (line 11) | render () {
  class AssetDisplay (line 69) | class AssetDisplay extends React.Component {
    method componentDidMount (line 70) | componentDidMount () {
    method render (line 74) | render () {

FILE: client/src/containers/AssetInfo/view.jsx
  class AssetInfo (line 17) | class AssetInfo extends React.Component {
    method render (line 18) | render () {

FILE: client/src/containers/ChannelClaimsDisplay/view.jsx
  class ChannelClaimsDisplay (line 7) | class ChannelClaimsDisplay extends React.Component {
    method constructor (line 8) | constructor (props) {
    method showPreviousResultsPage (line 13) | showPreviousResultsPage () {
    method showNextResultsPage (line 18) | showNextResultsPage () {
    method showNewPage (line 23) | showNewPage (page) {
    method render (line 27) | render () {

FILE: client/src/containers/ChannelCreateForm/view.jsx
  class ChannelCreateForm (line 8) | class ChannelCreateForm extends React.Component {
    method constructor (line 9) | constructor (props) {
    method cleanseNameInput (line 15) | cleanseNameInput (input) {
    method cleansePasswordInput (line 20) | cleansePasswordInput (input) {
    method handleNameInput (line 24) | handleNameInput (event) {
    method handlePasswordInput (line 33) | handlePasswordInput (event) {
    method handleSubmit (line 42) | handleSubmit (event) {
    method returnErrors (line 47) | returnErrors () {
    method render (line 56) | render () {

FILE: client/src/containers/ChannelLoginForm/view.jsx
  class ChannelLoginForm (line 8) | class ChannelLoginForm extends React.Component {
    method constructor (line 9) | constructor (props) {
    method handleInput (line 19) | handleInput (event) {
    method loginToChannel (line 24) | loginToChannel (event) {
    method render (line 50) | render () {

FILE: client/src/containers/ChannelSelect/view.jsx
  class ChannelSelect (line 12) | class ChannelSelect extends React.Component {
    method constructor (line 13) | constructor (props) {
    method componentWillMount (line 18) | componentWillMount () {
    method toggleAnonymousPublish (line 27) | toggleAnonymousPublish (event) {
    method handleSelection (line 35) | handleSelection (event) {
    method render (line 39) | render () {

FILE: client/src/containers/ChannelTools/view.jsx
  class ChannelTools (line 6) | class ChannelTools extends React.Component {
    method render (line 7) | render () {

FILE: client/src/containers/Dropzone/view.jsx
  class Dropzone (line 20) | class Dropzone extends React.Component {
    method constructor (line 21) | constructor (props) {
    method componentDidMount (line 53) | componentDidMount() {
    method componentDidUpdate (line 60) | componentDidUpdate(prevProps) {
    method updateFilePreview (line 66) | updateFilePreview() {
    method handleDrop (line 78) | handleDrop (event) {
    method handleDragOver (line 91) | handleDragOver (event) {
    method handleDragEnd (line 95) | handleDragEnd (event) {
    method handleDragEnter (line 106) | handleDragEnter () {
    method handleDragLeave (line 110) | handleDragLeave () {
    method handleMouseEnter (line 114) | handleMouseEnter () {
    method handleMouseLeave (line 118) | handleMouseLeave () {
    method handleClick (line 122) | handleClick (event) {
    method handleFileInput (line 127) | handleFileInput (event) {
    method chooseFile (line 133) | chooseFile (file) {
    method selectFileFromCanvas (line 145) | selectFileFromCanvas (canvas) {
    method render (line 162) | render () {

FILE: client/src/containers/NavigationLinks/view.jsx
  constant VIEW (line 6) | const VIEW = 'VIEW';
  constant LOGOUT (line 7) | const LOGOUT = 'LOGOUT';
  class NavigationLinks (line 9) | class NavigationLinks extends React.Component {
    method constructor (line 10) | constructor (props) {
    method componentDidMount (line 14) | componentDidMount () {
    method handleSelection (line 17) | handleSelection (event) {
    method render (line 32) | render () {

FILE: client/src/containers/PublishDetails/view.jsx
  class PublishDetails (line 16) | class PublishDetails extends React.Component {
    method constructor (line 17) | constructor (props) {
    method onPublishSubmit (line 23) | onPublishSubmit () {
    method abandonClaim (line 26) | abandonClaim () {
    method onCancel (line 33) | onCancel () {
    method render (line 43) | render () {

FILE: client/src/containers/PublishDisabledMessage/view.jsx
  class PublishDisabledMessage (line 3) | class PublishDisabledMessage extends React.Component {
    method render (line 4) | render () {

FILE: client/src/containers/PublishMetadataInputs/view.jsx
  class PublishMetadataInputs (line 8) | class PublishMetadataInputs extends React.Component {
    method constructor (line 9) | constructor (props) {
    method toggleShowInputs (line 15) | toggleShowInputs () {
    method handleInput (line 18) | handleInput (event) {
    method handleSelect (line 24) | handleSelect (event) {
    method render (line 32) | render () {

FILE: client/src/containers/PublishStatus/view.jsx
  class PublishStatus (line 7) | class PublishStatus extends React.Component {
    method render (line 8) | render () {

FILE: client/src/containers/PublishThumbnailInput/view.jsx
  function dataURItoBlob (line 5) | function dataURItoBlob (dataURI) {
  class PublishThumbnailInput (line 18) | class PublishThumbnailInput extends React.Component {
    method constructor (line 19) | constructor (props) {
    method componentDidMount (line 32) | componentDidMount () {
    method componentWillReceiveProps (line 36) | componentWillReceiveProps (nextProps) {
    method setVideoSource (line 43) | setVideoSource (file) {
    method handleVideoLoadedData (line 53) | handleVideoLoadedData (event) {
    method handleSliderChange (line 68) | handleSliderChange (event) {
    method createThumbnail (line 78) | createThumbnail () {
    method render (line 95) | render () {

FILE: client/src/containers/PublishTitleInput/view.jsx
  class PublishTitleInput (line 3) | class PublishTitleInput extends React.Component {
    method constructor (line 4) | constructor (props) {
    method handleInput (line 8) | handleInput (e) {
    method render (line 13) | render () {

FILE: client/src/containers/PublishTool/view.jsx
  class PublishTool (line 9) | class PublishTool extends React.Component {
    method render (line 10) | render () {

FILE: client/src/containers/PublishUrlInput/view.jsx
  class PublishUrlInput (line 5) | class PublishUrlInput extends React.Component {
    method constructor (line 6) | constructor (props) {
    method cleanseInput (line 10) | cleanseInput (input) {
    method componentDidMount (line 15) | componentDidMount () {
    method componentWillReceiveProps (line 21) | componentWillReceiveProps ({ claim, fileName }) {
    method setInitialClaimName (line 27) | setInitialClaimName (fileName) {
    method handleInput (line 32) | handleInput (event) {
    method updateAndValidateClaimInput (line 37) | updateAndValidateClaimInput (value) {
    method render (line 45) | render () {

FILE: client/src/containers/SEO/view.jsx
  class SEO (line 13) | class SEO extends React.Component {
    method render (line 14) | render () {

FILE: client/src/containers/SiteDescription/view.jsx
  class SiteDescription (line 3) | class SiteDescription extends React.Component {
    method render (line 4) | render () {

FILE: client/src/pages/AboutPage/index.jsx
  class AboutPage (line 8) | class AboutPage extends React.Component {
    method render (line 9) | render () {

FILE: client/src/pages/ContentPageWrapper/view.jsx
  class ContentPageWrapper (line 15) | class ContentPageWrapper extends React.Component {
    method componentDidMount (line 16) | componentDidMount () {
    method componentWillReceiveProps (line 20) | componentWillReceiveProps (nextProps) {
    method render (line 25) | render () {

FILE: client/src/pages/EditPage/view.jsx
  class EditPage (line 6) | class EditPage extends React.Component {
    method componentDidMount (line 7) | componentDidMount () {
    method componentWillUnmount (line 16) | componentWillUnmount () {
    method render (line 19) | render () {

FILE: client/src/pages/ErrorPage/index.jsx
  class ErrorPage (line 5) | class ErrorPage extends React.Component {
    method render (line 6) | render () {

FILE: client/src/pages/FaqPage/index.jsx
  class FaqPage (line 5) | class FaqPage extends React.Component {
    method render (line 6) | render () {

FILE: client/src/pages/FourOhFourPage/index.jsx
  class FourOhForPage (line 4) | class FourOhForPage extends React.Component {
    method render (line 5) | render () {

FILE: client/src/pages/HomePage/view.jsx
  class HomePage (line 6) | class HomePage extends React.Component {
    method componentWillUnmount (line 7) | componentWillUnmount () {
    method render (line 10) | render () {

FILE: client/src/pages/LoginPage/view.jsx
  class LoginPage (line 9) | class LoginPage extends React.Component {
    method componentWillReceiveProps (line 10) | componentWillReceiveProps (newProps) {
    method render (line 16) | render () {

FILE: client/src/pages/MultisitePage/index.jsx
  class MultisitePage (line 50) | class MultisitePage extends React.Component {
    method render (line 51) | render () {

FILE: client/src/pages/PopularPage/view.jsx
  class PopularPage (line 4) | class PopularPage extends React.Component {
    method componentDidMount (line 5) | componentDidMount () {
    method componentWillReceiveProps (line 9) | componentWillReceiveProps (nextProps) {
    method render (line 15) | render () {

FILE: client/src/pages/ShowAssetDetails/view.jsx
  class ShowAssetDetails (line 10) | class ShowAssetDetails extends React.Component {
    method constructor (line 12) | constructor (props) {
    method toggleExpandDetails (line 17) | toggleExpandDetails () {
    method render (line 21) | render () {

FILE: client/src/pages/ShowAssetLite/view.jsx
  class ShowLite (line 18) | class ShowLite extends React.Component {
    method render (line 19) | render () {

FILE: client/src/pages/ShowChannel/view.jsx
  class ShowChannel (line 8) | class ShowChannel extends React.Component {
    method render (line 9) | render () {

FILE: client/src/pages/TosPage/index.jsx
  class TosPage (line 5) | class TosPage extends React.Component {
    method render (line 6) | render () {

FILE: client/src/utils/createAssetMetaTags.js
  constant VIDEO (line 11) | const VIDEO = 'VIDEO';
  constant IMAGE (line 12) | const IMAGE = 'IMAGE';
  constant GIF (line 13) | const GIF = 'GIF';
  constant TEXT (line 14) | const TEXT = 'TEXT';

FILE: client/src/utils/dynamicImport.js
  function getDeepestChildValue (line 1) | function getDeepestChildValue (parent, childrenKeys) {

FILE: client/src/utils/file.js
  constant SIZE_MB (line 7) | const SIZE_MB = 1000000;
  function validateFile (line 9) | function validateFile(file) {

FILE: client/src/utils/request.js
  function parseJSON (line 3) | function parseJSON (response) {
  function checkStatus (line 10) | function checkStatus (response, jsonResponse) {
  function request (line 19) | function request (url, options) {

FILE: server/chainquery/index.js
  constant DATABASE_STRUCTURE (line 24) | const DATABASE_STRUCTURE = {
  constant DATABASE_STRUCTURE_KEYS (line 94) | const DATABASE_STRUCTURE_KEYS = Object.keys(DATABASE_STRUCTURE);

FILE: server/chainquery/models/AbnormalClaimModel.js
  method set (line 18) | set() { }
  method set (line 22) | set() { }
  method set (line 26) | set() { }
  method set (line 30) | set() { }
  method set (line 34) | set() { }
  method set (line 38) | set() { }
  method set (line 42) | set() { }
  method set (line 46) | set() { }
  method set (line 50) | set() { }
  method set (line 54) | set() { }
  method set (line 58) | set() { }
  method set (line 62) | set() { }

FILE: server/chainquery/models/AddressModel.js
  method set (line 18) | set() { }
  method set (line 22) | set() { }
  method set (line 26) | set() { }
  method set (line 30) | set() { }
  method set (line 34) | set() { }

FILE: server/chainquery/models/BlockModel.js
  method set (line 18) | set() { }
  method set (line 22) | set() { }
  method set (line 26) | set() { }
  method set (line 30) | set() { }
  method set (line 34) | set() { }
  method set (line 38) | set() { }
  method set (line 42) | set() { }
  method set (line 46) | set() { }
  method set (line 50) | set() { }
  method set (line 54) | set() { }
  method set (line 58) | set() { }
  method set (line 62) | set() { }
  method set (line 66) | set() { }
  method set (line 70) | set() { }
  method set (line 74) | set() { }
  method set (line 78) | set() { }
  method set (line 82) | set() { }
  method set (line 86) | set() { }
  method set (line 90) | set() { }
  method set (line 94) | set() { }

FILE: server/chainquery/models/ClaimModel.js
  method generated_extension (line 8) | generated_extension() {
  method set (line 25) | set() {}
  method set (line 29) | set() {}
  method set (line 33) | set() {}
  method set (line 37) | set() {}
  method set (line 41) | set() {}
  method set (line 45) | set() {}
  method set (line 49) | set() {}
  method set (line 53) | set() {}
  method set (line 57) | set() {}
  method set (line 61) | set() {}
  method set (line 65) | set() {}
  method set (line 69) | set() {}
  method set (line 73) | set() {}
  method set (line 77) | set() {}
  method set (line 81) | set() {}
  method set (line 85) | set() {}
  method set (line 89) | set() {}
  method set (line 93) | set() {}
  method set (line 97) | set() {}
  method set (line 101) | set() {}
  method set (line 105) | set() {}
  method set (line 109) | set() {}
  method set (line 113) | set() {}
  method set (line 117) | set() {}
  method set (line 121) | set() {}
  method set (line 125) | set() {}
  method set (line 129) | set() {}
  method set (line 133) | set() {}
  method set (line 137) | set() {}
  method set (line 141) | set() {}
  method set (line 145) | set() {}

FILE: server/chainquery/models/InputModel.js
  method set (line 18) | set() { }
  method set (line 22) | set() { }
  method set (line 26) | set() { }
  method set (line 30) | set() { }
  method set (line 34) | set() { }
  method set (line 38) | set() { }
  method set (line 42) | set() { }
  method set (line 46) | set() { }
  method set (line 50) | set() { }
  method set (line 54) | set() { }
  method set (line 58) | set() { }
  method set (line 62) | set() { }
  method set (line 66) | set() { }
  method set (line 70) | set() { }
  method set (line 74) | set() { }

FILE: server/chainquery/models/OutputModel.js
  method set (line 18) | set() { }
  method set (line 22) | set() { }
  method set (line 26) | set() { }
  method set (line 30) | set() { }
  method set (line 34) | set() { }
  method set (line 38) | set() { }
  method set (line 42) | set() { }
  method set (line 46) | set() { }
  method set (line 50) | set() { }
  method set (line 54) | set() { }
  method set (line 58) | set() { }
  method set (line 62) | set() { }
  method set (line 66) | set() { }
  method set (line 70) | set() { }
  method set (line 74) | set() { }

FILE: server/chainquery/models/SupportModel.js
  method set (line 18) | set() { }
  method set (line 22) | set() { }
  method set (line 26) | set() { }
  method set (line 30) | set() { }
  method set (line 34) | set() { }
  method set (line 38) | set() { }
  method set (line 42) | set() { }
  method set (line 46) | set() { }

FILE: server/chainquery/models/TransactionAddressModel.js
  method set (line 18) | set() { }
  method set (line 23) | set() { }
  method set (line 27) | set() { }
  method set (line 31) | set() { }

FILE: server/chainquery/models/TransactionModel.js
  method set (line 18) | set() { }
  method set (line 22) | set() { }
  method set (line 26) | set() { }
  method set (line 30) | set() { }
  method set (line 34) | set() { }
  method set (line 38) | set() { }
  method set (line 42) | set() { }
  method set (line 46) | set() { }
  method set (line 50) | set() { }
  method set (line 54) | set() { }
  method set (line 58) | set() { }
  method set (line 62) | set() { }
  method set (line 66) | set() { }
  method set (line 70) | set() {}

FILE: server/chainquery/tables/abnormalClaimTable.js
  method createModel (line 4) | createModel(...args) {
  method associate (line 8) | associate(db) {

FILE: server/chainquery/tables/addressTable.js
  method createModel (line 4) | createModel(...args) {
  method associate (line 8) | associate(db) {

FILE: server/chainquery/tables/blockTable.js
  method createModel (line 4) | createModel(...args) {
  method associate (line 8) | associate(db) {

FILE: server/chainquery/tables/claimTable.js
  method createModel (line 4) | createModel(...args) {
  method associate (line 8) | associate(db) {

FILE: server/chainquery/tables/inputTable.js
  method createModel (line 4) | createModel(...args) {
  method associate (line 8) | associate(db) {

FILE: server/chainquery/tables/outputTable.js
  method createModel (line 4) | createModel(...args) {
  method associate (line 8) | associate(db) {

FILE: server/chainquery/tables/supportTable.js
  method createModel (line 4) | createModel(...args) {
  method associate (line 8) | associate(db) {

FILE: server/chainquery/tables/transactionAddressTable.js
  method createModel (line 4) | createModel(...args) {
  method associate (line 8) | associate(db) {

FILE: server/chainquery/tables/transactionTable.js
  method createModel (line 4) | createModel(...args) {
  method associate (line 8) | associate(db) {

FILE: server/controllers/api/channel/availability/index.js
  function addAtSymbolIfNecessary (line 11) | function addAtSymbolIfNecessary (name) {

FILE: server/controllers/api/channel/claims/channelPagination.js
  constant CLAIMS_PER_PAGE (line 1) | const CLAIMS_PER_PAGE = 12;
  method returnPaginatedChannelClaims (line 4) | returnPaginatedChannelClaims (channelName, longChannelClaimId, claims, p...
  method getPageFromQuery (line 18) | getPageFromQuery (page) {
  method extractPageFromClaims (line 24) | extractPageFromClaims (claims, pageNumber) {
  method determineTotalPages (line 34) | determineTotalPages (claims) {
  method determinePreviousPage (line 50) | determinePreviousPage (currentPage) {
  method determineNextPage (line 56) | determineNextPage (totalPages, currentPage) {
  method determineTotalClaims (line 62) | determineTotalClaims (claims) {

FILE: server/controllers/api/channel/claims/index.js
  constant NO_CHANNEL (line 4) | const NO_CHANNEL = 'NO_CHANNEL';

FILE: server/controllers/api/channel/data/index.js
  constant NO_CHANNEL (line 6) | const NO_CHANNEL = 'NO_CHANNEL';
  constant LONG_ID (line 7) | const LONG_ID = 'longId';
  constant SHORT_ID (line 8) | const SHORT_ID = 'shortId';
  constant LONG_CLAIM_LENGTH (line 9) | const LONG_CLAIM_LENGTH = 40;

FILE: server/controllers/api/claim/longId/index.js
  constant NO_CHANNEL (line 8) | const NO_CHANNEL = 'NO_CHANNEL';
  constant NO_CLAIM (line 9) | const NO_CLAIM = 'NO_CLAIM';
  constant BLOCKED_CLAIM (line 10) | const BLOCKED_CLAIM = 'BLOCKED_CLAIM';

FILE: server/controllers/api/claim/publish/index.js
  constant CLAIM_TAKEN (line 28) | const CLAIM_TAKEN = 'CLAIM_TAKEN';
  constant UNAPPROVED_CHANNEL (line 29) | const UNAPPROVED_CHANNEL = 'UNAPPROVED_CHANNEL';

FILE: server/controllers/api/claim/publish/validateFileForPublish.js
  constant SIZE_MB (line 7) | const SIZE_MB = 1000000;

FILE: server/controllers/api/claim/publish/validateFileTypeAndSize.js
  constant SIZE_MB (line 7) | const SIZE_MB = 1000000;

FILE: server/controllers/api/homepage/data/index.js
  constant NO_CHANNEL (line 5) | const NO_CHANNEL = 'NO_CHANNEL';

FILE: server/controllers/assets/constants/request_types.js
  constant SERVE (line 1) | const SERVE = 'SERVE';
  constant SHOW (line 2) | const SHOW = 'SHOW';

FILE: server/controllers/assets/utils/determineRequestType.js
  function clientWantsAsset (line 3) | function clientWantsAsset ({accept, range}) {

FILE: server/controllers/assets/utils/flipClaimNameAndId.js
  function isValidClaimId (line 1) | function isValidClaimId (claimId) {
  function isValidShortId (line 5) | function isValidShortId (claimId) {
  function isValidShortIdOrClaimId (line 9) | function isValidShortIdOrClaimId (input) {

FILE: server/controllers/assets/utils/getClaimIdAndServeAsset.js
  constant NO_CHANNEL (line 12) | const NO_CHANNEL = 'NO_CHANNEL';
  constant NO_CLAIM (line 13) | const NO_CLAIM = 'NO_CLAIM';
  constant BLOCKED_CLAIM (line 14) | const BLOCKED_CLAIM = 'BLOCKED_CLAIM';
  constant NO_FILE (line 15) | const NO_FILE = 'NO_FILE';
  constant CONTENT_UNAVAILABLE (line 16) | const CONTENT_UNAVAILABLE = 'CONTENT_UNAVAILABLE';

FILE: server/controllers/assets/utils/getLocalFileRecord.js
  constant NO_FILE (line 3) | const NO_FILE = 'NO_FILE';

FILE: server/controllers/assets/utils/transformImage.js
  function _cropCenter (line 44) | function _cropCenter(path, cropWidth, cropHeight, originalWidth, origina...

FILE: server/controllers/utils/errorHandlers.js
  method createErrorResponsePayload (line 38) | createErrorResponsePayload (status, message) {

FILE: server/index.js
  function Server (line 35) | function Server() {

FILE: server/lbrynet/index.js
  method publishClaim (line 11) | publishClaim(publishParams) {
  method getClaim (line 35) | getClaim(uri) {
  method getFileListFileByOutpoint (line 56) | getFileListFileByOutpoint(outpoint) {
  method abandonClaim (line 76) | async abandonClaim({ outpoint }) {
  method getClaimList (line 92) | getClaimList(claimName) {
  method resolveUri (line 110) | resolveUri(uri) {
  method getDownloadDirectory (line 140) | getDownloadDirectory() {
  method createChannel (line 170) | createChannel(name) {
  method getAccountBalance (line 191) | getAccountBalance() {

FILE: server/middleware/httpContextMiddleware.js
  function setRouteDataInContextMiddleware (line 3) | function setRouteDataInContextMiddleware(routePath, routeData) {

FILE: server/middleware/logMetricsMiddleware.js
  function logMetricsMiddleware (line 5) | function logMetricsMiddleware (req, res, next) {
  function setRouteDataInContextMiddleware (line 46) | function setRouteDataInContextMiddleware (routePath, routeData) {

FILE: server/models/blocked.js
  constant BLOCKED_CLAIM (line 2) | const BLOCKED_CLAIM = 'BLOCKED_CLAIM';

FILE: server/models/certificate.js
  constant NO_CHANNEL (line 4) | const NO_CHANNEL = 'NO_CHANNEL';
  function isLongChannelId (line 6) | function isLongChannelId (channelId) {
  function isShortChannelId (line 10) | function isShortChannelId (channelId) {

FILE: server/models/claim.js
  constant NO_CLAIM (line 12) | const NO_CLAIM = 'NO_CLAIM';
  function determineFileExtensionFromContentType (line 14) | function determineFileExtensionFromContentType(contentType) {
  function determineThumbnail (line 33) | function determineThumbnail(storedThumbnail, defaultThumbnail) {
  function prepareClaimData (line 40) | function prepareClaimData(claim) {
  function isLongClaimId (line 48) | function isLongClaimId(claimId) {
  function isShortClaimId (line 52) | function isShortClaimId(claimId) {

FILE: server/models/utils/createFileRecordData.js
  function createFileRecordDataAfterGet (line 3) | async function createFileRecordDataAfterGet(resolveResult, getResult) {
  function createFileRecordDataAfterPublish (line 22) | async function createFileRecordDataAfterPublish(

FILE: server/models/utils/trendingAnalysis.js
  constant ZSCORE_CRITICAL_THRESHOLD (line 1) | const ZSCORE_CRITICAL_THRESHOLD = 1.96;
  constant ZSCORE_NINETYNINTH (line 2) | const ZSCORE_NINETYNINTH = 2.326347875;
  constant ONE_DIV_SQRT_2PI (line 3) | const ONE_DIV_SQRT_2PI = 0.3989422804014327;
  constant MAX_P_PRECISION (line 4) | const MAX_P_PRECISION = Math.exp(-16);
  constant MIN_P (line 5) | const MIN_P = -6.44357455534;
  constant MAX_P (line 6) | const MAX_P = 6.44357455534;

FILE: server/utils/awaitFileSize.js
  function delay (line 4) | function delay(t) {
  function checkFileList (line 13) | function checkFileList() {

FILE: server/utils/configureLogging.js
  function configureLogging (line 6) | function configureLogging() {

FILE: server/utils/configureSlack.js
  function configureSlack (line 7) | function configureSlack () {

FILE: server/utils/createModuleAliases.js
  constant WWW_SPEECH_ROOT (line 2) | const WWW_SPEECH_ROOT = resolve(process.cwd());

FILE: server/utils/getMediaDimensions.js
  function getMediaDimensions (line 5) | async function getMediaDimensions (fileType, filePath) {

FILE: server/utils/videoProcessing.js
  function getVideoHeightAndWidth (line 3) | async function getVideoHeightAndWidth (filePath) {

FILE: test/end-to-end/end-to-end.test.js
  function testFor200StatusResponse (line 14) | function testFor200StatusResponse (host, url) {
  function testShowRequestFor200StatusResponse (line 26) | function testShowRequestFor200StatusResponse (host, url) {

FILE: utils/createModuleAliases.js
  constant DEFAULT_ROOT (line 3) | const DEFAULT_ROOT = 'client/src';
  constant CUSTOM_ROOT (line 4) | const CUSTOM_ROOT = 'site/custom/src';
  constant DEFAULT_SCSS_ROOT (line 5) | const DEFAULT_SCSS_ROOT = 'client/scss';
  constant CUSTOM_SCSS_ROOT (line 6) | const CUSTOM_SCSS_ROOT = 'site/custom/scss';

FILE: utils/isApprovedChannel.js
  function isApprovedChannel (line 1) | function isApprovedChannel (channel, channels) {

FILE: utils/validateFileForPublish.js
  constant SIZE_MB (line 5) | const SIZE_MB = 1000000;
  function validateFileForPublish (line 7) | function validateFileForPublish(file) {

FILE: webpack/webpack.client.config.js
  constant SCSS_ROOT (line 7) | const SCSS_ROOT = Path.resolve(__dirname, '../client/scss/');
  constant CLIENT_ROOT (line 8) | const CLIENT_ROOT = Path.resolve(__dirname, '../client/');
  constant CUSTOM_CLIENT_ROOT (line 9) | const CUSTOM_CLIENT_ROOT = Path.resolve(__dirname, '../site/custom/');

FILE: webpack/webpack.server.config.js
  constant SCSS_ROOT (line 7) | const SCSS_ROOT = Path.resolve(__dirname, '../client/scss/');
  constant CLIENT_ROOT (line 8) | const CLIENT_ROOT = Path.resolve(__dirname, '../client/');
  constant CUSTOM_CLIENT_ROOT (line 9) | const CUSTOM_CLIENT_ROOT = Path.resolve(__dirname, '../site/custom/');
Condensed preview — 442 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (600K chars).
[
  {
    "path": ".babelrc",
    "chars": 134,
    "preview": "{\n  \"presets\": [\"@babel/env\", \"@babel/react\"],\n  \"plugins\": [\"react-hot-loader/babel\", \"@babel/plugin-proposal-object-re"
  },
  {
    "path": ".eslintignore",
    "chars": 99,
    "preview": "client/build\nnode_modules/\npublic/bundle\nserver/render/build\nserver/bundle\ntest/\nserver/chainquery\n"
  },
  {
    "path": ".eslintrc",
    "chars": 868,
    "preview": "{\n  \"parser\": \"babel-eslint\",\n  \"extends\": [\"standard\", \"standard-jsx\"],\n  \"env\": {\n    \"es6\": true,\n    \"jest\": true,\n "
  },
  {
    "path": ".gitignore",
    "chars": 275,
    "preview": ".DS_Store\n*.log\n.idea/\n\nnode_modules\n\nclient/build\n\nsite/\n\ndevConfig/sequelizeCliConfig.js\ndevConfig/testingConfig.js\n\ns"
  },
  {
    "path": ".npmignore",
    "chars": 38,
    "preview": "client/src\nserver/render/src\n.babelrc\n"
  },
  {
    "path": ".nvmrc",
    "chars": 8,
    "preview": "v8.12.0\n"
  },
  {
    "path": ".prettierrc.json",
    "chars": 73,
    "preview": "{\n  \"trailingComma\": \"es5\",\n  \"printWidth\": 100,\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": ".sequelizerc",
    "chars": 278,
    "preview": "const path = require('path');\n\nmodule.exports = {\n  'config': path.resolve('devConfig', 'sequelizeCliConfig.js'),\n  'mod"
  },
  {
    "path": ".travis.yml",
    "chars": 1320,
    "preview": "dist: xenial\n#addons:\n#  apt:\n#    sources:\n#      - mysql-5.7-trusty\n#    packages:\n#      - mysql-server\n#      - mysq"
  },
  {
    "path": "LICENSE",
    "chars": 1081,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2017-2020 LBRY Inc.\n\nPermission is hereby granted, free of charge, to any person ob"
  },
  {
    "path": "README.md",
    "chars": 11575,
    "preview": "# Spee.ch\n\nspee.ch provides a user-friendly, custom-designed, image and video hosting site backed by a decentralized net"
  },
  {
    "path": "changelog.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "cli/configure.js",
    "chars": 8594,
    "preview": "const inquirer = require('inquirer');\nconst fs = require('fs');\nconst Path = require('path');\nconst axios = require('axi"
  },
  {
    "path": "cli/defaults/chainqueryConfig.json",
    "chars": 198,
    "preview": "{\n  \"host\": \"public.chainquery.lbry.com\",\n  \"port\": \"3306\",\n  \"timeout\": 30,\n  \"database\": \"chainquery\",\n  \"username\": \""
  },
  {
    "path": "cli/defaults/lbryConfig.json",
    "chars": 70,
    "preview": "{\n  \"apiHost\": \"localhost\",\n  \"apiPort\": \"5279\",\n  \"getTimeout\": 30\n}\n"
  },
  {
    "path": "cli/defaults/loggerConfig.json",
    "chars": 28,
    "preview": "{\n  \"logLevel\": \"verbose\"\n}\n"
  },
  {
    "path": "cli/defaults/mysqlConfig.json",
    "chars": 65,
    "preview": "{\n  \"database\": \"lbry\",\n  \"username\": \"root\",\n  \"password\": \"\"\n}\n"
  },
  {
    "path": "cli/defaults/siteConfig.json",
    "chars": 2859,
    "preview": "{\n  \"analytics\": {\n    \"googleId\": null\n  },\n  \"assetDefaults\": {\n    \"title\": \"Default Content Title\",\n    \"description"
  },
  {
    "path": "cli/defaults/slackConfig.json",
    "chars": 86,
    "preview": "{\n  \"slackWebHook\": false,\n  \"slackErrorChannel\": false,\n  \"slackInfoChannel\": false\n}"
  },
  {
    "path": "cli/questions/mysqlQuestions.js",
    "chars": 777,
    "preview": "const database = (defaultAnswer) => {\n  return {\n    type   : 'input',\n    message: 'What is the name of the MySQL DATAB"
  },
  {
    "path": "cli/questions/siteQuestions.js",
    "chars": 1468,
    "preview": "const makeDir = require('make-dir');\n\nconst port = (defaultAnswer) => {\n  return {\n    type   : 'input',\n    message: 'E"
  },
  {
    "path": "client/scss/_asset-blocked.scss",
    "chars": 78,
    "preview": ".asset-blocked__image {\nwidth: 100%;\n}\n\n.asset-blocked__text {\nwidth: 100%;\n}\n"
  },
  {
    "path": "client/scss/_asset-display.scss",
    "chars": 2254,
    "preview": ".asset-main {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.asset-document {\n  max-width: 1000px"
  },
  {
    "path": "client/scss/_asset-preview.scss",
    "chars": 1699,
    "preview": ".asset-preview {\n  display: flex;\n  flex-direction: column;\n  background: $card-color;\n  color: $text-color;\n  width: 24"
  },
  {
    "path": "client/scss/_body.scss",
    "chars": 182,
    "preview": "body {\n  margin: 0;\n  padding: 0;\n  min-height: 100%;\n  word-wrap: break-word;\n  display: -webkit-flex;\n  display: flex;"
  },
  {
    "path": "client/scss/_button-primary.scss",
    "chars": 377,
    "preview": ".button--primary, .button--primary:focus, .button--primary:active {\n  border-color: $primary-color;\n  color: $primary-co"
  },
  {
    "path": "client/scss/_button-secondary.scss",
    "chars": 304,
    "preview": ".button--secondary, .button--secondary:focus, .button--secondary:active  {\n  border-bottom-color: $secondary-color;\n  co"
  },
  {
    "path": "client/scss/_button.scss",
    "chars": 358,
    "preview": "button {\n  cursor: pointer;\n  &:active\n  {\n    outline: 0;\n  }\n}\n\n.button--primary, .button--secondary\n{\n  border-width:"
  },
  {
    "path": "client/scss/_channel-claims-display.scss",
    "chars": 677,
    "preview": ".channel-claims-display {\n  width: 100%;\n  display: grid;\n  grid-gap: $tertiary-padding;\n  align-content: space-around;\n"
  },
  {
    "path": "client/scss/_claim-pending.scss",
    "chars": 218,
    "preview": ".claim-pending {\n  display: inline-block;\n  position: absolute;\n  top: 10px;\n  left: 10px;\n  padding: 5px;\n  border-radi"
  },
  {
    "path": "client/scss/_click-to-copy.scss",
    "chars": 708,
    "preview": ".click-to-copy-wrap {\n  display: flex;\n  width: 100%;\n  justify-content: space-between;\n  align-items: center;\n  cursor:"
  },
  {
    "path": "client/scss/_dropzone.scss",
    "chars": 1779,
    "preview": ".dropzone-wrapper {\n  // fill the parent flex container\n  flex: 1 0 auto;\n  // be a flex container for children\n  displa"
  },
  {
    "path": "client/scss/_form-feedback.scss",
    "chars": 246,
    "preview": ".form-feedback {\n  padding-top: $thin-padding;\n  padding-bottom: $thin-padding;\n}\n\n.form-feedback--default {\n  color: $s"
  },
  {
    "path": "client/scss/_form.scss",
    "chars": 109,
    "preview": ".form-group {\n  padding-bottom: $secondary-padding;\n}\n\n.form-title {\n  padding-bottom: $secondary-padding;\n}\n"
  },
  {
    "path": "client/scss/_horizontal-split.scss",
    "chars": 1194,
    "preview": ".horizontal-split {\n  max-width: $width-content-constrained;\n  width: 100%;\n  display       : flex;\n  flex-direction : r"
  },
  {
    "path": "client/scss/_html.scss",
    "chars": 52,
    "preview": "html {\n  margin: 0;\n  padding: 0;\n  height: 100%;\n}\n"
  },
  {
    "path": "client/scss/_input.scss",
    "chars": 692,
    "preview": "input:-webkit-autofill {\n  -webkit-box-shadow: 0 0 0px 1000px white inset;\n}\n\ninput {\n  margin: 0;\n  padding: $input-pad"
  },
  {
    "path": "client/scss/_label.scss",
    "chars": 476,
    "preview": ".label {\n  padding-top: $thin-padding;\n  padding-bottom: $thin-padding;\n  display: inline-block;\n  font-weight: bold;\n  "
  },
  {
    "path": "client/scss/_link.scss",
    "chars": 301,
    "preview": "a, a:visited {\n  text-decoration: none;\n}\n\n.link--primary, .link--primary:visited {\n  color: $primary-color;\n  &:hover {"
  },
  {
    "path": "client/scss/_markdown.scss",
    "chars": 2710,
    "preview": ".markdown-preview {\n\n  margin: $tertiary-padding 0px;\n\n  h1,\n  h2,\n  h3 {\n    font-size: inherit;\n    font-weight: inher"
  },
  {
    "path": "client/scss/_media-queries.scss",
    "chars": 128,
    "preview": "@media (max-width: $break-point-x-large) {\n  // hide site description in nav bar\n  .site-description {\n    display: none"
  },
  {
    "path": "client/scss/_nav-bar.scss",
    "chars": 986,
    "preview": ".nav-bar {\n  box-sizing: border-box;\n  padding: $thin-padding $primary-padding;\n  background: $chrome-color;\n  flex: 0 1"
  },
  {
    "path": "client/scss/_page-content.scss",
    "chars": 216,
    "preview": ".page-content {\n  margin: $primary-padding;\n  // fill the parent flex container\n  flex: 1 0 auto;\n  // be a flex contain"
  },
  {
    "path": "client/scss/_page-layout-show-lite.scss",
    "chars": 208,
    "preview": ".page-layout-show-lite {\n  flex: 1 0 auto;\n  display: flex;\n  flex-direction: column;\n  .content {\n    flex: 1 0 auto;\n "
  },
  {
    "path": "client/scss/_page-layout.scss",
    "chars": 512,
    "preview": ".page-layout {\n  flex: 1 0 auto;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  max-width: 100%;\n  "
  },
  {
    "path": "client/scss/_progress-bar.scss",
    "chars": 190,
    "preview": ".progress-bar__wrapper {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.progress-bar--inactive {\n"
  },
  {
    "path": "client/scss/_publish-disabled-message.scss",
    "chars": 233,
    "preview": ".publish-disabled-message {\n  // fill the parent flex container\n  flex: 1 0 auto;\n  // be a flex container for children\n"
  },
  {
    "path": "client/scss/_publish-preview.scss",
    "chars": 221,
    "preview": ".publish-form__title {\n  max-width: $width-content-constrained;\n  margin-left: auto;\n  margin-right: auto;\n\n  @media (ma"
  },
  {
    "path": "client/scss/_publish-status.scss",
    "chars": 222,
    "preview": ".publish-status {\n  // fill the parent flex container\n  flex: 1 0 auto;\n  // be a flex container for children\n  display:"
  },
  {
    "path": "client/scss/_publish-url-input.scss",
    "chars": 318,
    "preview": ".publish-url-input {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align"
  },
  {
    "path": "client/scss/_react-app.scss",
    "chars": 135,
    "preview": "#react-app {\n  flex: 1 0 auto;\n  display: -webkit-flex;\n  display: flex;\n  -webkit-flex-direction: column;\n  flex-direct"
  },
  {
    "path": "client/scss/_reset.scss",
    "chars": 96,
    "preview": "button, input, textarea, label, select, option {\n  font-family: inherit;\n  font-size: inherit;\n}"
  },
  {
    "path": "client/scss/_row.scss",
    "chars": 520,
    "preview": ".row {\n  margin-bottom: 1.2em;\n}\n\n.row-labeled {\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justi"
  },
  {
    "path": "client/scss/_select.scss",
    "chars": 117,
    "preview": "select {\n  margin: 0;\n  display: inline-block;\n  background: $background-color;\n  border: 0;\n  color: $text-color;\n}\n"
  },
  {
    "path": "client/scss/_share-buttons.scss",
    "chars": 777,
    "preview": ".share-buttons {\n  display: flex;\n  align-items: center;\n  \n  a {\n    display: block;\n    width: 30px;\n    height: 30px;"
  },
  {
    "path": "client/scss/_social-share-link.scss",
    "chars": 160,
    "preview": ".social-share-link {\n  flex-wrap: wrap;\n  margin-right: -0.5em;\n  margin-left: -0.5em;\n}\n\n.social-share-link > a{\n  padd"
  },
  {
    "path": "client/scss/_space-around.scss",
    "chars": 91,
    "preview": ".space-around {\n  display: flex;\n  justify-content: space-around;\n  align-items: center;\n}\n"
  },
  {
    "path": "client/scss/_space-between.scss",
    "chars": 93,
    "preview": ".space-between {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n"
  },
  {
    "path": "client/scss/_text.scss",
    "chars": 702,
    "preview": "// set defaults\n\nh1, h2, h3, h4, p {\n  margin: 0;\n}\n\nbody {\n  color: $text-color;\n  font-family: 'Circular', serif;\n  fo"
  },
  {
    "path": "client/scss/_textarea.scss",
    "chars": 106,
    "preview": "textarea {\n  margin: 0;\n  padding: $input-padding;\n  display: inline-block;\n  width: $input-full-width;\n}\n"
  },
  {
    "path": "client/scss/_tooltip.scss",
    "chars": 804,
    "preview": "/* Tooltip container */\n.tooltip {\n  position: relative;\n}\n/* Tooltip text */\n.tooltip > .tooltip-text {\n  visibility: h"
  },
  {
    "path": "client/scss/_variables.scss",
    "chars": 1536,
    "preview": "//backgrounds\n$base-color: white; //default white\n$card-color: white; //default white\n$chrome-color: white; //default wh"
  },
  {
    "path": "client/scss/_video.scss",
    "chars": 162,
    "preview": "video:-moz-full-screen {\n  border:none;\n  padding:0;\n}\nvideo:-webkit-full-screen {\n  border:none;\n  padding:0;\n}\nvideo:f"
  },
  {
    "path": "client/scss/all.scss",
    "chars": 1286,
    "preview": "@import '~scss/_variables';\n@import '~scss/_reset';\n@import '~scss/font/_font.scss';\n@import '~scss/_html';\n@import '~sc"
  },
  {
    "path": "client/scss/font/Lekton/OFL.txt",
    "chars": 4368,
    "preview": "Copyright (c) 2008-2010, Isia Urbino (http://www.isiaurbino.net)\n\nThis Font Software is licensed under the SIL Open Font"
  },
  {
    "path": "client/scss/font/_font.scss",
    "chars": 584,
    "preview": "@font-face {\n  font-family: 'Lekton';\n  src: url('./font/Lekton/Lekton-Regular.ttf');\n}\n\n@font-face {\n  font-family: 'Le"
  },
  {
    "path": "client/src/actions/channel.js",
    "chars": 457,
    "preview": "import * as actions from '../constants/channel_action_types';\n\n// export action creators\n\nexport function updateLoggedIn"
  },
  {
    "path": "client/src/actions/channelCreate.js",
    "chars": 792,
    "preview": "import * as actions from '../constants/channel_create_action_types';\n\n// export action creators\n\nexport function updateC"
  },
  {
    "path": "client/src/actions/index.js",
    "chars": 154,
    "preview": "// import { } from './channel';\n// import { } from './publish';\nimport { onHandleShowPageUri } from './show';\n\nexport de"
  },
  {
    "path": "client/src/actions/publish.js",
    "chars": 1880,
    "preview": "import * as actions from '../constants/publish_action_types';\n\n// export action creators\nexport function selectFile (fil"
  },
  {
    "path": "client/src/actions/show.js",
    "chars": 3554,
    "preview": "import * as actions from '../constants/show_action_types';\nimport { ASSET_DETAILS, ASSET_LITE, CHANNEL, SPECIAL_ASSET } "
  },
  {
    "path": "client/src/api/assetApi.js",
    "chars": 1420,
    "preview": "import Request from '../utils/request';\n\nexport function getLongClaimId(host, name, modifier) {\n  let body = {};\n  // cr"
  },
  {
    "path": "client/src/api/authApi.js",
    "chars": 328,
    "preview": "import Request from '../utils/request';\n\nexport function checkForLoggedInChannelApi () {\n  const url = `/user`;\n  const "
  },
  {
    "path": "client/src/api/channelApi.js",
    "chars": 815,
    "preview": "import Request from '../utils/request';\n\nexport function getChannelData (host, name, id) {\n  if (!id) id = 'none';\n  con"
  },
  {
    "path": "client/src/api/fileApi.js",
    "chars": 334,
    "preview": "import Request from '../utils/request';\n\nexport function checkFileAvailability (claimId, host, name) {\n  const url = `${"
  },
  {
    "path": "client/src/api/homepageApi.js",
    "chars": 177,
    "preview": "import Request from '../utils/request';\n\nexport function getHomepageChannelsData (host, name, id) {\n  const url = `${hos"
  },
  {
    "path": "client/src/api/specialAssetApi.js",
    "chars": 201,
    "preview": "import Request from '../utils/request';\n\nexport function getSpecialAssetClaims (host, name, page) {\n  if (!page) page = "
  },
  {
    "path": "client/src/app.js",
    "chars": 1335,
    "preview": "import React from 'react';\nimport { hot } from 'react-hot-loader/root'\nimport { Route, Switch } from 'react-router-dom';"
  },
  {
    "path": "client/src/channels/publish.js",
    "chars": 2200,
    "preview": "import {buffers, END, eventChannel} from 'redux-saga';\n\nexport const makePublishRequestChannel = (fd, isUpdate) => {\n  r"
  },
  {
    "path": "client/src/components/AboutSpeechDetails/index.jsx",
    "chars": 606,
    "preview": "import React from 'react';\nimport Row from '@components/Row';\n\nconst AboutSpeechDetails = () => {\n  return (\n    <div>\n "
  },
  {
    "path": "client/src/components/AboutSpeechOverview/index.jsx",
    "chars": 805,
    "preview": "import React from 'react';\nimport Row from '@components/Row';\n\nconst AboutSpeechOverview = () => {\n  return (\n    <div>\n"
  },
  {
    "path": "client/src/components/ActiveStatusBar/index.jsx",
    "chars": 155,
    "preview": "import React from 'react';\n\nconst ActiveStatusBar = () => {\n  return <span className='progress-bar--active'>| </span>;\n}"
  },
  {
    "path": "client/src/components/AssetInfoFooter/index.js",
    "chars": 400,
    "preview": "import React from 'react';\nimport Row from '@components/Row';\n\nconst AssetInfoFooter = ({ assetUrl, name }) => {\n  retur"
  },
  {
    "path": "client/src/components/AssetPreview/index.jsx",
    "chars": 2958,
    "preview": "import React from 'react';\nimport { Link } from 'react-router-dom';\nimport createCanonicalLink from '@globalutils/create"
  },
  {
    "path": "client/src/components/AssetShareButtons/index.js",
    "chars": 1581,
    "preview": "import React from 'react';\nimport SocialShareLink from '@components/SocialShareLink';\n\nconst AssetShareButtons = ({ asse"
  },
  {
    "path": "client/src/components/ButtonPrimary/index.jsx",
    "chars": 289,
    "preview": "import React from 'react';\n\nconst ButtonPrimary  = ({ value, onClickHandler, type = 'button' }) => {\n  return (\n    <but"
  },
  {
    "path": "client/src/components/ButtonPrimaryJumbo/index.jsx",
    "chars": 278,
    "preview": "import React from 'react';\n\nconst ButtonPrimaryJumbo  = ({ value, onClickHandler }) => {\n  return (\n    <button\n      cl"
  },
  {
    "path": "client/src/components/ButtonSecondary/index.jsx",
    "chars": 256,
    "preview": "import React from 'react';\n\nconst ButtonPrimary  = ({ value, onClickHandler }) => {\n  return (\n    <button\n      classNa"
  },
  {
    "path": "client/src/components/ChannelAbout/index.jsx",
    "chars": 679,
    "preview": "import React from 'react';\n\nconst ChannelAbout = () => {\n  return (\n    <div className={'text--large'}>\n      <p>Channel"
  },
  {
    "path": "client/src/components/ChannelCreateNameInput/index.jsx",
    "chars": 964,
    "preview": "import React from 'react';\nimport Label from '@components/Label';\nimport RowLabeled from '@components/RowLabeled';\n\ncons"
  },
  {
    "path": "client/src/components/ChannelCreatePasswordInput/index.jsx",
    "chars": 619,
    "preview": "import React from 'react';\nimport Label from '@components/Label';\nimport RowLabeled from '@components/RowLabeled';\n\ncons"
  },
  {
    "path": "client/src/components/ChannelInfoDisplay/index.jsx",
    "chars": 249,
    "preview": "import React from 'react';\n// TODO: factor out longId OR implement tooltip display\nconst ChannelInfoDisplay = ({name, lo"
  },
  {
    "path": "client/src/components/ChannelLoginNameInput/index.jsx",
    "chars": 687,
    "preview": "import React from 'react';\nimport RowLabeled from '@components/RowLabeled';\nimport Label from '@components/Label';\n\ncons"
  },
  {
    "path": "client/src/components/ChannelLoginPasswordInput/index.jsx",
    "chars": 677,
    "preview": "import React from 'react';\nimport RowLabeled from '@components/RowLabeled';\nimport Label from '@components/Label';\n\ncons"
  },
  {
    "path": "client/src/components/ChannelSelectDropdown/index.jsx",
    "chars": 587,
    "preview": "import React from 'react';\nimport { LOGIN, CREATE } from '../../constants/publish_channel_select_states';\n\nconst Channel"
  },
  {
    "path": "client/src/components/ChooseAnonymousPublishRadio/index.jsx",
    "chars": 565,
    "preview": "import React from 'react';\n\nconst ChooseAnonymousPublishRadio = ({ publishInChannel, toggleAnonymousPublish }) => {\n  re"
  },
  {
    "path": "client/src/components/ChooseChannelPublishRadio/index.jsx",
    "chars": 562,
    "preview": "import React from 'react';\n\nconst ChooseChannelPublishRadio = ({ publishInChannel, toggleAnonymousPublish }) => {\n  retu"
  },
  {
    "path": "client/src/components/ClickToCopy/index.jsx",
    "chars": 989,
    "preview": "import React from 'react';\nimport * as Icon from 'react-feather';\n\nclass ClickToCopy extends React.Component {\n  constru"
  },
  {
    "path": "client/src/components/DropzoneDropItDisplay/index.jsx",
    "chars": 198,
    "preview": "import React from 'react';\n\nconst DropzoneDropItDisplay = () => {\n  return (\n    <div className={'dropzone-dropit-displa"
  },
  {
    "path": "client/src/components/DropzoneInstructionsDisplay/index.jsx",
    "chars": 1011,
    "preview": "import React from 'react';\nimport FormFeedbackDisplay from '@components/FormFeedbackDisplay';\nimport Row from '@componen"
  },
  {
    "path": "client/src/components/DropzonePreviewImage/index.jsx",
    "chars": 2098,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\n\nclass PublishPreview extends React.Component {\n  constru"
  },
  {
    "path": "client/src/components/ErrorBoundary/index.jsx",
    "chars": 624,
    "preview": "import React from 'react';\nclass ErrorBoundary extends React.Component {\n  constructor(props) {\n    super(props);\n    th"
  },
  {
    "path": "client/src/components/ExpandingTextArea/index.jsx",
    "chars": 833,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\n\nclass ExpandingTextarea extends Component"
  },
  {
    "path": "client/src/components/FileViewer/index.jsx",
    "chars": 1564,
    "preview": "import React from 'react';\nimport ReactMarkdown from 'react-markdown/with-html';\nimport { serving } from '@config/siteCo"
  },
  {
    "path": "client/src/components/FormFeedbackDisplay/index.jsx",
    "chars": 596,
    "preview": "import React from 'react';\n\nconst FormFeedbackDisplay = ({ errorMessage, defaultMessage }) => {\n  return (errorMessage |"
  },
  {
    "path": "client/src/components/GAListener/index.jsx",
    "chars": 766,
    "preview": "import React from 'react';\nimport GoogleAnalytics from 'react-ga';\nimport { withRouter } from 'react-router-dom';\n\nimpor"
  },
  {
    "path": "client/src/components/HorizontalSplit/index.jsx",
    "chars": 944,
    "preview": "import React from 'react';\n\nclass HorizontalSplit extends React.Component {\n  render () {\n    const { leftSide, rightSid"
  },
  {
    "path": "client/src/components/InactiveStatusBar/index.jsx",
    "chars": 161,
    "preview": "import React from 'react';\n\nconst InactiveStatusBar = () => {\n  return <span className='progress-bar--inactive'>| </span"
  },
  {
    "path": "client/src/components/Label/index.jsx",
    "chars": 168,
    "preview": "import React from 'react';\n\nconst Label = ({ value }) => {\n  return (\n    <label\n      className='label'\n    >\n      {va"
  },
  {
    "path": "client/src/components/Logo/index.jsx",
    "chars": 1454,
    "preview": "import React from 'react';\nimport { Link } from 'react-router-dom';\n\nfunction Logo () {\n  return (\n    <svg version='1.1"
  },
  {
    "path": "client/src/components/Memeify/EditableFontface/index.js",
    "chars": 2581,
    "preview": "import React, { Component } from 'react';\n\nconst DEFAULT_TEXT_RENDER = (text) => text;\n\nexport default class EditableFon"
  },
  {
    "path": "client/src/components/Memeify/FontFaces/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "client/src/components/Memeify/FontFaces/GreenMachine.js",
    "chars": 339,
    "preview": "export default {\n  editorStyle: {\n    fontFamily: 'courier, Courier New',\n    fontWeight: 'bold',\n    fontSize: '2em',\n "
  },
  {
    "path": "client/src/components/Memeify/FontFaces/Inferno.js",
    "chars": 690,
    "preview": "export default {\n  editorStyle: {\n    fontFamily: 'helvetica, Helvetica Nue',\n    fontWeight: 'bold',\n    fontSize: '2em"
  },
  {
    "path": "client/src/components/Memeify/FontFaces/Lazer.js",
    "chars": 691,
    "preview": "export default {\n  editorStyle: {\n    fontFamily: 'helvetica, Helvetica Nue',\n    fontWeight: 'bold',\n    fontSize: '2em"
  },
  {
    "path": "client/src/components/Memeify/FontFaces/Neon.js",
    "chars": 530,
    "preview": "export default {\n  editorStyle: {\n    fontFamily: 'Helvetica, Arial',\n    fontWeight: 'bold',\n    fontSize: '2em',\n    l"
  },
  {
    "path": "client/src/components/Memeify/FontFaces/OldBlue.js",
    "chars": 1247,
    "preview": "import React from 'react';\n\nconst charToFullWidth = char => {\n\tconst c = char.charCodeAt( 0 )\n\treturn c >= 33 && c <= 12"
  },
  {
    "path": "client/src/components/Memeify/FontFaces/Outline.js",
    "chars": 410,
    "preview": "export default {\n  editorStyle: {\n    fontFamily: 'arial',\n    fontWeight: 'bold',\n    fontSize: '2em',\n  },\n  text: {\n "
  },
  {
    "path": "client/src/components/Memeify/FontFaces/RetroRainbow.js",
    "chars": 551,
    "preview": "export default {\n  editorStyle: {\n    fontFamily: 'Arial, sans-serif',\n    fontWeight: 'bold',\n    fontSize: '1.2em',\n  "
  },
  {
    "path": "client/src/components/Memeify/FontFaces/TheSpecial.js",
    "chars": 20513,
    "preview": "import React from 'react';\n\nexport default {\n  editorStyle: {\n    fontFamily: 'Arial, sans-serif',\n    fontWeight: 'bold"
  },
  {
    "path": "client/src/components/Memeify/FontFaces/VaporWave.js",
    "chars": 1149,
    "preview": "import React from 'react';\n\nconst charToFullWidth = char => {\n\tconst c = char.charCodeAt( 0 )\n\treturn c >= 33 && c <= 12"
  },
  {
    "path": "client/src/components/Memeify/RichDraggable/index.js",
    "chars": 1420,
    "preview": "import React, { Component } from 'react';\nimport Draggable from 'react-draggable';\n\nlet body;\ntry {\n  body = document.bo"
  },
  {
    "path": "client/src/components/Memeify/index.js",
    "chars": 7577,
    "preview": "import { library } from '@fortawesome/fontawesome-svg-core'\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawes"
  },
  {
    "path": "client/src/components/NavBar/index.jsx",
    "chars": 483,
    "preview": "import React from 'react';\nimport SpaceBetween from '@components/SpaceBetween';\nimport Logo from '@components/Logo';\nimp"
  },
  {
    "path": "client/src/components/NavBarChannelOptionsDropdown/index.jsx",
    "chars": 577,
    "preview": "import React from 'react';\n\nfunction NavBarChannelDropdown ({ channelName, handleSelection, defaultSelection, VIEW, LOGO"
  },
  {
    "path": "client/src/components/PageLayout/index.jsx",
    "chars": 549,
    "preview": "import React from 'react';\n\nimport SEO from '@containers/SEO';\nimport NavBar from '@components/NavBar';\n\nclass PageLayou"
  },
  {
    "path": "client/src/components/PageLayoutShowLite/index.jsx",
    "chars": 460,
    "preview": "import React from 'react';\n\nimport SEO from '@containers/SEO';\n\nclass PageLayoutShowLite extends React.Component {\n  sho"
  },
  {
    "path": "client/src/components/ProgressBar/index.jsx",
    "chars": 2003,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport ActiveStatusBar from '../ActiveStatusBar';\nimport "
  },
  {
    "path": "client/src/components/PublishDescriptionInput/index.jsx",
    "chars": 780,
    "preview": "import React from 'react';\nimport RowLabeled from '@components/RowLabeled';\nimport Label from '@components/Label';\nimpor"
  },
  {
    "path": "client/src/components/PublishFinePrint/index.jsx",
    "chars": 481,
    "preview": "import React from 'react';\n\nconst PublishFinePrint  = () => {\n  return (\n    <p className={'text--extra-small text--seco"
  },
  {
    "path": "client/src/components/PublishLicenseInput/index.jsx",
    "chars": 803,
    "preview": "import React from 'react';\nimport RowLabeled from '@components/RowLabeled';\nimport Label from '@components/Label';\nimpor"
  },
  {
    "path": "client/src/components/PublishLicenseUrlInput/index.jsx",
    "chars": 829,
    "preview": "import React from 'react';\nimport RowLabeled from '@components/RowLabeled';\nimport Label from '@components/Label';\nimpor"
  },
  {
    "path": "client/src/components/PublishNsfwInput/index.jsx",
    "chars": 525,
    "preview": "import React from 'react';\nimport RowLabeled from '@components/RowLabeled';\nimport Label from '@components/Label';\n\ncons"
  },
  {
    "path": "client/src/components/PublishPreview/index.jsx",
    "chars": 932,
    "preview": "import React from 'react';\nimport HorizontalSplit from '@components/HorizontalSplit';\nimport Dropzone from '@containers/"
  },
  {
    "path": "client/src/components/PublishUrlMiddleDisplay/index.jsx",
    "chars": 913,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\n\nfunction UrlMiddle ({publishInChannel, selectedChannel, "
  },
  {
    "path": "client/src/components/Row/index.jsx",
    "chars": 198,
    "preview": "import React from 'react';\n\nclass Row extends React.Component {\n  render () {\n    return (\n      <div className={'row'}>"
  },
  {
    "path": "client/src/components/RowLabeled/index.jsx",
    "chars": 334,
    "preview": "import React from 'react';\n\nclass RowLabeled extends React.Component {\n  render () {\n    return (\n      <div className={"
  },
  {
    "path": "client/src/components/SocialShareLink/index.jsx",
    "chars": 250,
    "preview": "import React from 'react';\n\nclass SocialShareLink extends React.Component {\n  render () {\n    return (\n      <div classN"
  },
  {
    "path": "client/src/components/SpaceAround/index.jsx",
    "chars": 223,
    "preview": "import React from 'react';\n\nclass SpaceAround extends React.Component {\n  render () {\n    return (\n      <div className="
  },
  {
    "path": "client/src/components/SpaceBetween/index.jsx",
    "chars": 226,
    "preview": "import React from 'react';\n\nclass SpaceBetween extends React.Component {\n  render () {\n    return (\n      <div className"
  },
  {
    "path": "client/src/components/VerticalSplit/index.jsx",
    "chars": 252,
    "preview": "import React from 'react';\n\nclass VerticalSplit extends React.Component {\n  render () {\n    return (\n      <div classNam"
  },
  {
    "path": "client/src/constants/asset_display_states.js",
    "chars": 152,
    "preview": "export const LOCAL_CHECK = 'LOCAL_CHECK';\nexport const UNAVAILABLE = 'UNAVAILABLE';\nexport const ERROR = 'ERROR';\nexport"
  },
  {
    "path": "client/src/constants/channel_action_types.js",
    "chars": 154,
    "preview": "export const CHANNEL_UPDATE = 'CHANNEL_UPDATE';\nexport const CHANNEL_LOGIN_CHECK = 'CHANNEL_LOGIN_CHECK';\nexport const C"
  },
  {
    "path": "client/src/constants/channel_create_action_types.js",
    "chars": 336,
    "preview": "export const CHANNEL_CREATE_UPDATE_NAME = 'CHANNEL_CREATE_UPDATE_NAME';\nexport const CHANNEL_CREATE_UPDATE_PASSWORD = 'C"
  },
  {
    "path": "client/src/constants/confirmation_messages.js",
    "chars": 100,
    "preview": "export const SAVE = 'Everything not saved will be lost. Are you sure you want to leave this page?';\n"
  },
  {
    "path": "client/src/constants/publish_action_types.js",
    "chars": 774,
    "preview": "export const FILE_SELECTED = 'FILE_SELECTED';\nexport const FILE_CLEAR = 'FILE_CLEAR';\nexport const METADATA_UPDATE = 'ME"
  },
  {
    "path": "client/src/constants/publish_channel_select_states.js",
    "chars": 62,
    "preview": "export const LOGIN = 'Existing';\nexport const CREATE = 'New';\n"
  },
  {
    "path": "client/src/constants/publish_claim_states.js",
    "chars": 224,
    "preview": "export const LOAD_START = 'LOAD_START';\nexport const LOADING = 'LOADING';\nexport const PUBLISHING = 'PUBLISHING';\nexport"
  },
  {
    "path": "client/src/constants/publish_license_urls.js",
    "chars": 1011,
    "preview": "export const CC_LICENSES = [\n  {\n    value: 'CC Attr. 4.0 Int',\n    url: 'https://creativecommons.org/licenses/by/4.0/le"
  },
  {
    "path": "client/src/constants/show_action_types.js",
    "chars": 1167,
    "preview": "// request actions\nexport const HANDLE_SHOW_URI = 'HANDLE_SHOW_URI';\nexport const HANDLE_SHOW_HOMEPAGE = 'HANDLE_SHOW_HO"
  },
  {
    "path": "client/src/constants/show_request_types.js",
    "chars": 166,
    "preview": "export const CHANNEL = 'CHANNEL';\nexport const ASSET_LITE = 'ASSET_LITE';\nexport const ASSET_DETAILS = 'ASSET_DETAILS';\n"
  },
  {
    "path": "client/src/containers/AssetBlocked/index.js",
    "chars": 299,
    "preview": "import { connect } from 'react-redux';\nimport View from './view';\nimport { selectAsset } from '../../selectors/show';\n\nc"
  },
  {
    "path": "client/src/containers/AssetBlocked/view.jsx",
    "chars": 1528,
    "preview": "import React from 'react';\nimport createCanonicalLink from '@globalutils/createCanonicalLink';\nimport HorizontalSplit fr"
  },
  {
    "path": "client/src/containers/AssetDisplay/index.js",
    "chars": 693,
    "preview": "import { connect } from 'react-redux';\nimport View from './view';\nimport { fileRequested } from '../../actions/show';\nim"
  },
  {
    "path": "client/src/containers/AssetDisplay/view.jsx",
    "chars": 4189,
    "preview": "import React from 'react';\nimport Row from '@components/Row';\nimport ProgressBar from '@components/ProgressBar';\nimport "
  },
  {
    "path": "client/src/containers/AssetInfo/index.js",
    "chars": 525,
    "preview": "import { connect } from 'react-redux';\nimport View from './view';\nimport { selectAsset } from '../../selectors/show';\n\nc"
  },
  {
    "path": "client/src/containers/AssetInfo/view.jsx",
    "chars": 6066,
    "preview": "import React from 'react';\nimport { Link } from 'react-router-dom';\nimport Label from '@components/Label';\nimport RowLab"
  },
  {
    "path": "client/src/containers/AssetTitle/index.js",
    "chars": 300,
    "preview": "import { connect } from 'react-redux';\nimport View from './view';\nimport { selectAsset } from '../../selectors/show';\n\nc"
  },
  {
    "path": "client/src/containers/AssetTitle/view.jsx",
    "chars": 156,
    "preview": "import React from 'react';\n\nconst AssetTitle = ({ title }) => {\n  return (\n    <h2 className='asset-title'>{title}</h2>\n"
  },
  {
    "path": "client/src/containers/ChannelClaimsDisplay/index.js",
    "chars": 603,
    "preview": "import { connect } from 'react-redux';\nimport { onUpdateChannelClaims } from '../../actions/show';\nimport View from './v"
  },
  {
    "path": "client/src/containers/ChannelClaimsDisplay/view.jsx",
    "chars": 2111,
    "preview": "import React from 'react';\nimport AssetPreview from '@components/AssetPreview';\nimport Row from '@components/Row';\nimpor"
  },
  {
    "path": "client/src/containers/ChannelCreateForm/index.js",
    "chars": 571,
    "preview": "import { connect } from 'react-redux';\nimport View from './view';\nimport {\n  updateChannelAvailability,\n  updateChannelC"
  },
  {
    "path": "client/src/containers/ChannelCreateForm/view.jsx",
    "chars": 2975,
    "preview": "import React from 'react';\nimport ChannelCreateNameInput from '@components/ChannelCreateNameInput';\nimport ChannelCreate"
  },
  {
    "path": "client/src/containers/ChannelLoginForm/index.js",
    "chars": 473,
    "preview": "import { connect } from 'react-redux';\nimport { updateLoggedInChannel } from '../../actions/channel';\nimport { updateSel"
  },
  {
    "path": "client/src/containers/ChannelLoginForm/view.jsx",
    "chars": 2201,
    "preview": "import React from 'react';\nimport request from '../../utils/request';\nimport FormFeedbackDisplay from '@components/FormF"
  },
  {
    "path": "client/src/containers/ChannelSelect/index.js",
    "chars": 1137,
    "preview": "import {connect} from 'react-redux';\nimport {setPublishInChannel, updateSelectedChannel, updateError} from '../../action"
  },
  {
    "path": "client/src/containers/ChannelSelect/view.jsx",
    "chars": 3201,
    "preview": "import React from 'react';\nimport ChannelLoginForm from '@containers/ChannelLoginForm';\nimport ChannelCreateForm from '@"
  },
  {
    "path": "client/src/containers/ChannelTools/index.js",
    "chars": 228,
    "preview": "import { connect } from 'react-redux';\nimport View from './view';\n\nconst mapStateToProps = ({ site: { closedRegistration"
  },
  {
    "path": "client/src/containers/ChannelTools/view.jsx",
    "chars": 631,
    "preview": "import React from 'react';\nimport ChannelLoginForm from '@containers/ChannelLoginForm';\nimport ChannelCreateForm from '@"
  },
  {
    "path": "client/src/containers/Dropzone/index.js",
    "chars": 1399,
    "preview": "import { connect } from 'react-redux';\nimport { selectFile, updateError, clearFile } from '../../actions/publish';\nimpor"
  },
  {
    "path": "client/src/containers/Dropzone/view.jsx",
    "chars": 7796,
    "preview": "import React from 'react';\n\nimport Memeify from '@components/Memeify';\nimport DropzonePreviewImage from '@components/Dro"
  },
  {
    "path": "client/src/containers/NavigationLinks/index.jsx",
    "chars": 730,
    "preview": "import { connect } from 'react-redux';\nimport { logOutChannel, checkForLoggedInChannel } from '../../actions/channel';\ni"
  },
  {
    "path": "client/src/containers/NavigationLinks/view.jsx",
    "chars": 2253,
    "preview": "import React from 'react';\nimport { NavLink, withRouter } from 'react-router-dom';\nimport NavBarChannelOptionsDropdown f"
  },
  {
    "path": "client/src/containers/PublishDetails/index.js",
    "chars": 547,
    "preview": "import { connect } from 'react-redux';\nimport { clearFile, startPublish, abandonClaim } from '../../actions/publish';\nim"
  },
  {
    "path": "client/src/containers/PublishDetails/view.jsx",
    "chars": 3078,
    "preview": "import React from 'react';\nimport {Link, withRouter} from 'react-router-dom';\nimport PublishUrlInput from '@containers/P"
  },
  {
    "path": "client/src/containers/PublishDisabledMessage/index.js",
    "chars": 221,
    "preview": "import { connect } from 'react-redux';\nimport View from './view';\n\nconst mapStateToProps = ({ publish }) => {\n  return {"
  },
  {
    "path": "client/src/containers/PublishDisabledMessage/view.jsx",
    "chars": 611,
    "preview": "import React from 'react';\n\nclass PublishDisabledMessage extends React.Component {\n  render () {\n    const message = thi"
  },
  {
    "path": "client/src/containers/PublishMetadataInputs/index.js",
    "chars": 774,
    "preview": "import { connect } from 'react-redux';\nimport { updateMetadata, toggleMetadataInputs } from '../../actions/publish';\nimp"
  },
  {
    "path": "client/src/containers/PublishMetadataInputs/view.jsx",
    "chars": 2359,
    "preview": "import React from 'react';\nimport PublishDescriptionInput from '@components/PublishDescriptionInput';\nimport PublishLice"
  },
  {
    "path": "client/src/containers/PublishStatus/index.js",
    "chars": 363,
    "preview": "import {connect} from 'react-redux';\nimport {clearFile} from '../../actions/publish';\nimport View from './view';\n\nconst "
  },
  {
    "path": "client/src/containers/PublishStatus/view.jsx",
    "chars": 2832,
    "preview": "import React from 'react';\nimport ProgressBar from '@components/ProgressBar';\nimport * as publishStates from '../../cons"
  },
  {
    "path": "client/src/containers/PublishThumbnailInput/index.js",
    "chars": 324,
    "preview": "import { connect } from 'react-redux';\nimport { onNewThumbnail } from '../../actions/publish';\nimport View from './view'"
  },
  {
    "path": "client/src/containers/PublishThumbnailInput/view.jsx",
    "chars": 4630,
    "preview": "import React from 'react';\nimport FormFeedbackDisplay from '@components/FormFeedbackDisplay';\nimport SpaceBetween from '"
  },
  {
    "path": "client/src/containers/PublishTitleInput/index.js",
    "chars": 448,
    "preview": "import { connect } from 'react-redux';\nimport { updateMetadata } from '../../actions/publish';\nimport View from './view'"
  },
  {
    "path": "client/src/containers/PublishTitleInput/view.jsx",
    "chars": 649,
    "preview": "import React from 'react';\n\nclass PublishTitleInput extends React.Component {\n  constructor (props) {\n    super(props);\n"
  },
  {
    "path": "client/src/containers/PublishTool/index.js",
    "chars": 622,
    "preview": "import {connect} from 'react-redux';\nimport View from './view';\nimport {selectAsset} from '../../selectors/show';\nimport"
  },
  {
    "path": "client/src/containers/PublishTool/view.jsx",
    "chars": 1183,
    "preview": "import React from 'react';\nimport { withRouter, Prompt } from 'react-router';\nimport Dropzone from '@containers/Dropzone"
  },
  {
    "path": "client/src/containers/PublishUrlInput/index.js",
    "chars": 730,
    "preview": "import { connect } from 'react-redux';\nimport { updateClaim, updateError, validateClaim } from '../../actions/publish';\n"
  },
  {
    "path": "client/src/containers/PublishUrlInput/view.jsx",
    "chars": 2545,
    "preview": "import React from 'react';\nimport UrlMiddle from '@components/PublishUrlMiddleDisplay';\nimport FormFeedbackDisplay from "
  },
  {
    "path": "client/src/containers/SEO/index.js",
    "chars": 109,
    "preview": "import { connect } from 'react-redux';\nimport View from './view';\n\nexport default connect(null, null)(View);\n"
  },
  {
    "path": "client/src/containers/SEO/view.jsx",
    "chars": 1387,
    "preview": "import React from 'react';\nimport Helmet from 'react-helmet';\nimport PropTypes from 'prop-types';\n\nimport createPageTitl"
  },
  {
    "path": "client/src/containers/SiteDescription/index.jsx",
    "chars": 219,
    "preview": "import { connect } from 'react-redux';\nimport View from './view';\n\nconst mapStateToProps = ({ site }) => {\n  return {\n  "
  },
  {
    "path": "client/src/containers/SiteDescription/view.jsx",
    "chars": 281,
    "preview": "import React from 'react';\n\nclass SiteDescription extends React.Component {\n  render () {\n    return (\n      <div classN"
  },
  {
    "path": "client/src/index.js",
    "chars": 1385,
    "preview": "import React from 'react';\nimport { hydrate } from 'react-dom';\nimport { Provider } from 'react-redux';\nimport { createS"
  },
  {
    "path": "client/src/pages/AboutPage/index.jsx",
    "chars": 685,
    "preview": "import React from 'react';\nimport { withRouter } from 'react-router';\nimport PageLayout from '@components/PageLayout';\ni"
  },
  {
    "path": "client/src/pages/ContentPageWrapper/index.jsx",
    "chars": 381,
    "preview": "import { connect } from 'react-redux';\nimport { onHandleShowPageUri } from '../../actions/show';\nimport View from './vie"
  },
  {
    "path": "client/src/pages/ContentPageWrapper/view.jsx",
    "chars": 1679,
    "preview": "import React from 'react';\nimport ErrorPage from '@pages/ErrorPage';\nimport ShowAssetLite from '@pages/ShowAssetLite';\ni"
  },
  {
    "path": "client/src/pages/EditPage/index.js",
    "chars": 669,
    "preview": "import { connect } from 'react-redux';\nimport { setUpdateTrue, setHasChanged, updateMetadata, clearFile } from '../../ac"
  },
  {
    "path": "client/src/pages/EditPage/view.jsx",
    "chars": 1159,
    "preview": "import React from 'react';\nimport PageLayout from '@components/PageLayout';\nimport { Redirect } from 'react-router-dom';"
  },
  {
    "path": "client/src/pages/ErrorPage/index.jsx",
    "chars": 446,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport PageLayout from '@components/PageLayout';\n\nclass E"
  }
]

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

About this extraction

This page contains the full source code of the lbryio/spee.ch GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 442 files (537.4 KB), approximately 157.1k tokens, and a symbol index with 603 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!