]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'feature_add_add-button_to_home_view' of git://github.com/philjak/BookSt...
authorDan Brown <redacted>
Sat, 30 Jan 2021 16:40:13 +0000 (16:40 +0000)
committerDan Brown <redacted>
Sat, 30 Jan 2021 16:40:13 +0000 (16:40 +0000)
813 files changed:
.env.example
.env.example.complete
.github/FUNDING.yml [new file with mode: 0644]
.github/ISSUE_TEMPLATE/api_request.md [new file with mode: 0644]
.github/ISSUE_TEMPLATE/bug_report.md
.github/ISSUE_TEMPLATE/feature_request.md
.github/ISSUE_TEMPLATE/language_request.md [new file with mode: 0644]
.github/translators.txt [new file with mode: 0644]
.github/workflows/phpunit.yml
.github/workflows/test-migrations.yml [new file with mode: 0644]
.gitignore
LICENSE
app/Actions/Activity.php
app/Actions/ActivityService.php
app/Actions/ActivityType.php [new file with mode: 0644]
app/Actions/Comment.php
app/Actions/CommentRepo.php
app/Actions/Tag.php
app/Actions/TagRepo.php
app/Actions/ViewService.php
app/Api/ApiDocsGenerator.php [new file with mode: 0644]
app/Api/ApiToken.php [new file with mode: 0644]
app/Api/ApiTokenGuard.php [new file with mode: 0644]
app/Api/ListingResponseBuilder.php [new file with mode: 0644]
app/Auth/Access/ExternalAuthService.php [new file with mode: 0644]
app/Auth/Access/ExternalBaseUserProvider.php [moved from app/Providers/LdapUserProvider.php with 61% similarity]
app/Auth/Access/Guards/ExternalBaseSessionGuard.php [new file with mode: 0644]
app/Auth/Access/Guards/LdapSessionGuard.php [new file with mode: 0644]
app/Auth/Access/Guards/Saml2SessionGuard.php [new file with mode: 0644]
app/Auth/Access/Ldap.php
app/Auth/Access/LdapService.php
app/Auth/Access/RegistrationService.php [new file with mode: 0644]
app/Auth/Access/Saml2Service.php [new file with mode: 0644]
app/Auth/Access/SocialAuthService.php
app/Auth/Permissions/JointPermission.php
app/Auth/Permissions/PermissionService.php
app/Auth/Permissions/PermissionsRepo.php
app/Auth/Permissions/RolePermission.php
app/Auth/Role.php
app/Auth/SocialAccount.php
app/Auth/User.php
app/Auth/UserRepo.php
app/Config/api.php [new file with mode: 0644]
app/Config/app.php
app/Config/auth.php
app/Config/filesystems.php
app/Config/logging.php
app/Config/saml2.php [new file with mode: 0644]
app/Config/services.php
app/Config/session.php
app/Config/setting-defaults.php
app/Config/snappy.php
app/Console/Commands/CleanupImages.php
app/Console/Commands/ClearRevisions.php
app/Console/Commands/ClearViews.php
app/Console/Commands/CopyShelfPermissions.php [new file with mode: 0644]
app/Console/Commands/CreateAdmin.php
app/Console/Commands/DeleteUsers.php
app/Console/Commands/RegenerateCommentContent.php [new file with mode: 0644]
app/Console/Commands/RegeneratePermissions.php
app/Console/Commands/RegenerateSearch.php
app/Console/Commands/UpdateUrl.php [new file with mode: 0644]
app/Entities/BreadcrumbsViewComposer.php
app/Entities/Chapter.php [deleted file]
app/Entities/EntityProvider.php
app/Entities/Managers/TrashCan.php [deleted file]
app/Entities/Models/Book.php [moved from app/Entities/Book.php with 75% similarity]
app/Entities/Models/BookChild.php [moved from app/Entities/BookChild.php with 83% similarity]
app/Entities/Models/Bookshelf.php [moved from app/Entities/Bookshelf.php with 79% similarity]
app/Entities/Models/Chapter.php [new file with mode: 0644]
app/Entities/Models/Deletion.php [new file with mode: 0644]
app/Entities/Models/Entity.php [moved from app/Entities/Entity.php with 69% similarity]
app/Entities/Models/HasCoverImage.php [moved from app/Entities/HasCoverImage.php with 90% similarity]
app/Entities/Models/Page.php [moved from app/Entities/Page.php with 66% similarity]
app/Entities/Models/PageRevision.php [moved from app/Entities/PageRevision.php with 96% similarity]
app/Entities/Models/SearchTerm.php [moved from app/Entities/SearchTerm.php with 89% similarity]
app/Entities/Repos/BaseRepo.php
app/Entities/Repos/BookRepo.php
app/Entities/Repos/BookshelfRepo.php
app/Entities/Repos/ChapterRepo.php
app/Entities/Repos/PageRepo.php
app/Entities/SlugGenerator.php [deleted file]
app/Entities/Tools/BookContents.php [moved from app/Entities/Managers/BookContents.php with 89% similarity]
app/Entities/Tools/ExportFormatter.php [moved from app/Entities/ExportService.php with 85% similarity]
app/Entities/Tools/Markdown/CustomStrikeThroughExtension.php [new file with mode: 0644]
app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php [new file with mode: 0644]
app/Entities/Tools/PageContent.php [moved from app/Entities/Managers/PageContent.php with 73% similarity]
app/Entities/Tools/PageEditActivity.php [moved from app/Entities/Managers/PageEditActivity.php with 95% similarity]
app/Entities/Tools/PermissionsUpdater.php [new file with mode: 0644]
app/Entities/Tools/SearchIndex.php [new file with mode: 0644]
app/Entities/Tools/SearchOptions.php [new file with mode: 0644]
app/Entities/Tools/SearchRunner.php [moved from app/Entities/SearchService.php with 52% similarity]
app/Entities/Tools/ShelfContext.php [moved from app/Entities/Managers/EntityContext.php with 55% similarity]
app/Entities/Tools/SiblingFetcher.php [new file with mode: 0644]
app/Entities/Tools/SlugGenerator.php [new file with mode: 0644]
app/Entities/Tools/TrashCan.php [new file with mode: 0644]
app/Exceptions/ApiAuthException.php [new file with mode: 0644]
app/Exceptions/Handler.php
app/Exceptions/JsonDebugException.php [new file with mode: 0644]
app/Exceptions/LoginAttemptEmailNeededException.php [new file with mode: 0644]
app/Exceptions/LoginAttemptException.php [new file with mode: 0644]
app/Exceptions/NotifyException.php
app/Exceptions/SamlException.php [moved from app/Exceptions/AuthException.php with 50% similarity]
app/Exceptions/UnauthorizedException.php [new file with mode: 0644]
app/Http/Controllers/Api/ApiController.php [new file with mode: 0644]
app/Http/Controllers/Api/ApiDocsController.php [new file with mode: 0644]
app/Http/Controllers/Api/BookApiController.php [new file with mode: 0644]
app/Http/Controllers/Api/BookExportApiController.php [new file with mode: 0644]
app/Http/Controllers/Api/BookshelfApiController.php [new file with mode: 0644]
app/Http/Controllers/Api/ChapterApiController.php [new file with mode: 0644]
app/Http/Controllers/Api/ChapterExportApiController.php [new file with mode: 0644]
app/Http/Controllers/Api/PageApiController.php [new file with mode: 0644]
app/Http/Controllers/Api/PageExportApiController.php [new file with mode: 0644]
app/Http/Controllers/AttachmentController.php
app/Http/Controllers/AuditLogController.php [new file with mode: 0644]
app/Http/Controllers/Auth/ConfirmEmailController.php
app/Http/Controllers/Auth/ForgotPasswordController.php
app/Http/Controllers/Auth/LoginController.php
app/Http/Controllers/Auth/RegisterController.php
app/Http/Controllers/Auth/ResetPasswordController.php
app/Http/Controllers/Auth/Saml2Controller.php [new file with mode: 0644]
app/Http/Controllers/Auth/SocialController.php [new file with mode: 0644]
app/Http/Controllers/Auth/UserInviteController.php
app/Http/Controllers/BookController.php
app/Http/Controllers/BookExportController.php
app/Http/Controllers/BookSortController.php
app/Http/Controllers/BookshelfController.php
app/Http/Controllers/ChapterController.php
app/Http/Controllers/ChapterExportController.php
app/Http/Controllers/CommentController.php
app/Http/Controllers/Controller.php
app/Http/Controllers/HomeController.php
app/Http/Controllers/Images/DrawioImageController.php
app/Http/Controllers/Images/GalleryImageController.php
app/Http/Controllers/Images/ImageController.php
app/Http/Controllers/MaintenanceController.php [new file with mode: 0644]
app/Http/Controllers/PageController.php
app/Http/Controllers/PageExportController.php
app/Http/Controllers/PageRevisionController.php
app/Http/Controllers/PageTemplateController.php
app/Http/Controllers/RecycleBinController.php [new file with mode: 0644]
app/Http/Controllers/RoleController.php [moved from app/Http/Controllers/PermissionController.php with 68% similarity]
app/Http/Controllers/SearchController.php
app/Http/Controllers/SettingController.php
app/Http/Controllers/StatusController.php [new file with mode: 0644]
app/Http/Controllers/TagController.php
app/Http/Controllers/UserApiTokenController.php [new file with mode: 0644]
app/Http/Controllers/UserController.php
app/Http/Controllers/UserSearchController.php [new file with mode: 0644]
app/Http/Kernel.php
app/Http/Middleware/ApiAuthenticate.php [new file with mode: 0644]
app/Http/Middleware/Authenticate.php
app/Http/Middleware/CheckGuard.php [new file with mode: 0644]
app/Http/Middleware/ChecksForEmailConfirmation.php [new file with mode: 0644]
app/Http/Middleware/ControlIframeSecurity.php [new file with mode: 0644]
app/Http/Middleware/GlobalViewData.php [deleted file]
app/Http/Middleware/Localization.php
app/Http/Middleware/PermissionMiddleware.php
app/Http/Middleware/StartSessionIfCookieExists.php [new file with mode: 0644]
app/Http/Middleware/ThrottleApiRequests.php [new file with mode: 0644]
app/Http/Middleware/TrustProxies.php
app/Http/Middleware/VerifyCsrfToken.php
app/Interfaces/Loggable.php [new file with mode: 0644]
app/Ownable.php [deleted file]
app/Providers/AppServiceProvider.php
app/Providers/AuthServiceProvider.php
app/Providers/CustomValidationServiceProvider.php [new file with mode: 0644]
app/Providers/RouteServiceProvider.php
app/Settings/SettingService.php
app/Traits/HasCreatorAndUpdater.php [new file with mode: 0644]
app/Traits/HasOwner.php [new file with mode: 0644]
app/Uploads/Attachment.php
app/Uploads/AttachmentService.php
app/Uploads/Image.php
app/Uploads/ImageRepo.php
app/Uploads/ImageService.php
app/Uploads/UploadService.php [deleted file]
app/Uploads/UserAvatars.php [new file with mode: 0644]
app/helpers.php
artisan
bootstrap/init.php [deleted file]
composer.json
composer.lock
database/factories/ModelFactory.php
database/migrations/2018_08_04_115700_create_bookshelves_table.php
database/migrations/2019_07_07_112515_add_template_support.php
database/migrations/2019_12_29_120917_add_api_auth.php [new file with mode: 0644]
database/migrations/2020_08_04_111754_drop_joint_permissions_id.php [new file with mode: 0644]
database/migrations/2020_08_04_131052_remove_role_name_field.php [new file with mode: 0644]
database/migrations/2020_09_19_094251_add_activity_indexes.php [new file with mode: 0644]
database/migrations/2020_09_27_210059_add_entity_soft_deletes.php [new file with mode: 0644]
database/migrations/2020_09_27_210528_create_deletions_table.php [new file with mode: 0644]
database/migrations/2020_11_07_232321_simplify_activities_table.php [new file with mode: 0644]
database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php [new file with mode: 0644]
database/seeds/DummyContentSeeder.php
database/seeds/LargeContentSeeder.php
dev/api/requests/books-create.json [new file with mode: 0644]
dev/api/requests/books-update.json [new file with mode: 0644]
dev/api/requests/chapters-create.json [new file with mode: 0644]
dev/api/requests/chapters-update.json [new file with mode: 0644]
dev/api/requests/pages-create.json [new file with mode: 0644]
dev/api/requests/pages-update.json [new file with mode: 0644]
dev/api/requests/shelves-create.json [new file with mode: 0644]
dev/api/requests/shelves-update.json [new file with mode: 0644]
dev/api/responses/books-create.json [new file with mode: 0644]
dev/api/responses/books-list.json [new file with mode: 0644]
dev/api/responses/books-read.json [new file with mode: 0644]
dev/api/responses/books-update.json [new file with mode: 0644]
dev/api/responses/chapters-create.json [new file with mode: 0644]
dev/api/responses/chapters-list.json [new file with mode: 0644]
dev/api/responses/chapters-read.json [new file with mode: 0644]
dev/api/responses/chapters-update.json [new file with mode: 0644]
dev/api/responses/pages-create.json [new file with mode: 0644]
dev/api/responses/pages-list.json [new file with mode: 0644]
dev/api/responses/pages-read.json [new file with mode: 0644]
dev/api/responses/pages-update.json [new file with mode: 0644]
dev/api/responses/shelves-create.json [new file with mode: 0644]
dev/api/responses/shelves-list.json [new file with mode: 0644]
dev/api/responses/shelves-read.json [new file with mode: 0644]
dev/api/responses/shelves-update.json [new file with mode: 0644]
dev/docker/entrypoint.app.sh
dev/docker/entrypoint.node.sh
dev/docs/components.md [new file with mode: 0644]
package-lock.json
package.json
phpcs.xml
phpunit.xml
public/.htaccess
public/index.php
public/libs/tinymce/plugins/help/plugin.min.js
public/libs/tinymce/plugins/image/plugin.min.js
public/libs/tinymce/plugins/imagetools/plugin.min.js
public/libs/tinymce/plugins/lists/plugin.min.js
public/libs/tinymce/plugins/paste/plugin.min.js
public/libs/tinymce/plugins/print/plugin.min.js
public/libs/tinymce/plugins/table/plugin.min.js
public/libs/tinymce/plugins/textpattern/plugin.min.js
public/libs/tinymce/plugins/visualchars/plugin.min.js
public/libs/tinymce/plugins/wordcount/plugin.min.js
public/libs/tinymce/skins/dark/Variables.less [new file with mode: 0644]
public/libs/tinymce/skins/dark/content.inline.min.css [new file with mode: 0644]
public/libs/tinymce/skins/dark/content.min.css [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/readme.md [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce-small.eot [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce-small.json [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce-small.svg [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce-small.ttf [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce-small.woff [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce.eot [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce.json [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce.svg [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce.ttf [new file with mode: 0644]
public/libs/tinymce/skins/dark/fonts/tinymce.woff [new file with mode: 0644]
public/libs/tinymce/skins/dark/img/anchor.gif [new file with mode: 0644]
public/libs/tinymce/skins/dark/img/loader.gif [new file with mode: 0644]
public/libs/tinymce/skins/dark/img/object.gif [new file with mode: 0644]
public/libs/tinymce/skins/dark/img/trans.gif [new file with mode: 0644]
public/libs/tinymce/skins/dark/skin.ie7.min.css [new file with mode: 0644]
public/libs/tinymce/skins/dark/skin.json [new file with mode: 0644]
public/libs/tinymce/skins/dark/skin.min.css [new file with mode: 0644]
public/libs/tinymce/skins/lightgray/content.inline.min.css
public/libs/tinymce/skins/lightgray/content.min.css
public/libs/tinymce/tinymce.min.js
readme.md
resources/icons/dark-mode.svg [new file with mode: 0644]
resources/icons/fullscreen.svg [new file with mode: 0644]
resources/icons/light-mode.svg [new file with mode: 0644]
resources/icons/saml2.svg [new file with mode: 0644]
resources/js/components/add-remove-rows.js [new file with mode: 0644]
resources/js/components/ajax-delete-row.js [new file with mode: 0644]
resources/js/components/ajax-form.js [new file with mode: 0644]
resources/js/components/attachments.js [new file with mode: 0644]
resources/js/components/auto-suggest.js [new file with mode: 0644]
resources/js/components/book-sort.js
resources/js/components/breadcrumb-listing.js [deleted file]
resources/js/components/code-editor.js [new file with mode: 0644]
resources/js/components/code-highlighter.js [new file with mode: 0644]
resources/js/components/collapsible.js
resources/js/components/details-highlighter.js [new file with mode: 0644]
resources/js/components/dropdown-search.js [new file with mode: 0644]
resources/js/components/dropdown.js
resources/js/components/dropzone.js [new file with mode: 0644]
resources/js/components/entity-search.js [new file with mode: 0644]
resources/js/components/entity-selector-popup.js
resources/js/components/event-emit-select.js [new file with mode: 0644]
resources/js/components/image-manager.js [new file with mode: 0644]
resources/js/components/index.js
resources/js/components/markdown-editor.js
resources/js/components/notification.js
resources/js/components/optional-input.js [new file with mode: 0644]
resources/js/components/overlay.js [deleted file]
resources/js/components/page-comments.js
resources/js/components/page-editor.js [new file with mode: 0644]
resources/js/components/popup.js [new file with mode: 0644]
resources/js/components/setting-app-color-picker.js
resources/js/components/setting-color-picker.js [new file with mode: 0644]
resources/js/components/sortable-list.js [new file with mode: 0644]
resources/js/components/submit-on-change.js [new file with mode: 0644]
resources/js/components/tabs.js [new file with mode: 0644]
resources/js/components/tag-manager.js [new file with mode: 0644]
resources/js/components/template-manager.js
resources/js/components/user-select.js [new file with mode: 0644]
resources/js/components/wysiwyg-editor.js
resources/js/index.js
resources/js/services/animations.js
resources/js/services/clipboard.js [new file with mode: 0644]
resources/js/services/code.js
resources/js/services/dom.js
resources/js/services/drawio.js
resources/js/services/events.js
resources/js/services/http.js
resources/js/services/translations.js
resources/js/services/util.js
resources/js/vues/attachment-manager.js [deleted file]
resources/js/vues/code-editor.js [deleted file]
resources/js/vues/components/autosuggest.js [deleted file]
resources/js/vues/components/dropzone.js [deleted file]
resources/js/vues/entity-dashboard.js [deleted file]
resources/js/vues/image-manager.js [deleted file]
resources/js/vues/page-editor.js [deleted file]
resources/js/vues/search.js [deleted file]
resources/js/vues/tag-manager.js [deleted file]
resources/js/vues/vues.js [deleted file]
resources/lang/ar/activities.php
resources/lang/ar/auth.php
resources/lang/ar/common.php
resources/lang/ar/components.php
resources/lang/ar/entities.php
resources/lang/ar/errors.php
resources/lang/ar/passwords.php
resources/lang/ar/settings.php
resources/lang/ar/validation.php
resources/lang/bg/activities.php [new file with mode: 0644]
resources/lang/bg/auth.php [new file with mode: 0644]
resources/lang/bg/common.php [new file with mode: 0644]
resources/lang/bg/components.php [new file with mode: 0644]
resources/lang/bg/entities.php [new file with mode: 0644]
resources/lang/bg/errors.php [new file with mode: 0644]
resources/lang/bg/pagination.php [new file with mode: 0644]
resources/lang/bg/passwords.php [new file with mode: 0644]
resources/lang/bg/settings.php [new file with mode: 0644]
resources/lang/bg/validation.php [new file with mode: 0644]
resources/lang/cs/activities.php
resources/lang/cs/auth.php
resources/lang/cs/common.php
resources/lang/cs/components.php
resources/lang/cs/entities.php
resources/lang/cs/errors.php
resources/lang/cs/pagination.php
resources/lang/cs/passwords.php
resources/lang/cs/settings.php
resources/lang/cs/validation.php
resources/lang/da/activities.php [new file with mode: 0644]
resources/lang/da/auth.php [new file with mode: 0644]
resources/lang/da/common.php [new file with mode: 0644]
resources/lang/da/components.php [new file with mode: 0644]
resources/lang/da/entities.php [new file with mode: 0644]
resources/lang/da/errors.php [new file with mode: 0644]
resources/lang/da/pagination.php [new file with mode: 0644]
resources/lang/da/passwords.php [new file with mode: 0644]
resources/lang/da/settings.php [new file with mode: 0644]
resources/lang/da/validation.php [new file with mode: 0644]
resources/lang/de/activities.php
resources/lang/de/auth.php
resources/lang/de/common.php
resources/lang/de/components.php
resources/lang/de/entities.php
resources/lang/de/errors.php
resources/lang/de/passwords.php
resources/lang/de/settings.php
resources/lang/de/validation.php
resources/lang/de_informal/activities.php
resources/lang/de_informal/auth.php
resources/lang/de_informal/common.php
resources/lang/de_informal/components.php
resources/lang/de_informal/entities.php
resources/lang/de_informal/errors.php
resources/lang/de_informal/passwords.php
resources/lang/de_informal/settings.php
resources/lang/de_informal/validation.php
resources/lang/en/activities.php
resources/lang/en/auth.php
resources/lang/en/common.php
resources/lang/en/components.php
resources/lang/en/entities.php
resources/lang/en/errors.php
resources/lang/en/passwords.php
resources/lang/en/settings.php
resources/lang/en/validation.php
resources/lang/es/activities.php
resources/lang/es/auth.php
resources/lang/es/common.php
resources/lang/es/components.php
resources/lang/es/entities.php
resources/lang/es/errors.php
resources/lang/es/passwords.php
resources/lang/es/settings.php
resources/lang/es/validation.php
resources/lang/es_AR/activities.php
resources/lang/es_AR/auth.php
resources/lang/es_AR/common.php
resources/lang/es_AR/components.php
resources/lang/es_AR/entities.php
resources/lang/es_AR/errors.php
resources/lang/es_AR/passwords.php
resources/lang/es_AR/settings.php
resources/lang/es_AR/validation.php
resources/lang/fa/activities.php [new file with mode: 0644]
resources/lang/fa/auth.php [new file with mode: 0644]
resources/lang/fa/common.php [new file with mode: 0644]
resources/lang/fa/components.php [new file with mode: 0644]
resources/lang/fa/entities.php [new file with mode: 0644]
resources/lang/fa/errors.php [new file with mode: 0644]
resources/lang/fa/pagination.php [new file with mode: 0644]
resources/lang/fa/passwords.php [new file with mode: 0644]
resources/lang/fa/settings.php [new file with mode: 0644]
resources/lang/fa/validation.php [new file with mode: 0644]
resources/lang/fr/activities.php
resources/lang/fr/auth.php
resources/lang/fr/common.php
resources/lang/fr/components.php
resources/lang/fr/entities.php
resources/lang/fr/errors.php
resources/lang/fr/passwords.php
resources/lang/fr/settings.php
resources/lang/fr/validation.php
resources/lang/he/activities.php [new file with mode: 0644]
resources/lang/he/auth.php [new file with mode: 0644]
resources/lang/he/common.php [new file with mode: 0644]
resources/lang/he/components.php [new file with mode: 0644]
resources/lang/he/entities.php [new file with mode: 0644]
resources/lang/he/errors.php [new file with mode: 0644]
resources/lang/he/pagination.php [new file with mode: 0644]
resources/lang/he/passwords.php [new file with mode: 0644]
resources/lang/he/settings.php [new file with mode: 0755]
resources/lang/he/validation.php [new file with mode: 0644]
resources/lang/hu/activities.php
resources/lang/hu/auth.php
resources/lang/hu/common.php
resources/lang/hu/components.php
resources/lang/hu/entities.php
resources/lang/hu/errors.php
resources/lang/hu/passwords.php
resources/lang/hu/settings.php
resources/lang/hu/validation.php
resources/lang/it/activities.php
resources/lang/it/auth.php
resources/lang/it/common.php
resources/lang/it/components.php
resources/lang/it/entities.php
resources/lang/it/errors.php
resources/lang/it/passwords.php
resources/lang/it/settings.php
resources/lang/it/validation.php
resources/lang/ja/activities.php
resources/lang/ja/auth.php
resources/lang/ja/common.php
resources/lang/ja/components.php
resources/lang/ja/entities.php
resources/lang/ja/errors.php
resources/lang/ja/passwords.php
resources/lang/ja/settings.php
resources/lang/ja/validation.php
resources/lang/ko/activities.php
resources/lang/ko/auth.php
resources/lang/ko/common.php
resources/lang/ko/components.php
resources/lang/ko/entities.php
resources/lang/ko/errors.php
resources/lang/ko/passwords.php
resources/lang/ko/settings.php
resources/lang/ko/validation.php
resources/lang/nb/activities.php [new file with mode: 0644]
resources/lang/nb/auth.php [new file with mode: 0644]
resources/lang/nb/common.php [new file with mode: 0644]
resources/lang/nb/components.php [new file with mode: 0644]
resources/lang/nb/entities.php [new file with mode: 0644]
resources/lang/nb/errors.php [new file with mode: 0644]
resources/lang/nb/pagination.php [new file with mode: 0644]
resources/lang/nb/passwords.php [new file with mode: 0644]
resources/lang/nb/settings.php [new file with mode: 0644]
resources/lang/nb/validation.php [new file with mode: 0644]
resources/lang/nl/activities.php
resources/lang/nl/auth.php
resources/lang/nl/common.php
resources/lang/nl/components.php
resources/lang/nl/entities.php
resources/lang/nl/errors.php
resources/lang/nl/passwords.php
resources/lang/nl/settings.php
resources/lang/nl/validation.php
resources/lang/pl/activities.php
resources/lang/pl/auth.php
resources/lang/pl/common.php
resources/lang/pl/components.php
resources/lang/pl/entities.php
resources/lang/pl/errors.php
resources/lang/pl/passwords.php
resources/lang/pl/settings.php
resources/lang/pl/validation.php
resources/lang/pt/activities.php [new file with mode: 0644]
resources/lang/pt/auth.php [new file with mode: 0644]
resources/lang/pt/common.php [new file with mode: 0644]
resources/lang/pt/components.php [new file with mode: 0644]
resources/lang/pt/entities.php [new file with mode: 0644]
resources/lang/pt/errors.php [new file with mode: 0644]
resources/lang/pt/pagination.php [new file with mode: 0644]
resources/lang/pt/passwords.php [new file with mode: 0644]
resources/lang/pt/settings.php [new file with mode: 0644]
resources/lang/pt/validation.php [new file with mode: 0644]
resources/lang/pt_BR/activities.php
resources/lang/pt_BR/auth.php
resources/lang/pt_BR/common.php
resources/lang/pt_BR/components.php
resources/lang/pt_BR/entities.php
resources/lang/pt_BR/errors.php
resources/lang/pt_BR/passwords.php
resources/lang/pt_BR/settings.php
resources/lang/pt_BR/validation.php
resources/lang/ru/activities.php
resources/lang/ru/auth.php
resources/lang/ru/common.php
resources/lang/ru/components.php
resources/lang/ru/entities.php
resources/lang/ru/errors.php
resources/lang/ru/passwords.php
resources/lang/ru/settings.php
resources/lang/ru/validation.php
resources/lang/sk/activities.php
resources/lang/sk/auth.php
resources/lang/sk/common.php
resources/lang/sk/components.php
resources/lang/sk/entities.php
resources/lang/sk/errors.php
resources/lang/sk/passwords.php
resources/lang/sk/settings.php
resources/lang/sk/validation.php
resources/lang/sl/activities.php [new file with mode: 0644]
resources/lang/sl/auth.php [new file with mode: 0644]
resources/lang/sl/common.php [new file with mode: 0644]
resources/lang/sl/components.php [new file with mode: 0644]
resources/lang/sl/entities.php [new file with mode: 0644]
resources/lang/sl/errors.php [new file with mode: 0644]
resources/lang/sl/pagination.php [new file with mode: 0644]
resources/lang/sl/passwords.php [new file with mode: 0644]
resources/lang/sl/settings.php [new file with mode: 0644]
resources/lang/sl/validation.php [new file with mode: 0644]
resources/lang/sv/activities.php
resources/lang/sv/auth.php
resources/lang/sv/common.php
resources/lang/sv/components.php
resources/lang/sv/entities.php
resources/lang/sv/errors.php
resources/lang/sv/passwords.php
resources/lang/sv/settings.php
resources/lang/sv/validation.php
resources/lang/th/activities.php [new file with mode: 0644]
resources/lang/th/auth.php [new file with mode: 0644]
resources/lang/th/common.php [new file with mode: 0644]
resources/lang/th/components.php [new file with mode: 0644]
resources/lang/th/entities.php [new file with mode: 0644]
resources/lang/th/errors.php [new file with mode: 0644]
resources/lang/th/pagination.php [new file with mode: 0644]
resources/lang/th/passwords.php [new file with mode: 0644]
resources/lang/th/settings.php [new file with mode: 0644]
resources/lang/th/validation.php [new file with mode: 0644]
resources/lang/tr/activities.php
resources/lang/tr/auth.php
resources/lang/tr/common.php
resources/lang/tr/components.php
resources/lang/tr/entities.php
resources/lang/tr/errors.php
resources/lang/tr/passwords.php
resources/lang/tr/settings.php
resources/lang/tr/validation.php
resources/lang/uk/activities.php
resources/lang/uk/auth.php
resources/lang/uk/common.php
resources/lang/uk/components.php
resources/lang/uk/entities.php
resources/lang/uk/errors.php
resources/lang/uk/passwords.php
resources/lang/uk/settings.php
resources/lang/uk/validation.php
resources/lang/vi/activities.php [new file with mode: 0644]
resources/lang/vi/auth.php [new file with mode: 0644]
resources/lang/vi/common.php [new file with mode: 0644]
resources/lang/vi/components.php [new file with mode: 0644]
resources/lang/vi/entities.php [new file with mode: 0644]
resources/lang/vi/errors.php [new file with mode: 0644]
resources/lang/vi/pagination.php [new file with mode: 0644]
resources/lang/vi/passwords.php [new file with mode: 0644]
resources/lang/vi/settings.php [new file with mode: 0644]
resources/lang/vi/validation.php [new file with mode: 0644]
resources/lang/zh_CN/activities.php
resources/lang/zh_CN/auth.php
resources/lang/zh_CN/common.php
resources/lang/zh_CN/components.php
resources/lang/zh_CN/entities.php
resources/lang/zh_CN/errors.php
resources/lang/zh_CN/passwords.php
resources/lang/zh_CN/settings.php
resources/lang/zh_CN/validation.php
resources/lang/zh_TW/activities.php
resources/lang/zh_TW/auth.php
resources/lang/zh_TW/common.php
resources/lang/zh_TW/components.php
resources/lang/zh_TW/entities.php
resources/lang/zh_TW/errors.php
resources/lang/zh_TW/passwords.php
resources/lang/zh_TW/settings.php
resources/lang/zh_TW/validation.php
resources/sass/_blocks.scss
resources/sass/_buttons.scss
resources/sass/_codemirror.scss
resources/sass/_colors.scss
resources/sass/_components.scss
resources/sass/_forms.scss
resources/sass/_header.scss
resources/sass/_html.scss
resources/sass/_layout.scss
resources/sass/_lists.scss
resources/sass/_mixins.scss
resources/sass/_pages.scss
resources/sass/_spacing.scss
resources/sass/_tables.scss
resources/sass/_text.scss
resources/sass/_tinymce.scss
resources/sass/_variables.scss
resources/sass/export-styles.scss
resources/sass/print-styles.scss
resources/sass/styles.scss
resources/views/api-docs/index.blade.php [new file with mode: 0644]
resources/views/attachments/list.blade.php [new file with mode: 0644]
resources/views/attachments/manager-edit-form.blade.php [new file with mode: 0644]
resources/views/attachments/manager-link-form.blade.php [new file with mode: 0644]
resources/views/attachments/manager-list.blade.php [new file with mode: 0644]
resources/views/attachments/manager.blade.php [new file with mode: 0644]
resources/views/auth/forms/login/ldap.blade.php
resources/views/auth/forms/login/saml2.blade.php [new file with mode: 0644]
resources/views/auth/forms/login/standard.blade.php
resources/views/auth/login.blade.php
resources/views/auth/register.blade.php
resources/views/base.blade.php
resources/views/books/export.blade.php
resources/views/books/form.blade.php
resources/views/books/grid-item.blade.php [deleted file]
resources/views/books/index.blade.php
resources/views/books/list.blade.php
resources/views/books/show.blade.php
resources/views/books/sort-box.blade.php
resources/views/chapters/child-menu.blade.php
resources/views/chapters/export.blade.php
resources/views/chapters/form.blade.php
resources/views/chapters/list-item.blade.php
resources/views/chapters/show.blade.php
resources/views/comments/comment.blade.php
resources/views/comments/comments.blade.php
resources/views/comments/create.blade.php
resources/views/common/header.blade.php
resources/views/common/home-book.blade.php
resources/views/common/home-custom.blade.php
resources/views/common/home-shelves.blade.php
resources/views/common/home-sidebar.blade.php
resources/views/common/home.blade.php
resources/views/components/code-editor.blade.php
resources/views/components/dropzone.blade.php [new file with mode: 0644]
resources/views/components/entity-selector-popup.blade.php
resources/views/components/expand-toggle.blade.php
resources/views/components/image-manager-form.blade.php [new file with mode: 0644]
resources/views/components/image-manager-list.blade.php [new file with mode: 0644]
resources/views/components/image-manager.blade.php
resources/views/components/page-picker.blade.php
resources/views/components/setting-entity-color-picker.blade.php [new file with mode: 0644]
resources/views/components/tag-manager-list.blade.php [new file with mode: 0644]
resources/views/components/tag-manager.blade.php
resources/views/components/user-select-list.blade.php [new file with mode: 0644]
resources/views/components/user-select.blade.php [new file with mode: 0644]
resources/views/errors/404.blade.php
resources/views/errors/500.blade.php
resources/views/form/date.blade.php [new file with mode: 0644]
resources/views/form/entity-permissions.blade.php
resources/views/form/role-checkboxes.blade.php
resources/views/form/text.blade.php
resources/views/pages/attachment-manager.blade.php [deleted file]
resources/views/pages/edit.blade.php
resources/views/pages/editor-toolbox.blade.php
resources/views/pages/export.blade.php
resources/views/pages/form.blade.php
resources/views/pages/markdown-editor.blade.php
resources/views/pages/page-display.blade.php
resources/views/pages/pdf.blade.php [deleted file]
resources/views/pages/revisions.blade.php
resources/views/pages/show.blade.php
resources/views/pages/wysiwyg-editor.blade.php
resources/views/partials/activity-item.blade.php
resources/views/partials/book-tree.blade.php
resources/views/partials/breadcrumb-listing.blade.php
resources/views/partials/breadcrumbs.blade.php
resources/views/partials/custom-styles.blade.php
resources/views/partials/dark-mode-toggle.blade.php [new file with mode: 0644]
resources/views/partials/entity-dashboard-search-box.blade.php [deleted file]
resources/views/partials/entity-dashboard-search-results.blade.php [deleted file]
resources/views/partials/entity-display-item.blade.php [new file with mode: 0644]
resources/views/partials/entity-export-menu.blade.php
resources/views/partials/entity-grid-item.blade.php [new file with mode: 0644]
resources/views/partials/entity-meta.blade.php
resources/views/partials/entity-search-form.blade.php [new file with mode: 0644]
resources/views/partials/entity-search-results.blade.php [new file with mode: 0644]
resources/views/partials/export-styles.blade.php [new file with mode: 0644]
resources/views/partials/notifications.blade.php
resources/views/partials/sort.blade.php
resources/views/partials/table-user.blade.php [new file with mode: 0644]
resources/views/partials/view-toggle.blade.php
resources/views/search/all.blade.php
resources/views/search/book.blade.php [deleted file]
resources/views/search/form/boolean-filter.blade.php [new file with mode: 0644]
resources/views/search/form/date-filter.blade.php [new file with mode: 0644]
resources/views/search/form/term-list.blade.php [new file with mode: 0644]
resources/views/search/form/type-filter.blade.php [new file with mode: 0644]
resources/views/settings/audit.blade.php [new file with mode: 0644]
resources/views/settings/index.blade.php
resources/views/settings/maintenance.blade.php
resources/views/settings/navbar-with-version.blade.php [new file with mode: 0644]
resources/views/settings/navbar.blade.php
resources/views/settings/recycle-bin/deletable-entity-list.blade.php [new file with mode: 0644]
resources/views/settings/recycle-bin/destroy.blade.php [new file with mode: 0644]
resources/views/settings/recycle-bin/index.blade.php [new file with mode: 0644]
resources/views/settings/recycle-bin/restore.blade.php [new file with mode: 0644]
resources/views/settings/roles/delete.blade.php
resources/views/settings/roles/form.blade.php
resources/views/shelves/form.blade.php
resources/views/shelves/grid-item.blade.php [deleted file]
resources/views/shelves/index.blade.php
resources/views/shelves/list-item.blade.php
resources/views/shelves/list.blade.php
resources/views/shelves/show.blade.php
resources/views/users/api-tokens/create.blade.php [new file with mode: 0644]
resources/views/users/api-tokens/delete.blade.php [new file with mode: 0644]
resources/views/users/api-tokens/edit.blade.php [new file with mode: 0644]
resources/views/users/api-tokens/form.blade.php [new file with mode: 0644]
resources/views/users/api-tokens/list.blade.php [new file with mode: 0644]
resources/views/users/create.blade.php
resources/views/users/delete.blade.php
resources/views/users/edit.blade.php
resources/views/users/form.blade.php
resources/views/users/index.blade.php
routes/api.php [new file with mode: 0644]
routes/web.php
tests/ActivityTrackingTest.php
tests/Api/ApiAuthTest.php [new file with mode: 0644]
tests/Api/ApiConfigTest.php [new file with mode: 0644]
tests/Api/ApiDocsTest.php [new file with mode: 0644]
tests/Api/ApiListingTest.php [new file with mode: 0644]
tests/Api/BooksApiTest.php [new file with mode: 0644]
tests/Api/ChaptersApiTest.php [new file with mode: 0644]
tests/Api/PagesApiTest.php [new file with mode: 0644]
tests/Api/ShelvesApiTest.php [new file with mode: 0644]
tests/Api/TestsApi.php [new file with mode: 0644]
tests/AuditLogTest.php [new file with mode: 0644]
tests/Auth/AuthTest.php
tests/Auth/LdapTest.php
tests/Auth/Saml2Test.php [new file with mode: 0644]
tests/Auth/SocialAuthTest.php
tests/Auth/UserInviteTest.php
tests/BrowserKitTest.php
tests/CommandsTest.php
tests/Entity/BookShelfTest.php
tests/Entity/BookTest.php [new file with mode: 0644]
tests/Entity/ChapterTest.php [new file with mode: 0644]
tests/Entity/CommentSettingTest.php
tests/Entity/CommentTest.php
tests/Entity/EntitySearchTest.php
tests/Entity/EntityTest.php
tests/Entity/ExportTest.php
tests/Entity/MarkdownTest.php
tests/Entity/PageContentTest.php
tests/Entity/PageDraftTest.php
tests/Entity/PageRevisionTest.php
tests/Entity/PageTemplateTest.php
tests/Entity/PageTest.php [new file with mode: 0644]
tests/Entity/SearchOptionsTest.php [new file with mode: 0644]
tests/Entity/SortTest.php
tests/Entity/TagTest.php
tests/ErrorTest.php
tests/HomepageTest.php
tests/LanguageTest.php
tests/Permissions/EntityOwnerChangeTest.php [new file with mode: 0644]
tests/Permissions/EntityPermissionsTest.php [moved from tests/Permissions/RestrictionsTest.php with 95% similarity]
tests/Permissions/ExportPermissionsTest.php [new file with mode: 0644]
tests/Permissions/RolesTest.php
tests/PublicActionTest.php
tests/RecycleBinTest.php [new file with mode: 0644]
tests/SecurityHeaderTest.php [new file with mode: 0644]
tests/SharedTestHelpers.php
tests/StatusTest.php [new file with mode: 0644]
tests/TestCase.php
tests/TestEmailTest.php
tests/TestResponse.php
tests/Unit/ConfigTest.php
tests/Unit/UrlTest.php
tests/Uploads/AttachmentTest.php
tests/Uploads/AvatarTest.php
tests/Uploads/DrawioTest.php [new file with mode: 0644]
tests/Uploads/ImageTest.php
tests/Uploads/UsesImages.php
tests/User/UserApiTokenTest.php [new file with mode: 0644]
tests/User/UserManagementTest.php [new file with mode: 0644]
tests/User/UserPreferencesTest.php [moved from tests/UserPreferencesTest.php with 78% similarity]
tests/User/UserProfileTest.php [moved from tests/UserProfileTest.php with 72% similarity]
tests/test-data/compressed.png [new file with mode: 0644]
version
webpack.config.js [deleted file]

index 9e64828b5de45065f0e21d766ef3d44fb594b700..05383f04abcce2f08d732f2b08719cb5b3775a76 100644 (file)
@@ -1,14 +1,24 @@
+# This file, when named as ".env" in the root of your BookStack install
+# folder, is used for the core configuration of the application.
+# By default this file contains the most common required options but
+# a full list of options can be found in the '.env.example.complete' file.
+
+# NOTE: If any of your values contain a space or a hash you will need to
+# wrap the entire value in quotes. (eg. MAIL_FROM_NAME="BookStack Mailer")
+
 # Application key
 # Used for encryption where needed.
 # Run `php artisan key:generate` to generate a valid key.
 APP_KEY=SomeRandomString
 
 # Application URL
-# Remove the hash below and set a URL if using BookStack behind
-# a proxy, if using a third-party authentication option.
 # This must be the root URL that you want to host BookStack on.
-# All URL's in BookStack will be generated using this value.
-#APP_URL=https://example.com
+# All URLs in BookStack will be generated using this value
+# to ensure URLs generated are consistent and secure.
+# If you change this in the future you may need to run a command
+# to update stored URLs in the database. Command example:
+# php artisan bookstack:update-url https://old.example.com https://new.example.com
+APP_URL=https://example.com
 
 # Database details
 DB_HOST=localhost
@@ -17,15 +27,18 @@ DB_USERNAME=database_username
 DB_PASSWORD=database_user_password
 
 # Mail system to use
-# Can be 'smtp', 'mail' or 'sendmail'
+# Can be 'smtp' or 'sendmail'
 MAIL_DRIVER=smtp
 
+# Mail sender details
+MAIL_FROM_NAME="BookStack"
+
 # SMTP mail options
+# These settings can be checked using the "Send a Test Email"
+# feature found in the "Settings > Maintenance" area of the system.
 MAIL_HOST=localhost
 MAIL_PORT=1025
 MAIL_USERNAME=null
 MAIL_PASSWORD=null
-MAIL_ENCRYPTION=null
-
-
-# A full list of options can be found in the '.env.example.complete' file.
\ No newline at end of file
+MAIL_ENCRYPTION=null
\ No newline at end of file
index c4c3f0b85ff1192839395d1cc7edb3b965802b8f..e3dbdb857ddde1c7393e944a17ab9bea0f6b7104 100644 (file)
@@ -37,6 +37,11 @@ APP_AUTO_LANG_PUBLIC=true
 # Valid timezone values can be found here: https://www.php.net/manual/en/timezones.php
 APP_TIMEZONE=UTC
 
+# Application theme
+# Used to specific a themes/<APP_THEME> folder where BookStack UI
+# overrides can be made. Defaults to disabled.
+APP_THEME=false
+
 # Database details
 # Host can contain a port (localhost:3306) or a separate DB_PORT option can be used.
 DB_HOST=localhost
@@ -121,7 +126,7 @@ STORAGE_S3_ENDPOINT=https://my-custom-s3-compatible.service.com:8001
 STORAGE_URL=false
 
 # Authentication method to use
-# Can be 'standard' or 'ldap'
+# Can be 'standard', 'ldap' or 'saml2'
 AUTH_METHOD=standard
 
 # Social authentication configuration
@@ -191,9 +196,11 @@ LDAP_PASS=false
 LDAP_USER_FILTER=false
 LDAP_VERSION=false
 LDAP_TLS_INSECURE=false
+LDAP_ID_ATTRIBUTE=uid
 LDAP_EMAIL_ATTRIBUTE=mail
 LDAP_DISPLAY_NAME_ATTRIBUTE=cn
 LDAP_FOLLOW_REFERRALS=true
+LDAP_DUMP_USER_DETAILS=false
 
 # LDAP group sync configuration
 # Refer to https://www.bookstackapp.com/docs/admin/ldap-auth/
@@ -201,6 +208,26 @@ LDAP_USER_TO_GROUPS=false
 LDAP_GROUP_ATTRIBUTE="memberOf"
 LDAP_REMOVE_FROM_GROUPS=false
 
+# SAML authentication configuration
+# Refer to https://www.bookstackapp.com/docs/admin/saml2-auth/
+SAML2_NAME=SSO
+SAML2_EMAIL_ATTRIBUTE=email
+SAML2_DISPLAY_NAME_ATTRIBUTES=username
+SAML2_EXTERNAL_ID_ATTRIBUTE=null
+SAML2_IDP_ENTITYID=null
+SAML2_IDP_SSO=null
+SAML2_IDP_SLO=null
+SAML2_IDP_x509=null
+SAML2_ONELOGIN_OVERRIDES=null
+SAML2_DUMP_USER_DETAILS=false
+SAML2_AUTOLOAD_METADATA=false
+
+# SAML group sync configuration
+# Refer to https://www.bookstackapp.com/docs/admin/saml2-auth/
+SAML2_USER_TO_GROUPS=false
+SAML2_GROUP_ATTRIBUTE=group
+SAML2_REMOVE_FROM_GROUPS=false
+
 # Disable default third-party services such as Gravatar and Draw.IO
 # Service-specific options will override this option
 DISABLE_EXTERNAL_SERVICES=false
@@ -211,7 +238,10 @@ DISABLE_EXTERNAL_SERVICES=false
 # Example: AVATAR_URL=https://seccdn.libravatar.org/avatar/${hash}?s=${size}&d=identicon
 AVATAR_URL=
 
-# Enable Draw.io integration
+# Enable diagrams.net integration
+# Can simply be true/false to enable/disable the integration.
+# Alternatively, It can be URL to the diagrams.net instance you want to use.
+# For URLs, The following URL parameters should be included: embed=1&proto=json&spin=1
 DRAWIO=true
 
 # Default item listing view
@@ -225,6 +255,14 @@ APP_VIEWS_BOOKSHELVES=grid
 # If set to 'false' a limit will not be enforced.
 REVISION_LIMIT=50
 
+# Recycle Bin Lifetime
+# The number of days that content will remain in the recycle bin before
+# being considered for auto-removal. It is not a guarantee that content will
+# be removed after this time.
+# Set to 0 for no recycle bin functionality.
+# Set to -1 for unlimited recycle bin lifetime.
+RECYCLE_BIN_LIFETIME=30
+
 # Allow <script> tags in page content
 # Note, if set to 'true' the page editor may still escape scripts.
 ALLOW_CONTENT_SCRIPTS=false
@@ -235,3 +273,23 @@ ALLOW_CONTENT_SCRIPTS=false
 # Contents of the robots.txt file can be overridden, making this option obsolete.
 ALLOW_ROBOTS=null
 
+# A list of hosts that BookStack can be iframed within.
+# Space separated if multiple. BookStack host domain is auto-inferred.
+# For Example: ALLOWED_IFRAME_HOSTS="https://example.com https://a.example.com"
+# Setting this option will also auto-adjust cookies to be SameSite=None.
+ALLOWED_IFRAME_HOSTS=null
+
+# The default and maximum item-counts for listing API requests.
+API_DEFAULT_ITEM_COUNT=100
+API_MAX_ITEM_COUNT=500
+
+# The number of API requests that can be made per minute by a single user.
+API_REQUESTS_PER_MIN=180
+
+# Enable the logging of failed email+password logins with the given message.
+# The default log channel below uses the php 'error_log' function which commonly
+# results in messages being output to the webserver error logs.
+# The message can contain a %u parameter which will be replaced with the login
+# user identifier (Username or email).
+LOG_FAILED_LOGIN_MESSAGE=false
+LOG_FAILED_LOGIN_CHANNEL=errorlog_plain_webserver
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644 (file)
index 0000000..01b8471
--- /dev/null
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github: [ssddanbrown]
diff --git a/.github/ISSUE_TEMPLATE/api_request.md b/.github/ISSUE_TEMPLATE/api_request.md
new file mode 100644 (file)
index 0000000..dc050ef
--- /dev/null
@@ -0,0 +1,17 @@
+---
+name: New API Endpoint or Feature
+about: Request a new endpoint or API feature be added
+labels: ":nut_and_bolt: API Request"
+---
+
+#### API Endpoint or Feature
+
+Clearly describe what you'd like to have added to the API. 
+
+#### Use-Case
+
+Explain the use-case that you're working-on that requires the above request.
+
+#### Additional Context
+
+If required, add any other context about the feature request here.
\ No newline at end of file
index 8b3d29c2d291d444ecf235e9744056fd8866a2c9..c4444f242344d19294f14ba4d3edd1cf3a604600 100644 (file)
@@ -1,5 +1,5 @@
 ---
-name: Bug report
+name: Bug Report
 about: Create a report to help us improve
 
 ---
index 7f38b9cdc2a19f93b2aaf2ab013ff8656b4eb4b8..781cca5b8de289d57e739fe981af6f80674be348 100644 (file)
@@ -1,5 +1,5 @@
 ---
-name: Feature request
+name: Feature Request
 about: Suggest an idea for this project
 
 ---
diff --git a/.github/ISSUE_TEMPLATE/language_request.md b/.github/ISSUE_TEMPLATE/language_request.md
new file mode 100644 (file)
index 0000000..249ef78
--- /dev/null
@@ -0,0 +1,13 @@
+---
+name: Language Request
+about: Request a new language to be added to Crowdin for you to translate
+
+---
+
+### Language To Add
+
+_Specify here the language you want to add._
+
+----
+
+_This issue template is to request a new language be added to our [Crowdin translation management project](https://crowdin.com/project/bookstack). Please don't use this template to request a new language that you are not prepared to provide translations for._   
\ No newline at end of file
diff --git a/.github/translators.txt b/.github/translators.txt
new file mode 100644 (file)
index 0000000..9849b4c
--- /dev/null
@@ -0,0 +1,136 @@
+Name :: Languages
+@robertlandes :: German
+@SergioMendolia :: French
+@NakaharaL :: Portuguese, Brazilian
+@ReeseSebastian :: German
+@arietimmerman :: Dutch
+@diegoseso :: Spanish
+@S64 :: Japanese
+@JachuPL :: Polish
+@Joorem :: French
+@timoschwarzer :: German
+@sanderdw :: Dutch
+@lbguilherme :: Portuguese, Brazilian
+@marcusforsberg :: Swedish
+@artur-trzesiok :: Polish
+@Alwaysin :: French
+@msaus :: Japanese
+@moucho :: Spanish
+@vriic :: German
+@DeehSlash :: Portuguese, Brazilian
+@alex2702 :: German
+@nicobubulle :: French
+@kmoj86 :: Arabic
+@houbaron :: Chinese Traditional; Chinese Simplified
+@mullinsmikey :: Russian
+@limkukhyun :: Korean
+@CliffyPrime :: German
+@kejjang :: Chinese Traditional
+@TheLastOperator :: French
+@qianmengnet :: Simplified Chinese
+@ezzra :: German; German Informal
+@vasiliev123 :: Polish
+@Mant1kor :: Ukrainian
+@Xiphoseer :: German; German Informal
+@maantje :: Dutch
+@cima :: Czech
+@agvol :: Russian
+@Hambern :: Swedish
+@NootoNooto :: Dutch
+@kostefun :: Russian
+@lucaguindani :: French
+@miles75 :: Hungarian
+@danielroehrig-mm :: German
+@oykenfurkan :: Turkish
+@qligier :: French
+@johnroyer :: Traditional Chinese
+@artskoczylas :: Polish
+@dellamina :: Italian
+@jzoy :: Simplified Chinese
+@ististudio :: Korean
+@leomartinez :: Spanish Argentina
+cipi1965 :: Italian
+Mykola Ronik (Mantikor) :: Ukrainian
+furkanoyk :: Turkish
+m0uch0 :: Spanish
+Maxim Zalata (zlatin) :: Russian; Ukrainian
+nutsflag :: French
+Leonardo Mario Martinez (leonardo.m.martinez) :: Spanish, Argentina
+Rodrigo Saczuk Niz (rodrigoniz) :: Portuguese, Brazilian
+叫钦叔就好 (254351722) :: Chinese Traditional; Chinese Simplified
+aekramer :: Dutch
+JachuPL :: Polish
+milesteg :: Hungarian
+Beenbag :: German; German Informal
+Lett3rs :: Danish
+Julian (julian.henneberg) :: German; German Informal
+3GNWn :: Danish
+dbguichu :: Chinese Simplified
+Randy Kim (hyunjun) :: Korean
+Francesco M. Taurino (ftaurino) :: Italian
+DanielFrederiksen :: Danish
+Finn Wessel (19finnwessel6) :: German Informal; German
+Gustav Kånåhols (Kurbitz) :: Swedish
+Vuong Trung Hieu (fpooon) :: Vietnamese
+Emil Petersen (emoyly) :: Danish
+mrjaboozy :: Slovenian
+Statium :: Russian
+Mikkel Struntze (MStruntze) :: Danish
+kostefun :: Russian
+Tuyen.NG (tuyendev) :: Vietnamese
+Ghost_chu (dbguichu) :: Chinese Simplified
+Ziipen :: Danish
+Samuel Schwarz (Guiph7quan) :: Czech
+Aleph (toishoki) :: Turkish
+Julio Alberto García (Yllelder) :: Spanish
+Rafael (raribeir) :: Portuguese, Brazilian
+Hiroyuki Odake (dakesan) :: Japanese
+Alex Lee (qianmengnet) :: Chinese Simplified
+swinn37 :: French
+Hasan Özbey (the-turk) :: Turkish
+rcy :: Swedish
+Ali Yasir Yılmaz (ayyilmaz) :: Turkish
+scureza :: Italian
+Biepa :: German Informal; German
+syecu :: Chinese Simplified
+Lap1t0r :: French
+Thinkverse (thinkverse) :: Swedish
+alef (toishoki) :: Turkish
+Robbert Feunekes (Muukuro) :: Dutch
+seohyeon.joo :: Korean
+Orenda (OREDNA) :: Bulgarian
+Marek Pavelka (marapavelka) :: Czech
+Venkinovec :: Czech
+Tommy Ku (tommyku) :: Chinese Traditional; Japanese
+Michał Bielejewski  (bielej) :: Polish
+jozefrebjak :: Slovak
+Ikhwan Koo (Ikhwan.Koo) :: Korean
+Whay (remkovdhoef) :: Dutch
+jc7115 :: Chinese Traditional
+주서현 (seohyeon.joo) :: Korean
+ReadySystems :: Arabic
+HFinch :: German; German Informal
+brechtgijsens :: Dutch
+Lowkey (v587ygq) :: Chinese Simplified
+sdl-blue :: German Informal
+sqlik :: Polish
+Roy van Schaijk (royvanschaijk) :: Dutch
+Simsimpicpic :: French
+Zenahr Barzani (Zenahr) :: German; Japanese; Dutch; German Informal
+tatsuya.info :: Japanese
+fadiapp :: Arabic
+Jakub Bouček (jakubboucek) :: Czech
+Marco (cdrfun) :: German
+10935336 :: Chinese Simplified
+孟繁阳 (FanyangMeng) :: Chinese Simplified
+Andrej Močan (andrejm) :: Slovenian
+gilane9_ :: Arabic
+Raed alnahdi (raednahdi) :: Arabic
+Xiphoseer :: German
+MerlinSVK (merlinsvk) :: Slovak
+Kauê Sena (kaue.sena.ks) :: Portuguese, Brazilian
+MatthieuParis :: French
+Douradinho :: Portuguese, Brazilian
+Gaku Yaguchi (tama11) :: Japanese
+johnroyer :: Chinese Traditional
+jackaaa :: Chinese Traditional
index 922aa50677e815803ba88ff2277b8795668a7808..7646f06879ab81802f774d19e2610476f02f265c 100644 (file)
@@ -1,26 +1,54 @@
 name: phpunit
 
-on: [push, pull_request]
+on:
+  push:
+    branches:
+      - master
+      - release
+  pull_request:
+    branches:
+      - '*'
+      - '*/*'
+      - '!l10n_master'
 
 jobs:
   build:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        php: [7.2, 7.3]
+        php: [7.2, 7.4]
     steps:
     - uses: actions/checkout@v1
+
+    - name: Get Composer Cache Directory
+      id: composer-cache
+      run: |
+        echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+    - name: Cache composer packages
+      uses: actions/cache@v1
+      with:
+        path: ${{ steps.composer-cache.outputs.dir }}
+        key: ${{ runner.os }}-composer-${{ matrix.php }}
+
+    - name: Start Database
+      run: |
+        sudo /etc/init.d/mysql start
+
     - name: Setup Database
       run: |
         mysql -uroot -proot -e 'CREATE DATABASE IF NOT EXISTS `bookstack-test`;'
         mysql -uroot -proot -e "CREATE USER 'bookstack-test'@'localhost' IDENTIFIED BY 'bookstack-test';"
         mysql -uroot -proot -e "GRANT ALL ON \`bookstack-test\`.* TO 'bookstack-test'@'localhost';"
         mysql -uroot -proot -e 'FLUSH PRIVILEGES;'
+
     - name: Install composer dependencies & Test
       run: composer install --prefer-dist --no-interaction --ansi
+
     - name: Migrate and seed the database
       run: |
         php${{ matrix.php }} artisan migrate --force -n --database=mysql_testing
         php${{ matrix.php }} artisan db:seed --force -n --class=DummyContentSeeder --database=mysql_testing
+
     - name: phpunit
       run: php${{ matrix.php }} ./vendor/bin/phpunit
diff --git a/.github/workflows/test-migrations.yml b/.github/workflows/test-migrations.yml
new file mode 100644 (file)
index 0000000..bff6f70
--- /dev/null
@@ -0,0 +1,58 @@
+name: test-migrations
+
+on:
+  push:
+    branches:
+      - master
+      - release
+  pull_request:
+    branches:
+      - '*'
+      - '*/*'
+      - '!l10n_master'
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        php: [7.2, 7.4]
+    steps:
+      - uses: actions/checkout@v1
+
+      - name: Get Composer Cache Directory
+        id: composer-cache
+        run: |
+          echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+      - name: Cache composer packages
+        uses: actions/cache@v1
+        with:
+          path: ${{ steps.composer-cache.outputs.dir }}
+          key: ${{ runner.os }}-composer-${{ matrix.php }}
+
+      - name: Start MySQL
+        run: |
+          sudo /etc/init.d/mysql start
+
+      - name: Create database & user
+        run: |
+          mysql -uroot -proot -e 'CREATE DATABASE IF NOT EXISTS `bookstack-test`;'
+          mysql -uroot -proot -e "CREATE USER 'bookstack-test'@'localhost' IDENTIFIED BY 'bookstack-test';"
+          mysql -uroot -proot -e "GRANT ALL ON \`bookstack-test\`.* TO 'bookstack-test'@'localhost';"
+          mysql -uroot -proot -e 'FLUSH PRIVILEGES;'
+
+      - name: Install composer dependencies
+        run: composer install --prefer-dist --no-interaction --ansi
+
+      - name: Start migration test
+        run: |
+          php${{ matrix.php }} artisan migrate --force -n --database=mysql_testing
+
+      - name: Start migration:rollback test
+        run: |
+          php${{ matrix.php }} artisan migrate:rollback --force -n --database=mysql_testing
+
+      - name: Start migration rerun test
+        run: |
+          php${{ matrix.php }} artisan migrate --force -n --database=mysql_testing
index e5579e4a62f21ca24e6f804fdee275b9655519b6..fc0f10a000593b26f6e41b17485ced22c0f7fbe3 100644 (file)
@@ -22,4 +22,5 @@ nbproject
 .project
 .settings/
 webpack-stats.json
-.phpunit.result.cache
\ No newline at end of file
+.phpunit.result.cache
+.DS_Store
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 080c54b3e7a66c66ecdd40ea413d7811ab6d4425..61aeaad8c8323f444c5f7b7af001f5e141dd03fa 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2018 Dan Brown and the BookStack Project contributors
+Copyright (c) 2020 Dan Brown and the BookStack Project contributors
 https://github.com/BookStackApp/BookStack/graphs/contributors
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
index 05f0129ddff7c322f8f900a3f5bdf4e6dba65fda..9d256c9b2918b2cdf0d7f543662bd7a7fb920f87 100644 (file)
@@ -3,18 +3,19 @@
 namespace BookStack\Actions;
 
 use BookStack\Auth\User;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Entity;
 use BookStack\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Support\Str;
 
 /**
- * @property string $key
+ * @property string $type
  * @property User $user
  * @property Entity $entity
- * @property string $extra
+ * @property string $detail
  * @property string $entity_type
  * @property int $entity_id
  * @property int $user_id
- * @property int $book_id
  */
 class Activity extends Model
 {
@@ -32,29 +33,35 @@ class Activity extends Model
 
     /**
      * Get the user this activity relates to.
-     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
      */
-    public function user()
+    public function user(): BelongsTo
     {
         return $this->belongsTo(User::class);
     }
 
     /**
-     * Returns text from the language files, Looks up by using the
-     * activity key.
+     * Returns text from the language files, Looks up by using the activity key.
      */
-    public function getText()
+    public function getText(): string
     {
-        return trans('activities.' . $this->key);
+        return trans('activities.' . $this->type);
+    }
+
+    /**
+     * Check if this activity is intended to be for an entity.
+     */
+    public function isForEntity(): bool
+    {
+        return Str::startsWith($this->type, [
+            'page_', 'chapter_', 'book_', 'bookshelf_'
+        ]);
     }
 
     /**
      * Checks if another Activity matches the general information of another.
-     * @param $activityB
-     * @return bool
      */
-    public function isSimilarTo($activityB)
+    public function isSimilarTo(Activity $activityB): bool
     {
-        return [$this->key, $this->entity_type, $this->entity_id] === [$activityB->key, $activityB->entity_type, $activityB->entity_id];
+        return [$this->type, $this->entity_type, $this->entity_id] === [$activityB->type, $activityB->entity_type, $activityB->entity_id];
     }
 }
index f56f1ca57e03c304c4f4fdcd7201293afe45f8ba..b2a35fd2a5115c7ba869a35ddbd34e69287b8e6d 100644 (file)
@@ -1,67 +1,60 @@
 <?php namespace BookStack\Actions;
 
 use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Entities\Book;
-use BookStack\Entities\Entity;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use BookStack\Interfaces\Loggable;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Relations\Relation;
+use Illuminate\Support\Facades\Log;
 
 class ActivityService
 {
     protected $activity;
-    protected $user;
     protected $permissionService;
 
-    /**
-     * ActivityService constructor.
-     * @param Activity $activity
-     * @param PermissionService $permissionService
-     */
     public function __construct(Activity $activity, PermissionService $permissionService)
     {
         $this->activity = $activity;
         $this->permissionService = $permissionService;
-        $this->user = user();
     }
 
     /**
-     * Add activity data to database.
-     * @param \BookStack\Entities\Entity $entity
-     * @param string $activityKey
-     * @param int $bookId
+     * Add activity data to database for an entity.
      */
-    public function add(Entity $entity, string $activityKey, int $bookId = null)
+    public function addForEntity(Entity $entity, string $type)
     {
-        $activity = $this->newActivityForUser($activityKey, $bookId);
+        $activity = $this->newActivityForUser($type);
         $entity->activity()->save($activity);
-        $this->setNotification($activityKey);
+        $this->setNotification($type);
     }
 
     /**
-     * Adds a activity history with a message, without binding to a entity.
-     * @param string $activityKey
-     * @param string $message
-     * @param int $bookId
+     * Add a generic activity event to the database.
+     * @param string|Loggable $detail
      */
-    public function addMessage(string $activityKey, string $message, int $bookId = null)
+    public function add(string $type, $detail = '')
     {
-        $this->newActivityForUser($activityKey, $bookId)->forceFill([
-            'extra' => $message
-        ])->save();
+        if ($detail instanceof Loggable) {
+            $detail = $detail->logDescriptor();
+        }
 
-        $this->setNotification($activityKey);
+        $activity = $this->newActivityForUser($type);
+        $activity->detail = $detail;
+        $activity->save();
+        $this->setNotification($type);
     }
 
     /**
      * Get a new activity instance for the current user.
-     * @param string $key
-     * @param int|null $bookId
-     * @return Activity
      */
-    protected function newActivityForUser(string $key, int $bookId = null)
+    protected function newActivityForUser(string $type): Activity
     {
         return $this->activity->newInstance()->forceFill([
-            'key' => strtolower($key),
-            'user_id' => $this->user->id,
-            'book_id' => $bookId ?? 0,
+            'type'     => strtolower($type),
+            'user_id' => user()->id,
         ]);
     }
 
@@ -69,34 +62,25 @@ class ActivityService
      * Removes the entity attachment from each of its activities
      * and instead uses the 'extra' field with the entities name.
      * Used when an entity is deleted.
-     * @param \BookStack\Entities\Entity $entity
-     * @return mixed
      */
     public function removeEntity(Entity $entity)
     {
-        // TODO - Rewrite to db query.
-        $activities = $entity->activity;
-        foreach ($activities as $activity) {
-            $activity->extra = $entity->name;
-            $activity->entity_id = 0;
-            $activity->entity_type = null;
-            $activity->save();
-        }
-        return $activities;
+        $entity->activity()->update([
+            'detail'       => $entity->name,
+            'entity_id'   => null,
+            'entity_type' => null,
+        ]);
     }
 
     /**
      * Gets the latest activity.
-     * @param int $count
-     * @param int $page
-     * @return array
      */
-    public function latest($count = 20, $page = 0)
+    public function latest(int $count = 20, int $page = 0): array
     {
         $activityList = $this->permissionService
             ->filterRestrictedEntityRelations($this->activity, 'activities', 'entity_id', 'entity_type')
             ->orderBy('created_at', 'desc')
-            ->with('user', 'entity')
+            ->with(['user', 'entity'])
             ->skip($count * $page)
             ->take($count)
             ->get();
@@ -107,24 +91,33 @@ class ActivityService
     /**
      * Gets the latest activity for an entity, Filtering out similar
      * items to prevent a message activity list.
-     * @param \BookStack\Entities\Entity $entity
-     * @param int $count
-     * @param int $page
-     * @return array
      */
-    public function entityActivity($entity, $count = 20, $page = 1)
+    public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array
     {
+        /** @var [string => int[]] $queryIds */
+        $queryIds = [$entity->getMorphClass() => [$entity->id]];
+
         if ($entity->isA('book')) {
-            $query = $this->activity->where('book_id', '=', $entity->id);
-        } else {
-            $query = $this->activity->where('entity_type', '=', $entity->getMorphClass())
-                ->where('entity_id', '=', $entity->id);
+            $queryIds[(new Chapter)->getMorphClass()] = $entity->chapters()->visible()->pluck('id');
         }
-        
-        $activity = $this->permissionService
-            ->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
-            ->orderBy('created_at', 'desc')
-            ->with(['entity', 'user.avatar'])
+        if ($entity->isA('book') || $entity->isA('chapter')) {
+            $queryIds[(new Page)->getMorphClass()] = $entity->pages()->visible()->pluck('id');
+        }
+
+        $query = $this->activity->newQuery();
+        $query->where(function (Builder $query) use ($queryIds) {
+            foreach ($queryIds as $morphClass => $idArr) {
+                $query->orWhere(function (Builder $innerQuery) use ($morphClass, $idArr) {
+                    $innerQuery->where('entity_type', '=', $morphClass)
+                        ->whereIn('entity_id', $idArr);
+                });
+            }
+        });
+
+        $activity = $query->orderBy('created_at', 'desc')
+            ->with(['entity' => function (Relation $query) {
+                $query->withTrashed();
+            }, 'user.avatar'])
             ->skip($count * ($page - 1))
             ->take($count)
             ->get();
@@ -133,18 +126,18 @@ class ActivityService
     }
 
     /**
-     * Get latest activity for a user, Filtering out similar
-     * items.
-     * @param $user
-     * @param int $count
-     * @param int $page
-     * @return array
+     * Get latest activity for a user, Filtering out similar items.
      */
-    public function userActivity($user, $count = 20, $page = 0)
+    public function userActivity(User $user, int $count = 20, int $page = 0): array
     {
         $activityList = $this->permissionService
             ->filterRestrictedEntityRelations($this->activity, 'activities', 'entity_id', 'entity_type')
-            ->orderBy('created_at', 'desc')->where('user_id', '=', $user->id)->skip($count * $page)->take($count)->get();
+            ->orderBy('created_at', 'desc')
+            ->where('user_id', '=', $user->id)
+            ->skip($count * $page)
+            ->take($count)
+            ->get();
+
         return $this->filterSimilar($activityList);
     }
 
@@ -153,34 +146,47 @@ class ActivityService
      * @param Activity[] $activities
      * @return array
      */
-    protected function filterSimilar($activities)
+    protected function filterSimilar(iterable $activities): array
     {
         $newActivity = [];
-        $previousItem = false;
+        $previousItem = null;
+
         foreach ($activities as $activityItem) {
-            if ($previousItem === false) {
-                $previousItem = $activityItem;
-                $newActivity[] = $activityItem;
-                continue;
-            }
-            if (!$activityItem->isSimilarTo($previousItem)) {
+            if (!$previousItem || !$activityItem->isSimilarTo($previousItem)) {
                 $newActivity[] = $activityItem;
             }
+
             $previousItem = $activityItem;
         }
+
         return $newActivity;
     }
 
     /**
      * Flashes a notification message to the session if an appropriate message is available.
-     * @param $activityKey
      */
-    protected function setNotification($activityKey)
+    protected function setNotification(string $type)
     {
-        $notificationTextKey = 'activities.' . $activityKey . '_notification';
+        $notificationTextKey = 'activities.' . $type . '_notification';
         if (trans()->has($notificationTextKey)) {
             $message = trans($notificationTextKey);
             session()->flash('success', $message);
         }
     }
+
+    /**
+     * Log out a failed login attempt, Providing the given username
+     * as part of the message if the '%u' string is used.
+     */
+    public function logFailedLogin(string $username)
+    {
+        $message = config('logging.failed_login.message');
+        if (!$message) {
+            return;
+        }
+
+        $message = str_replace("%u", $username, $message);
+        $channel = config('logging.failed_login.channel');
+        Log::channel($channel)->warning($message);
+    }
 }
diff --git a/app/Actions/ActivityType.php b/app/Actions/ActivityType.php
new file mode 100644 (file)
index 0000000..216f612
--- /dev/null
@@ -0,0 +1,51 @@
+<?php namespace BookStack\Actions;
+
+class ActivityType
+{
+    const PAGE_CREATE = 'page_create';
+    const PAGE_UPDATE = 'page_update';
+    const PAGE_DELETE = 'page_delete';
+    const PAGE_RESTORE = 'page_restore';
+    const PAGE_MOVE = 'page_move';
+
+    const CHAPTER_CREATE = 'chapter_create';
+    const CHAPTER_UPDATE = 'chapter_update';
+    const CHAPTER_DELETE = 'chapter_delete';
+    const CHAPTER_MOVE = 'chapter_move';
+
+    const BOOK_CREATE = 'book_create';
+    const BOOK_UPDATE = 'book_update';
+    const BOOK_DELETE = 'book_delete';
+    const BOOK_SORT = 'book_sort';
+
+    const BOOKSHELF_CREATE = 'bookshelf_create';
+    const BOOKSHELF_UPDATE = 'bookshelf_update';
+    const BOOKSHELF_DELETE = 'bookshelf_delete';
+
+    const COMMENTED_ON = 'commented_on';
+    const PERMISSIONS_UPDATE = 'permissions_update';
+
+    const SETTINGS_UPDATE = 'settings_update';
+    const MAINTENANCE_ACTION_RUN = 'maintenance_action_run';
+
+    const RECYCLE_BIN_EMPTY = 'recycle_bin_empty';
+    const RECYCLE_BIN_RESTORE = 'recycle_bin_restore';
+    const RECYCLE_BIN_DESTROY = 'recycle_bin_destroy';
+
+    const USER_CREATE = 'user_create';
+    const USER_UPDATE = 'user_update';
+    const USER_DELETE = 'user_delete';
+
+    const API_TOKEN_CREATE = 'api_token_create';
+    const API_TOKEN_UPDATE = 'api_token_update';
+    const API_TOKEN_DELETE = 'api_token_delete';
+
+    const ROLE_CREATE = 'role_create';
+    const ROLE_UPDATE = 'role_update';
+    const ROLE_DELETE = 'role_delete';
+
+    const AUTH_PASSWORD_RESET = 'auth_password_reset_request';
+    const AUTH_PASSWORD_RESET_UPDATE = 'auth_password_reset_update';
+    const AUTH_LOGIN = 'auth_login';
+    const AUTH_REGISTER = 'auth_register';
+}
\ No newline at end of file
index f138ee4a1ffed5da7bb1ead3a581d9045901179f..f5269e2534d7a8ea2cbc77cbfdefa5adac815847 100644 (file)
@@ -1,26 +1,34 @@
 <?php namespace BookStack\Actions;
 
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
 
-class Comment extends Ownable
+/**
+ * @property string text
+ * @property string html
+ * @property int|null parent_id
+ * @property int local_id
+ */
+class Comment extends Model
 {
-    protected $fillable = ['text', 'html', 'parent_id'];
+    use HasCreatorAndUpdater;
+
+    protected $fillable = ['text', 'parent_id'];
     protected $appends = ['created', 'updated'];
 
     /**
      * Get the entity that this comment belongs to
-     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
      */
-    public function entity()
+    public function entity(): MorphTo
     {
         return $this->morphTo('entity');
     }
 
     /**
      * Check if a comment has been updated since creation.
-     * @return bool
      */
-    public function isUpdated()
+    public function isUpdated(): bool
     {
         return $this->updated_at->timestamp > $this->created_at->timestamp;
     }
index 5ff639e2eb7275aefb281ef7b6e5c029a4e4ae62..13a83e7fdd247064983c64928811b49db2ba4ca1 100644 (file)
@@ -1,23 +1,21 @@
 <?php namespace BookStack\Actions;
 
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Entity;
+use League\CommonMark\CommonMarkConverter;
+use BookStack\Facades\Activity as ActivityService;
 
 /**
  * Class CommentRepo
- * @package BookStack\Repos
  */
 class CommentRepo
 {
 
     /**
-     * @var \BookStack\Actions\Comment $comment
+     * @var Comment $comment
      */
     protected $comment;
 
-    /**
-     * CommentRepo constructor.
-     * @param \BookStack\Actions\Comment $comment
-     */
+
     public function __construct(Comment $comment)
     {
         $this->comment = $comment;
@@ -25,65 +23,72 @@ class CommentRepo
 
     /**
      * Get a comment by ID.
-     * @param $id
-     * @return \BookStack\Actions\Comment|\Illuminate\Database\Eloquent\Model
      */
-    public function getById($id)
+    public function getById(int $id): Comment
     {
         return $this->comment->newQuery()->findOrFail($id);
     }
 
     /**
      * Create a new comment on an entity.
-     * @param \BookStack\Entities\Entity $entity
-     * @param array $data
-     * @return \BookStack\Actions\Comment
      */
-    public function create(Entity $entity, $data = [])
+    public function create(Entity $entity, string $text, ?int $parent_id): Comment
     {
         $userId = user()->id;
-        $comment = $this->comment->newInstance($data);
+        $comment = $this->comment->newInstance();
+
+        $comment->text = $text;
+        $comment->html = $this->commentToHtml($text);
         $comment->created_by = $userId;
         $comment->updated_by = $userId;
         $comment->local_id = $this->getNextLocalId($entity);
+        $comment->parent_id = $parent_id;
+
         $entity->comments()->save($comment);
+        ActivityService::addForEntity($entity, ActivityType::COMMENTED_ON);
         return $comment;
     }
 
     /**
      * Update an existing comment.
-     * @param \BookStack\Actions\Comment $comment
-     * @param array $input
-     * @return mixed
      */
-    public function update($comment, $input)
+    public function update(Comment $comment, string $text): Comment
     {
         $comment->updated_by = user()->id;
-        $comment->update($input);
+        $comment->text = $text;
+        $comment->html = $this->commentToHtml($text);
+        $comment->save();
         return $comment;
     }
 
     /**
      * Delete a comment from the system.
-     * @param \BookStack\Actions\Comment $comment
-     * @return mixed
      */
-    public function delete($comment)
+    public function delete(Comment $comment)
     {
-        return $comment->delete();
+        $comment->delete();
+    }
+
+    /**
+     * Convert the given comment markdown text to HTML.
+     */
+    public function commentToHtml(string $commentText): string
+    {
+        $converter = new CommonMarkConverter([
+            'html_input' => 'strip',
+            'max_nesting_level' => 10,
+            'allow_unsafe_links' => false,
+        ]);
+
+        return $converter->convertToHtml($commentText);
     }
 
     /**
      * Get the next local ID relative to the linked entity.
-     * @param \BookStack\Entities\Entity $entity
-     * @return int
      */
-    protected function getNextLocalId(Entity $entity)
+    protected function getNextLocalId(Entity $entity): int
     {
         $comments = $entity->comments(false)->orderBy('local_id', 'desc')->first();
-        if ($comments === null) {
-            return 1;
-        }
-        return $comments->local_id + 1;
+        return ($comments->local_id ?? 0) + 1;
     }
 }
index 38d0458e46425f53fd13c6421d745c8fe67b0101..5968ffe6d5ea9d875cf4fa574ac63d3d4c8d62a1 100644 (file)
@@ -2,13 +2,10 @@
 
 use BookStack\Model;
 
-/**
- * Class Attribute
- * @package BookStack
- */
 class Tag extends Model
 {
     protected $fillable = ['name', 'value', 'order'];
+    protected $hidden = ['id', 'entity_id', 'entity_type', 'created_at', 'updated_at'];
 
     /**
      * Get the entity that this tag belongs to
index 0cbfa4163bf9120226aafea71e678a4f13b920a8..f58589ccd589c4d019d055156f8596974da447c4 100644 (file)
@@ -1,72 +1,32 @@
 <?php namespace BookStack\Actions;
 
 use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Entity;
+use DB;
+use Illuminate\Support\Collection;
 
-/**
- * Class TagRepo
- * @package BookStack\Repos
- */
 class TagRepo
 {
 
     protected $tag;
-    protected $entity;
     protected $permissionService;
 
     /**
      * TagRepo constructor.
-     * @param \BookStack\Actions\Tag $attr
-     * @param \BookStack\Entities\Entity $ent
-     * @param \BookStack\Auth\Permissions\PermissionService $ps
      */
-    public function __construct(Tag $attr, Entity $ent, PermissionService $ps)
+    public function __construct(Tag $tag, PermissionService $ps)
     {
-        $this->tag = $attr;
-        $this->entity = $ent;
+        $this->tag = $tag;
         $this->permissionService = $ps;
     }
 
-    /**
-     * Get an entity instance of its particular type.
-     * @param $entityType
-     * @param $entityId
-     * @param string $action
-     * @return \Illuminate\Database\Eloquent\Model|null|static
-     */
-    public function getEntity($entityType, $entityId, $action = 'view')
-    {
-        $entityInstance = $this->entity->getEntityInstance($entityType);
-        $searchQuery = $entityInstance->where('id', '=', $entityId)->with('tags');
-        $searchQuery = $this->permissionService->enforceEntityRestrictions($entityType, $searchQuery, $action);
-        return $searchQuery->first();
-    }
-
-    /**
-     * Get all tags for a particular entity.
-     * @param string $entityType
-     * @param int $entityId
-     * @return mixed
-     */
-    public function getForEntity($entityType, $entityId)
-    {
-        $entity = $this->getEntity($entityType, $entityId);
-        if ($entity === null) {
-            return collect();
-        }
-
-        return $entity->tags;
-    }
-
     /**
      * Get tag name suggestions from scanning existing tag names.
      * If no search term is given the 50 most popular tag names are provided.
-     * @param $searchTerm
-     * @return array
      */
-    public function getNameSuggestions($searchTerm = false)
+    public function getNameSuggestions(?string $searchTerm): Collection
     {
-        $query = $this->tag->select('*', \DB::raw('count(*) as count'))->groupBy('name');
+        $query = $this->tag->select('*', DB::raw('count(*) as count'))->groupBy('name');
 
         if ($searchTerm) {
             $query = $query->where('name', 'LIKE', $searchTerm . '%')->orderBy('name', 'desc');
@@ -82,13 +42,10 @@ class TagRepo
      * Get tag value suggestions from scanning existing tag values.
      * If no search is given the 50 most popular values are provided.
      * Passing a tagName will only find values for a tags with a particular name.
-     * @param $searchTerm
-     * @param $tagName
-     * @return array
      */
-    public function getValueSuggestions($searchTerm = false, $tagName = false)
+    public function getValueSuggestions(?string $searchTerm, ?string $tagName): Collection
     {
-        $query = $this->tag->select('*', \DB::raw('count(*) as count'))->groupBy('value');
+        $query = $this->tag->select('*', DB::raw('count(*) as count'))->groupBy('value');
 
         if ($searchTerm) {
             $query = $query->where('value', 'LIKE', $searchTerm . '%')->orderBy('value', 'desc');
@@ -96,7 +53,7 @@ class TagRepo
             $query = $query->orderBy('count', 'desc')->take(50);
         }
 
-        if ($tagName !== false) {
+        if ($tagName) {
             $query = $query->where('name', '=', $tagName);
         }
 
@@ -106,35 +63,28 @@ class TagRepo
 
     /**
      * Save an array of tags to an entity
-     * @param \BookStack\Entities\Entity $entity
-     * @param array $tags
-     * @return array|\Illuminate\Database\Eloquent\Collection
      */
-    public function saveTagsToEntity(Entity $entity, $tags = [])
+    public function saveTagsToEntity(Entity $entity, array $tags = []): iterable
     {
         $entity->tags()->delete();
-        $newTags = [];
-        foreach ($tags as $tag) {
-            if (trim($tag['name']) === '') {
-                continue;
-            }
-            $newTags[] = $this->newInstanceFromInput($tag);
-        }
+
+        $newTags = collect($tags)->filter(function ($tag) {
+            return boolval(trim($tag['name']));
+        })->map(function ($tag) {
+            return $this->newInstanceFromInput($tag);
+        })->all();
 
         return $entity->tags()->saveMany($newTags);
     }
 
     /**
      * Create a new Tag instance from user input.
-     * @param $input
-     * @return \BookStack\Actions\Tag
+     * Input must be an array with a 'name' and an optional 'value' key.
      */
-    protected function newInstanceFromInput($input)
+    protected function newInstanceFromInput(array $input): Tag
     {
         $name = trim($input['name']);
         $value = isset($input['value']) ? trim($input['value']) : '';
-        // Any other modification or cleanup required can go here
-        $values = ['name' => $name, 'value' => $value];
-        return $this->tag->newInstance($values);
+        return $this->tag->newInstance(['name' => $name, 'value' => $value]);
     }
 }
index 324bfaa4ef9fb14c722148529c4bb895692a12e8..51a60d96e20effd49c6eda2ea7f247deb828d6a6 100644 (file)
@@ -1,8 +1,8 @@
 <?php namespace BookStack\Actions;
 
 use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Entities\Book;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Entity;
 use BookStack\Entities\EntityProvider;
 use DB;
 use Illuminate\Support\Collection;
@@ -28,7 +28,7 @@ class ViewService
 
     /**
      * Add a view to the given entity.
-     * @param \BookStack\Entities\Entity $entity
+     * @param \BookStack\Entities\Models\Entity $entity
      * @return int
      */
     public function add(Entity $entity)
@@ -74,34 +74,36 @@ class ViewService
             $query->whereIn('viewable_type', $this->entityProvider->getMorphClasses($filterModels));
         }
 
-        return $query->with('viewable')->skip($skipCount)->take($count)->get()->pluck('viewable');
+        return $query->with('viewable')
+            ->skip($skipCount)
+            ->take($count)
+            ->get()
+            ->pluck('viewable')
+            ->filter();
     }
 
     /**
      * Get all recently viewed entities for the current user.
-     * @param int $count
-     * @param int $page
-     * @param Entity|bool $filterModel
-     * @return mixed
      */
-    public function getUserRecentlyViewed($count = 10, $page = 0, $filterModel = false)
+    public function getUserRecentlyViewed(int $count = 10, int $page = 1)
     {
         $user = user();
         if ($user === null || $user->isDefault()) {
             return collect();
         }
 
-        $query = $this->permissionService
-            ->filterRestrictedEntityRelations($this->view, 'views', 'viewable_id', 'viewable_type');
-
-        if ($filterModel) {
-            $query = $query->where('viewable_type', '=', $filterModel->getMorphClass());
+        $all = collect();
+        /** @var Entity $instance */
+        foreach ($this->entityProvider->all() as $name => $instance) {
+            $items = $instance::visible()->withLastView()
+                ->orderBy('last_viewed_at', 'desc')
+                ->skip($count * ($page - 1))
+                ->take($count)
+                ->get();
+            $all = $all->concat($items);
         }
-        $query = $query->where('user_id', '=', $user->id);
 
-        $viewables = $query->with('viewable')->orderBy('updated_at', 'desc')
-            ->skip($count * $page)->take($count)->get()->pluck('viewable');
-        return $viewables;
+        return $all->sortByDesc('last_viewed_at')->slice(0, $count);
     }
 
     /**
diff --git a/app/Api/ApiDocsGenerator.php b/app/Api/ApiDocsGenerator.php
new file mode 100644 (file)
index 0000000..2953647
--- /dev/null
@@ -0,0 +1,146 @@
+<?php namespace BookStack\Api;
+
+use BookStack\Http\Controllers\Api\ApiController;
+use Illuminate\Contracts\Container\BindingResolutionException;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Route;
+use Illuminate\Support\Str;
+use ReflectionClass;
+use ReflectionException;
+use ReflectionMethod;
+
+class ApiDocsGenerator
+{
+
+    protected $reflectionClasses = [];
+    protected $controllerClasses = [];
+
+    /**
+     * Load the docs form the cache if existing
+     * otherwise generate and store in the cache.
+     */
+    public static function generateConsideringCache(): Collection
+    {
+        $appVersion = trim(file_get_contents(base_path('version')));
+        $cacheKey = 'api-docs::' . $appVersion;
+        if (Cache::has($cacheKey) && config('app.env') === 'production') {
+            $docs = Cache::get($cacheKey);
+        } else {
+            $docs = (new static())->generate();
+            Cache::put($cacheKey, $docs, 60 * 24);
+        }
+        return $docs;
+    }
+
+    /**
+     * Generate API documentation.
+     */
+    protected function generate(): Collection
+    {
+        $apiRoutes = $this->getFlatApiRoutes();
+        $apiRoutes = $this->loadDetailsFromControllers($apiRoutes);
+        $apiRoutes = $this->loadDetailsFromFiles($apiRoutes);
+        $apiRoutes = $apiRoutes->groupBy('base_model');
+        return $apiRoutes;
+    }
+
+    /**
+     * Load any API details stored in static files.
+     */
+    protected function loadDetailsFromFiles(Collection $routes): Collection
+    {
+        return $routes->map(function (array $route) {
+            $exampleTypes = ['request', 'response'];
+            foreach ($exampleTypes as $exampleType) {
+                $exampleFile = base_path("dev/api/{$exampleType}s/{$route['name']}.json");
+                $exampleContent = file_exists($exampleFile) ? file_get_contents($exampleFile) : null;
+                $route["example_{$exampleType}"] = $exampleContent;
+            }
+            return $route;
+        });
+    }
+
+    /**
+     * Load any details we can fetch from the controller and its methods.
+     */
+    protected function loadDetailsFromControllers(Collection $routes): Collection
+    {
+        return $routes->map(function (array $route) {
+            $method = $this->getReflectionMethod($route['controller'], $route['controller_method']);
+            $comment = $method->getDocComment();
+            $route['description'] = $comment ? $this->parseDescriptionFromMethodComment($comment) : null;
+            $route['body_params'] = $this->getBodyParamsFromClass($route['controller'], $route['controller_method']);
+            return $route;
+        });
+    }
+
+    /**
+     * Load body params and their rules by inspecting the given class and method name.
+     * @throws BindingResolutionException
+     */
+    protected function getBodyParamsFromClass(string $className, string $methodName): ?array
+    {
+        /** @var ApiController $class */
+        $class = $this->controllerClasses[$className] ?? null;
+        if ($class === null) {
+            $class = app()->make($className);
+            $this->controllerClasses[$className] = $class;
+        }
+
+        $rules = $class->getValdationRules()[$methodName] ?? [];
+        foreach ($rules as $param => $ruleString) {
+            $rules[$param] = explode('|', $ruleString);
+        }
+        return count($rules) > 0 ? $rules : null;
+    }
+
+    /**
+     * Parse out the description text from a class method comment.
+     */
+    protected function parseDescriptionFromMethodComment(string $comment)
+    {
+        $matches = [];
+        preg_match_all('/^\s*?\*\s((?![@\s]).*?)$/m', $comment, $matches);
+        return implode(' ', $matches[1] ?? []);
+    }
+
+    /**
+     * Get a reflection method from the given class name and method name.
+     * @throws ReflectionException
+     */
+    protected function getReflectionMethod(string $className, string $methodName): ReflectionMethod
+    {
+        $class = $this->reflectionClasses[$className] ?? null;
+        if ($class === null) {
+            $class = new ReflectionClass($className);
+            $this->reflectionClasses[$className] = $class;
+        }
+
+        return $class->getMethod($methodName);
+    }
+
+    /**
+     * Get the system API routes, formatted into a flat collection.
+     */
+    protected function getFlatApiRoutes(): Collection
+    {
+        return collect(Route::getRoutes()->getRoutes())->filter(function ($route) {
+            return strpos($route->uri, 'api/') === 0;
+        })->map(function ($route) {
+            [$controller, $controllerMethod] = explode('@', $route->action['uses']);
+            $baseModelName = explode('.', explode('/', $route->uri)[1])[0];
+            $shortName = $baseModelName . '-' . $controllerMethod;
+            return [
+                'name' => $shortName,
+                'uri' => $route->uri,
+                'method' => $route->methods[0],
+                'controller' => $controller,
+                'controller_method' => $controllerMethod,
+                'controller_method_kebab' => Str::kebab($controllerMethod),
+                'base_model' => $baseModelName,
+            ];
+        });
+    }
+
+}
\ No newline at end of file
diff --git a/app/Api/ApiToken.php b/app/Api/ApiToken.php
new file mode 100644 (file)
index 0000000..defaa7e
--- /dev/null
@@ -0,0 +1,49 @@
+<?php namespace BookStack\Api;
+
+use BookStack\Auth\User;
+use BookStack\Interfaces\Loggable;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Support\Carbon;
+
+/**
+ * Class ApiToken
+ * @property int $id
+ * @property string $token_id
+ * @property string $secret
+ * @property string $name
+ * @property Carbon $expires_at
+ * @property User $user
+ */
+class ApiToken extends Model implements Loggable
+{
+    protected $fillable = ['name', 'expires_at'];
+    protected $casts = [
+        'expires_at' => 'date:Y-m-d'
+    ];
+
+    /**
+     * Get the user that this token belongs to.
+     */
+    public function user(): BelongsTo
+    {
+        return $this->belongsTo(User::class);
+    }
+
+    /**
+     * Get the default expiry value for an API token.
+     * Set to 100 years from now.
+     */
+    public static function defaultExpiry(): string
+    {
+        return Carbon::now()->addYears(100)->format('Y-m-d');
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function logDescriptor(): string
+    {
+        return "({$this->id}) {$this->name}; User: {$this->user->logDescriptor()}";
+    }
+}
diff --git a/app/Api/ApiTokenGuard.php b/app/Api/ApiTokenGuard.php
new file mode 100644 (file)
index 0000000..e0a50eb
--- /dev/null
@@ -0,0 +1,166 @@
+<?php
+
+namespace BookStack\Api;
+
+use BookStack\Exceptions\ApiAuthException;
+use Illuminate\Auth\GuardHelpers;
+use Illuminate\Contracts\Auth\Authenticatable;
+use Illuminate\Contracts\Auth\Guard;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\Hash;
+use Symfony\Component\HttpFoundation\Request;
+
+class ApiTokenGuard implements Guard
+{
+
+    use GuardHelpers;
+
+    /**
+     * The request instance.
+     */
+    protected $request;
+
+
+    /**
+     * The last auth exception thrown in this request.
+     * @var ApiAuthException
+     */
+    protected $lastAuthException;
+
+    /**
+     * ApiTokenGuard constructor.
+     */
+    public function __construct(Request $request)
+    {
+        $this->request = $request;
+    }
+    
+    /**
+     * @inheritDoc
+     */
+    public function user()
+    {
+        // Return the user if we've already retrieved them.
+        // Effectively a request-instance cache for this method.
+        if (!is_null($this->user)) {
+            return $this->user;
+        }
+
+        $user = null;
+        try {
+            $user = $this->getAuthorisedUserFromRequest();
+        } catch (ApiAuthException $exception) {
+            $this->lastAuthException = $exception;
+        }
+
+        $this->user = $user;
+        return $user;
+    }
+
+    /**
+     * Determine if current user is authenticated. If not, throw an exception.
+     *
+     * @return \Illuminate\Contracts\Auth\Authenticatable
+     *
+     * @throws ApiAuthException
+     */
+    public function authenticate()
+    {
+        if (! is_null($user = $this->user())) {
+            return $user;
+        }
+
+        if ($this->lastAuthException) {
+            throw $this->lastAuthException;
+        }
+
+        throw new ApiAuthException('Unauthorized');
+    }
+
+    /**
+     * Check the API token in the request and fetch a valid authorised user.
+     * @throws ApiAuthException
+     */
+    protected function getAuthorisedUserFromRequest(): Authenticatable
+    {
+        $authToken = trim($this->request->headers->get('Authorization', ''));
+        $this->validateTokenHeaderValue($authToken);
+
+        [$id, $secret] = explode(':', str_replace('Token ', '', $authToken));
+        $token = ApiToken::query()
+            ->where('token_id', '=', $id)
+            ->with(['user'])->first();
+
+        $this->validateToken($token, $secret);
+
+        return $token->user;
+    }
+
+    /**
+     * Validate the format of the token header value string.
+     * @throws ApiAuthException
+     */
+    protected function validateTokenHeaderValue(string $authToken): void
+    {
+        if (empty($authToken)) {
+            throw new ApiAuthException(trans('errors.api_no_authorization_found'));
+        }
+
+        if (strpos($authToken, ':') === false || strpos($authToken, 'Token ') !== 0) {
+            throw new ApiAuthException(trans('errors.api_bad_authorization_format'));
+        }
+    }
+
+    /**
+     * Validate the given secret against the given token and ensure the token
+     * currently has access to the instance API.
+     * @throws ApiAuthException
+     */
+    protected function validateToken(?ApiToken $token, string $secret): void
+    {
+        if ($token === null) {
+            throw new ApiAuthException(trans('errors.api_user_token_not_found'));
+        }
+
+        if (!Hash::check($secret, $token->secret)) {
+            throw new ApiAuthException(trans('errors.api_incorrect_token_secret'));
+        }
+
+        $now = Carbon::now();
+        if ($token->expires_at <= $now) {
+            throw new ApiAuthException(trans('errors.api_user_token_expired'), 403);
+        }
+
+        if (!$token->user->can('access-api')) {
+            throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function validate(array $credentials = [])
+    {
+        if (empty($credentials['id']) || empty($credentials['secret'])) {
+            return false;
+        }
+
+        $token = ApiToken::query()
+            ->where('token_id', '=', $credentials['id'])
+            ->with(['user'])->first();
+
+        if ($token === null) {
+            return false;
+        }
+
+        return Hash::check($credentials['secret'], $token->secret);
+    }
+
+    /**
+     * "Log out" the currently authenticated user.
+     */
+    public function logout()
+    {
+        $this->user = null;
+    }
+}
\ No newline at end of file
diff --git a/app/Api/ListingResponseBuilder.php b/app/Api/ListingResponseBuilder.php
new file mode 100644 (file)
index 0000000..df4cb8b
--- /dev/null
@@ -0,0 +1,138 @@
+<?php namespace BookStack\Api;
+
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Http\Request;
+
+class ListingResponseBuilder
+{
+
+    protected $query;
+    protected $request;
+    protected $fields;
+
+    protected $filterOperators = [
+        'eq'   => '=',
+        'ne'   => '!=',
+        'gt'   => '>',
+        'lt'   => '<',
+        'gte'  => '>=',
+        'lte'  => '<=',
+        'like' => 'like'
+    ];
+
+    /**
+     * ListingResponseBuilder constructor.
+     */
+    public function __construct(Builder $query, Request $request, array $fields)
+    {
+        $this->query = $query;
+        $this->request = $request;
+        $this->fields = $fields;
+    }
+
+    /**
+     * Get the response from this builder.
+     */
+    public function toResponse()
+    {
+        $filteredQuery = $this->filterQuery($this->query);
+
+        $total = $filteredQuery->count();
+        $data = $this->fetchData($filteredQuery);
+
+        return response()->json([
+            'data' => $data,
+            'total' => $total,
+        ]);
+    }
+
+    /**
+     * Fetch the data to return in the response.
+     */
+    protected function fetchData(Builder $query): Collection
+    {
+        $query = $this->countAndOffsetQuery($query);
+        $query = $this->sortQuery($query);
+        return $query->get($this->fields);
+    }
+
+    /**
+     * Apply any filtering operations found in the request.
+     */
+    protected function filterQuery(Builder $query): Builder
+    {
+        $query = clone $query;
+        $requestFilters = $this->request->get('filter', []);
+        if (!is_array($requestFilters)) {
+            return $query;
+        }
+
+        $queryFilters = collect($requestFilters)->map(function ($value, $key) {
+            return $this->requestFilterToQueryFilter($key, $value);
+        })->filter(function ($value) {
+            return !is_null($value);
+        })->values()->toArray();
+
+        return $query->where($queryFilters);
+    }
+
+    /**
+     * Convert a request filter query key/value pair into a [field, op, value] where condition.
+     */
+    protected function requestFilterToQueryFilter($fieldKey, $value): ?array
+    {
+        $splitKey = explode(':', $fieldKey);
+        $field = $splitKey[0];
+        $filterOperator = $splitKey[1] ?? 'eq';
+
+        if (!in_array($field, $this->fields)) {
+            return null;
+        }
+
+        if (!in_array($filterOperator, array_keys($this->filterOperators))) {
+            $filterOperator = 'eq';
+        }
+
+        $queryOperator = $this->filterOperators[$filterOperator];
+        return [$field, $queryOperator, $value];
+    }
+
+    /**
+     * Apply sorting operations to the query from given parameters
+     * otherwise falling back to the first given field, ascending.
+     */
+    protected function sortQuery(Builder $query): Builder
+    {
+        $query = clone $query;
+        $defaultSortName = $this->fields[0];
+        $direction = 'asc';
+
+        $sort = $this->request->get('sort', '');
+        if (strpos($sort, '-') === 0) {
+            $direction = 'desc';
+        }
+
+        $sortName = ltrim($sort, '+- ');
+        if (!in_array($sortName, $this->fields)) {
+            $sortName = $defaultSortName;
+        }
+
+        return $query->orderBy($sortName, $direction);
+    }
+
+    /**
+     * Apply count and offset for paging, based on params from the request while falling
+     * back to system defined default, taking the max limit into account.
+     */
+    protected function countAndOffsetQuery(Builder $query): Builder
+    {
+        $query = clone $query;
+        $offset = max(0, $this->request->get('offset', 0));
+        $maxCount = config('api.max_item_count');
+        $count = $this->request->get('count', config('api.default_item_count'));
+        $count = max(min($maxCount, $count), 1);
+
+        return $query->skip($offset)->take($count);
+    }
+}
diff --git a/app/Auth/Access/ExternalAuthService.php b/app/Auth/Access/ExternalAuthService.php
new file mode 100644 (file)
index 0000000..4c71db2
--- /dev/null
@@ -0,0 +1,75 @@
+<?php namespace BookStack\Auth\Access;
+
+use BookStack\Auth\Role;
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\DB;
+
+class ExternalAuthService
+{
+    /**
+     * Check a role against an array of group names to see if it matches.
+     * Checked against role 'external_auth_id' if set otherwise the name of the role.
+     */
+    protected function roleMatchesGroupNames(Role $role, array $groupNames): bool
+    {
+        if ($role->external_auth_id) {
+            return $this->externalIdMatchesGroupNames($role->external_auth_id, $groupNames);
+        }
+
+        $roleName = str_replace(' ', '-', trim(strtolower($role->display_name)));
+        return in_array($roleName, $groupNames);
+    }
+
+    /**
+     * Check if the given external auth ID string matches one of the given group names.
+     */
+    protected function externalIdMatchesGroupNames(string $externalId, array $groupNames): bool
+    {
+        $externalAuthIds = explode(',', strtolower($externalId));
+
+        foreach ($externalAuthIds as $externalAuthId) {
+            if (in_array(trim($externalAuthId), $groupNames)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Match an array of group names to BookStack system roles.
+     * Formats group names to be lower-case and hyphenated.
+     */
+    protected function matchGroupsToSystemsRoles(array $groupNames): Collection
+    {
+        foreach ($groupNames as $i => $groupName) {
+            $groupNames[$i] = str_replace(' ', '-', trim(strtolower($groupName)));
+        }
+
+        $roles = Role::query()->get(['id', 'external_auth_id', 'display_name']);
+        $matchedRoles = $roles->filter(function (Role $role) use ($groupNames) {
+            return $this->roleMatchesGroupNames($role, $groupNames);
+        });
+
+        return $matchedRoles->pluck('id');
+    }
+
+    /**
+     * Sync the groups to the user roles for the current user
+     */
+    public function syncWithGroups(User $user, array $userGroups): void
+    {
+        // Get the ids for the roles from the names
+        $groupsAsRoles = $this->matchGroupsToSystemsRoles($userGroups);
+
+        // Sync groups
+        if ($this->config['remove_from_groups']) {
+            $user->roles()->sync($groupsAsRoles);
+            $user->attachDefaultRole();
+        } else {
+            $user->roles()->syncWithoutDetaching($groupsAsRoles);
+        }
+    }
+}
similarity index 61%
rename from app/Providers/LdapUserProvider.php
rename to app/Auth/Access/ExternalBaseUserProvider.php
index 9c91def2f1f55b650b845f5913fa3563f47092e3..69295ee4e900188cc2c7c3097b0a3c3379195b57 100644 (file)
@@ -1,12 +1,11 @@
 <?php
 
-namespace BookStack\Providers;
+namespace BookStack\Auth\Access;
 
-use BookStack\Auth\Access\LdapService;
 use Illuminate\Contracts\Auth\Authenticatable;
 use Illuminate\Contracts\Auth\UserProvider;
 
-class LdapUserProvider implements UserProvider
+class ExternalBaseUserProvider implements UserProvider
 {
 
     /**
@@ -16,21 +15,13 @@ class LdapUserProvider implements UserProvider
      */
     protected $model;
 
-    /**
-     * @var \BookStack\Auth\LdapService
-     */
-    protected $ldapService;
-
-
     /**
      * LdapUserProvider constructor.
      * @param             $model
-     * @param \BookStack\Auth\LdapService $ldapService
      */
-    public function __construct($model, LdapService $ldapService)
+    public function __construct(string $model)
     {
         $this->model = $model;
-        $this->ldapService = $ldapService;
     }
 
     /**
@@ -44,7 +35,6 @@ class LdapUserProvider implements UserProvider
         return new $class;
     }
 
-
     /**
      * Retrieve a user by their unique identifier.
      *
@@ -65,12 +55,7 @@ class LdapUserProvider implements UserProvider
      */
     public function retrieveByToken($identifier, $token)
     {
-        $model = $this->createModel();
-
-        return $model->newQuery()
-            ->where($model->getAuthIdentifierName(), $identifier)
-            ->where($model->getRememberTokenName(), $token)
-            ->first();
+        return null;
     }
 
 
@@ -83,10 +68,7 @@ class LdapUserProvider implements UserProvider
      */
     public function updateRememberToken(Authenticatable $user, $token)
     {
-        if ($user->exists) {
-            $user->setRememberToken($token);
-            $user->save();
-        }
+        //
     }
 
     /**
@@ -97,27 +79,11 @@ class LdapUserProvider implements UserProvider
      */
     public function retrieveByCredentials(array $credentials)
     {
-        // Get user via LDAP
-        $userDetails = $this->ldapService->getUserDetails($credentials['username']);
-        if ($userDetails === null) {
-            return null;
-        }
-
         // Search current user base by looking up a uid
         $model = $this->createModel();
-        $currentUser = $model->newQuery()
-            ->where('external_auth_id', $userDetails['uid'])
+        return $model->newQuery()
+            ->where('external_auth_id', $credentials['external_auth_id'])
             ->first();
-
-        if ($currentUser !== null) {
-            return $currentUser;
-        }
-
-        $model->name = $userDetails['name'];
-        $model->external_auth_id = $userDetails['uid'];
-        $model->email = $userDetails['email'];
-        $model->email_confirmed = false;
-        return $model;
     }
 
     /**
@@ -129,6 +95,7 @@ class LdapUserProvider implements UserProvider
      */
     public function validateCredentials(Authenticatable $user, array $credentials)
     {
-        return $this->ldapService->validateUserCredentials($user, $credentials['username'], $credentials['password']);
+        // Should be done in the guard.
+        return false;
     }
 }
diff --git a/app/Auth/Access/Guards/ExternalBaseSessionGuard.php b/app/Auth/Access/Guards/ExternalBaseSessionGuard.php
new file mode 100644 (file)
index 0000000..9a0c691
--- /dev/null
@@ -0,0 +1,303 @@
+<?php
+
+namespace BookStack\Auth\Access\Guards;
+
+use BookStack\Auth\Access\RegistrationService;
+use Illuminate\Auth\GuardHelpers;
+use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
+use Illuminate\Contracts\Auth\StatefulGuard;
+use Illuminate\Contracts\Auth\UserProvider;
+use Illuminate\Contracts\Session\Session;
+
+/**
+ * Class BaseSessionGuard
+ * A base implementation of a session guard. Is a copy of the default Laravel
+ * guard with 'remember' functionality removed. Basic auth and event emission
+ * has also been removed to keep this simple. Designed to be extended by external
+ * Auth Guards.
+ */
+class ExternalBaseSessionGuard implements StatefulGuard
+{
+    use GuardHelpers;
+
+    /**
+     * The name of the Guard. Typically "session".
+     *
+     * Corresponds to guard name in authentication configuration.
+     *
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * The user we last attempted to retrieve.
+     *
+     * @var \Illuminate\Contracts\Auth\Authenticatable
+     */
+    protected $lastAttempted;
+
+    /**
+     * The session used by the guard.
+     *
+     * @var \Illuminate\Contracts\Session\Session
+     */
+    protected $session;
+
+    /**
+     * Indicates if the logout method has been called.
+     *
+     * @var bool
+     */
+    protected $loggedOut = false;
+
+    /**
+     * Service to handle common registration actions.
+     *
+     * @var RegistrationService
+     */
+    protected $registrationService;
+
+    /**
+     * Create a new authentication guard.
+     *
+     * @return void
+     */
+    public function __construct(string $name, UserProvider $provider, Session $session, RegistrationService $registrationService)
+    {
+        $this->name = $name;
+        $this->session = $session;
+        $this->provider = $provider;
+        $this->registrationService = $registrationService;
+    }
+
+    /**
+     * Get the currently authenticated user.
+     *
+     * @return \Illuminate\Contracts\Auth\Authenticatable|null
+     */
+    public function user()
+    {
+        if ($this->loggedOut) {
+            return;
+        }
+
+        // If we've already retrieved the user for the current request we can just
+        // return it back immediately. We do not want to fetch the user data on
+        // every call to this method because that would be tremendously slow.
+        if (! is_null($this->user)) {
+            return $this->user;
+        }
+
+        $id = $this->session->get($this->getName());
+
+        // First we will try to load the user using the
+        // identifier in the session if one exists.
+        if (! is_null($id)) {
+            $this->user = $this->provider->retrieveById($id);
+        }
+
+        return $this->user;
+    }
+
+    /**
+     * Get the ID for the currently authenticated user.
+     *
+     * @return int|null
+     */
+    public function id()
+    {
+        if ($this->loggedOut) {
+            return;
+        }
+
+        return $this->user()
+            ? $this->user()->getAuthIdentifier()
+            : $this->session->get($this->getName());
+    }
+
+    /**
+     * Log a user into the application without sessions or cookies.
+     *
+     * @param  array  $credentials
+     * @return bool
+     */
+    public function once(array $credentials = [])
+    {
+        if ($this->validate($credentials)) {
+            $this->setUser($this->lastAttempted);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Log the given user ID into the application without sessions or cookies.
+     *
+     * @param  mixed  $id
+     * @return \Illuminate\Contracts\Auth\Authenticatable|false
+     */
+    public function onceUsingId($id)
+    {
+        if (! is_null($user = $this->provider->retrieveById($id))) {
+            $this->setUser($user);
+
+            return $user;
+        }
+
+        return false;
+    }
+
+    /**
+     * Validate a user's credentials.
+     *
+     * @param  array  $credentials
+     * @return bool
+     */
+    public function validate(array $credentials = [])
+    {
+        return false;
+    }
+
+
+    /**
+     * Attempt to authenticate a user using the given credentials.
+     *
+     * @param  array  $credentials
+     * @param  bool  $remember
+     * @return bool
+     */
+    public function attempt(array $credentials = [], $remember = false)
+    {
+        return false;
+    }
+
+    /**
+     * Log the given user ID into the application.
+     *
+     * @param  mixed  $id
+     * @param  bool  $remember
+     * @return \Illuminate\Contracts\Auth\Authenticatable|false
+     */
+    public function loginUsingId($id, $remember = false)
+    {
+        if (! is_null($user = $this->provider->retrieveById($id))) {
+            $this->login($user, $remember);
+
+            return $user;
+        }
+
+        return false;
+    }
+
+    /**
+     * Log a user into the application.
+     *
+     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
+     * @param  bool  $remember
+     * @return void
+     */
+    public function login(AuthenticatableContract $user, $remember = false)
+    {
+        $this->updateSession($user->getAuthIdentifier());
+
+        $this->setUser($user);
+    }
+
+    /**
+     * Update the session with the given ID.
+     *
+     * @param  string  $id
+     * @return void
+     */
+    protected function updateSession($id)
+    {
+        $this->session->put($this->getName(), $id);
+
+        $this->session->migrate(true);
+    }
+
+    /**
+     * Log the user out of the application.
+     *
+     * @return void
+     */
+    public function logout()
+    {
+        $this->clearUserDataFromStorage();
+
+        // Now we will clear the users out of memory so they are no longer available
+        // as the user is no longer considered as being signed into this
+        // application and should not be available here.
+        $this->user = null;
+
+        $this->loggedOut = true;
+    }
+
+    /**
+     * Remove the user data from the session and cookies.
+     *
+     * @return void
+     */
+    protected function clearUserDataFromStorage()
+    {
+        $this->session->remove($this->getName());
+    }
+
+    /**
+     * Get the last user we attempted to authenticate.
+     *
+     * @return \Illuminate\Contracts\Auth\Authenticatable
+     */
+    public function getLastAttempted()
+    {
+        return $this->lastAttempted;
+    }
+
+    /**
+     * Get a unique identifier for the auth session value.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return 'login_'.$this->name.'_'.sha1(static::class);
+    }
+
+    /**
+     * Determine if the user was authenticated via "remember me" cookie.
+     *
+     * @return bool
+     */
+    public function viaRemember()
+    {
+        return false;
+    }
+
+    /**
+     * Return the currently cached user.
+     *
+     * @return \Illuminate\Contracts\Auth\Authenticatable|null
+     */
+    public function getUser()
+    {
+        return $this->user;
+    }
+
+    /**
+     * Set the current user.
+     *
+     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
+     * @return $this
+     */
+    public function setUser(AuthenticatableContract $user)
+    {
+        $this->user = $user;
+
+        $this->loggedOut = false;
+
+        return $this;
+    }
+
+}
diff --git a/app/Auth/Access/Guards/LdapSessionGuard.php b/app/Auth/Access/Guards/LdapSessionGuard.php
new file mode 100644 (file)
index 0000000..652141c
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+
+namespace BookStack\Auth\Access\Guards;
+
+use BookStack\Auth\Access\LdapService;
+use BookStack\Auth\Access\RegistrationService;
+use BookStack\Auth\User;
+use BookStack\Auth\UserRepo;
+use BookStack\Exceptions\LdapException;
+use BookStack\Exceptions\LoginAttemptException;
+use BookStack\Exceptions\LoginAttemptEmailNeededException;
+use BookStack\Exceptions\UserRegistrationException;
+use Illuminate\Contracts\Auth\UserProvider;
+use Illuminate\Contracts\Session\Session;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Str;
+
+class LdapSessionGuard extends ExternalBaseSessionGuard
+{
+
+    protected $ldapService;
+
+    /**
+     * LdapSessionGuard constructor.
+     */
+    public function __construct($name,
+        UserProvider $provider,
+        Session $session,
+        LdapService $ldapService,
+        RegistrationService $registrationService
+    )
+    {
+        $this->ldapService = $ldapService;
+        parent::__construct($name, $provider, $session, $registrationService);
+    }
+
+    /**
+     * Validate a user's credentials.
+     *
+     * @param array $credentials
+     * @return bool
+     * @throws LdapException
+     */
+    public function validate(array $credentials = [])
+    {
+        $userDetails = $this->ldapService->getUserDetails($credentials['username']);
+
+        if (isset($userDetails['uid'])) {
+            $this->lastAttempted = $this->provider->retrieveByCredentials([
+                'external_auth_id' => $userDetails['uid']
+            ]);
+        }
+
+        return $this->ldapService->validateUserCredentials($userDetails, $credentials['password']);
+    }
+
+    /**
+     * Attempt to authenticate a user using the given credentials.
+     *
+     * @param array $credentials
+     * @param bool $remember
+     * @return bool
+     * @throws LoginAttemptException
+     * @throws LdapException
+     */
+    public function attempt(array $credentials = [], $remember = false)
+    {
+        $username = $credentials['username'];
+        $userDetails = $this->ldapService->getUserDetails($username);
+
+        $user = null;
+        if (isset($userDetails['uid'])) {
+            $this->lastAttempted = $user = $this->provider->retrieveByCredentials([
+                'external_auth_id' => $userDetails['uid']
+            ]);
+        }
+
+        if (!$this->ldapService->validateUserCredentials($userDetails, $credentials['password'])) {
+            return false;
+        }
+
+        if (is_null($user)) {
+            try {
+                $user = $this->createNewFromLdapAndCreds($userDetails, $credentials);
+            } catch (UserRegistrationException $exception) {
+                throw new LoginAttemptException($exception->message);
+            }
+        }
+
+        // Sync LDAP groups if required
+        if ($this->ldapService->shouldSyncGroups()) {
+            $this->ldapService->syncGroups($user, $username);
+        }
+
+        $this->login($user, $remember);
+        return true;
+    }
+
+    /**
+     * Create a new user from the given ldap credentials and login credentials
+     * @throws LoginAttemptEmailNeededException
+     * @throws LoginAttemptException
+     * @throws UserRegistrationException
+     */
+    protected function createNewFromLdapAndCreds(array $ldapUserDetails, array $credentials): User
+    {
+        $email = trim($ldapUserDetails['email'] ?: ($credentials['email'] ?? ''));
+
+        if (empty($email)) {
+            throw new LoginAttemptEmailNeededException();
+        }
+
+        $details = [
+            'name' => $ldapUserDetails['name'],
+            'email' => $ldapUserDetails['email'] ?: $credentials['email'],
+            'external_auth_id' => $ldapUserDetails['uid'],
+            'password' => Str::random(32),
+        ];
+
+        return $this->registrationService->registerUser($details, null, false);
+    }
+
+}
diff --git a/app/Auth/Access/Guards/Saml2SessionGuard.php b/app/Auth/Access/Guards/Saml2SessionGuard.php
new file mode 100644 (file)
index 0000000..68683bb
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+namespace BookStack\Auth\Access\Guards;
+
+/**
+ * Saml2 Session Guard
+ *
+ * The saml2 login process is async in nature meaning it does not fit very well
+ * into the default laravel 'Guard' auth flow. Instead most of the logic is done
+ * via the Saml2 controller & Saml2Service. This class provides a safer, thin
+ * version of SessionGuard.
+ */
+class Saml2SessionGuard extends ExternalBaseSessionGuard
+{
+    /**
+     * Validate a user's credentials.
+     *
+     * @param array $credentials
+     * @return bool
+     */
+    public function validate(array $credentials = [])
+    {
+        return false;
+    }
+
+    /**
+     * Attempt to authenticate a user using the given credentials.
+     *
+     * @param array $credentials
+     * @param bool $remember
+     * @return bool
+     */
+    public function attempt(array $credentials = [], $remember = false)
+    {
+        return false;
+    }
+
+}
index 843a2f204920e9bd5154efe477c5212f73efa057..6b7bd9b9bf2a4d4699a74609ab507d33cc72f98d 100644 (file)
@@ -4,7 +4,6 @@
  * Class Ldap
  * An object-orientated thin abstraction wrapper for common PHP LDAP functions.
  * Allows the standard LDAP functions to be mocked for testing.
- * @package BookStack\Services
  */
 class Ldap
 {
index c7415e1f738e723bd18e98ea9a3f5c6a217bb697..92234edcf906bc2934a47443af011de388d2f853 100644 (file)
@@ -1,37 +1,29 @@
 <?php namespace BookStack\Auth\Access;
 
-use BookStack\Auth\Access;
-use BookStack\Auth\Role;
 use BookStack\Auth\User;
-use BookStack\Auth\UserRepo;
+use BookStack\Exceptions\JsonDebugException;
 use BookStack\Exceptions\LdapException;
-use Illuminate\Contracts\Auth\Authenticatable;
-use Illuminate\Database\Eloquent\Builder;
+use ErrorException;
 
 /**
  * Class LdapService
  * Handles any app-specific LDAP tasks.
- * @package BookStack\Services
  */
-class LdapService
+class LdapService extends ExternalAuthService
 {
 
     protected $ldap;
     protected $ldapConnection;
     protected $config;
-    protected $userRepo;
     protected $enabled;
 
     /**
      * LdapService constructor.
-     * @param Ldap $ldap
-     * @param \BookStack\Auth\UserRepo $userRepo
      */
-    public function __construct(Access\Ldap $ldap, UserRepo $userRepo)
+    public function __construct(Ldap $ldap)
     {
         $this->ldap = $ldap;
         $this->config = config('services.ldap');
-        $this->userRepo = $userRepo;
         $this->enabled = config('auth.method') === 'ldap';
     }
 
@@ -45,17 +37,21 @@ class LdapService
     }
 
     /**
-     * Search for attributes for a specific user on the ldap
-     * @param string $userName
-     * @param array $attributes
-     * @return null|array
+     * Search for attributes for a specific user on the ldap.
      * @throws LdapException
      */
-    private function getUserWithAttributes($userName, $attributes)
+    private function getUserWithAttributes(string $userName, array $attributes): ?array
     {
         $ldapConnection = $this->getConnection();
         $this->bindSystemUser($ldapConnection);
 
+        // Clean attributes
+        foreach ($attributes as $index => $attribute) {
+            if (strpos($attribute, 'BIN;') === 0) {
+                $attributes[$index] = substr($attribute, strlen('BIN;'));
+            }
+        }
+
         // Find user
         $userFilter = $this->buildFilter($this->config['user_filter'], ['user' => $userName]);
         $baseDn = $this->config['base_dn'];
@@ -73,69 +69,78 @@ class LdapService
     /**
      * Get the details of a user from LDAP using the given username.
      * User found via configurable user filter.
-     * @param $userName
-     * @return array|null
      * @throws LdapException
      */
-    public function getUserDetails($userName)
+    public function getUserDetails(string $userName): ?array
     {
+        $idAttr = $this->config['id_attribute'];
         $emailAttr = $this->config['email_attribute'];
         $displayNameAttr = $this->config['display_name_attribute'];
 
-        $user = $this->getUserWithAttributes($userName, ['cn', 'uid', 'dn', $emailAttr, $displayNameAttr]);
+        $user = $this->getUserWithAttributes($userName, ['cn', 'dn', $idAttr, $emailAttr, $displayNameAttr]);
 
         if ($user === null) {
             return null;
         }
 
         $userCn = $this->getUserResponseProperty($user, 'cn', null);
-        return [
-            'uid'   => $this->getUserResponseProperty($user, 'uid', $user['dn']),
+        $formatted = [
+            'uid'   => $this->getUserResponseProperty($user, $idAttr, $user['dn']),
             'name'  => $this->getUserResponseProperty($user, $displayNameAttr, $userCn),
             'dn'    => $user['dn'],
             'email' => $this->getUserResponseProperty($user, $emailAttr, null),
         ];
+
+        if ($this->config['dump_user_details']) {
+            throw new JsonDebugException([
+                'details_from_ldap' => $user,
+                'details_bookstack_parsed' => $formatted,
+            ]);
+        }
+
+        return $formatted;
     }
 
     /**
      * Get a property from an LDAP user response fetch.
      * Handles properties potentially being part of an array.
-     * @param array $userDetails
-     * @param string $propertyKey
-     * @param $defaultValue
-     * @return mixed
+     * If the given key is prefixed with 'BIN;', that indicator will be stripped
+     * from the key and any fetched values will be converted from binary to hex.
      */
     protected function getUserResponseProperty(array $userDetails, string $propertyKey, $defaultValue)
     {
+        $isBinary = strpos($propertyKey, 'BIN;') === 0;
+        $propertyKey = strtolower($propertyKey);
+        $value = $defaultValue;
+
+        if ($isBinary) {
+            $propertyKey = substr($propertyKey, strlen('BIN;'));
+        }
+
         if (isset($userDetails[$propertyKey])) {
-            return (is_array($userDetails[$propertyKey]) ? $userDetails[$propertyKey][0] : $userDetails[$propertyKey]);
+            $value = (is_array($userDetails[$propertyKey]) ? $userDetails[$propertyKey][0] : $userDetails[$propertyKey]);
+            if ($isBinary) {
+                $value = bin2hex($value);
+            }
         }
 
-        return $defaultValue;
+        return $value;
     }
 
     /**
-     * @param Authenticatable $user
-     * @param string $username
-     * @param string $password
-     * @return bool
+     * Check if the given credentials are valid for the given user.
      * @throws LdapException
      */
-    public function validateUserCredentials(Authenticatable $user, $username, $password)
+    public function validateUserCredentials(?array $ldapUserDetails, string $password): bool
     {
-        $ldapUser = $this->getUserDetails($username);
-        if ($ldapUser === null) {
-            return false;
-        }
-
-        if ($ldapUser['uid'] !== $user->external_auth_id) {
+        if (is_null($ldapUserDetails)) {
             return false;
         }
 
         $ldapConnection = $this->getConnection();
         try {
-            $ldapBind = $this->ldap->bind($ldapConnection, $ldapUser['dn'], $password);
-        } catch (\ErrorException $e) {
+            $ldapBind = $this->ldap->bind($ldapConnection, $ldapUserDetails['dn'], $password);
+        } catch (ErrorException $e) {
             $ldapBind = false;
         }
 
@@ -205,12 +210,10 @@ class LdapService
     }
 
     /**
-     * Parse a LDAP server string and return the host and port for
-     * a connection. Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'
-     * @param $serverString
-     * @return array
+     * Parse a LDAP server string and return the host and port for a connection.
+     * Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'.
      */
-    protected function parseServerString($serverString)
+    protected function parseServerString(string $serverString): array
     {
         $serverNameParts = explode(':', $serverString);
 
@@ -227,11 +230,8 @@ class LdapService
 
     /**
      * Build a filter string by injecting common variables.
-     * @param string $filterString
-     * @param array $attrs
-     * @return string
      */
-    protected function buildFilter($filterString, array $attrs)
+    protected function buildFilter(string $filterString, array $attrs): string
     {
         $newAttrs = [];
         foreach ($attrs as $key => $attrText) {
@@ -242,12 +242,10 @@ class LdapService
     }
 
     /**
-     * Get the groups a user is a part of on ldap
-     * @param string $userName
-     * @return array
+     * Get the groups a user is a part of on ldap.
      * @throws LdapException
      */
-    public function getUserGroups($userName)
+    public function getUserGroups(string $userName): array
     {
         $groupsAttr = $this->config['group_attribute'];
         $user = $this->getUserWithAttributes($userName, [$groupsAttr]);
@@ -262,40 +260,36 @@ class LdapService
     }
 
     /**
-     * Get the parent groups of an array of groups
-     * @param array $groupsArray
-     * @param array $checked
-     * @return array
+     * Get the parent groups of an array of groups.
      * @throws LdapException
      */
-    private function getGroupsRecursive($groupsArray, $checked)
+    private function getGroupsRecursive(array $groupsArray, array $checked): array
     {
-        $groups_to_add = [];
+        $groupsToAdd = [];
         foreach ($groupsArray as $groupName) {
             if (in_array($groupName, $checked)) {
                 continue;
             }
 
-            $groupsToAdd = $this->getGroupGroups($groupName);
-            $groups_to_add = array_merge($groups_to_add, $groupsToAdd);
+            $parentGroups = $this->getGroupGroups($groupName);
+            $groupsToAdd = array_merge($groupsToAdd, $parentGroups);
             $checked[] = $groupName;
         }
-        $groupsArray = array_unique(array_merge($groupsArray, $groups_to_add), SORT_REGULAR);
 
-        if (!empty($groups_to_add)) {
-            return $this->getGroupsRecursive($groupsArray, $checked);
-        } else {
+        $groupsArray = array_unique(array_merge($groupsArray, $groupsToAdd), SORT_REGULAR);
+
+        if (empty($groupsToAdd)) {
             return $groupsArray;
         }
+
+        return $this->getGroupsRecursive($groupsArray, $checked);
     }
 
     /**
-     * Get the parent groups of a single group
-     * @param string $groupName
-     * @return array
+     * Get the parent groups of a single group.
      * @throws LdapException
      */
-    private function getGroupGroups($groupName)
+    private function getGroupGroups(string $groupName): array
     {
         $ldapConnection = $this->getConnection();
         $this->bindSystemUser($ldapConnection);
@@ -312,17 +306,14 @@ class LdapService
             return [];
         }
 
-        $groupGroups = $this->groupFilter($groups[0]);
-        return $groupGroups;
+        return $this->groupFilter($groups[0]);
     }
 
     /**
-     * Filter out LDAP CN and DN language in a ldap search return
-     * Gets the base CN (common name) of the string
-     * @param array $userGroupSearchResponse
-     * @return array
+     * Filter out LDAP CN and DN language in a ldap search return.
+     * Gets the base CN (common name) of the string.
      */
-    protected function groupFilter(array $userGroupSearchResponse)
+    protected function groupFilter(array $userGroupSearchResponse): array
     {
         $groupsAttr = strtolower($this->config['group_attribute']);
         $ldapGroups = [];
@@ -343,73 +334,12 @@ class LdapService
     }
 
     /**
-     * Sync the LDAP groups to the user roles for the current user
-     * @param \BookStack\Auth\User $user
-     * @param string $username
+     * Sync the LDAP groups to the user roles for the current user.
      * @throws LdapException
      */
     public function syncGroups(User $user, string $username)
     {
         $userLdapGroups = $this->getUserGroups($username);
-
-        // Get the ids for the roles from the names
-        $ldapGroupsAsRoles = $this->matchLdapGroupsToSystemsRoles($userLdapGroups);
-
-        // Sync groups
-        if ($this->config['remove_from_groups']) {
-            $user->roles()->sync($ldapGroupsAsRoles);
-            $this->userRepo->attachDefaultRole($user);
-        } else {
-            $user->roles()->syncWithoutDetaching($ldapGroupsAsRoles);
-        }
-    }
-
-    /**
-     * Match an array of group names from LDAP to BookStack system roles.
-     * Formats LDAP group names to be lower-case and hyphenated.
-     * @param array $groupNames
-     * @return \Illuminate\Support\Collection
-     */
-    protected function matchLdapGroupsToSystemsRoles(array $groupNames)
-    {
-        foreach ($groupNames as $i => $groupName) {
-            $groupNames[$i] = str_replace(' ', '-', trim(strtolower($groupName)));
-        }
-
-        $roles = Role::query()->where(function (Builder $query) use ($groupNames) {
-            $query->whereIn('name', $groupNames);
-            foreach ($groupNames as $groupName) {
-                $query->orWhere('external_auth_id', 'LIKE', '%' . $groupName . '%');
-            }
-        })->get();
-
-        $matchedRoles = $roles->filter(function (Role $role) use ($groupNames) {
-            return $this->roleMatchesGroupNames($role, $groupNames);
-        });
-
-        return $matchedRoles->pluck('id');
-    }
-
-    /**
-     * Check a role against an array of group names to see if it matches.
-     * Checked against role 'external_auth_id' if set otherwise the name of the role.
-     * @param \BookStack\Auth\Role $role
-     * @param array $groupNames
-     * @return bool
-     */
-    protected function roleMatchesGroupNames(Role $role, array $groupNames)
-    {
-        if ($role->external_auth_id) {
-            $externalAuthIds = explode(',', strtolower($role->external_auth_id));
-            foreach ($externalAuthIds as $externalAuthId) {
-                if (in_array(trim($externalAuthId), $groupNames)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        $roleName = str_replace(' ', '-', trim(strtolower($role->display_name)));
-        return in_array($roleName, $groupNames);
+        $this->syncWithGroups($user, $userLdapGroups);
     }
 }
diff --git a/app/Auth/Access/RegistrationService.php b/app/Auth/Access/RegistrationService.php
new file mode 100644 (file)
index 0000000..2aff6c3
--- /dev/null
@@ -0,0 +1,113 @@
+<?php namespace BookStack\Auth\Access;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\SocialAccount;
+use BookStack\Auth\User;
+use BookStack\Auth\UserRepo;
+use BookStack\Exceptions\UserRegistrationException;
+use BookStack\Facades\Activity;
+use Exception;
+
+class RegistrationService
+{
+
+    protected $userRepo;
+    protected $emailConfirmationService;
+
+    /**
+     * RegistrationService constructor.
+     */
+    public function __construct(UserRepo $userRepo, EmailConfirmationService $emailConfirmationService)
+    {
+        $this->userRepo = $userRepo;
+        $this->emailConfirmationService = $emailConfirmationService;
+    }
+
+    /**
+     * Check whether or not registrations are allowed in the app settings.
+     * @throws UserRegistrationException
+     */
+    public function ensureRegistrationAllowed()
+    {
+        if (!$this->registrationAllowed()) {
+            throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login');
+        }
+    }
+
+    /**
+     * Check if standard BookStack User registrations are currently allowed.
+     * Does not prevent external-auth based registration.
+     */
+    protected function registrationAllowed(): bool
+    {
+        $authMethod = config('auth.method');
+        $authMethodsWithRegistration = ['standard'];
+        return in_array($authMethod, $authMethodsWithRegistration) && setting('registration-enabled');
+    }
+
+    /**
+     * The registrations flow for all users.
+     * @throws UserRegistrationException
+     */
+    public function registerUser(array $userData, ?SocialAccount $socialAccount = null, bool $emailConfirmed = false): User
+    {
+        $userEmail = $userData['email'];
+
+        // Email restriction
+        $this->ensureEmailDomainAllowed($userEmail);
+
+        // Ensure user does not already exist
+        $alreadyUser = !is_null($this->userRepo->getByEmail($userEmail));
+        if ($alreadyUser) {
+            throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $userEmail]), '/login');
+        }
+
+        // Create the user
+        $newUser = $this->userRepo->registerNew($userData, $emailConfirmed);
+
+        // Assign social account if given
+        if ($socialAccount) {
+            $newUser->socialAccounts()->save($socialAccount);
+        }
+
+        Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);
+
+        // Start email confirmation flow if required
+        if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {
+            $newUser->save();
+
+            try {
+                $this->emailConfirmationService->sendConfirmation($newUser);
+                session()->flash('sent-email-confirmation', true);
+            } catch (Exception $e) {
+                $message = trans('auth.email_confirm_send_error');
+                throw new UserRegistrationException($message, '/register/confirm');
+            }
+
+        }
+
+        return $newUser;
+    }
+
+    /**
+     * Ensure that the given email meets any active email domain registration restrictions.
+     * Throws if restrictions are active and the email does not match an allowed domain.
+     * @throws UserRegistrationException
+     */
+    protected function ensureEmailDomainAllowed(string $userEmail): void
+    {
+        $registrationRestrict = setting('registration-restrict');
+
+        if (!$registrationRestrict) {
+            return;
+        }
+
+        $restrictedEmailDomains = explode(',', str_replace(' ', '', $registrationRestrict));
+        $userEmailDomain = $domain = mb_substr(mb_strrchr($userEmail, "@"), 1);
+        if (!in_array($userEmailDomain, $restrictedEmailDomains)) {
+            $redirect = $this->registrationAllowed() ? '/register' : '/login';
+            throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), $redirect);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/app/Auth/Access/Saml2Service.php b/app/Auth/Access/Saml2Service.php
new file mode 100644 (file)
index 0000000..0316ff9
--- /dev/null
@@ -0,0 +1,380 @@
+<?php namespace BookStack\Auth\Access;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\User;
+use BookStack\Exceptions\JsonDebugException;
+use BookStack\Exceptions\SamlException;
+use BookStack\Exceptions\UserRegistrationException;
+use BookStack\Facades\Activity;
+use Exception;
+use Illuminate\Support\Str;
+use OneLogin\Saml2\Auth;
+use OneLogin\Saml2\Error;
+use OneLogin\Saml2\IdPMetadataParser;
+use OneLogin\Saml2\ValidationError;
+
+/**
+ * Class Saml2Service
+ * Handles any app-specific SAML tasks.
+ */
+class Saml2Service extends ExternalAuthService
+{
+    protected $config;
+    protected $registrationService;
+    protected $user;
+
+    /**
+     * Saml2Service constructor.
+     */
+    public function __construct(RegistrationService $registrationService, User $user)
+    {
+        $this->config = config('saml2');
+        $this->registrationService = $registrationService;
+        $this->user = $user;
+    }
+
+    /**
+     * Initiate a login flow.
+     * @throws Error
+     */
+    public function login(): array
+    {
+        $toolKit = $this->getToolkit();
+        $returnRoute = url('/saml2/acs');
+        return [
+            'url' => $toolKit->login($returnRoute, [], false, false, true),
+            'id' => $toolKit->getLastRequestID(),
+        ];
+    }
+
+    /**
+     * Initiate a logout flow.
+     * @throws Error
+     */
+    public function logout(): array
+    {
+        $toolKit = $this->getToolkit();
+        $returnRoute = url('/');
+
+        try {
+            $url = $toolKit->logout($returnRoute, [], null, null, true);
+            $id = $toolKit->getLastRequestID();
+        } catch (Error $error) {
+            if ($error->getCode() !== Error::SAML_SINGLE_LOGOUT_NOT_SUPPORTED) {
+                throw $error;
+            }
+
+            $this->actionLogout();
+            $url = '/';
+            $id = null;
+        }
+
+        return ['url' => $url, 'id' => $id];
+    }
+
+    /**
+     * Process the ACS response from the idp and return the
+     * matching, or new if registration active, user matched to the idp.
+     * Returns null if not authenticated.
+     * @throws Error
+     * @throws SamlException
+     * @throws ValidationError
+     * @throws JsonDebugException
+     * @throws UserRegistrationException
+     */
+    public function processAcsResponse(?string $requestId): ?User
+    {
+        $toolkit = $this->getToolkit();
+        $toolkit->processResponse($requestId);
+        $errors = $toolkit->getErrors();
+
+        if (!empty($errors)) {
+            throw new Error(
+                'Invalid ACS Response: '.implode(', ', $errors)
+            );
+        }
+
+        if (!$toolkit->isAuthenticated()) {
+            return null;
+        }
+
+        $attrs = $toolkit->getAttributes();
+        $id = $toolkit->getNameId();
+
+        return $this->processLoginCallback($id, $attrs);
+    }
+
+    /**
+     * Process a response for the single logout service.
+     * @throws Error
+     */
+    public function processSlsResponse(?string $requestId): ?string
+    {
+        $toolkit = $this->getToolkit();
+        $redirect = $toolkit->processSLO(true, $requestId, false, null, true);
+
+        $errors = $toolkit->getErrors();
+
+        if (!empty($errors)) {
+            throw new Error(
+                'Invalid SLS Response: '.implode(', ', $errors)
+            );
+        }
+
+        $this->actionLogout();
+        return $redirect;
+    }
+
+    /**
+     * Do the required actions to log a user out.
+     */
+    protected function actionLogout()
+    {
+        auth()->logout();
+        session()->invalidate();
+    }
+
+    /**
+     * Get the metadata for this service provider.
+     * @throws Error
+     */
+    public function metadata(): string
+    {
+        $toolKit = $this->getToolkit();
+        $settings = $toolKit->getSettings();
+        $metadata = $settings->getSPMetadata();
+        $errors = $settings->validateMetadata($metadata);
+
+        if (!empty($errors)) {
+            throw new Error(
+                'Invalid SP metadata: '.implode(', ', $errors),
+                Error::METADATA_SP_INVALID
+            );
+        }
+
+        return $metadata;
+    }
+
+    /**
+     * Load the underlying Onelogin SAML2 toolkit.
+     * @throws Error
+     * @throws Exception
+     */
+    protected function getToolkit(): Auth
+    {
+        $settings = $this->config['onelogin'];
+        $overrides = $this->config['onelogin_overrides'] ?? [];
+
+        if ($overrides && is_string($overrides)) {
+            $overrides = json_decode($overrides, true);
+        }
+
+        $metaDataSettings = [];
+        if ($this->config['autoload_from_metadata']) {
+            $metaDataSettings = IdPMetadataParser::parseRemoteXML($settings['idp']['entityId']);
+        }
+
+        $spSettings = $this->loadOneloginServiceProviderDetails();
+        $settings = array_replace_recursive($settings, $spSettings, $metaDataSettings, $overrides);
+        return new Auth($settings);
+    }
+
+    /**
+     * Load dynamic service provider options required by the onelogin toolkit.
+     */
+    protected function loadOneloginServiceProviderDetails(): array
+    {
+        $spDetails = [
+            'entityId' => url('/saml2/metadata'),
+            'assertionConsumerService' => [
+                'url' => url('/saml2/acs'),
+            ],
+            'singleLogoutService' => [
+                'url' => url('/saml2/sls')
+            ],
+        ];
+
+        return [
+            'baseurl' => url('/saml2'),
+            'sp' => $spDetails
+        ];
+    }
+
+    /**
+     * Check if groups should be synced.
+     */
+    protected function shouldSyncGroups(): bool
+    {
+        return $this->config['user_to_groups'] !== false;
+    }
+
+    /**
+     * Calculate the display name
+     */
+    protected function getUserDisplayName(array $samlAttributes, string $defaultValue): string
+    {
+        $displayNameAttr = $this->config['display_name_attributes'];
+
+        $displayName = [];
+        foreach ($displayNameAttr as $dnAttr) {
+            $dnComponent = $this->getSamlResponseAttribute($samlAttributes, $dnAttr, null);
+            if ($dnComponent !== null) {
+                $displayName[] = $dnComponent;
+            }
+        }
+
+        if (count($displayName) == 0) {
+            $displayName = $defaultValue;
+        } else {
+            $displayName = implode(' ', $displayName);
+        }
+
+        return $displayName;
+    }
+
+    /**
+     * Get the value to use as the external id saved in BookStack
+     * used to link the user to an existing BookStack DB user.
+     */
+    protected function getExternalId(array $samlAttributes, string $defaultValue)
+    {
+        $userNameAttr = $this->config['external_id_attribute'];
+        if ($userNameAttr === null) {
+            return $defaultValue;
+        }
+
+        return $this->getSamlResponseAttribute($samlAttributes, $userNameAttr, $defaultValue);
+    }
+
+    /**
+     * Extract the details of a user from a SAML response.
+     */
+    protected function getUserDetails(string $samlID, $samlAttributes): array
+    {
+        $emailAttr = $this->config['email_attribute'];
+        $externalId = $this->getExternalId($samlAttributes, $samlID);
+
+        $defaultEmail = filter_var($samlID, FILTER_VALIDATE_EMAIL) ? $samlID : null;
+        $email = $this->getSamlResponseAttribute($samlAttributes, $emailAttr, $defaultEmail);
+
+        return [
+            'external_id' => $externalId,
+            'name' => $this->getUserDisplayName($samlAttributes, $externalId),
+            'email' => $email,
+            'saml_id' => $samlID,
+        ];
+    }
+
+    /**
+     * Get the groups a user is a part of from the SAML response.
+     */
+    public function getUserGroups(array $samlAttributes): array
+    {
+        $groupsAttr = $this->config['group_attribute'];
+        $userGroups = $samlAttributes[$groupsAttr] ?? null;
+
+        if (!is_array($userGroups)) {
+            $userGroups = [];
+        }
+
+        return $userGroups;
+    }
+
+    /**
+     *  For an array of strings, return a default for an empty array,
+     *  a string for an array with one element and the full array for
+     *  more than one element.
+     */
+    protected function simplifyValue(array $data, $defaultValue)
+    {
+        switch (count($data)) {
+            case 0:
+                $data = $defaultValue;
+                break;
+            case 1:
+                $data = $data[0];
+                break;
+        }
+        return $data;
+    }
+
+    /**
+     * Get a property from an SAML response.
+     * Handles properties potentially being an array.
+     */
+    protected function getSamlResponseAttribute(array $samlAttributes, string $propertyKey, $defaultValue)
+    {
+        if (isset($samlAttributes[$propertyKey])) {
+            return $this->simplifyValue($samlAttributes[$propertyKey], $defaultValue);
+        }
+
+        return $defaultValue;
+    }
+
+    /**
+     * Get the user from the database for the specified details.
+     * @throws UserRegistrationException
+     */
+    protected function getOrRegisterUser(array $userDetails): ?User
+    {
+        $user = $this->user->newQuery()
+          ->where('external_auth_id', '=', $userDetails['external_id'])
+          ->first();
+
+        if (is_null($user)) {
+            $userData = [
+                'name' => $userDetails['name'],
+                'email' => $userDetails['email'],
+                'password' => Str::random(32),
+                'external_auth_id' => $userDetails['external_id'],
+            ];
+
+            $user = $this->registrationService->registerUser($userData, null, false);
+        }
+
+        return $user;
+    }
+
+    /**
+     * Process the SAML response for a user. Login the user when
+     * they exist, optionally registering them automatically.
+     * @throws SamlException
+     * @throws JsonDebugException
+     * @throws UserRegistrationException
+     */
+    public function processLoginCallback(string $samlID, array $samlAttributes): User
+    {
+        $userDetails = $this->getUserDetails($samlID, $samlAttributes);
+        $isLoggedIn = auth()->check();
+
+        if ($this->config['dump_user_details']) {
+            throw new JsonDebugException([
+                'id_from_idp' => $samlID,
+                'attrs_from_idp' => $samlAttributes,
+                'attrs_after_parsing' => $userDetails,
+            ]);
+        }
+
+        if ($userDetails['email'] === null) {
+            throw new SamlException(trans('errors.saml_no_email_address'));
+        }
+
+        if ($isLoggedIn) {
+            throw new SamlException(trans('errors.saml_already_logged_in'), '/login');
+        }
+
+        $user = $this->getOrRegisterUser($userDetails);
+        if ($user === null) {
+            throw new SamlException(trans('errors.saml_user_not_registered', ['name' => $userDetails['external_id']]), '/login');
+        }
+
+        if ($this->shouldSyncGroups()) {
+            $groups = $this->getUserGroups($samlAttributes);
+            $this->syncWithGroups($user, $groups);
+        }
+
+        auth()->login($user);
+        Activity::add(ActivityType::AUTH_LOGIN, "saml2; {$user->logDescriptor()}");
+        return $user;
+    }
+}
index 9c8d1a81f43ebb8f30d66807a14349cab2882a39..b0383a938522e0ba67cad2213a29895f5d82cba2 100644 (file)
@@ -1,13 +1,17 @@
 <?php namespace BookStack\Auth\Access;
 
+use BookStack\Actions\ActivityType;
 use BookStack\Auth\SocialAccount;
 use BookStack\Auth\UserRepo;
 use BookStack\Exceptions\SocialDriverNotConfigured;
 use BookStack\Exceptions\SocialSignInAccountNotUsed;
 use BookStack\Exceptions\UserRegistrationException;
+use BookStack\Facades\Activity;
 use Illuminate\Support\Str;
 use Laravel\Socialite\Contracts\Factory as Socialite;
+use Laravel\Socialite\Contracts\Provider;
 use Laravel\Socialite\Contracts\User as SocialUser;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 
 class SocialAuthService
 {
@@ -20,9 +24,6 @@ class SocialAuthService
 
     /**
      * SocialAuthService constructor.
-     * @param \BookStack\Auth\UserRepo      $userRepo
-     * @param Socialite     $socialite
-     * @param SocialAccount $socialAccount
      */
     public function __construct(UserRepo $userRepo, Socialite $socialite, SocialAccount $socialAccount)
     {
@@ -34,11 +35,9 @@ class SocialAuthService
 
     /**
      * Start the social login path.
-     * @param string $socialDriver
-     * @return \Symfony\Component\HttpFoundation\RedirectResponse
      * @throws SocialDriverNotConfigured
      */
-    public function startLogIn($socialDriver)
+    public function startLogIn(string $socialDriver): RedirectResponse
     {
         $driver = $this->validateDriver($socialDriver);
         return $this->getSocialDriver($driver)->redirect();
@@ -46,11 +45,9 @@ class SocialAuthService
 
     /**
      * Start the social registration process
-     * @param string $socialDriver
-     * @return \Symfony\Component\HttpFoundation\RedirectResponse
      * @throws SocialDriverNotConfigured
      */
-    public function startRegister($socialDriver)
+    public function startRegister(string $socialDriver): RedirectResponse
     {
         $driver = $this->validateDriver($socialDriver);
         return $this->getSocialDriver($driver)->redirect();
@@ -58,12 +55,9 @@ class SocialAuthService
 
     /**
      * Handle the social registration process on callback.
-     * @param string $socialDriver
-     * @param SocialUser $socialUser
-     * @return SocialUser
      * @throws UserRegistrationException
      */
-    public function handleRegistrationCallback(string $socialDriver, SocialUser $socialUser)
+    public function handleRegistrationCallback(string $socialDriver, SocialUser $socialUser): SocialUser
     {
         // Check social account has not already been used
         if ($this->socialAccount->where('driver_id', '=', $socialUser->getId())->exists()) {
@@ -72,7 +66,7 @@ class SocialAuthService
 
         if ($this->userRepo->getByEmail($socialUser->getEmail())) {
             $email = $socialUser->getEmail();
-            throw new UserRegistrationException(trans('errors.social_account_in_use', ['socialAccount'=>$socialDriver, 'email' => $email]), '/login');
+            throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $email]), '/login');
         }
 
         return $socialUser;
@@ -80,11 +74,9 @@ class SocialAuthService
 
     /**
      * Get the social user details via the social driver.
-     * @param string $socialDriver
-     * @return SocialUser
      * @throws SocialDriverNotConfigured
      */
-    public function getSocialUser(string $socialDriver)
+    public function getSocialUser(string $socialDriver): SocialUser
     {
         $driver = $this->validateDriver($socialDriver);
         return $this->socialite->driver($driver)->user();
@@ -92,12 +84,9 @@ class SocialAuthService
 
     /**
      * Handle the login process on a oAuth callback.
-     * @param $socialDriver
-     * @param SocialUser $socialUser
-     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
      * @throws SocialSignInAccountNotUsed
      */
-    public function handleLoginCallback($socialDriver, SocialUser $socialUser)
+    public function handleLoginCallback(string $socialDriver, SocialUser $socialUser)
     {
         $socialId = $socialUser->getId();
 
@@ -111,6 +100,7 @@ class SocialAuthService
         // Simply log the user into the application.
         if (!$isLoggedIn && $socialAccount !== null) {
             auth()->login($socialAccount->user);
+            Activity::add(ActivityType::AUTH_LOGIN, $socialAccount);
             return redirect()->intended('/');
         }
 
@@ -137,7 +127,7 @@ class SocialAuthService
 
         // Otherwise let the user know this social account is not used by anyone.
         $message = trans('errors.social_account_not_used', ['socialAccount' => $titleCaseDriver]);
-        if (setting('registration-enabled')) {
+        if (setting('registration-enabled') && config('auth.method') !== 'ldap' && config('auth.method') !== 'saml2') {
             $message .= trans('errors.social_account_register_instructions', ['socialAccount' => $titleCaseDriver]);
         }
         
@@ -146,18 +136,16 @@ class SocialAuthService
 
     /**
      * Ensure the social driver is correct and supported.
-     *
-     * @param $socialDriver
-     * @return string
      * @throws SocialDriverNotConfigured
      */
-    private function validateDriver($socialDriver)
+    protected function validateDriver(string $socialDriver): string
     {
         $driver = trim(strtolower($socialDriver));
 
         if (!in_array($driver, $this->validSocialDrivers)) {
             abort(404, trans('errors.social_driver_not_found'));
         }
+
         if (!$this->checkDriverConfigured($driver)) {
             throw new SocialDriverNotConfigured(trans('errors.social_driver_not_configured', ['socialAccount' => Str::title($socialDriver)]));
         }
@@ -167,10 +155,8 @@ class SocialAuthService
 
     /**
      * Check a social driver has been configured correctly.
-     * @param $driver
-     * @return bool
      */
-    private function checkDriverConfigured($driver)
+    protected function checkDriverConfigured(string $driver): bool
     {
         $lowerName = strtolower($driver);
         $configPrefix = 'services.' . $lowerName . '.';
@@ -180,55 +166,48 @@ class SocialAuthService
 
     /**
      * Gets the names of the active social drivers.
-     * @return array
      */
-    public function getActiveDrivers()
+    public function getActiveDrivers(): array
     {
         $activeDrivers = [];
+
         foreach ($this->validSocialDrivers as $driverKey) {
             if ($this->checkDriverConfigured($driverKey)) {
                 $activeDrivers[$driverKey] = $this->getDriverName($driverKey);
             }
         }
+
         return $activeDrivers;
     }
 
     /**
      * Get the presentational name for a driver.
-     * @param $driver
-     * @return mixed
      */
-    public function getDriverName($driver)
+    public function getDriverName(string $driver): string
     {
         return config('services.' . strtolower($driver) . '.name');
     }
 
     /**
      * Check if the current config for the given driver allows auto-registration.
-     * @param string $driver
-     * @return bool
      */
-    public function driverAutoRegisterEnabled(string $driver)
+    public function driverAutoRegisterEnabled(string $driver): bool
     {
         return config('services.' . strtolower($driver) . '.auto_register') === true;
     }
 
     /**
      * Check if the current config for the given driver allow email address auto-confirmation.
-     * @param string $driver
-     * @return bool
      */
-    public function driverAutoConfirmEmailEnabled(string $driver)
+    public function driverAutoConfirmEmailEnabled(string $driver): bool
     {
         return config('services.' . strtolower($driver) . '.auto_confirm') === true;
     }
 
     /**
-     * @param string $socialDriver
-     * @param SocialUser $socialUser
-     * @return SocialAccount
+     * Fill and return a SocialAccount from the given driver name and SocialUser.
      */
-    public function fillSocialAccount($socialDriver, $socialUser)
+    public function fillSocialAccount(string $socialDriver, SocialUser $socialUser): SocialAccount
     {
         $this->socialAccount->fill([
             'driver'    => $socialDriver,
@@ -240,28 +219,26 @@ class SocialAuthService
 
     /**
      * Detach a social account from a user.
-     * @param $socialDriver
      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
      */
-    public function detachSocialAccount($socialDriver)
+    public function detachSocialAccount(string $socialDriver)
     {
         user()->socialAccounts()->where('driver', '=', $socialDriver)->delete();
-        session()->flash('success', trans('settings.users_social_disconnected', ['socialAccount' => Str::title($socialDriver)]));
-        return redirect(user()->getEditUrl());
     }
 
     /**
      * Provide redirect options per service for the Laravel Socialite driver
-     * @param $driverName
-     * @return \Laravel\Socialite\Contracts\Provider
      */
-    public function getSocialDriver(string $driverName)
+    public function getSocialDriver(string $driverName): Provider
     {
         $driver = $this->socialite->driver($driverName);
 
         if ($driverName === 'google' && config('services.google.select_account')) {
             $driver->with(['prompt' => 'select_account']);
         }
+        if ($driverName === 'azure') {
+            $driver->with(['resource' => 'https://graph.windows.net']);
+        }
 
         return $driver;
     }
index c48549b8f00c5cac2e9996fa9c4c4e0d483af365..6f7fa582b83a6610b57813bc009fd44582761ab3 100644 (file)
@@ -1,27 +1,28 @@
 <?php namespace BookStack\Auth\Permissions;
 
 use BookStack\Auth\Role;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Entity;
 use BookStack\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\MorphOne;
 
 class JointPermission extends Model
 {
+    protected $primaryKey = null;
     public $timestamps = false;
 
     /**
      * Get the role that this points to.
-     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
      */
-    public function role()
+    public function role(): BelongsTo
     {
         return $this->belongsTo(Role::class);
     }
 
     /**
      * Get the entity this points to.
-     * @return \Illuminate\Database\Eloquent\Relations\MorphOne
      */
-    public function entity()
+    public function entity(): MorphOne
     {
         return $this->morphOne(Entity::class, 'entity');
     }
index 97cc1ca241e84209f235136550378f3cb8f43f81..89c8a5fbb25a55fe54fd95cc8d2721c27c6b9334 100644 (file)
@@ -2,13 +2,12 @@
 
 use BookStack\Auth\Permissions;
 use BookStack\Auth\Role;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Entity;
 use BookStack\Entities\EntityProvider;
-use BookStack\Entities\Page;
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
+use BookStack\Traits\HasOwner;
 use Illuminate\Database\Connection;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Query\Builder as QueryBuilder;
@@ -51,11 +50,6 @@ class PermissionService
 
     /**
      * PermissionService constructor.
-     * @param JointPermission $jointPermission
-     * @param EntityPermission $entityPermission
-     * @param Role $role
-     * @param Connection $db
-     * @param EntityProvider $entityProvider
      */
     public function __construct(
         JointPermission $jointPermission,
@@ -82,7 +76,7 @@ class PermissionService
 
     /**
      * Prepare the local entity cache and ensure it's empty
-     * @param \BookStack\Entities\Entity[] $entities
+     * @param \BookStack\Entities\Models\Entity[] $entities
      */
     protected function readyEntityCache($entities = [])
     {
@@ -119,7 +113,7 @@ class PermissionService
     /**
      * Get a chapter via ID, Checks local cache
      * @param $chapterId
-     * @return \BookStack\Entities\Book
+     * @return \BookStack\Entities\Models\Book
      */
     protected function getChapter($chapterId)
     {
@@ -176,7 +170,7 @@ class PermissionService
         });
 
         // Chunk through all bookshelves
-        $this->entityProvider->bookshelf->newQuery()->select(['id', 'restricted', 'created_by'])
+        $this->entityProvider->bookshelf->newQuery()->withTrashed()->select(['id', 'restricted', 'owned_by'])
             ->chunk(50, function ($shelves) use ($roles) {
                 $this->buildJointPermissionsForShelves($shelves, $roles);
             });
@@ -188,11 +182,11 @@ class PermissionService
      */
     protected function bookFetchQuery()
     {
-        return $this->entityProvider->book->newQuery()
-            ->select(['id', 'restricted', 'created_by'])->with(['chapters' => function ($query) {
-                $query->select(['id', 'restricted', 'created_by', 'book_id']);
+        return $this->entityProvider->book->withTrashed()->newQuery()
+            ->select(['id', 'restricted', 'owned_by'])->with(['chapters' => function ($query) {
+                $query->withTrashed()->select(['id', 'restricted', 'owned_by', 'book_id']);
             }, 'pages'  => function ($query) {
-                $query->select(['id', 'restricted', 'created_by', 'book_id', 'chapter_id']);
+                $query->withTrashed()->select(['id', 'restricted', 'owned_by', 'book_id', 'chapter_id']);
             }]);
     }
 
@@ -238,7 +232,7 @@ class PermissionService
 
     /**
      * Rebuild the entity jointPermissions for a particular entity.
-     * @param \BookStack\Entities\Entity $entity
+     * @param \BookStack\Entities\Models\Entity $entity
      * @throws \Throwable
      */
     public function buildJointPermissionsForEntity(Entity $entity)
@@ -294,7 +288,7 @@ class PermissionService
         });
 
         // Chunk through all bookshelves
-        $this->entityProvider->bookshelf->newQuery()->select(['id', 'restricted', 'created_by'])
+        $this->entityProvider->bookshelf->newQuery()->select(['id', 'restricted', 'owned_by'])
             ->chunk(50, function ($shelves) use ($roles) {
                 $this->buildJointPermissionsForShelves($shelves, $roles);
             });
@@ -333,7 +327,7 @@ class PermissionService
 
     /**
      * Delete all of the entity jointPermissions for a list of entities.
-     * @param \BookStack\Entities\Entity[] $entities
+     * @param \BookStack\Entities\Models\Entity[] $entities
      * @throws \Throwable
      */
     protected function deleteManyJointPermissionsForEntities($entities)
@@ -414,7 +408,7 @@ class PermissionService
 
     /**
      * Get the actions related to an entity.
-     * @param \BookStack\Entities\Entity $entity
+     * @param \BookStack\Entities\Models\Entity $entity
      * @return array
      */
     protected function getActions(Entity $entity)
@@ -500,7 +494,7 @@ class PermissionService
     /**
      * Create an array of data with the information of an entity jointPermissions.
      * Used to build data for bulk insertion.
-     * @param \BookStack\Entities\Entity $entity
+     * @param \BookStack\Entities\Models\Entity $entity
      * @param Role $role
      * @param $action
      * @param $permissionAll
@@ -516,21 +510,19 @@ class PermissionService
             'action'             => $action,
             'has_permission'     => $permissionAll,
             'has_permission_own' => $permissionOwn,
-            'created_by'         => $entity->getRawAttribute('created_by')
+            'owned_by'         => $entity->getRawAttribute('owned_by')
         ];
     }
 
     /**
      * Checks if an entity has a restriction set upon it.
-     * @param Ownable $ownable
-     * @param $permission
-     * @return bool
+     * @param HasCreatorAndUpdater|HasOwner $ownable
      */
-    public function checkOwnableUserAccess(Ownable $ownable, $permission)
+    public function checkOwnableUserAccess(Model $ownable, string $permission): bool
     {
         $explodedPermission = explode('-', $permission);
 
-        $baseQuery = $ownable->where('id', '=', $ownable->id);
+        $baseQuery = $ownable->newQuery()->where('id', '=', $ownable->id);
         $action = end($explodedPermission);
         $this->currentAction = $action;
 
@@ -541,7 +533,8 @@ class PermissionService
             $allPermission = $this->currentUser() && $this->currentUser()->can($permission . '-all');
             $ownPermission = $this->currentUser() && $this->currentUser()->can($permission . '-own');
             $this->currentAction = 'view';
-            $isOwner = $this->currentUser() && $this->currentUser()->id === $ownable->created_by;
+            $ownerField = ($ownable instanceof Entity) ? 'owned_by' : 'created_by';
+            $isOwner = $this->currentUser() && $this->currentUser()->id === $ownable->$ownerField;
             return ($allPermission || ($isOwner && $ownPermission));
         }
 
@@ -574,7 +567,7 @@ class PermissionService
                 $query->where('has_permission', '=', 1)
                     ->orWhere(function ($query2) use ($userId) {
                         $query2->where('has_permission_own', '=', 1)
-                            ->where('created_by', '=', $userId);
+                            ->where('owned_by', '=', $userId);
                     });
             });
 
@@ -591,7 +584,7 @@ class PermissionService
     /**
      * Check if an entity has restrictions set on itself or its
      * parent tree.
-     * @param \BookStack\Entities\Entity $entity
+     * @param \BookStack\Entities\Models\Entity $entity
      * @param $action
      * @return bool|mixed
      */
@@ -623,7 +616,7 @@ class PermissionService
                         $query->where('has_permission', '=', true)
                             ->orWhere(function ($query) {
                                 $query->where('has_permission_own', '=', true)
-                                    ->where('created_by', '=', $this->currentUser()->id);
+                                    ->where('owned_by', '=', $this->currentUser()->id);
                             });
                     });
             });
@@ -647,7 +640,7 @@ class PermissionService
                         $query->where('has_permission', '=', true)
                             ->orWhere(function (Builder $query) {
                                 $query->where('has_permission_own', '=', true)
-                                    ->where('created_by', '=', $this->currentUser()->id);
+                                    ->where('owned_by', '=', $this->currentUser()->id);
                             });
                     });
             });
@@ -664,7 +657,7 @@ class PermissionService
             $query->where('draft', '=', false)
                 ->orWhere(function (Builder $query) {
                     $query->where('draft', '=', true)
-                        ->where('created_by', '=', $this->currentUser()->id);
+                        ->where('owned_by', '=', $this->currentUser()->id);
                 });
         });
     }
@@ -672,7 +665,7 @@ class PermissionService
     /**
      * Add restrictions for a generic entity
      * @param string $entityType
-     * @param Builder|\BookStack\Entities\Entity $query
+     * @param Builder|\BookStack\Entities\Models\Entity $query
      * @param string $action
      * @return Builder
      */
@@ -684,7 +677,7 @@ class PermissionService
                 $query->where('draft', '=', false)
                     ->orWhere(function ($query) {
                         $query->where('draft', '=', true)
-                            ->where('created_by', '=', $this->currentUser()->id);
+                            ->where('owned_by', '=', $this->currentUser()->id);
                     });
             });
         }
@@ -718,7 +711,7 @@ class PermissionService
                     ->where(function ($query) {
                         $query->where('has_permission', '=', true)->orWhere(function ($query) {
                             $query->where('has_permission_own', '=', true)
-                                ->where('created_by', '=', $this->currentUser()->id);
+                                ->where('owned_by', '=', $this->currentUser()->id);
                         });
                     });
             });
@@ -754,7 +747,7 @@ class PermissionService
                         ->where(function ($query) {
                             $query->where('has_permission', '=', true)->orWhere(function ($query) {
                                 $query->where('has_permission_own', '=', true)
-                                    ->where('created_by', '=', $this->currentUser()->id);
+                                    ->where('owned_by', '=', $this->currentUser()->id);
                             });
                         });
                 });
index 56ef193015f7103a98bb440ec60498e5bf765c25..f54612a4339a3423557a994a3fa876636215799b 100644 (file)
@@ -1,9 +1,11 @@
 <?php namespace BookStack\Auth\Permissions;
 
-use BookStack\Auth\Permissions;
+use BookStack\Actions\ActivityType;
 use BookStack\Auth\Role;
 use BookStack\Exceptions\PermissionsException;
-use Illuminate\Support\Str;
+use BookStack\Facades\Activity;
+use Exception;
+use Illuminate\Database\Eloquent\Collection;
 
 class PermissionsRepo
 {
@@ -16,11 +18,8 @@ class PermissionsRepo
 
     /**
      * PermissionsRepo constructor.
-     * @param RolePermission $permission
-     * @param Role $role
-     * @param \BookStack\Auth\Permissions\PermissionService $permissionService
      */
-    public function __construct(RolePermission $permission, Role $role, Permissions\PermissionService $permissionService)
+    public function __construct(RolePermission $permission, Role $role, PermissionService $permissionService)
     {
         $this->permission = $permission;
         $this->role = $role;
@@ -29,64 +28,51 @@ class PermissionsRepo
 
     /**
      * Get all the user roles from the system.
-     * @return \Illuminate\Database\Eloquent\Collection|static[]
      */
-    public function getAllRoles()
+    public function getAllRoles(): Collection
     {
         return $this->role->all();
     }
 
     /**
      * Get all the roles except for the provided one.
-     * @param Role $role
-     * @return mixed
      */
-    public function getAllRolesExcept(Role $role)
+    public function getAllRolesExcept(Role $role): Collection
     {
         return $this->role->where('id', '!=', $role->id)->get();
     }
 
     /**
      * Get a role via its ID.
-     * @param $id
-     * @return mixed
      */
-    public function getRoleById($id)
+    public function getRoleById($id): Role
     {
-        return $this->role->findOrFail($id);
+        return $this->role->newQuery()->findOrFail($id);
     }
 
     /**
      * Save a new role into the system.
-     * @param array $roleData
-     * @return Role
      */
-    public function saveNewRole($roleData)
+    public function saveNewRole(array $roleData): Role
     {
         $role = $this->role->newInstance($roleData);
-        $role->name = str_replace(' ', '-', strtolower($roleData['display_name']));
-        // Prevent duplicate names
-        while ($this->role->where('name', '=', $role->name)->count() > 0) {
-            $role->name .= strtolower(Str::random(2));
-        }
         $role->save();
 
         $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
         $this->assignRolePermissions($role, $permissions);
         $this->permissionService->buildJointPermissionForRole($role);
+        Activity::add(ActivityType::ROLE_CREATE, $role);
         return $role;
     }
 
     /**
      * Updates an existing role.
      * Ensure Admin role always have core permissions.
-     * @param $roleId
-     * @param $roleData
-     * @throws PermissionsException
      */
-    public function updateRole($roleId, $roleData)
+    public function updateRole($roleId, array $roleData)
     {
-        $role = $this->role->findOrFail($roleId);
+        /** @var Role $role */
+        $role = $this->role->newQuery()->findOrFail($roleId);
 
         $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
         if ($role->system_name === 'admin') {
@@ -104,20 +90,24 @@ class PermissionsRepo
         $role->fill($roleData);
         $role->save();
         $this->permissionService->buildJointPermissionForRole($role);
+        Activity::add(ActivityType::ROLE_UPDATE, $role);
     }
 
     /**
      * Assign an list of permission names to an role.
-     * @param Role $role
-     * @param array $permissionNameArray
      */
-    public function assignRolePermissions(Role $role, $permissionNameArray = [])
+    protected function assignRolePermissions(Role $role, array $permissionNameArray = [])
     {
         $permissions = [];
         $permissionNameArray = array_values($permissionNameArray);
-        if ($permissionNameArray && count($permissionNameArray) > 0) {
-            $permissions = $this->permission->whereIn('name', $permissionNameArray)->pluck('id')->toArray();
+
+        if ($permissionNameArray) {
+            $permissions = $this->permission->newQuery()
+                ->whereIn('name', $permissionNameArray)
+                ->pluck('id')
+                ->toArray();
         }
+
         $role->permissions()->sync($permissions);
     }
 
@@ -126,13 +116,13 @@ class PermissionsRepo
      * Check it's not an admin role or set as default before deleting.
      * If an migration Role ID is specified the users assign to the current role
      * will be added to the role of the specified id.
-     * @param $roleId
-     * @param $migrateRoleId
      * @throws PermissionsException
+     * @throws Exception
      */
     public function deleteRole($roleId, $migrateRoleId)
     {
-        $role = $this->role->findOrFail($roleId);
+        /** @var Role $role */
+        $role = $this->role->newQuery()->findOrFail($roleId);
 
         // Prevent deleting admin role or default registration role.
         if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {
@@ -142,14 +132,15 @@ class PermissionsRepo
         }
 
         if ($migrateRoleId) {
-            $newRole = $this->role->find($migrateRoleId);
+            $newRole = $this->role->newQuery()->find($migrateRoleId);
             if ($newRole) {
-                $users = $role->users->pluck('id')->toArray();
+                $users = $role->users()->pluck('id')->toArray();
                 $newRole->users()->sync($users);
             }
         }
 
         $this->permissionService->deleteJointPermissionsForRole($role);
+        Activity::add(ActivityType::ROLE_DELETE, $role);
         $role->delete();
     }
 }
index 8b07b3073bf9add0eb1f4a216844c69e05235832..7f44ff8152b5a23b759b5fce3a83238da01855f9 100644 (file)
@@ -3,6 +3,9 @@
 use BookStack\Auth\Role;
 use BookStack\Model;
 
+/**
+ * @property int $id
+ */
 class RolePermission extends Model
 {
     /**
index 712f5299b7b1d2ff5485179621aeddf9a1c5efbd..629cd6a955d8abf7961b67aa1c598d1d62d30658 100644 (file)
@@ -2,9 +2,21 @@
 
 use BookStack\Auth\Permissions\JointPermission;
 use BookStack\Auth\Permissions\RolePermission;
+use BookStack\Interfaces\Loggable;
 use BookStack\Model;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Database\Eloquent\Relations\HasMany;
 
-class Role extends Model
+/**
+ * Class Role
+ * @property int $id
+ * @property string $display_name
+ * @property string $description
+ * @property string $external_auth_id
+ * @property string $system_name
+ */
+class Role extends Model implements Loggable
 {
 
     protected $fillable = ['display_name', 'description', 'external_auth_id'];
@@ -12,16 +24,15 @@ class Role extends Model
     /**
      * The roles that belong to the role.
      */
-    public function users()
+    public function users(): BelongsToMany
     {
         return $this->belongsToMany(User::class)->orderBy('name', 'asc');
     }
 
     /**
      * Get all related JointPermissions.
-     * @return \Illuminate\Database\Eloquent\Relations\HasMany
      */
-    public function jointPermissions()
+    public function jointPermissions(): HasMany
     {
         return $this->hasMany(JointPermission::class);
     }
@@ -29,17 +40,15 @@ class Role extends Model
     /**
      * The RolePermissions that belong to the role.
      */
-    public function permissions()
+    public function permissions(): BelongsToMany
     {
         return $this->belongsToMany(RolePermission::class, 'permission_role', 'role_id', 'permission_id');
     }
 
     /**
      * Check if this role has a permission.
-     * @param $permissionName
-     * @return bool
      */
-    public function hasPermission($permissionName)
+    public function hasPermission(string $permissionName): bool
     {
         $permissions = $this->getRelationValue('permissions');
         foreach ($permissions as $permission) {
@@ -52,7 +61,6 @@ class Role extends Model
 
     /**
      * Add a permission to this role.
-     * @param RolePermission $permission
      */
     public function attachPermission(RolePermission $permission)
     {
@@ -61,48 +69,49 @@ class Role extends Model
 
     /**
      * Detach a single permission from this role.
-     * @param RolePermission $permission
      */
     public function detachPermission(RolePermission $permission)
     {
-        $this->permissions()->detach($permission->id);
+        $this->permissions()->detach([$permission->id]);
     }
 
     /**
-     * Get the role object for the specified role.
-     * @param $roleName
-     * @return Role
+     * Get the role of the specified display name.
      */
-    public static function getRole($roleName)
+    public static function getRole(string $displayName): ?Role
     {
-        return static::query()->where('name', '=', $roleName)->first();
+        return static::query()->where('display_name', '=', $displayName)->first();
     }
 
     /**
      * Get the role object for the specified system role.
-     * @param $roleName
-     * @return Role
      */
-    public static function getSystemRole($roleName)
+    public static function getSystemRole(string $systemName): ?Role
     {
-        return static::query()->where('system_name', '=', $roleName)->first();
+        return static::query()->where('system_name', '=', $systemName)->first();
     }
 
     /**
      * Get all visible roles
-     * @return mixed
      */
-    public static function visible()
+    public static function visible(): Collection
     {
         return static::query()->where('hidden', '=', false)->orderBy('name')->get();
     }
 
     /**
      * Get the roles that can be restricted.
-     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
      */
-    public static function restrictable()
+    public static function restrictable(): Collection
     {
         return static::query()->where('system_name', '!=', 'admin')->get();
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function logDescriptor(): string
+    {
+        return "({$this->id}) {$this->display_name}";
+    }
 }
index 804dbe6292973c16b7dc068ad00dd353c33fa48d..116cdc8546957a4071ad54bbc52aba5b8b6ede6c 100644 (file)
@@ -1,8 +1,14 @@
 <?php namespace BookStack\Auth;
 
+use BookStack\Interfaces\Loggable;
 use BookStack\Model;
 
-class SocialAccount extends Model
+/**
+ * Class SocialAccount
+ * @property string $driver
+ * @property User $user
+ */
+class SocialAccount extends Model implements Loggable
 {
 
     protected $fillable = ['user_id', 'driver', 'driver_id', 'timestamps'];
@@ -11,4 +17,12 @@ class SocialAccount extends Model
     {
         return $this->belongsTo(User::class);
     }
+
+    /**
+     * @inheritDoc
+     */
+    public function logDescriptor(): string
+    {
+        return "{$this->driver}; {$this->user->logDescriptor()}";
+    }
 }
index bce418a7421fb40ddeb34b0e0dacd192d7048e2a..9d7eaa72e211a2206a88baa0bcc909bf6eedc9a2 100644 (file)
@@ -1,19 +1,25 @@
 <?php namespace BookStack\Auth;
 
+use BookStack\Api\ApiToken;
+use BookStack\Interfaces\Loggable;
 use BookStack\Model;
 use BookStack\Notifications\ResetPassword;
 use BookStack\Uploads\Image;
 use Carbon\Carbon;
+use Exception;
 use Illuminate\Auth\Authenticatable;
 use Illuminate\Auth\Passwords\CanResetPassword;
 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Database\Eloquent\Relations\HasMany;
 use Illuminate\Notifications\Notifiable;
+use Illuminate\Support\Collection;
 
 /**
  * Class User
- * @package BookStack\Auth
  * @property string $id
  * @property string $name
  * @property string $email
@@ -25,7 +31,7 @@ use Illuminate\Notifications\Notifiable;
  * @property string $external_auth_id
  * @property string $system_name
  */
-class User extends Model implements AuthenticatableContract, CanResetPasswordContract
+class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable
 {
     use Authenticatable, CanResetPassword, Notifiable;
 
@@ -41,15 +47,20 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
      */
     protected $fillable = ['name', 'email'];
 
+    protected $casts = ['last_activity_at' => 'datetime'];
+
     /**
      * The attributes excluded from the model's JSON form.
      * @var array
      */
-    protected $hidden = ['password', 'remember_token'];
+    protected $hidden = [
+        'password', 'remember_token', 'system_name', 'email_confirmed', 'external_auth_id', 'email',
+        'created_at', 'updated_at', 'image_id',
+    ];
 
     /**
      * This holds the user's permissions when loaded.
-     * @var array
+     * @var ?Collection
      */
     protected $permissions;
 
@@ -96,12 +107,10 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
 
     /**
      * Check if the user has a role.
-     * @param $role
-     * @return mixed
      */
-    public function hasRole($role)
+    public function hasRole($roleId): bool
     {
-        return $this->roles->pluck('name')->contains($role);
+        return $this->roles->pluck('id')->contains($roleId);
     }
 
     /**
@@ -115,57 +124,67 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
     }
 
     /**
-     * Get all permissions belonging to a the current user.
-     * @param bool $cache
-     * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
+     * Attach the default system role to this user.
      */
-    public function permissions($cache = true)
+    public function attachDefaultRole(): void
     {
-        if (isset($this->permissions) && $cache) {
-            return $this->permissions;
+        $roleId = setting('registration-role');
+        if ($roleId && $this->roles()->where('id', '=', $roleId)->count() === 0) {
+            $this->roles()->attach($roleId);
         }
-        $this->load('roles.permissions');
-        $permissions = $this->roles->map(function ($role) {
-            return $role->permissions;
-        })->flatten()->unique();
-        $this->permissions = $permissions;
-        return $permissions;
     }
 
     /**
      * Check if the user has a particular permission.
-     * @param $permissionName
-     * @return bool
      */
-    public function can($permissionName)
+    public function can(string $permissionName): bool
     {
         if ($this->email === 'guest') {
             return false;
         }
-        return $this->permissions()->pluck('name')->contains($permissionName);
+
+        return $this->permissions()->contains($permissionName);
     }
 
     /**
-     * Attach a role to this user.
-     * @param Role $role
+     * Get all permissions belonging to a the current user.
      */
-    public function attachRole(Role $role)
+    protected function permissions(): Collection
     {
-        $this->attachRoleId($role->id);
+        if (isset($this->permissions)) {
+            return $this->permissions;
+        }
+
+        $this->permissions = $this->newQuery()->getConnection()->table('role_user', 'ru')
+            ->select('role_permissions.name as name')->distinct()
+            ->leftJoin('permission_role', 'ru.role_id', '=', 'permission_role.role_id')
+            ->leftJoin('role_permissions', 'permission_role.permission_id', '=', 'role_permissions.id')
+            ->where('ru.user_id', '=', $this->id)
+            ->get()
+            ->pluck('name');
+
+        return $this->permissions;
     }
 
     /**
-     * Attach a role id to this user.
-     * @param $id
+     * Clear any cached permissions on this instance.
      */
-    public function attachRoleId($id)
+    public function clearPermissionCache()
     {
-        $this->roles()->attach($id);
+        $this->permissions = null;
+    }
+
+    /**
+     * Attach a role to this user.
+     */
+    public function attachRole(Role $role)
+    {
+        $this->roles()->attach($role->id);
     }
 
     /**
      * Get the social account associated with this user.
-     * @return \Illuminate\Database\Eloquent\Relations\HasMany
+     * @return HasMany
      */
     public function socialAccounts()
     {
@@ -202,7 +221,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
 
         try {
             $avatar = $this->avatar ? url($this->avatar->getThumb($size, $size, false)) : $default;
-        } catch (\Exception $err) {
+        } catch (Exception $err) {
             $avatar = $default;
         }
         return $avatar;
@@ -210,27 +229,47 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
 
     /**
      * Get the avatar for the user.
-     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+     * @return BelongsTo
      */
     public function avatar()
     {
         return $this->belongsTo(Image::class, 'image_id');
     }
 
+    /**
+     * Get the API tokens assigned to this user.
+     */
+    public function apiTokens(): HasMany
+    {
+        return $this->hasMany(ApiToken::class);
+    }
+
+    /**
+     * Get the last activity time for this user.
+     */
+    public function scopeWithLastActivityAt(Builder $query)
+    {
+        $query->addSelect(['activities.created_at as last_activity_at'])
+            ->leftJoinSub(function (\Illuminate\Database\Query\Builder $query) {
+                $query->from('activities')->select('user_id')
+                    ->selectRaw('max(created_at) as created_at')
+                    ->groupBy('user_id');
+            }, 'activities', 'users.id', '=', 'activities.user_id');
+    }
+
     /**
      * Get the url for editing this user.
-     * @return string
      */
-    public function getEditUrl()
+    public function getEditUrl(string $path = ''): string
     {
-        return url('/settings/users/' . $this->id);
+        $uri = '/settings/users/' . $this->id . '/' . trim($path, '/');
+        return url(rtrim($uri, '/'));
     }
 
     /**
      * Get the url that links to this user's profile.
-     * @return mixed
      */
-    public function getProfileUrl()
+    public function getProfileUrl(): string
     {
         return url('/user/' . $this->id);
     }
@@ -263,4 +302,12 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
     {
         $this->notify(new ResetPassword($token));
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function logDescriptor(): string
+    {
+        return "({$this->id}) {$this->name}";
+    }
 }
index a903e2c38324e27c1d646655d9758d8e1febb471..29a0ebc14aceae23c3e33fcfece504b4897d14b8 100644 (file)
@@ -1,69 +1,69 @@
 <?php namespace BookStack\Auth;
 
 use Activity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
 use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\UserUpdateException;
 use BookStack\Uploads\Image;
+use BookStack\Uploads\UserAvatars;
 use Exception;
 use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Pagination\LengthAwarePaginator;
 use Images;
 use Log;
 
 class UserRepo
 {
-
-    protected $user;
-    protected $role;
+    protected $userAvatar;
 
     /**
      * UserRepo constructor.
      */
-    public function __construct(User $user, Role $role)
+    public function __construct(UserAvatars $userAvatar)
     {
-        $this->user = $user;
-        $this->role = $role;
+        $this->userAvatar = $userAvatar;
     }
 
     /**
-     * @param string $email
-     * @return User|null
+     * Get a user by their email address.
      */
-    public function getByEmail($email)
+    public function getByEmail(string $email): ?User
     {
-        return $this->user->where('email', '=', $email)->first();
+        return User::query()->where('email', '=', $email)->first();
     }
 
     /**
-     * @param int $id
-     * @return User
+     * Get a user by their ID.
      */
-    public function getById($id)
+    public function getById(int $id): User
     {
-        return $this->user->newQuery()->findOrFail($id);
+        return User::query()->findOrFail($id);
     }
 
     /**
      * Get all the users with their permissions.
-     * @return Builder|static
      */
-    public function getAllUsers()
+    public function getAllUsers(): Collection
     {
-        return $this->user->with('roles', 'avatar')->orderBy('name', 'asc')->get();
+        return User::query()->with('roles', 'avatar')->orderBy('name', 'asc')->get();
     }
 
     /**
      * Get all the users with their permissions in a paginated format.
-     * @param int $count
-     * @param $sortData
-     * @return Builder|static
      */
-    public function getAllUsersPaginatedAndSorted($count, $sortData)
+    public function getAllUsersPaginatedAndSorted(int $count, array $sortData): LengthAwarePaginator
     {
-        $query = $this->user->with('roles', 'avatar')->orderBy($sortData['sort'], $sortData['order']);
+        $sort = $sortData['sort'];
+
+        $query = User::query()->select(['*'])
+            ->withLastActivityAt()
+            ->with(['roles', 'avatar'])
+            ->orderBy($sort, $sortData['order']);
 
         if ($sortData['search']) {
             $term = '%' . $sortData['search'] . '%';
@@ -78,41 +78,24 @@ class UserRepo
 
      /**
      * Creates a new user and attaches a role to them.
-     * @param array $data
-     * @param boolean $verifyEmail
-     * @return User
      */
-    public function registerNew(array $data, $verifyEmail = false)
+    public function registerNew(array $data, bool $emailConfirmed = false): User
     {
-        $user = $this->create($data, $verifyEmail);
-        $this->attachDefaultRole($user);
+        $user = $this->create($data, $emailConfirmed);
+        $user->attachDefaultRole();
         $this->downloadAndAssignUserAvatar($user);
 
         return $user;
     }
 
-    /**
-     * Give a user the default role. Used when creating a new user.
-     * @param User $user
-     */
-    public function attachDefaultRole(User $user)
-    {
-        $roleId = setting('registration-role');
-        if ($roleId !== false && $user->roles()->where('id', '=', $roleId)->count() === 0) {
-            $user->attachRoleId($roleId);
-        }
-    }
-
     /**
      * Assign a user to a system-level role.
-     * @param User $user
-     * @param $systemRoleName
      * @throws NotFoundException
      */
-    public function attachSystemRole(User $user, $systemRoleName)
+    public function attachSystemRole(User $user, string $systemRoleName)
     {
-        $role = $this->role->newQuery()->where('system_name', '=', $systemRoleName)->first();
-        if ($role === null) {
+        $role = Role::getSystemRole($systemRoleName);
+        if (is_null($role)) {
             throw new NotFoundException("Role '{$systemRoleName}' not found");
         }
         $user->attachRole($role);
@@ -120,26 +103,23 @@ class UserRepo
 
     /**
      * Checks if the give user is the only admin.
-     * @param User $user
-     * @return bool
      */
-    public function isOnlyAdmin(User $user)
+    public function isOnlyAdmin(User $user): bool
     {
         if (!$user->hasSystemRole('admin')) {
             return false;
         }
 
-        $adminRole = $this->role->getSystemRole('admin');
-        if ($adminRole->users->count() > 1) {
+        $adminRole = Role::getSystemRole('admin');
+        if ($adminRole->users()->count() > 1) {
             return false;
         }
+
         return true;
     }
 
     /**
      * Set the assigned user roles via an array of role IDs.
-     * @param User $user
-     * @param array $roles
      * @throws UserUpdateException
      */
     public function setUserRoles(User $user, array $roles)
@@ -154,14 +134,11 @@ class UserRepo
     /**
      * Check if the given user is the last admin and their new roles no longer
      * contains the admin role.
-     * @param User $user
-     * @param array $newRoles
-     * @return bool
      */
     protected function demotingLastAdmin(User $user, array $newRoles) : bool
     {
         if ($this->isOnlyAdmin($user)) {
-            $adminRole = $this->role->getSystemRole('admin');
+            $adminRole = Role::getSystemRole('admin');
             if (!in_array(strval($adminRole->id), $newRoles)) {
                 return true;
             }
@@ -172,45 +149,62 @@ class UserRepo
 
     /**
      * Create a new basic instance of user.
-     * @param array $data
-     * @param boolean $verifyEmail
-     * @return User
      */
-    public function create(array $data, $verifyEmail = false)
+    public function create(array $data, bool $emailConfirmed = false): User
     {
-        return $this->user->forceCreate([
+        $details = [
             'name'     => $data['name'],
             'email'    => $data['email'],
             'password' => bcrypt($data['password']),
-            'email_confirmed' => $verifyEmail
-        ]);
+            'email_confirmed' => $emailConfirmed,
+            'external_auth_id' => $data['external_auth_id'] ?? '',
+        ];
+        return User::query()->forceCreate($details);
     }
 
     /**
      * Remove the given user from storage, Delete all related content.
-     * @param User $user
      * @throws Exception
      */
-    public function destroy(User $user)
+    public function destroy(User $user, ?int $newOwnerId = null)
     {
         $user->socialAccounts()->delete();
+        $user->apiTokens()->delete();
         $user->delete();
         
         // Delete user profile images
-        $profileImages = Image::where('type', '=', 'user')->where('uploaded_to', '=', $user->id)->get();
+        $profileImages = Image::query()->where('type', '=', 'user')
+            ->where('uploaded_to', '=', $user->id)
+            ->get();
+
         foreach ($profileImages as $image) {
             Images::destroy($image);
         }
+
+        if (!empty($newOwnerId)) {
+            $newOwner = User::query()->find($newOwnerId);
+            if (!is_null($newOwner)) {
+                $this->migrateOwnership($user, $newOwner);
+            }
+        }
+    }
+
+    /**
+     * Migrate ownership of items in the system from one user to another.
+     */
+    protected function migrateOwnership(User $fromUser, User $toUser)
+    {
+        $entities = (new EntityProvider)->all();
+        foreach ($entities as $instance) {
+            $instance->newQuery()->where('owned_by', '=', $fromUser->id)
+                ->update(['owned_by' => $toUser->id]);
+        }
     }
 
     /**
      * Get the latest activity for a user.
-     * @param User $user
-     * @param int $count
-     * @param int $page
-     * @return array
      */
-    public function getActivity(User $user, $count = 20, $page = 0)
+    public function getActivity(User $user, int $count = 20, int $page = 0): array
     {
         return Activity::userActivity($user, $count, $page);
     }
@@ -251,33 +245,22 @@ class UserRepo
 
     /**
      * Get the roles in the system that are assignable to a user.
-     * @return mixed
      */
-    public function getAllRoles()
+    public function getAllRoles(): Collection
     {
-        return $this->role->newQuery()->orderBy('name', 'asc')->get();
+        return Role::query()->orderBy('display_name', 'asc')->get();
     }
 
     /**
      * Get an avatar image for a user and set it as their avatar.
      * Returns early if avatars disabled or not set in config.
-     * @param User $user
-     * @return bool
      */
-    public function downloadAndAssignUserAvatar(User $user)
+    public function downloadAndAssignUserAvatar(User $user): void
     {
-        if (!Images::avatarFetchEnabled()) {
-            return false;
-        }
-
         try {
-            $avatar = Images::saveUserAvatar($user);
-            $user->avatar()->associate($avatar);
-            $user->save();
-            return true;
+            $this->userAvatar->fetchAndAssignToUser($user);
         } catch (Exception $e) {
             Log::error('Failed to save user avatar image');
-            return false;
         }
     }
 }
diff --git a/app/Config/api.php b/app/Config/api.php
new file mode 100644 (file)
index 0000000..6afea2d
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * API configuration options.
+ *
+ * Changes to these config files are not supported by BookStack and may break upon updates.
+ * Configuration should be altered via the `.env` file or environment variables.
+ * Do not edit this file unless you're happy to maintain any changes yourself.
+ */
+
+return [
+
+    // The default number of items that are returned in listing API requests.
+    // This count can often be overridden, up the the max option, per-request via request options.
+    'default_item_count' => env('API_DEFAULT_ITEM_COUNT', 100),
+
+    // The maximum number of items that can be returned in a listing API request.
+    'max_item_count' => env('API_MAX_ITEM_COUNT', 500),
+
+    // The number of API requests that can be made per minute by a single user.
+    'requests_per_minute' => env('API_REQUESTS_PER_MIN', 180)
+
+];
index 0d06a9b21d7d846a94b49ab8677ced43b3c676cf..762845e9f2bb681d47504c66a72cf8d37d49cc85 100755 (executable)
@@ -31,6 +31,13 @@ return [
     // If set to false then a limit will not be enforced.
     'revision_limit' => env('REVISION_LIMIT', 50),
 
+    // The number of days that content will remain in the recycle bin before
+    // being considered for auto-removal. It is not a guarantee that content will
+    // be removed after this time.
+    // Set to 0 for no recycle bin functionality.
+    // Set to -1 for unlimited recycle bin lifetime.
+    'recycle_bin_lifetime' => env('RECYCLE_BIN_LIFETIME', 30),
+
     // Allow <script> tags to entered within page content.
     // <script> tags are escaped by default.
     // Even when overridden the WYSIWYG editor may still escape script content.
@@ -45,6 +52,10 @@ return [
     // and used by BookStack in URL generation.
     'url' => env('APP_URL', '') === 'http://bookstack.dev' ? '' : env('APP_URL', ''),
 
+    // A list of hosts that BookStack can be iframed within.
+    // Space separated if multiple. BookStack host domain is auto-inferred.
+    'iframe_hosts' => env('ALLOWED_IFRAME_HOSTS', null),
+
     // Application timezone for back-end date functions.
     'timezone' => env('APP_TIMEZONE', 'UTC'),
 
@@ -52,7 +63,7 @@ return [
     'locale' => env('APP_LANG', 'en'),
 
     // Locales available
-    'locales' => ['en', 'ar', 'de', 'de_informal', 'es', 'es_AR', 'fr', 'hu', 'nl', 'pt_BR', 'sk', 'cs', 'sv', 'ko', 'ja', 'pl', 'it', 'ru', 'uk', 'zh_CN', 'zh_TW', 'tr'],
+    'locales' => ['en', 'ar', 'bg', 'cs', 'da', 'de', 'de_informal', 'es', 'es_AR', 'fa', 'fr', 'he', 'hu', 'it', 'ja', 'ko', 'nl', 'nb', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl',  'ru', 'th', 'tr', 'uk', 'vi', 'zh_CN', 'zh_TW',],
 
     //  Application Fallback Locale
     'fallback_locale' => 'en',
@@ -117,6 +128,7 @@ return [
         BookStack\Providers\EventServiceProvider::class,
         BookStack\Providers\RouteServiceProvider::class,
         BookStack\Providers\CustomFacadeProvider::class,
+        BookStack\Providers\CustomValidationServiceProvider::class,
     ],
 
     /*
index 5535a6f9ce88b1c5ea6fd972d212f00337dfdb24..51b152ff184805e67e5addd1573a6e2258c51e9d 100644 (file)
 return [
 
     // Method of authentication to use
-    // Options: standard, ldap
+    // Options: standard, ldap, saml2
     'method' => env('AUTH_METHOD', 'standard'),
 
     // Authentication Defaults
     // This option controls the default authentication "guard" and password
     // reset options for your application.
     'defaults' => [
-        'guard' => 'web',
+        'guard' => env('AUTH_METHOD', 'standard'),
         'passwords' => 'users',
     ],
 
@@ -26,17 +26,22 @@ return [
     // All authentication drivers have a user provider. This defines how the
     // users are actually retrieved out of your database or other storage
     // mechanisms used by this application to persist your user's data.
-    // Supported: "session", "token"
+    // Supported drivers: "session", "api-token", "ldap-session"
     'guards' => [
-        'web' => [
+        'standard' => [
             'driver' => 'session',
             'provider' => 'users',
         ],
-
+        'ldap' => [
+            'driver' => 'ldap-session',
+            'provider' => 'external',
+        ],
+        'saml2' => [
+            'driver' => 'saml2-session',
+            'provider' => 'external',
+        ],
         'api' => [
-            'driver' => 'token',
-            'provider' => 'users',
-            'hash' => false,
+            'driver' => 'api-token',
         ],
     ],
 
@@ -44,17 +49,15 @@ return [
     // All authentication drivers have a user provider. This defines how the
     // users are actually retrieved out of your database or other storage
     // mechanisms used by this application to persist your user's data.
-    // Supported: database, eloquent, ldap
     'providers' => [
         'users' => [
-            'driver' => env('AUTH_METHOD', 'standard') === 'standard' ? 'eloquent' : env('AUTH_METHOD'),
+            'driver' => 'eloquent',
+            'model' => \BookStack\Auth\User::class,
+        ],
+        'external' => [
+            'driver' => 'external-users',
             'model' => \BookStack\Auth\User::class,
         ],
-
-        // 'users' => [
-        //     'driver' => 'database',
-        //     'table' => 'users',
-        // ],
     ],
 
     // Resetting Passwords
index bd7d28300abae17112857ead07d3d000c4fd823b..30a5c53691d3a514d852d9d7fbe8f697d7d9321d 100644 (file)
@@ -42,13 +42,6 @@ return [
             'root'   => storage_path(),
         ],
 
-        'ftp' => [
-            'driver'   => 'ftp',
-            'host'     => 'ftp.example.com',
-            'username' => 'your-username',
-            'password' => 'your-password',
-        ],
-
         's3' => [
             'driver' => 's3',
             'key'    => env('STORAGE_S3_KEY', 'your-key'),
@@ -59,16 +52,6 @@ return [
             'use_path_style_endpoint' => env('STORAGE_S3_ENDPOINT', null) !== null,
         ],
 
-        'rackspace' => [
-            'driver'    => 'rackspace',
-            'username'  => 'your-username',
-            'key'       => 'your-key',
-            'container' => 'your-container',
-            'endpoint'  => 'https://identity.api.rackspacecloud.com/v2.0/',
-            'region'    => 'IAD',
-            'url_type'  => 'publicURL',
-        ],
-
     ],
 
 ];
index 0b55dc24db921f55bb01460fe249560349736c4f..afd56e48256e492a069e1ea996942c6d73940e2e 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use Monolog\Formatter\LineFormatter;
+use Monolog\Handler\ErrorLogHandler;
 use Monolog\Handler\NullHandler;
 use Monolog\Handler\StreamHandler;
 
@@ -73,10 +75,38 @@ return [
             'level' => 'debug',
         ],
 
+        // Custom errorlog implementation that logs out a plain,
+        // non-formatted message intended for the webserver log.
+        'errorlog_plain_webserver' => [
+            'driver' => 'monolog',
+            'level' => 'debug',
+            'handler' => ErrorLogHandler::class,
+            'handler_with' => [4],
+            'formatter' => LineFormatter::class,
+            'formatter_with' => [
+                'format' => "%message%",
+            ],
+        ],
+
         'null' => [
             'driver' => 'monolog',
             'handler' => NullHandler::class,
         ],
+
+        // Testing channel
+        // Uses a shared testing instance during tests
+        // so that logs can be checked against.
+        'testing' => [
+            'driver' => 'testing',
+        ],
+    ],
+
+
+    // Failed Login Message
+    // Allows a configurable message to be logged when a login request fails.
+    'failed_login' => [
+        'message' => env('LOG_FAILED_LOGIN_MESSAGE', null),
+        'channel' => env('LOG_FAILED_LOGIN_CHANNEL', 'errorlog_plain_webserver'),
     ],
 
 ];
diff --git a/app/Config/saml2.php b/app/Config/saml2.php
new file mode 100644 (file)
index 0000000..d695abf
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+
+return [
+
+    // Display name, shown to users, for SAML2 option
+    'name' => env('SAML2_NAME', 'SSO'),
+
+    // Dump user details after a login request for debugging purposes
+    'dump_user_details' => env('SAML2_DUMP_USER_DETAILS', false),
+
+    // Attribute, within a SAML response, to find the user's email address
+    'email_attribute' => env('SAML2_EMAIL_ATTRIBUTE', 'email'),
+    // Attribute, within a SAML response, to find the user's display name
+    'display_name_attributes' => explode('|', env('SAML2_DISPLAY_NAME_ATTRIBUTES', 'username')),
+    // Attribute, within a SAML response, to use to connect a BookStack user to the SAML user.
+    'external_id_attribute' => env('SAML2_EXTERNAL_ID_ATTRIBUTE', null),
+
+    // Group sync options
+    // Enable syncing, upon login, of SAML2 groups to BookStack groups
+    'user_to_groups' => env('SAML2_USER_TO_GROUPS', false),
+    // Attribute, within a SAML response, to find group names on
+    'group_attribute' => env('SAML2_GROUP_ATTRIBUTE', 'group'),
+    // When syncing groups, remove any groups that no longer match. Otherwise sync only adds new groups.
+    'remove_from_groups' => env('SAML2_REMOVE_FROM_GROUPS', false),
+
+    // Autoload IDP details from the metadata endpoint
+    'autoload_from_metadata' => env('SAML2_AUTOLOAD_METADATA', false),
+
+    // Overrides, in JSON format, to the configuration passed to underlying onelogin library.
+    'onelogin_overrides' => env('SAML2_ONELOGIN_OVERRIDES', null),
+
+
+    'onelogin' => [
+        // If 'strict' is True, then the PHP Toolkit will reject unsigned
+        // or unencrypted messages if it expects them signed or encrypted
+        // Also will reject the messages if not strictly follow the SAML
+        // standard: Destination, NameId, Conditions ... are validated too.
+        'strict' => true,
+
+        // Enable debug mode (to print errors)
+        'debug' => env('APP_DEBUG', false),
+
+        // Set a BaseURL to be used instead of try to guess
+        // the BaseURL of the view that process the SAML Message.
+        // Ex. http://sp.example.com/
+        //     http://example.com/sp/
+        'baseurl' => null,
+
+        // Service Provider Data that we are deploying
+        'sp' => [
+            // Identifier of the SP entity  (must be a URI)
+            'entityId' => '',
+
+            // Specifies info about where and how the <AuthnResponse> message MUST be
+            // returned to the requester, in this case our SP.
+            'assertionConsumerService' => [
+                // URL Location where the <Response> from the IdP will be returned
+                'url' => '',
+                // SAML protocol binding to be used when returning the <Response>
+                // message.  Onelogin Toolkit supports for this endpoint the
+                // HTTP-POST binding only
+                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
+            ],
+
+            // Specifies info about where and how the <Logout Response> message MUST be
+            // returned to the requester, in this case our SP.
+            'singleLogoutService' => [
+                // URL Location where the <Response> from the IdP will be returned
+                'url' => '',
+                // SAML protocol binding to be used when returning the <Response>
+                // message.  Onelogin Toolkit supports for this endpoint the
+                // HTTP-Redirect binding only
+                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+            ],
+
+            // Specifies constraints on the name identifier to be used to
+            // represent the requested subject.
+            // Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported
+            'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
+            // Usually x509cert and privateKey of the SP are provided by files placed at
+            // the certs folder. But we can also provide them with the following parameters
+            'x509cert' => '',
+            'privateKey' => '',
+        ],
+        // Identity Provider Data that we want connect with our SP
+        'idp' => [
+            // Identifier of the IdP entity  (must be a URI)
+            'entityId' => env('SAML2_IDP_ENTITYID', null),
+            // SSO endpoint info of the IdP. (Authentication Request protocol)
+            'singleSignOnService' => [
+                // URL Target of the IdP where the SP will send the Authentication Request Message
+                'url' => env('SAML2_IDP_SSO', null),
+                // SAML protocol binding to be used when returning the <Response>
+                // message.  Onelogin Toolkit supports for this endpoint the
+                // HTTP-Redirect binding only
+                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+            ],
+            // SLO endpoint info of the IdP.
+            'singleLogoutService' => [
+                // URL Location of the IdP where the SP will send the SLO Request
+                'url' => env('SAML2_IDP_SLO', null),
+                // URL location of the IdP where the SP will send the SLO Response (ResponseLocation)
+                // if not set, url for the SLO Request will be used
+                'responseUrl' => null,
+                // SAML protocol binding to be used when returning the <Response>
+                // message.  Onelogin Toolkit supports for this endpoint the
+                // HTTP-Redirect binding only
+                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+            ],
+            // Public x509 certificate of the IdP
+            'x509cert' => env('SAML2_IDP_x509', null),
+            /*
+             *  Instead of use the whole x509cert you can use a fingerprint in
+             *  order to validate the SAMLResponse, but we don't recommend to use
+             *  that method on production since is exploitable by a collision
+             *  attack.
+             *  (openssl x509 -noout -fingerprint -in "idp.crt" to generate it,
+             *   or add for example the -sha256 , -sha384 or -sha512 parameter)
+             *
+             *  If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to
+             *  let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512
+             *  'sha1' is the default value.
+             */
+            // 'certFingerprint' => '',
+            // 'certFingerprintAlgorithm' => 'sha1',
+            /* In some scenarios the IdP uses different certificates for
+             * signing/encryption, or is under key rollover phase and more
+             * than one certificate is published on IdP metadata.
+             * In order to handle that the toolkit offers that parameter.
+             * (when used, 'x509cert' and 'certFingerprint' values are
+             * ignored).
+             */
+            // 'x509certMulti' => array(
+            //      'signing' => array(
+            //          0 => '<cert1-string>',
+            //      ),
+            //      'encryption' => array(
+            //          0 => '<cert2-string>',
+            //      )
+            // ),
+        ],
+    ],
+
+];
index 923015f6e62815e66b388a21f6e9d4b0b2282388..fcde621d2b5296bd9239085adc6409d7d39784a3 100644 (file)
@@ -118,11 +118,13 @@ return [
 
     'ldap' => [
         'server' => env('LDAP_SERVER', false),
+        'dump_user_details' => env('LDAP_DUMP_USER_DETAILS', false),
         'dn' => env('LDAP_DN', false),
         'pass' => env('LDAP_PASS', false),
         'base_dn' => env('LDAP_BASE_DN', false),
         'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))'),
         'version' => env('LDAP_VERSION', false),
+        'id_attribute' => env('LDAP_ID_ATTRIBUTE', 'uid'),
         'email_attribute' => env('LDAP_EMAIL_ATTRIBUTE', 'mail'),
         'display_name_attribute' => env('LDAP_DISPLAY_NAME_ATTRIBUTE', 'cn'),
         'follow_referrals' => env('LDAP_FOLLOW_REFERRALS', false),
@@ -130,6 +132,6 @@ return [
         'group_attribute' => env('LDAP_GROUP_ATTRIBUTE', 'memberOf'),
         'remove_from_groups' => env('LDAP_REMOVE_FROM_GROUPS', false),
         'tls_insecure' => env('LDAP_TLS_INSECURE', false),
-    ]
+    ],
 
 ];
index 37f1627bb5f5c151ae01748cdc06b8f5c7e7eeb2..571836bd2af2276a20732bfbe86459e83343551f 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use \Illuminate\Support\Str;
+
 /**
  * Session configuration options.
  *
@@ -69,7 +71,8 @@ return [
     // By setting this option to true, session cookies will only be sent back
     // to the server if the browser has a HTTPS connection. This will keep
     // the cookie from being sent to you if it can not be done securely.
-    'secure' => env('SESSION_SECURE_COOKIE', false),
+    'secure' => env('SESSION_SECURE_COOKIE', null)
+        ?? Str::startsWith(env('APP_URL'), 'https:'),
 
     // HTTP Access Only
     // Setting this value to true will prevent JavaScript from accessing the
@@ -80,6 +83,6 @@ return [
     // This option determines how your cookies behave when cross-site requests
     // take place, and can be used to mitigate CSRF attacks. By default, we
     // do not enable this as other CSRF protection services are in place.
-    // Options: lax, strict
-    'same_site' => null,
+    // Options: lax, strict, none
+    'same_site' => 'lax',
 ];
index c6080df1db6df81b434a935dde272c0f038c5c0a..d84c0c2641397700da7b551414122c66e88fa24b 100644 (file)
@@ -16,6 +16,11 @@ return [
     'app-editor'           => 'wysiwyg',
     'app-color'            => '#206ea7',
     'app-color-light'      => 'rgba(32,110,167,0.15)',
+    'bookshelf-color'      => '#a94747',
+    'book-color'           => '#077b70',
+    'chapter-color'        => '#af4d0d',
+    'page-color'           => '#206ea7',
+    'page-draft-color'     => '#7e50b1',
     'app-custom-head'      => false,
     'registration-enabled' => false,
 
index 60c26ffd531d2477b9c236c067388f274368bc9b..f347eda23349264d1b3d4118afadbedde0a5990a 100644 (file)
@@ -13,7 +13,9 @@ return [
         'enabled' => true,
         'binary'  => file_exists(base_path('wkhtmltopdf')) ? base_path('wkhtmltopdf') : env('WKHTMLTOPDF', false),
         'timeout' => false,
-        'options' => [],
+        'options' => [
+            'outline' => true
+        ],
         'env'     => [],
     ],
     'image' => [
index f2e2d9fbd4465a3544e96e54c4649fb54fe81e05..93ca367a2102739b7067ddac3dfd9416749b6514 100644 (file)
@@ -14,8 +14,8 @@ class CleanupImages extends Command
      * @var string
      */
     protected $signature = 'bookstack:cleanup-images
-                            {--a|all : Include images that are used in page revisions}
-                            {--f|force : Actually run the deletions}
+                            {--a|all : Also delete images that are only used in old revisions}
+                            {--f|force : Actually run the deletions, Defaults to a dry-run}
                             ';
 
     /**
index 15f1fcc0a7138f057d3894c727cb62774a0ef37b..681a7564b282e3e6d279c1ffa2b9ddfbce96488e 100644 (file)
@@ -2,7 +2,7 @@
 
 namespace BookStack\Console\Commands;
 
-use BookStack\Entities\PageRevision;
+use BookStack\Entities\Models\PageRevision;
 use Illuminate\Console\Command;
 
 class ClearRevisions extends Command
index 678c64d330179893880f707db7cd0f8a705bf297..35356210b66809c62687e8fbb1eaa437bc447106 100644 (file)
@@ -18,7 +18,7 @@ class ClearViews extends Command
      *
      * @var string
      */
-    protected $description = 'Clear all view-counts for all entities.';
+    protected $description = 'Clear all view-counts for all entities';
 
     /**
      * Create a new command instance.
diff --git a/app/Console/Commands/CopyShelfPermissions.php b/app/Console/Commands/CopyShelfPermissions.php
new file mode 100644 (file)
index 0000000..d220c59
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+
+namespace BookStack\Console\Commands;
+
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Repos\BookshelfRepo;
+use Illuminate\Console\Command;
+
+class CopyShelfPermissions extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'bookstack:copy-shelf-permissions
+                            {--a|all : Perform for all shelves in the system}
+                            {--s|slug= : The slug for a shelf to target}
+                            ';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Copy shelf permissions to all child books';
+
+    /**
+     * @var BookshelfRepo
+     */
+    protected $bookshelfRepo;
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct(BookshelfRepo $repo)
+    {
+        $this->bookshelfRepo = $repo;
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $shelfSlug = $this->option('slug');
+        $cascadeAll = $this->option('all');
+        $shelves = null;
+
+        if (!$cascadeAll && !$shelfSlug) {
+            $this->error('Either a --slug or --all option must be provided.');
+            return;
+        }
+
+        if ($cascadeAll) {
+            $continue = $this->confirm(
+                'Permission settings for all shelves will be cascaded. '.
+                        'Books assigned to multiple shelves will receive only the permissions of it\'s last processed shelf. '.
+                        'Are you sure you want to proceed?'
+            );
+
+            if (!$continue && !$this->hasOption('no-interaction')) {
+                return;
+            }
+
+            $shelves = Bookshelf::query()->get(['id', 'restricted']);
+        }
+
+        if ($shelfSlug) {
+            $shelves = Bookshelf::query()->where('slug', '=', $shelfSlug)->get(['id', 'restricted']);
+            if ($shelves->count() === 0) {
+                $this->info('No shelves found with the given slug.');
+            }
+        }
+
+        foreach ($shelves as $shelf) {
+            $this->bookshelfRepo->copyDownPermissions($shelf, false);
+            $this->info('Copied permissions for shelf [' . $shelf->id . ']');
+        }
+
+        $this->info('Permissions copied for ' . $shelves->count() . ' shelves.');
+    }
+}
index e67da871763f8b9ef379c8687961ec2612d33bc3..3d1a3dca08db4045e528bfcab92c13984cbdeff9 100644 (file)
@@ -28,8 +28,6 @@ class CreateAdmin extends Command
 
     /**
      * Create a new command instance.
-     *
-     * @param UserRepo $userRepo
      */
     public function __construct(UserRepo $userRepo)
     {
index 68c5bb738178f6b6fdfdbfeb0dec2d64dc5371f7..c73c883de2d2bd75535f817e5c10e03eff3e9ff4 100644 (file)
@@ -25,7 +25,7 @@ class DeleteUsers extends Command
      *
      * @var string
      */
-    protected $description = 'Delete users that are not "admin" or system users.';
+    protected $description = 'Delete users that are not "admin" or system users';
 
     public function __construct(User $user, UserRepo $userRepo)
     {
diff --git a/app/Console/Commands/RegenerateCommentContent.php b/app/Console/Commands/RegenerateCommentContent.php
new file mode 100644 (file)
index 0000000..587a5ed
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+namespace BookStack\Console\Commands;
+
+use BookStack\Actions\Comment;
+use BookStack\Actions\CommentRepo;
+use Illuminate\Console\Command;
+
+class RegenerateCommentContent extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'bookstack:regenerate-comment-content {--database= : The database connection to use.}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Regenerate the stored HTML of all comments';
+
+    /**
+     * @var CommentRepo
+     */
+    protected $commentRepo;
+
+    /**
+     * Create a new command instance.
+     */
+    public function __construct(CommentRepo $commentRepo)
+    {
+        $this->commentRepo = $commentRepo;
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $connection = \DB::getDefaultConnection();
+        if ($this->option('database') !== null) {
+            \DB::setDefaultConnection($this->option('database'));
+        }
+
+        Comment::query()->chunk(100, function ($comments) {
+            foreach ($comments as $comment) {
+                $comment->html = $this->commentRepo->commentToHtml($comment->text);
+                $comment->save();
+            }
+        });
+
+        \DB::setDefaultConnection($connection);
+        $this->comment('Comment HTML content has been regenerated');
+    }
+}
index 430b8fcb05fdad572a4f587548d271a230ea8394..4fde08e6b60e515004bdc0f5aea741a7fd44b91e 100644 (file)
@@ -30,8 +30,6 @@ class RegeneratePermissions extends Command
 
     /**
      * Create a new command instance.
-     *
-     * @param \BookStack\Auth\\BookStack\Auth\Permissions\PermissionService $permissionService
      */
     public function __construct(PermissionService $permissionService)
     {
index d27d73edc01dcbf55443f9bbf8f916539d0c5295..3dc3ec0af0e98b33bd3f1d741540dd13e1d319c5 100644 (file)
@@ -2,7 +2,8 @@
 
 namespace BookStack\Console\Commands;
 
-use BookStack\Entities\SearchService;
+use BookStack\Entities\Tools\SearchIndex;
+use DB;
 use Illuminate\Console\Command;
 
 class RegenerateSearch extends Command
@@ -21,17 +22,15 @@ class RegenerateSearch extends Command
      */
     protected $description = 'Re-index all content for searching';
 
-    protected $searchService;
+    protected $searchIndex;
 
     /**
      * Create a new command instance.
-     *
-     * @param \BookStack\Entities\SearchService $searchService
      */
-    public function __construct(SearchService $searchService)
+    public function __construct(SearchIndex $searchIndex)
     {
         parent::__construct();
-        $this->searchService = $searchService;
+        $this->searchIndex = $searchIndex;
     }
 
     /**
@@ -41,14 +40,13 @@ class RegenerateSearch extends Command
      */
     public function handle()
     {
-        $connection = \DB::getDefaultConnection();
+        $connection = DB::getDefaultConnection();
         if ($this->option('database') !== null) {
-            \DB::setDefaultConnection($this->option('database'));
-            $this->searchService->setConnection(\DB::connection($this->option('database')));
+            DB::setDefaultConnection($this->option('database'));
         }
 
-        $this->searchService->indexAllEntities();
-        \DB::setDefaultConnection($connection);
+        $this->searchIndex->indexAllEntities();
+        DB::setDefaultConnection($connection);
         $this->comment('Search index regenerated');
     }
 }
diff --git a/app/Console/Commands/UpdateUrl.php b/app/Console/Commands/UpdateUrl.php
new file mode 100644 (file)
index 0000000..b95e277
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+
+namespace BookStack\Console\Commands;
+
+use Illuminate\Console\Command;
+use Illuminate\Database\Connection;
+
+class UpdateUrl extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'bookstack:update-url
+                            {oldUrl : URL to replace}
+                            {newUrl : URL to use as the replacement}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Find and replace the given URLs in your BookStack database';
+
+    protected $db;
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct(Connection $db)
+    {
+        $this->db = $db;
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $oldUrl = str_replace("'", '', $this->argument('oldUrl'));
+        $newUrl = str_replace("'", '', $this->argument('newUrl'));
+
+        $urlPattern = '/https?:\/\/(.+)/';
+        if (!preg_match($urlPattern, $oldUrl) || !preg_match($urlPattern, $newUrl)) {
+            $this->error("The given urls are expected to be full urls starting with http:// or https://");
+            return 1;
+        }
+
+        if (!$this->checkUserOkayToProceed($oldUrl, $newUrl)) {
+            return 1;
+        }
+
+        $columnsToUpdateByTable = [
+            "attachments" => ["path"],
+            "pages" => ["html", "text", "markdown"],
+            "images" => ["url"],
+            "comments" => ["html", "text"],
+        ];
+
+        foreach ($columnsToUpdateByTable as $table => $columns) {
+            foreach ($columns as $column) {
+                $changeCount = $this->db->table($table)->update([
+                    $column => $this->db->raw("REPLACE({$column}, '{$oldUrl}', '{$newUrl}')")
+                ]);
+                $this->info("Updated {$changeCount} rows in {$table}->{$column}");
+            }
+        }
+
+        $this->info("URL update procedure complete.");
+        return 0;
+    }
+
+    /**
+     * Warn the user of the dangers of this operation.
+     * Returns a boolean indicating if they've accepted the warnings.
+     */
+    protected function checkUserOkayToProceed(string $oldUrl, string $newUrl): bool
+    {
+        $dangerWarning = "This will search for \"{$oldUrl}\" in your database and replace it with  \"{$newUrl}\".\n";
+        $dangerWarning .= "Are you sure you want to proceed?";
+        $backupConfirmation = "This operation could cause issues if used incorrectly. Have you made a backup of your existing database?";
+
+        return $this->confirm($dangerWarning) && $this->confirm($backupConfirmation);
+    }
+}
index 43d63d026021dd07edcb1165841018ee213b13a3..cf7cf296c94fdbd8cdcb3b7dffd59767cb8000ad 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Entities;
 
-use BookStack\Entities\Managers\EntityContext;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\ShelfContext;
 use Illuminate\View\View;
 
 class BreadcrumbsViewComposer
@@ -10,9 +11,9 @@ class BreadcrumbsViewComposer
 
     /**
      * BreadcrumbsViewComposer constructor.
-     * @param EntityContext $entityContextManager
+     * @param ShelfContext $entityContextManager
      */
-    public function __construct(EntityContext $entityContextManager)
+    public function __construct(ShelfContext $entityContextManager)
     {
         $this->entityContextManager = $entityContextManager;
     }
diff --git a/app/Entities/Chapter.php b/app/Entities/Chapter.php
deleted file mode 100644 (file)
index 848bc64..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php namespace BookStack\Entities;
-
-use Illuminate\Support\Collection;
-
-/**
- * Class Chapter
- * @property Collection<Page> $pages
- * @package BookStack\Entities
- */
-class Chapter extends BookChild
-{
-    public $searchFactor = 1.3;
-
-    protected $fillable = ['name', 'description', 'priority', 'book_id'];
-
-    /**
-     * Get the pages that this chapter contains.
-     * @param string $dir
-     * @return mixed
-     */
-    public function pages($dir = 'ASC')
-    {
-        return $this->hasMany(Page::class)->orderBy('priority', $dir);
-    }
-
-    /**
-     * Get the url of this chapter.
-     * @param string|bool $path
-     * @return string
-     */
-    public function getUrl($path = false)
-    {
-        $bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug;
-        $fullPath = '/books/' . urlencode($bookSlug) . '/chapter/' . urlencode($this->slug);
-
-        if ($path !== false) {
-            $fullPath .= '/' . trim($path, '/');
-        }
-
-        return url($fullPath);
-    }
-
-    /**
-     * Get an excerpt of this chapter's description to the specified length or less.
-     * @param int $length
-     * @return string
-     */
-    public function getExcerpt(int $length = 100)
-    {
-        $description = $this->text ?? $this->description;
-        return mb_strlen($description) > $length ? mb_substr($description, 0, $length-3) . '...' : $description;
-    }
-
-    /**
-     * Check if this chapter has any child pages.
-     * @return bool
-     */
-    public function hasChildren()
-    {
-        return count($this->pages) > 0;
-    }
-
-    /**
-     * Get the visible pages in this chapter.
-     */
-    public function getVisiblePages(): Collection
-    {
-        return $this->pages()->visible()
-        ->orderBy('draft', 'desc')
-        ->orderBy('priority', 'asc')
-        ->get();
-    }
-}
index 6bf923b3112aa8e7387fd6eedeb601a601511dad..c77a57d61a5710edf652082839492a394c21a341 100644 (file)
@@ -1,13 +1,18 @@
 <?php namespace BookStack\Entities;
 
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Models\PageRevision;
+
 /**
  * Class EntityProvider
  *
  * Provides access to the core entity models.
  * Wrapped up in this provider since they are often used together
  * so this is a neater alternative to injecting all in individually.
- *
- * @package BookStack\Entities
  */
 class EntityProvider
 {
@@ -37,26 +42,20 @@ class EntityProvider
      */
     public $pageRevision;
 
-    /**
-     * EntityProvider constructor.
-     */
-    public function __construct(
-        Bookshelf $bookshelf,
-        Book $book,
-        Chapter $chapter,
-        Page $page,
-        PageRevision $pageRevision
-    ) {
-        $this->bookshelf = $bookshelf;
-        $this->book = $book;
-        $this->chapter = $chapter;
-        $this->page = $page;
-        $this->pageRevision = $pageRevision;
+
+    public function __construct()
+    {
+        $this->bookshelf = new Bookshelf();
+        $this->book = new Book();
+        $this->chapter = new Chapter();
+        $this->page = new Page();
+        $this->pageRevision = new PageRevision();
     }
 
     /**
      * Fetch all core entity types as an associated array
      * with their basic names as the keys.
+     * @return array<Entity>
      */
     public function all(): array
     {
diff --git a/app/Entities/Managers/TrashCan.php b/app/Entities/Managers/TrashCan.php
deleted file mode 100644 (file)
index 1a32294..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-<?php namespace BookStack\Entities\Managers;
-
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
-use BookStack\Entities\HasCoverImage;
-use BookStack\Entities\Page;
-use BookStack\Exceptions\NotifyException;
-use BookStack\Facades\Activity;
-use BookStack\Uploads\AttachmentService;
-use BookStack\Uploads\ImageService;
-use Exception;
-use Illuminate\Contracts\Container\BindingResolutionException;
-
-class TrashCan
-{
-
-    /**
-     * Remove a bookshelf from the system.
-     * @throws Exception
-     */
-    public function destroyShelf(Bookshelf $shelf)
-    {
-        $this->destroyCommonRelations($shelf);
-        $shelf->delete();
-    }
-
-    /**
-     * Remove a book from the system.
-     * @throws NotifyException
-     * @throws BindingResolutionException
-     */
-    public function destroyBook(Book $book)
-    {
-        foreach ($book->pages as $page) {
-            $this->destroyPage($page);
-        }
-
-        foreach ($book->chapters as $chapter) {
-            $this->destroyChapter($chapter);
-        }
-
-        $this->destroyCommonRelations($book);
-        $book->delete();
-    }
-
-    /**
-     * Remove a page from the system.
-     * @throws NotifyException
-     */
-    public function destroyPage(Page $page)
-    {
-        // Check if set as custom homepage & remove setting if not used or throw error if active
-        $customHome = setting('app-homepage', '0:');
-        if (intval($page->id) === intval(explode(':', $customHome)[0])) {
-            if (setting('app-homepage-type') === 'page') {
-                throw new NotifyException(trans('errors.page_custom_home_deletion'), $page->getUrl());
-            }
-            setting()->remove('app-homepage');
-        }
-
-        $this->destroyCommonRelations($page);
-
-        // Delete Attached Files
-        $attachmentService = app(AttachmentService::class);
-        foreach ($page->attachments as $attachment) {
-            $attachmentService->deleteFile($attachment);
-        }
-
-        $page->delete();
-    }
-
-    /**
-     * Remove a chapter from the system.
-     * @throws Exception
-     */
-    public function destroyChapter(Chapter $chapter)
-    {
-        if (count($chapter->pages) > 0) {
-            foreach ($chapter->pages as $page) {
-                $page->chapter_id = 0;
-                $page->save();
-            }
-        }
-
-        $this->destroyCommonRelations($chapter);
-        $chapter->delete();
-    }
-
-    /**
-     * Update entity relations to remove or update outstanding connections.
-     */
-    protected function destroyCommonRelations(Entity $entity)
-    {
-        Activity::removeEntity($entity);
-        $entity->views()->delete();
-        $entity->permissions()->delete();
-        $entity->tags()->delete();
-        $entity->comments()->delete();
-        $entity->jointPermissions()->delete();
-        $entity->searchTerms()->delete();
-
-        if ($entity instanceof HasCoverImage && $entity->cover) {
-            $imageService = app()->make(ImageService::class);
-            $imageService->destroy($entity->cover);
-        }
-    }
-}
similarity index 75%
rename from app/Entities/Book.php
rename to app/Entities/Models/Book.php
index 4e54457b80391aa223442d92e065f376a12b86a9..6c56767655c894b22c548e7ff552a07f36940ea6 100644 (file)
@@ -1,4 +1,4 @@
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
 
 use BookStack\Uploads\Image;
 use Exception;
@@ -12,25 +12,20 @@ use Illuminate\Support\Collection;
  * @property string $description
  * @property int $image_id
  * @property Image|null $cover
- * @package BookStack\Entities
  */
 class Book extends Entity implements HasCoverImage
 {
     public $searchFactor = 2;
 
-    protected $fillable = ['name', 'description', 'image_id'];
+    protected $fillable = ['name', 'description'];
+    protected $hidden = ['restricted', 'pivot', 'image_id', 'deleted_at'];
 
     /**
      * Get the url for this book.
-     * @param string|bool $path
-     * @return string
      */
-    public function getUrl($path = false)
+    public function getUrl(string $path = ''): string
     {
-        if ($path !== false) {
-            return url('/books/' . urlencode($this->slug) . '/' . trim($path, '/'));
-        }
-        return url('/books/' . urlencode($this->slug));
+        return url('/books/' . implode('/', [urlencode($this->slug), trim($path, '/')]));
     }
 
     /**
@@ -114,17 +109,6 @@ class Book extends Entity implements HasCoverImage
     {
         $pages = $this->directPages()->visible()->get();
         $chapters = $this->chapters()->visible()->get();
-        return $pages->contact($chapters)->sortBy('priority')->sortByDesc('draft');
-    }
-
-    /**
-     * Get an excerpt of this book's description to the specified length or less.
-     * @param int $length
-     * @return string
-     */
-    public function getExcerpt(int $length = 100)
-    {
-        $description = $this->description;
-        return mb_strlen($description) > $length ? mb_substr($description, 0, $length-3) . '...' : $description;
+        return $pages->concat($chapters)->sortBy('priority')->sortByDesc('draft');
     }
 }
similarity index 83%
rename from app/Entities/BookChild.php
rename to app/Entities/Models/BookChild.php
index 6eac4375ddce6c271a06669a8eaa108b774d55e2..8b968cc8b8d8268ae755bbc79ad42bd24419031e 100644 (file)
@@ -1,5 +1,8 @@
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
 
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Book;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
@@ -10,7 +13,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
  * @property Book $book
  * @method Builder whereSlugs(string $bookSlug, string $childSlug)
  */
-class BookChild extends Entity
+abstract class BookChild extends Entity
 {
 
     /**
@@ -28,11 +31,10 @@ class BookChild extends Entity
 
     /**
      * Get the book this page sits in.
-     * @return BelongsTo
      */
     public function book(): BelongsTo
     {
-        return $this->belongsTo(Book::class);
+        return $this->belongsTo(Book::class)->withTrashed();
     }
 
     /**
@@ -45,9 +47,6 @@ class BookChild extends Entity
         $this->save();
         $this->refresh();
 
-        // Update related activity
-        $this->activity()->update(['book_id' => $newBookId]);
-
         // Update all child pages if a chapter
         if ($this instanceof Chapter) {
             foreach ($this->pages as $page) {
similarity index 79%
rename from app/Entities/Bookshelf.php
rename to app/Entities/Models/Bookshelf.php
index 62c7e2fe4f56c3e0a78f91105d6ba3c0da6dfad9..8ffd06d2e2f1b9d22dee6cabb23318976c85ddac 100644 (file)
@@ -1,4 +1,4 @@
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
 
 use BookStack\Uploads\Image;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -12,6 +12,8 @@ class Bookshelf extends Entity implements HasCoverImage
 
     protected $fillable = ['name', 'description', 'image_id'];
 
+    protected $hidden = ['restricted', 'image_id', 'deleted_at'];
+
     /**
      * Get the books in this shelf.
      * Should not be used directly since does not take into account permissions.
@@ -34,15 +36,10 @@ class Bookshelf extends Entity implements HasCoverImage
 
     /**
      * Get the url for this bookshelf.
-     * @param string|bool $path
-     * @return string
      */
-    public function getUrl($path = false)
+    public function getUrl(string $path = ''): string
     {
-        if ($path !== false) {
-            return url('/shelves/' . urlencode($this->slug) . '/' . trim($path, '/'));
-        }
-        return url('/shelves/' . urlencode($this->slug));
+        return url('/shelves/' . implode('/', [urlencode($this->slug), trim($path, '/')]));
     }
 
     /**
@@ -83,17 +80,6 @@ class Bookshelf extends Entity implements HasCoverImage
         return 'cover_shelf';
     }
 
-    /**
-     * Get an excerpt of this book's description to the specified length or less.
-     * @param int $length
-     * @return string
-     */
-    public function getExcerpt(int $length = 100)
-    {
-        $description = $this->description;
-        return mb_strlen($description) > $length ? mb_substr($description, 0, $length-3) . '...' : $description;
-    }
-
     /**
      * Check if this shelf contains the given book.
      * @param Book $book
diff --git a/app/Entities/Models/Chapter.php b/app/Entities/Models/Chapter.php
new file mode 100644 (file)
index 0000000..257b19e
--- /dev/null
@@ -0,0 +1,52 @@
+<?php namespace BookStack\Entities\Models;
+
+use Illuminate\Support\Collection;
+
+/**
+ * Class Chapter
+ * @property Collection<Page> $pages
+ */
+class Chapter extends BookChild
+{
+    public $searchFactor = 1.3;
+
+    protected $fillable = ['name', 'description', 'priority', 'book_id'];
+    protected $hidden = ['restricted', 'pivot', 'deleted_at'];
+
+    /**
+     * Get the pages that this chapter contains.
+     * @param string $dir
+     * @return mixed
+     */
+    public function pages($dir = 'ASC')
+    {
+        return $this->hasMany(Page::class)->orderBy('priority', $dir);
+    }
+
+    /**
+     * Get the url of this chapter.
+     */
+    public function getUrl($path = ''): string
+    {
+        $parts = [
+            'books',
+            urlencode($this->getAttribute('bookSlug') ?? $this->book->slug),
+            'chapter',
+            urlencode($this->slug),
+            trim($path, '/'),
+        ];
+
+        return url('/' . implode('/', $parts));
+    }
+
+    /**
+     * Get the visible pages in this chapter.
+     */
+    public function getVisiblePages(): Collection
+    {
+        return $this->pages()->visible()
+        ->orderBy('draft', 'desc')
+        ->orderBy('priority', 'asc')
+        ->get();
+    }
+}
diff --git a/app/Entities/Models/Deletion.php b/app/Entities/Models/Deletion.php
new file mode 100644 (file)
index 0000000..1be0ba4
--- /dev/null
@@ -0,0 +1,48 @@
+<?php namespace BookStack\Entities\Models;
+
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Entity;
+use BookStack\Interfaces\Loggable;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
+
+class Deletion extends Model implements Loggable
+{
+
+    /**
+     * Get the related deletable record.
+     */
+    public function deletable(): MorphTo
+    {
+        return $this->morphTo('deletable')->withTrashed();
+    }
+
+    /**
+     * The the user that performed the deletion.
+     */
+    public function deleter(): BelongsTo
+    {
+        return $this->belongsTo(User::class, 'deleted_by');
+    }
+
+    /**
+     * Create a new deletion record for the provided entity.
+     */
+    public static function createForEntity(Entity $entity): Deletion
+    {
+        $record = (new self())->forceFill([
+            'deleted_by' => user()->id,
+            'deletable_type' => $entity->getMorphClass(),
+            'deletable_id' => $entity->id,
+        ]);
+        $record->save();
+        return $record;
+    }
+
+    public function logDescriptor(): string
+    {
+        $deletable = $this->deletable()->first();
+        return "Deletion ({$this->id}) for {$deletable->getType()} ({$deletable->id}) {$deletable->name}";
+    }
+}
similarity index 69%
rename from app/Entities/Entity.php
rename to app/Entities/Models/Entity.php
index 5013c39cfcf2bf309f1931594ebe702dd00dc44c..c6b2468b0814afd124d1c64a4e0f8b116b9ec8d5 100644 (file)
@@ -1,4 +1,4 @@
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
 
 use BookStack\Actions\Activity;
 use BookStack\Actions\Comment;
@@ -6,12 +6,17 @@ use BookStack\Actions\Tag;
 use BookStack\Actions\View;
 use BookStack\Auth\Permissions\EntityPermission;
 use BookStack\Auth\Permissions\JointPermission;
+use BookStack\Entities\Tools\SearchIndex;
+use BookStack\Entities\Tools\SlugGenerator;
 use BookStack\Facades\Permissions;
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
+use BookStack\Traits\HasOwner;
 use Carbon\Carbon;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Collection;
 use Illuminate\Database\Eloquent\Relations\MorphMany;
+use Illuminate\Database\Eloquent\SoftDeletes;
 
 /**
  * Class Entity
@@ -31,11 +36,12 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
  * @method static Entity|Builder hasPermission(string $permission)
  * @method static Builder withLastView()
  * @method static Builder withViewCount()
- *
- * @package BookStack\Entities
  */
-class Entity extends Ownable
+abstract class Entity extends Model
 {
+    use SoftDeletes;
+    use HasCreatorAndUpdater;
+    use HasOwner;
 
     /**
      * @var string - Name of property where the main text content is found
@@ -50,7 +56,7 @@ class Entity extends Ownable
     /**
      * Get the entities that are visible to the current user.
      */
-    public function scopeVisible(Builder $query)
+    public function scopeVisible(Builder $query): Builder
     {
         return $this->scopeHasPermission($query, 'view');
     }
@@ -92,24 +98,18 @@ class Entity extends Ownable
     /**
      * Compares this entity to another given entity.
      * Matches by comparing class and id.
-     * @param $entity
-     * @return bool
      */
-    public function matches($entity)
+    public function matches(Entity $entity): bool
     {
         return [get_class($this), $this->id] === [get_class($entity), $entity->id];
     }
 
     /**
-     * Checks if an entity matches or contains another given entity.
-     * @param Entity $entity
-     * @return bool
+     * Checks if the current entity matches or contains the given.
      */
-    public function matchesOrContains(Entity $entity)
+    public function matchesOrContains(Entity $entity): bool
     {
-        $matches = [get_class($this), $this->id] === [get_class($entity), $entity->id];
-
-        if ($matches) {
+        if ($this->matches($entity)) {
             return true;
         }
 
@@ -126,9 +126,8 @@ class Entity extends Ownable
 
     /**
      * Gets the activity objects for this entity.
-     * @return MorphMany
      */
-    public function activity()
+    public function activity(): MorphMany
     {
         return $this->morphMany(Activity::class, 'entity')
             ->orderBy('created_at', 'desc');
@@ -137,26 +136,23 @@ class Entity extends Ownable
     /**
      * Get View objects for this entity.
      */
-    public function views()
+    public function views(): MorphMany
     {
         return $this->morphMany(View::class, 'viewable');
     }
 
     /**
      * Get the Tag models that have been user assigned to this entity.
-     * @return MorphMany
      */
-    public function tags()
+    public function tags(): MorphMany
     {
         return $this->morphMany(Tag::class, 'entity')->orderBy('order', 'asc');
     }
 
     /**
      * Get the comments for an entity
-     * @param bool $orderByCreated
-     * @return MorphMany
      */
-    public function comments($orderByCreated = true)
+    public function comments(bool $orderByCreated = true): MorphMany
     {
         $query = $this->morphMany(Comment::class, 'entity');
         return $orderByCreated ? $query->orderBy('created_at', 'asc') : $query;
@@ -164,9 +160,8 @@ class Entity extends Ownable
 
     /**
      * Get the related search terms.
-     * @return MorphMany
      */
-    public function searchTerms()
+    public function searchTerms(): MorphMany
     {
         return $this->morphMany(SearchTerm::class, 'entity');
     }
@@ -174,18 +169,15 @@ class Entity extends Ownable
     /**
      * Get this entities restrictions.
      */
-    public function permissions()
+    public function permissions(): MorphMany
     {
         return $this->morphMany(EntityPermission::class, 'restrictable');
     }
 
     /**
      * Check if this entity has a specific restriction set against it.
-     * @param $role_id
-     * @param $action
-     * @return bool
      */
-    public function hasRestriction($role_id, $action)
+    public function hasRestriction(int $role_id, string $action): bool
     {
         return $this->permissions()->where('role_id', '=', $role_id)
             ->where('action', '=', $action)->count() > 0;
@@ -193,55 +185,42 @@ class Entity extends Ownable
 
     /**
      * Get the entity jointPermissions this is connected to.
-     * @return MorphMany
      */
-    public function jointPermissions()
+    public function jointPermissions(): MorphMany
     {
         return $this->morphMany(JointPermission::class, 'entity');
     }
 
     /**
-     * Allows checking of the exact class, Used to check entity type.
-     * Cleaner method for is_a.
-     * @param $type
-     * @return bool
+     * Get the related delete records for this entity.
      */
-    public static function isA($type)
+    public function deletions(): MorphMany
     {
-        return static::getType() === strtolower($type);
+        return $this->morphMany(Deletion::class, 'deletable');
     }
 
     /**
-     * Get entity type.
-     * @return mixed
+     * Check if this instance or class is a certain type of entity.
+     * Examples of $type are 'page', 'book', 'chapter'
      */
-    public static function getType()
+    public static function isA(string $type): bool
     {
-        return strtolower(static::getClassName());
+        return static::getType() === strtolower($type);
     }
 
     /**
-     * Get an instance of an entity of the given type.
-     * @param $type
-     * @return Entity
+     * Get the entity type as a simple lowercase word.
      */
-    public static function getEntityInstance($type)
+    public static function getType(): string
     {
-        $types = ['Page', 'Book', 'Chapter', 'Bookshelf'];
-        $className = str_replace([' ', '-', '_'], '', ucwords($type));
-        if (!in_array($className, $types)) {
-            return null;
-        }
-
-        return app('BookStack\\Entities\\' . $className);
+        $className = array_slice(explode('\\', static::class), -1, 1)[0];
+        return strtolower($className);
     }
 
     /**
      * Gets a limited-length version of the entities name.
-     * @param int $length
-     * @return string
      */
-    public function getShortName($length = 25)
+    public function getShortName(int $length = 25): string
     {
         if (mb_strlen($this->name) <= $length) {
             return $this->name;
@@ -251,35 +230,45 @@ class Entity extends Ownable
 
     /**
      * Get the body text of this entity.
-     * @return mixed
      */
-    public function getText()
+    public function getText(): string
     {
-        return $this->{$this->textField};
+        return $this->{$this->textField} ?? '';
     }
 
     /**
      * Get an excerpt of this entity's descriptive content to the specified length.
-     * @param int $length
-     * @return mixed
      */
-    public function getExcerpt(int $length = 100)
+    public function getExcerpt(int $length = 100): string
     {
         $text = $this->getText();
+
         if (mb_strlen($text) > $length) {
             $text = mb_substr($text, 0, $length-3) . '...';
         }
+
         return trim($text);
     }
 
     /**
      * Get the url of this entity
-     * @param $path
-     * @return string
      */
-    public function getUrl($path = '/')
+    abstract public function getUrl(string $path = '/'): string;
+
+    /**
+     * Get the parent entity if existing.
+     * This is the "static" parent and does not include dynamic
+     * relations such as shelves to books.
+     */
+    public function getParent(): ?Entity
     {
-        return $path;
+        if ($this->isA('page')) {
+            return $this->chapter_id ? $this->chapter()->withTrashed()->first() : $this->book()->withTrashed()->first();
+        }
+        if ($this->isA('chapter')) {
+            return $this->book()->withTrashed()->first();
+        }
+        return null;
     }
 
     /**
@@ -288,7 +277,7 @@ class Entity extends Ownable
     public function rebuildPermissions()
     {
         /** @noinspection PhpUnhandledExceptionInspection */
-        Permissions::buildJointPermissionsForEntity($this);
+        Permissions::buildJointPermissionsForEntity(clone $this);
     }
 
     /**
@@ -296,8 +285,7 @@ class Entity extends Ownable
      */
     public function indexForSearch()
     {
-        $searchService = app()->make(SearchService::class);
-        $searchService->indexEntity($this);
+        app(SearchIndex::class)->indexEntity(clone $this);
     }
 
     /**
@@ -305,8 +293,7 @@ class Entity extends Ownable
      */
     public function refreshSlug(): string
     {
-        $generator = new SlugGenerator($this);
-        $this->slug = $generator->generate();
+        $this->slug = (new SlugGenerator)->generate($this);
         return $this->slug;
     }
 }
similarity index 90%
rename from app/Entities/HasCoverImage.php
rename to app/Entities/Models/HasCoverImage.php
index 31277f4b69c59bb659c2842277015ffbccca843c..f3a486d1877f32a2ef3fcf9f2145868d94337005 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 
-namespace BookStack\Entities;
+namespace BookStack\Entities\Models;
 
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
similarity index 66%
rename from app/Entities/Page.php
rename to app/Entities/Models/Page.php
index 76dc628fbf3f59e0bbbcf98897e715b743638213..739927aff6dad8561672a5687d504d18848079ee 100644 (file)
@@ -1,5 +1,6 @@
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
 
+use BookStack\Entities\Tools\PageContent;
 use BookStack\Uploads\Attachment;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Collection;
@@ -21,16 +22,23 @@ use Permissions;
  */
 class Page extends BookChild
 {
-    protected $fillable = ['name', 'html', 'priority', 'markdown'];
+    protected $fillable = ['name', 'priority', 'markdown'];
 
     protected $simpleAttributes = ['name', 'id', 'slug'];
 
     public $textField = 'text';
 
+    protected $hidden = ['html', 'markdown', 'text', 'restricted', 'pivot', 'deleted_at'];
+
+    protected $casts = [
+        'draft' => 'boolean',
+        'template' => 'boolean',
+    ];
+
     /**
      * Get the entities that are visible to the current user.
      */
-    public function scopeVisible(Builder $query)
+    public function scopeVisible(Builder $query): Builder
     {
         $query = Permissions::enforceDraftVisiblityOnQuery($query);
         return parent::scopeVisible($query);
@@ -47,14 +55,6 @@ class Page extends BookChild
         return $array;
     }
 
-    /**
-     * Get the parent item
-     */
-    public function parent(): Entity
-    {
-        return $this->chapter_id ? $this->chapter : $this->book;
-    }
-
     /**
      * Get the chapter that this page is in, If applicable.
      * @return BelongsTo
@@ -92,22 +92,19 @@ class Page extends BookChild
     }
 
     /**
-     * Get the url for this page.
-     * @param string|bool $path
-     * @return string
+     * Get the url of this page.
      */
-    public function getUrl($path = false)
+    public function getUrl($path = ''): string
     {
-        $bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug;
-        $midText = $this->draft ? '/draft/' : '/page/';
-        $idComponent = $this->draft ? $this->id : urlencode($this->slug);
+        $parts = [
+            'books',
+            urlencode($this->getAttribute('bookSlug') ?? $this->book->slug),
+            $this->draft ? 'draft' : 'page',
+            $this->draft ? $this->id : urlencode($this->slug),
+            trim($path, '/'),
+        ];
 
-        $url = '/books/' . urlencode($bookSlug) . $midText . $idComponent;
-        if ($path !== false) {
-            $url .= '/' . trim($path, '/');
-        }
-
-        return url($url);
+        return url('/' . implode('/', $parts));
     }
 
     /**
@@ -118,4 +115,15 @@ class Page extends BookChild
     {
         return $this->revisions()->first();
     }
+
+    /**
+     * Get this page for JSON display.
+     */
+    public function forJsonDisplay(): Page
+    {
+        $refreshed = $this->refresh()->unsetRelations()->load(['tags', 'createdBy', 'updatedBy', 'ownedBy']);
+        $refreshed->setHidden(array_diff($refreshed->getHidden(), ['html', 'markdown']));
+        $refreshed->html = (new PageContent($refreshed))->render();
+        return $refreshed;
+    }
 }
similarity index 96%
rename from app/Entities/PageRevision.php
rename to app/Entities/Models/PageRevision.php
index 13dc713ba43be37453ca525f06e91f143793476e..76a3b15ffd44ea64e176eb1020773dc0ca41ec70 100644 (file)
@@ -1,6 +1,7 @@
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
 
 use BookStack\Auth\User;
+use BookStack\Entities\Models\Page;
 use BookStack\Model;
 use Carbon\Carbon;
 
similarity index 89%
rename from app/Entities/SearchTerm.php
rename to app/Entities/Models/SearchTerm.php
index 886c4dbc1fe4a4041859357ebe293b8ecb79177d..f55cb8407b34c9ce5b4a1bf672178f320c840db5 100644 (file)
@@ -1,4 +1,4 @@
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
 
 use BookStack\Model;
 
index 23f07f82049bc8342346b33c8b889c11f155755e..8b2e70074fe09a3b95c8688b81061f90fef2760c 100644 (file)
@@ -2,11 +2,13 @@
 
 namespace BookStack\Entities\Repos;
 
+use BookStack\Actions\ActivityType;
 use BookStack\Actions\TagRepo;
-use BookStack\Entities\Book;
-use BookStack\Entities\Entity;
-use BookStack\Entities\HasCoverImage;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\HasCoverImage;
 use BookStack\Exceptions\ImageUploadException;
+use BookStack\Facades\Activity;
 use BookStack\Uploads\ImageRepo;
 use Illuminate\Http\UploadedFile;
 use Illuminate\Support\Collection;
@@ -18,10 +20,6 @@ class BaseRepo
     protected $imageRepo;
 
 
-    /**
-     * BaseRepo constructor.
-     * @param $tagRepo
-     */
     public function __construct(TagRepo $tagRepo, ImageRepo $imageRepo)
     {
         $this->tagRepo = $tagRepo;
@@ -37,6 +35,7 @@ class BaseRepo
         $entity->forceFill([
             'created_by' => user()->id,
             'updated_by' => user()->id,
+            'owned_by' => user()->id,
         ]);
         $entity->refreshSlug();
         $entity->save();
@@ -76,7 +75,7 @@ class BaseRepo
      * @throws ImageUploadException
      * @throws \Exception
      */
-    public function updateCoverImage(HasCoverImage $entity, UploadedFile $coverImage = null, bool $removeImage = false)
+    public function updateCoverImage(HasCoverImage $entity, ?UploadedFile $coverImage, bool $removeImage = false)
     {
         if ($coverImage) {
             $this->imageRepo->destroyImage($entity->cover);
@@ -91,29 +90,4 @@ class BaseRepo
             $entity->save();
         }
     }
-
-    /**
-     * Update the permissions of an entity.
-     */
-    public function updatePermissions(Entity $entity, bool $restricted, Collection $permissions = null)
-    {
-        $entity->restricted = $restricted;
-        $entity->permissions()->delete();
-
-        if (!is_null($permissions)) {
-            $entityPermissionData = $permissions->flatMap(function ($restrictions, $roleId) {
-                return collect($restrictions)->keys()->map(function ($action) use ($roleId) {
-                    return [
-                        'role_id' => $roleId,
-                        'action' => strtolower($action),
-                    ] ;
-                });
-            });
-
-            $entity->permissions()->createMany($entityPermissionData);
-        }
-
-        $entity->save();
-        $entity->rebuildPermissions();
-    }
 }
index 7fcc80face7c3833fd87c991402f62fbd37e2926..27d0b407541d02857d8aec46cadbeb71cf7bc840 100644 (file)
@@ -1,14 +1,14 @@
 <?php namespace BookStack\Entities\Repos;
 
+use BookStack\Actions\ActivityType;
 use BookStack\Actions\TagRepo;
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\TrashCan;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\TrashCan;
 use BookStack\Exceptions\ImageUploadException;
 use BookStack\Exceptions\NotFoundException;
-use BookStack\Exceptions\NotifyException;
+use BookStack\Facades\Activity;
 use BookStack\Uploads\ImageRepo;
 use Exception;
-use Illuminate\Contracts\Container\BindingResolutionException;
 use Illuminate\Contracts\Pagination\LengthAwarePaginator;
 use Illuminate\Http\UploadedFile;
 use Illuminate\Support\Collection;
@@ -22,7 +22,6 @@ class BookRepo
 
     /**
      * BookRepo constructor.
-     * @param $tagRepo
      */
     public function __construct(BaseRepo $baseRepo, TagRepo $tagRepo, ImageRepo $imageRepo)
     {
@@ -36,7 +35,7 @@ class BookRepo
      */
     public function getAllPaginated(int $count = 20, string $sort = 'name', string $order = 'asc'): LengthAwarePaginator
     {
-        return Book::visible()->orderBy($sort, $order)->paginate($count);
+        return Book::visible()->with('cover')->orderBy($sort, $order)->paginate($count);
     }
 
     /**
@@ -91,6 +90,7 @@ class BookRepo
     {
         $book = new Book();
         $this->baseRepo->create($book, $input);
+        Activity::addForEntity($book, ActivityType::BOOK_CREATE);
         return $book;
     }
 
@@ -100,6 +100,7 @@ class BookRepo
     public function update(Book $book, array $input): Book
     {
         $this->baseRepo->update($book, $input);
+        Activity::addForEntity($book, ActivityType::BOOK_UPDATE);
         return $book;
     }
 
@@ -108,27 +109,21 @@ class BookRepo
      * @throws ImageUploadException
      * @throws Exception
      */
-    public function updateCoverImage(Book $book, UploadedFile $coverImage = null, bool $removeImage = false)
+    public function updateCoverImage(Book $book, ?UploadedFile $coverImage, bool $removeImage = false)
     {
         $this->baseRepo->updateCoverImage($book, $coverImage, $removeImage);
     }
 
-    /**
-     * Update the permissions of a book.
-     */
-    public function updatePermissions(Book $book, bool $restricted, Collection $permissions = null)
-    {
-        $this->baseRepo->updatePermissions($book, $restricted, $permissions);
-    }
-
     /**
      * Remove a book from the system.
-     * @throws NotifyException
-     * @throws BindingResolutionException
+     * @throws Exception
      */
     public function destroy(Book $book)
     {
         $trashCan = new TrashCan();
-        $trashCan->destroyBook($book);
+        $trashCan->softDestroyBook($book);
+        Activity::addForEntity($book, ActivityType::BOOK_DELETE);
+
+        $trashCan->autoClearOld();
     }
 }
index ab4a518054eb89ac76c47855bd0d811f09792ea7..649f4b0c461c0b461701bd26508af44fe0ab43c2 100644 (file)
@@ -1,10 +1,12 @@
 <?php namespace BookStack\Entities\Repos;
 
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Managers\TrashCan;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Tools\TrashCan;
 use BookStack\Exceptions\ImageUploadException;
 use BookStack\Exceptions\NotFoundException;
+use BookStack\Facades\Activity;
 use Exception;
 use Illuminate\Contracts\Pagination\LengthAwarePaginator;
 use Illuminate\Http\UploadedFile;
@@ -16,7 +18,6 @@ class BookshelfRepo
 
     /**
      * BookshelfRepo constructor.
-     * @param $baseRepo
      */
     public function __construct(BaseRepo $baseRepo)
     {
@@ -28,8 +29,10 @@ class BookshelfRepo
      */
     public function getAllPaginated(int $count = 20, string $sort = 'name', string $order = 'asc'): LengthAwarePaginator
     {
-        return Bookshelf::visible()->with('visibleBooks')
-            ->orderBy($sort, $order)->paginate($count);
+        return Bookshelf::visible()
+            ->with(['visibleBooks', 'cover'])
+            ->orderBy($sort, $order)
+            ->paginate($count);
     }
 
     /**
@@ -85,16 +88,22 @@ class BookshelfRepo
         $shelf = new Bookshelf();
         $this->baseRepo->create($shelf, $input);
         $this->updateBooks($shelf, $bookIds);
+        Activity::addForEntity($shelf, ActivityType::BOOKSHELF_CREATE);
         return $shelf;
     }
 
     /**
-     * Create a new shelf in the system.
+     * Update an existing shelf in the system using the given input.
      */
-    public function update(Bookshelf $shelf, array $input, array $bookIds): Bookshelf
+    public function update(Bookshelf $shelf, array $input, ?array $bookIds): Bookshelf
     {
         $this->baseRepo->update($shelf, $input);
-        $this->updateBooks($shelf, $bookIds);
+
+        if (!is_null($bookIds)) {
+            $this->updateBooks($shelf, $bookIds);
+        }
+
+        Activity::addForEntity($shelf, ActivityType::BOOKSHELF_UPDATE);
         return $shelf;
     }
 
@@ -123,31 +132,23 @@ class BookshelfRepo
      * @throws ImageUploadException
      * @throws Exception
      */
-    public function updateCoverImage(Bookshelf $shelf, UploadedFile $coverImage = null, bool $removeImage = false)
+    public function updateCoverImage(Bookshelf $shelf, ?UploadedFile $coverImage, bool $removeImage = false)
     {
         $this->baseRepo->updateCoverImage($shelf, $coverImage, $removeImage);
     }
 
-    /**
-     * Update the permissions of a bookshelf.
-     */
-    public function updatePermissions(Bookshelf $shelf, bool $restricted, Collection $permissions = null)
-    {
-        $this->baseRepo->updatePermissions($shelf, $restricted, $permissions);
-    }
-
     /**
      * Copy down the permissions of the given shelf to all child books.
      */
-    public function copyDownPermissions(Bookshelf $shelf): int
+    public function copyDownPermissions(Bookshelf $shelf, $checkUserPermissions = true): int
     {
         $shelfPermissions = $shelf->permissions()->get(['role_id', 'action'])->toArray();
-        $shelfBooks = $shelf->books()->get();
+        $shelfBooks = $shelf->books()->get(['id', 'restricted']);
         $updatedBookCount = 0;
 
         /** @var Book $book */
         foreach ($shelfBooks as $book) {
-            if (!userCan('restrictions-manage', $book)) {
+            if ($checkUserPermissions && !userCan('restrictions-manage', $book)) {
                 continue;
             }
             $book->permissions()->delete();
@@ -168,6 +169,8 @@ class BookshelfRepo
     public function destroy(Bookshelf $shelf)
     {
         $trashCan = new TrashCan();
-        $trashCan->destroyShelf($shelf);
+        $trashCan->softDestroyShelf($shelf);
+        Activity::addForEntity($shelf, ActivityType::BOOKSHELF_DELETE);
+        $trashCan->autoClearOld();
     }
 }
index c6f3a2d2f0fc093c37b6e541081c20c977468c75..d56874e0d54a9b647ce59f4a84add65fa793f302 100644 (file)
@@ -1,15 +1,14 @@
 <?php namespace BookStack\Entities\Repos;
 
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Managers\TrashCan;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Tools\TrashCan;
 use BookStack\Exceptions\MoveOperationException;
 use BookStack\Exceptions\NotFoundException;
-use BookStack\Exceptions\NotifyException;
+use BookStack\Facades\Activity;
 use Exception;
-use Illuminate\Contracts\Container\BindingResolutionException;
-use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Support\Collection;
 
 class ChapterRepo
@@ -19,7 +18,6 @@ class ChapterRepo
 
     /**
      * ChapterRepo constructor.
-     * @param $baseRepo
      */
     public function __construct(BaseRepo $baseRepo)
     {
@@ -50,6 +48,7 @@ class ChapterRepo
         $chapter->book_id = $parentBook->id;
         $chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1;
         $this->baseRepo->create($chapter, $input);
+        Activity::addForEntity($chapter, ActivityType::CHAPTER_CREATE);
         return $chapter;
     }
 
@@ -59,17 +58,10 @@ class ChapterRepo
     public function update(Chapter $chapter, array $input): Chapter
     {
         $this->baseRepo->update($chapter, $input);
+        Activity::addForEntity($chapter, ActivityType::CHAPTER_UPDATE);
         return $chapter;
     }
 
-    /**
-     * Update the permissions of a chapter.
-     */
-    public function updatePermissions(Chapter $chapter, bool $restricted, Collection $permissions = null)
-    {
-        $this->baseRepo->updatePermissions($chapter, $restricted, $permissions);
-    }
-
     /**
      * Remove a chapter from the system.
      * @throws Exception
@@ -77,7 +69,9 @@ class ChapterRepo
     public function destroy(Chapter $chapter)
     {
         $trashCan = new TrashCan();
-        $trashCan->destroyChapter($chapter);
+        $trashCan->softDestroyChapter($chapter);
+        Activity::addForEntity($chapter, ActivityType::CHAPTER_DELETE);
+        $trashCan->autoClearOld();
     }
 
     /**
@@ -96,6 +90,7 @@ class ChapterRepo
             throw new MoveOperationException('Chapters can only be moved into books');
         }
 
+        /** @var Book $parent */
         $parent = Book::visible()->where('id', '=', $entityId)->first();
         if ($parent === null) {
             throw new MoveOperationException('Book to move chapter into not found');
@@ -103,6 +98,8 @@ class ChapterRepo
 
         $chapter->changeBook($parent->id);
         $chapter->rebuildPermissions();
+        Activity::addForEntity($chapter, ActivityType::CHAPTER_MOVE);
+
         return $parent;
     }
 }
index e49eeb1ef5518eb8aa124d11e981236f11ebbe04..4c59db4688bed3b3a6dcb758ea6f4ed0f1ae2f66 100644 (file)
@@ -1,17 +1,19 @@
 <?php namespace BookStack\Entities\Repos;
 
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Managers\PageContent;
-use BookStack\Entities\Managers\TrashCan;
-use BookStack\Entities\Page;
-use BookStack\Entities\PageRevision;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Tools\PageContent;
+use BookStack\Entities\Tools\TrashCan;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Models\PageRevision;
 use BookStack\Exceptions\MoveOperationException;
 use BookStack\Exceptions\NotFoundException;
-use BookStack\Exceptions\NotifyException;
 use BookStack\Exceptions\PermissionsException;
+use BookStack\Facades\Activity;
+use Exception;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Pagination\LengthAwarePaginator;
 use Illuminate\Support\Collection;
@@ -33,9 +35,9 @@ class PageRepo
      * Get a page by ID.
      * @throws NotFoundException
      */
-    public function getById(int $id): Page
+    public function getById(int $id, array $relations = ['book']): Page
     {
-        $page = Page::visible()->with(['book'])->find($id);
+        $page = Page::visible()->with($relations)->find($id);
 
         if (!$page) {
             throw new NotFoundException(trans('errors.page_not_found'));
@@ -128,6 +130,7 @@ class PageRepo
         $page = (new Page())->forceFill([
             'name' => trans('entities.pages_initial_name'),
             'created_by' => user()->id,
+            'owned_by' => user()->id,
             'updated_by' => user()->id,
             'draft' => true,
         ]);
@@ -150,12 +153,8 @@ class PageRepo
     public function publishDraft(Page $draft, array $input): Page
     {
         $this->baseRepo->update($draft, $input);
-        if (isset($input['template']) && userCan('templates-manage')) {
-            $draft->template = ($input['template'] === 'true');
-        }
+        $this->updateTemplateStatusAndContentFromInput($draft, $input);
 
-        $pageContent = new PageContent($draft);
-        $pageContent->setNewHTML($input['html']);
         $draft->draft = false;
         $draft->revision_count = 1;
         $draft->priority = $this->getNewPriority($draft);
@@ -164,7 +163,10 @@ class PageRepo
 
         $this->savePageRevision($draft, trans('entities.pages_initial_revision'));
         $draft->indexForSearch();
-        return $draft->refresh();
+        $draft->refresh();
+
+        Activity::addForEntity($draft, ActivityType::PAGE_CREATE);
+        return $draft;
     }
 
     /**
@@ -176,16 +178,10 @@ class PageRepo
         $oldHtml = $page->html;
         $oldName = $page->name;
 
-        if (isset($input['template']) && userCan('templates-manage')) {
-            $page->template = ($input['template'] === 'true');
-        }
-
+        $this->updateTemplateStatusAndContentFromInput($page, $input);
         $this->baseRepo->update($page, $input);
 
         // Update with new details
-        $page->fill($input);
-        $pageContent = new PageContent($page);
-        $pageContent->setNewHTML($input['html']);
         $page->revision_count++;
 
         if (setting('app-editor') !== 'markdown') {
@@ -203,15 +199,30 @@ class PageRepo
             $this->savePageRevision($page, $summary);
         }
 
+        Activity::addForEntity($page, ActivityType::PAGE_UPDATE);
         return $page;
     }
 
+    protected function updateTemplateStatusAndContentFromInput(Page $page, array $input)
+    {
+        if (isset($input['template']) && userCan('templates-manage')) {
+            $page->template = ($input['template'] === 'true');
+        }
+
+        $pageContent = new PageContent($page);
+        if (!empty($input['markdown'] ?? '')) {
+            $pageContent->setNewMarkdown($input['markdown']);
+        } else {
+            $pageContent->setNewHTML($input['html']);
+        }
+    }
+
     /**
      * Saves a page revision into the system.
      */
-    protected function savePageRevision(Page $page, string $summary = null)
+    protected function savePageRevision(Page $page, string $summary = null): PageRevision
     {
-        $revision = new PageRevision($page->toArray());
+        $revision = new PageRevision($page->getAttributes());
 
         if (setting('app-editor') !== 'markdown') {
             $revision->markdown = '';
@@ -238,11 +249,10 @@ class PageRepo
     {
         // If the page itself is a draft simply update that
         if ($page->draft) {
-            $page->fill($input);
             if (isset($input['html'])) {
-                $content = new PageContent($page);
-                $content->setNewHTML($input['html']);
+                (new PageContent($page))->setNewHTML($input['html']);
             }
+            $page->fill($input);
             $page->save();
             return $page;
         }
@@ -260,12 +270,14 @@ class PageRepo
 
     /**
      * Destroy a page from the system.
-     * @throws NotifyException
+     * @throws Exception
      */
     public function destroy(Page $page)
     {
         $trashCan = new TrashCan();
-        $trashCan->destroyPage($page);
+        $trashCan->softDestroyPage($page);
+        Activity::addForEntity($page, ActivityType::PAGE_DELETE);
+        $trashCan->autoClearOld();
     }
 
     /**
@@ -274,17 +286,20 @@ class PageRepo
     public function restoreRevision(Page $page, int $revisionId): Page
     {
         $page->revision_count++;
-        $this->savePageRevision($page);
-
         $revision = $page->revisions()->where('id', '=', $revisionId)->first();
+
         $page->fill($revision->toArray());
         $content = new PageContent($page);
-        $content->setNewHTML($page->html);
+        $content->setNewHTML($revision->html);
         $page->updated_by = user()->id;
         $page->refreshSlug();
         $page->save();
-
         $page->indexForSearch();
+
+        $summary = trans('entities.pages_revision_restored_from', ['id' => strval($revisionId), 'summary' => $revision->summary]);
+        $this->savePageRevision($page, $summary);
+
+        Activity::addForEntity($page, ActivityType::PAGE_RESTORE);
         return $page;
     }
 
@@ -295,7 +310,7 @@ class PageRepo
      * @throws MoveOperationException
      * @throws PermissionsException
      */
-    public function move(Page $page, string $parentIdentifier): Book
+    public function move(Page $page, string $parentIdentifier): Entity
     {
         $parent = $this->findParentByIdentifier($parentIdentifier);
         if ($parent === null) {
@@ -310,7 +325,8 @@ class PageRepo
         $page->changeBook($parent instanceof Book ? $parent->id : $parent->book->id);
         $page->rebuildPermissions();
 
-        return ($parent instanceof Book ? $parent : $parent->book);
+        Activity::addForEntity($page, ActivityType::PAGE_MOVE);
+        return $parent;
     }
 
     /**
@@ -321,7 +337,7 @@ class PageRepo
      */
     public function copy(Page $page, string $parentIdentifier = null, string $newName = null): Page
     {
-        $parent = $parentIdentifier ? $this->findParentByIdentifier($parentIdentifier) : $page->parent();
+        $parent = $parentIdentifier ? $this->findParentByIdentifier($parentIdentifier) : $page->getParent();
         if ($parent === null) {
             throw new MoveOperationException('Book or chapter to move page into not found');
         }
@@ -369,14 +385,6 @@ class PageRepo
         return $parentClass::visible()->where('id', '=', $entityId)->first();
     }
 
-    /**
-     * Update the permissions of a page.
-     */
-    public function updatePermissions(Page $page, bool $restricted, Collection $permissions = null)
-    {
-        $this->baseRepo->updatePermissions($page, $restricted, $permissions);
-    }
-
     /**
      * Change the page's parent to the given entity.
      */
@@ -440,8 +448,9 @@ class PageRepo
      */
     protected function getNewPriority(Page $page): int
     {
-        if ($page->parent() instanceof Chapter) {
-            $lastPage = $page->parent()->pages('desc')->first();
+        $parent = $page->getParent();
+        if ($parent instanceof Chapter) {
+            $lastPage = $parent->pages('desc')->first();
             return $lastPage ? $lastPage->priority + 1 : 0;
         }
 
diff --git a/app/Entities/SlugGenerator.php b/app/Entities/SlugGenerator.php
deleted file mode 100644 (file)
index 459a526..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php namespace BookStack\Entities;
-
-class SlugGenerator
-{
-
-    protected $entity;
-
-    /**
-     * SlugGenerator constructor.
-     * @param $entity
-     */
-    public function __construct(Entity $entity)
-    {
-        $this->entity = $entity;
-    }
-
-    /**
-     * Generate a fresh slug for the given entity.
-     * The slug will generated so it does not conflict within the same parent item.
-     */
-    public function generate(): string
-    {
-        $slug = $this->formatNameAsSlug($this->entity->name);
-        while ($this->slugInUse($slug)) {
-            $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
-        }
-        return $slug;
-    }
-
-    /**
-     * Format a name as a url slug.
-     */
-    protected function formatNameAsSlug(string $name): string
-    {
-        $slug = preg_replace('/[\+\/\\\?\@\}\{\.\,\=\[\]\#\&\!\*\'\;\:\$\%]/', '', mb_strtolower($name));
-        $slug = preg_replace('/\s{2,}/', ' ', $slug);
-        $slug = str_replace(' ', '-', $slug);
-        if ($slug === "") {
-            $slug = substr(md5(rand(1, 500)), 0, 5);
-        }
-        return $slug;
-    }
-
-    /**
-     * Check if a slug is already in-use for this
-     * type of model within the same parent.
-     */
-    protected function slugInUse(string $slug): bool
-    {
-        $query = $this->entity->newQuery()->where('slug', '=', $slug);
-
-        if ($this->entity instanceof BookChild) {
-            $query->where('book_id', '=', $this->entity->book_id);
-        }
-
-        if ($this->entity->id) {
-            $query->where('id', '!=', $this->entity->id);
-        }
-
-        return $query->count() > 0;
-    }
-}
similarity index 89%
rename from app/Entities/Managers/BookContents.php
rename to app/Entities/Tools/BookContents.php
index 8b8d02c1dd0481539b17dcd2286e963e85581196..71c8f8393a22dff67f95a4ec931ce236d4641b49 100644 (file)
@@ -1,10 +1,10 @@
-<?php namespace BookStack\Entities\Managers;
+<?php namespace BookStack\Entities\Tools;
 
-use BookStack\Entities\Book;
-use BookStack\Entities\BookChild;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\BookChild;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
 use BookStack\Exceptions\SortOperationException;
 use Illuminate\Support\Collection;
 
@@ -18,7 +18,6 @@ class BookContents
 
     /**
      * BookContents constructor.
-     * @param $book
      */
     public function __construct(Book $book)
     {
@@ -41,7 +40,6 @@ class BookContents
 
     /**
      * Get the contents as a sorted collection tree.
-     * TODO - Support $renderPages option
      */
     public function getTree(bool $showDrafts = false, bool $renderPages = false): Collection
     {
@@ -54,14 +52,22 @@ class BookContents
         $pages->groupBy('chapter_id')->each(function ($pages, $chapter_id) use ($chapterMap, &$lonePages) {
             $chapter = $chapterMap->get($chapter_id);
             if ($chapter) {
-                $chapter->setAttribute('pages', collect($pages)->sortBy($this->bookChildSortFunc()));
+                $chapter->setAttribute('visible_pages', collect($pages)->sortBy($this->bookChildSortFunc()));
             } else {
                 $lonePages = $lonePages->concat($pages);
             }
         });
 
-        $all->each(function (Entity $entity) {
+        $chapters->whereNull('visible_pages')->each(function (Chapter $chapter) {
+            $chapter->setAttribute('visible_pages', collect([]));
+        });
+
+        $all->each(function (Entity $entity) use ($renderPages) {
             $entity->setRelation('book', $this->book);
+
+            if ($renderPages && $entity->isA('page')) {
+                $entity->html = (new PageContent($entity))->render();
+            }
         });
 
         return collect($chapters)->concat($lonePages)->sortBy($this->bookChildSortFunc());
similarity index 85%
rename from app/Entities/ExportService.php
rename to app/Entities/Tools/ExportFormatter.php
index 3ec867959a7d8b1046127e90534bff5050ca73f6..eb8f6862f23fe76b703c8f0a2514b2f5d61acae9 100644 (file)
@@ -1,14 +1,15 @@
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Tools;
 
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Managers\PageContent;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
 use BookStack\Uploads\ImageService;
 use DomPDF;
 use Exception;
 use SnappyPDF;
 use Throwable;
 
-class ExportService
+class ExportFormatter
 {
 
     protected $imageService;
@@ -29,8 +30,9 @@ class ExportService
     public function pageToContainedHtml(Page $page)
     {
         $page->html = (new PageContent($page))->render();
-        $pageHtml = view('pages/export', [
-            'page' => $page
+        $pageHtml = view('pages.export', [
+            'page' => $page,
+            'format' => 'html',
         ])->render();
         return $this->containHtml($pageHtml);
     }
@@ -45,9 +47,10 @@ class ExportService
         $pages->each(function ($page) {
             $page->html = (new PageContent($page))->render();
         });
-        $html = view('chapters/export', [
+        $html = view('chapters.export', [
             'chapter' => $chapter,
-            'pages' => $pages
+            'pages' => $pages,
+            'format' => 'html',
         ])->render();
         return $this->containHtml($html);
     }
@@ -59,9 +62,10 @@ class ExportService
     public function bookToContainedHtml(Book $book)
     {
         $bookTree = (new BookContents($book))->getTree(false, true);
-        $html = view('books/export', [
+        $html = view('books.export', [
             'book' => $book,
-            'bookChildren' => $bookTree
+            'bookChildren' => $bookTree,
+            'format' => 'html',
         ])->render();
         return $this->containHtml($html);
     }
@@ -73,8 +77,9 @@ class ExportService
     public function pageToPdf(Page $page)
     {
         $page->html = (new PageContent($page))->render();
-        $html = view('pages/pdf', [
-            'page' => $page
+        $html = view('pages.export', [
+            'page' => $page,
+            'format' => 'pdf',
         ])->render();
         return $this->htmlToPdf($html);
     }
@@ -90,9 +95,10 @@ class ExportService
             $page->html = (new PageContent($page))->render();
         });
 
-        $html = view('chapters/export', [
+        $html = view('chapters.export', [
             'chapter' => $chapter,
-            'pages' => $pages
+            'pages' => $pages,
+            'format' => 'pdf',
         ])->render();
 
         return $this->htmlToPdf($html);
@@ -105,9 +111,10 @@ class ExportService
     public function bookToPdf(Book $book)
     {
         $bookTree = (new BookContents($book))->getTree(false, true);
-        $html = view('books/export', [
+        $html = view('books.export', [
             'book' => $book,
-            'bookChildren' => $bookTree
+            'bookChildren' => $bookTree,
+            'format' => 'pdf',
         ])->render();
         return $this->htmlToPdf($html);
     }
@@ -136,7 +143,7 @@ class ExportService
     protected function containHtml(string $htmlContent): string
     {
         $imageTagsOutput = [];
-        preg_match_all("/\<img.*src\=(\'|\")(.*?)(\'|\").*?\>/i", $htmlContent, $imageTagsOutput);
+        preg_match_all("/\<img.*?src\=(\'|\")(.*?)(\'|\").*?\>/i", $htmlContent, $imageTagsOutput);
 
         // Replace image src with base64 encoded image strings
         if (isset($imageTagsOutput[0]) && count($imageTagsOutput[0]) > 0) {
@@ -197,7 +204,7 @@ class ExportService
     {
         $text = $chapter->name . "\n\n";
         $text .= $chapter->description . "\n\n";
-        foreach ($chapter->pages as $page) {
+        foreach ($chapter->getVisiblePages() as $page) {
             $text .= $this->pageToPlainText($page);
         }
         return $text;
@@ -208,7 +215,7 @@ class ExportService
      */
     public function bookToPlainText(Book $book): string
     {
-        $bookTree = (new BookContents($book))->getTree(false, true);
+        $bookTree = (new BookContents($book))->getTree(false, false);
         $text = $book->name . "\n\n";
         foreach ($bookTree as $bookChild) {
             if ($bookChild->isA('chapter')) {
diff --git a/app/Entities/Tools/Markdown/CustomStrikeThroughExtension.php b/app/Entities/Tools/Markdown/CustomStrikeThroughExtension.php
new file mode 100644 (file)
index 0000000..cb8b0ff
--- /dev/null
@@ -0,0 +1,16 @@
+<?php namespace BookStack\Entities\Tools\Markdown;
+
+use League\CommonMark\ConfigurableEnvironmentInterface;
+use League\CommonMark\Extension\ExtensionInterface;
+use League\CommonMark\Extension\Strikethrough\Strikethrough;
+use League\CommonMark\Extension\Strikethrough\StrikethroughDelimiterProcessor;
+
+class CustomStrikeThroughExtension implements ExtensionInterface
+{
+
+    public function register(ConfigurableEnvironmentInterface $environment)
+    {
+        $environment->addDelimiterProcessor(new StrikethroughDelimiterProcessor());
+        $environment->addInlineRenderer(Strikethrough::class, new CustomStrikethroughRenderer());
+    }
+}
\ No newline at end of file
diff --git a/app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php b/app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php
new file mode 100644 (file)
index 0000000..4371fb8
--- /dev/null
@@ -0,0 +1,24 @@
+<?php namespace BookStack\Entities\Tools\Markdown;
+
+use League\CommonMark\ElementRendererInterface;
+use League\CommonMark\Extension\Strikethrough\Strikethrough;
+use League\CommonMark\HtmlElement;
+use League\CommonMark\Inline\Element\AbstractInline;
+use League\CommonMark\Inline\Renderer\InlineRendererInterface;
+
+/**
+ * This is a somewhat clone of the League\CommonMark\Extension\Strikethrough\StrikethroughRender
+ * class but modified slightly to use <s> HTML tags instead of <del> in order to
+ * match front-end markdown-it rendering.
+ */
+class CustomStrikethroughRenderer implements InlineRendererInterface
+{
+    public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
+    {
+        if (!($inline instanceof Strikethrough)) {
+            throw new \InvalidArgumentException('Incompatible inline type: ' . get_class($inline));
+        }
+
+        return new HtmlElement('s', $inline->getData('attributes', []), $htmlRenderer->renderInlines($inline->children()));
+    }
+}
\ No newline at end of file
similarity index 73%
rename from app/Entities/Managers/PageContent.php
rename to app/Entities/Tools/PageContent.php
index 36bc2445c33caeb015b7cf6acd847d8fe6eb0fa0..62982f4ad5dbbb3ea2524b83f7ed028da4c3971c 100644 (file)
@@ -1,10 +1,14 @@
-<?php namespace BookStack\Entities\Managers;
+<?php namespace BookStack\Entities\Tools;
 
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\Markdown\CustomStrikeThroughExtension;
 use DOMDocument;
-use DOMElement;
 use DOMNodeList;
 use DOMXPath;
+use League\CommonMark\CommonMarkConverter;
+use League\CommonMark\Environment;
+use League\CommonMark\Extension\Table\TableExtension;
+use League\CommonMark\Extension\TaskList\TaskListExtension;
 
 class PageContent
 {
@@ -26,6 +30,31 @@ class PageContent
     {
         $this->page->html = $this->formatHtml($html);
         $this->page->text = $this->toPlainText();
+        $this->page->markdown = '';
+    }
+
+    /**
+     * Update the content of the page with new provided Markdown content.
+     */
+    public function setNewMarkdown(string $markdown)
+    {
+        $this->page->markdown = $markdown;
+        $html = $this->markdownToHtml($markdown);
+        $this->page->html = $this->formatHtml($html);
+        $this->page->text = $this->toPlainText();
+    }
+
+    /**
+     * Convert the given Markdown content to a HTML string.
+     */
+    protected function markdownToHtml(string $markdown): string
+    {
+        $environment = Environment::createCommonMarkEnvironment();
+        $environment->addExtension(new TableExtension());
+        $environment->addExtension(new TaskListExtension());
+        $environment->addExtension(new CustomStrikeThroughExtension());
+        $converter = new CommonMarkConverter([], $environment);
+        return $converter->convertToHtml($markdown);
     }
 
     /**
@@ -44,18 +73,24 @@ class PageContent
         $container = $doc->documentElement;
         $body = $container->childNodes->item(0);
         $childNodes = $body->childNodes;
+        $xPath = new DOMXPath($doc);
 
         // Set ids on top-level nodes
         $idMap = [];
         foreach ($childNodes as $index => $childNode) {
-            $this->setUniqueId($childNode, $idMap);
+            [$oldId, $newId] = $this->setUniqueId($childNode, $idMap);
+            if ($newId && $newId !== $oldId) {
+                $this->updateLinks($xPath, '#' . $oldId, '#' . $newId);
+            }
         }
 
         // Ensure no duplicate ids within child items
-        $xPath = new DOMXPath($doc);
         $idElems = $xPath->query('//body//*//*[@id]');
         foreach ($idElems as $domElem) {
-            $this->setUniqueId($domElem, $idMap);
+            [$oldId, $newId] = $this->setUniqueId($domElem, $idMap);
+            if ($newId && $newId !== $oldId) {
+                $this->updateLinks($xPath, '#' . $oldId, '#' . $newId);
+            }
         }
 
         // Generate inner html as a string
@@ -67,23 +102,34 @@ class PageContent
         return $html;
     }
 
+    /**
+     * Update the all links to the $old location to instead point to $new.
+     */
+    protected function updateLinks(DOMXPath $xpath, string $old, string $new)
+    {
+        $old = str_replace('"', '', $old);
+        $matchingLinks = $xpath->query('//body//*//*[@href="'.$old.'"]');
+        foreach ($matchingLinks as $domElem) {
+            $domElem->setAttribute('href', $new);
+        }
+    }
+
     /**
      * Set a unique id on the given DOMElement.
      * A map for existing ID's should be passed in to check for current existence.
-     * @param DOMElement $element
-     * @param array $idMap
+     * Returns a pair of strings in the format [old_id, new_id]
      */
-    protected function setUniqueId($element, array &$idMap)
+    protected function setUniqueId(\DOMNode $element, array &$idMap): array
     {
         if (get_class($element) !== 'DOMElement') {
-            return;
+            return ['', ''];
         }
 
-        // Overwrite id if not a BookStack custom id
+        // Stop if there's an existing valid id that has not already been used.
         $existingId = $element->getAttribute('id');
         if (strpos($existingId, 'bkmrk') === 0 && !isset($idMap[$existingId])) {
             $idMap[$existingId] = true;
-            return;
+            return [$existingId, $existingId];
         }
 
         // Create an unique id for the element
@@ -100,6 +146,7 @@ class PageContent
 
         $element->setAttribute('id', $newId);
         $idMap[$newId] = true;
+        return [$existingId, $newId];
     }
 
     /**
@@ -108,7 +155,7 @@ class PageContent
     protected function toPlainText(): string
     {
         $html = $this->render(true);
-        return strip_tags($html);
+        return html_entity_decode(strip_tags($html));
     }
 
     /**
@@ -279,6 +326,24 @@ class PageContent
             $scriptElem->parentNode->removeChild($scriptElem);
         }
 
+        // Remove clickable links to JavaScript URI
+        $badLinks = $xPath->query('//*[contains(@href, \'javascript:\')]');
+        foreach ($badLinks as $badLink) {
+            $badLink->parentNode->removeChild($badLink);
+        }
+
+        // Remove forms with calls to JavaScript URI
+        $badForms = $xPath->query('//*[contains(@action, \'javascript:\')] | //*[contains(@formaction, \'javascript:\')]');
+        foreach ($badForms as $badForm) {
+            $badForm->parentNode->removeChild($badForm);
+        }
+
+        // Remove meta tag to prevent external redirects
+        $metaTags = $xPath->query('//meta[contains(@content, \'url\')]');
+        foreach ($metaTags as $metaTag) {
+            $metaTag->parentNode->removeChild($metaTag);
+        }
+
         // Remove data or JavaScript iFrames
         $badIframes = $xPath->query('//*[contains(@src, \'data:\')] | //*[contains(@src, \'javascript:\')] | //*[@srcdoc]');
         foreach ($badIframes as $badIframe) {
similarity index 95%
rename from app/Entities/Managers/PageEditActivity.php
rename to app/Entities/Tools/PageEditActivity.php
index cebbf8720f12a0ac7d66b8cc9e67dae3a5719db7..79de5c827987e8d1f9a4041d70d2598839c2c87c 100644 (file)
@@ -1,7 +1,7 @@
-<?php namespace BookStack\Entities\Managers;
+<?php namespace BookStack\Entities\Tools;
 
-use BookStack\Entities\Page;
-use BookStack\Entities\PageRevision;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Models\PageRevision;
 use Carbon\Carbon;
 use Illuminate\Database\Eloquent\Builder;
 
diff --git a/app/Entities/Tools/PermissionsUpdater.php b/app/Entities/Tools/PermissionsUpdater.php
new file mode 100644 (file)
index 0000000..8a27ce7
--- /dev/null
@@ -0,0 +1,68 @@
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Entity;
+use BookStack\Facades\Activity;
+use Illuminate\Http\Request;
+use Illuminate\Support\Collection;
+
+class PermissionsUpdater
+{
+
+    /**
+     * Update an entities permissions from a permission form submit request.
+     */
+    public function updateFromPermissionsForm(Entity $entity, Request $request)
+    {
+        $restricted = $request->get('restricted') === 'true';
+        $permissions = $request->get('restrictions', null);
+        $ownerId = $request->get('owned_by', null);
+
+        $entity->restricted = $restricted;
+        $entity->permissions()->delete();
+
+        if (!is_null($permissions)) {
+            $entityPermissionData = $this->formatPermissionsFromRequestToEntityPermissions($permissions);
+            $entity->permissions()->createMany($entityPermissionData);
+        }
+
+        if (!is_null($ownerId)) {
+            $this->updateOwnerFromId($entity, intval($ownerId));
+        }
+
+        $entity->save();
+        $entity->rebuildPermissions();
+
+        Activity::addForEntity($entity, ActivityType::PERMISSIONS_UPDATE);
+    }
+
+    /**
+     * Update the owner of the given entity.
+     * Checks the user exists in the system first.
+     * Does not save the model, just updates it.
+     */
+    protected function updateOwnerFromId(Entity $entity, int $newOwnerId)
+    {
+        $newOwner = User::query()->find($newOwnerId);
+        if (!is_null($newOwner)) {
+            $entity->owned_by = $newOwner->id;
+        }
+    }
+
+    /**
+     * Format permissions provided from a permission form to be
+     * EntityPermission data.
+     */
+    protected function formatPermissionsFromRequestToEntityPermissions(array $permissions): Collection
+    {
+        return collect($permissions)->flatMap(function ($restrictions, $roleId) {
+            return collect($restrictions)->keys()->map(function ($action) use ($roleId) {
+                return [
+                    'role_id' => $roleId,
+                    'action' => strtolower($action),
+                ] ;
+            });
+        });
+    }
+}
diff --git a/app/Entities/Tools/SearchIndex.php b/app/Entities/Tools/SearchIndex.php
new file mode 100644 (file)
index 0000000..81a5022
--- /dev/null
@@ -0,0 +1,120 @@
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\SearchTerm;
+use Illuminate\Support\Collection;
+
+class SearchIndex
+{
+    /**
+     * @var SearchTerm
+     */
+    protected $searchTerm;
+
+    /**
+     * @var EntityProvider
+     */
+    protected $entityProvider;
+
+
+    public function __construct(SearchTerm $searchTerm, EntityProvider $entityProvider)
+    {
+        $this->searchTerm = $searchTerm;
+        $this->entityProvider = $entityProvider;
+    }
+
+
+    /**
+     * Index the given entity.
+     */
+    public function indexEntity(Entity $entity)
+    {
+        $this->deleteEntityTerms($entity);
+        $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
+        $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
+        $terms = array_merge($nameTerms, $bodyTerms);
+        foreach ($terms as $index => $term) {
+            $terms[$index]['entity_type'] = $entity->getMorphClass();
+            $terms[$index]['entity_id'] = $entity->id;
+        }
+        $this->searchTerm->newQuery()->insert($terms);
+    }
+
+    /**
+     * Index multiple Entities at once
+     * @param Entity[] $entities
+     */
+    protected function indexEntities(array $entities)
+    {
+        $terms = [];
+        foreach ($entities as $entity) {
+            $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
+            $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
+            foreach (array_merge($nameTerms, $bodyTerms) as $term) {
+                $term['entity_id'] = $entity->id;
+                $term['entity_type'] = $entity->getMorphClass();
+                $terms[] = $term;
+            }
+        }
+
+        $chunkedTerms = array_chunk($terms, 500);
+        foreach ($chunkedTerms as $termChunk) {
+            $this->searchTerm->newQuery()->insert($termChunk);
+        }
+    }
+
+    /**
+     * Delete and re-index the terms for all entities in the system.
+     */
+    public function indexAllEntities()
+    {
+        $this->searchTerm->newQuery()->truncate();
+
+        foreach ($this->entityProvider->all() as $entityModel) {
+            $selectFields = ['id', 'name', $entityModel->textField];
+            $entityModel->newQuery()
+                ->withTrashed()
+                ->select($selectFields)
+                ->chunk(1000, function (Collection $entities) {
+                    $this->indexEntities($entities->all());
+                });
+        }
+    }
+
+    /**
+     * Delete related Entity search terms.
+     */
+    public function deleteEntityTerms(Entity $entity)
+    {
+        $entity->searchTerms()->delete();
+    }
+
+    /**
+     * Create a scored term array from the given text.
+     */
+    protected function generateTermArrayFromText(string $text, int $scoreAdjustment = 1): array
+    {
+        $tokenMap = []; // {TextToken => OccurrenceCount}
+        $splitChars = " \n\t.,!?:;()[]{}<>`'\"";
+        $token = strtok($text, $splitChars);
+
+        while ($token !== false) {
+            if (!isset($tokenMap[$token])) {
+                $tokenMap[$token] = 0;
+            }
+            $tokenMap[$token]++;
+            $token = strtok($splitChars);
+        }
+
+        $terms = [];
+        foreach ($tokenMap as $token => $count) {
+            $terms[] = [
+                'term' => $token,
+                'score' => $count * $scoreAdjustment
+            ];
+        }
+
+        return $terms;
+    }
+}
diff --git a/app/Entities/Tools/SearchOptions.php b/app/Entities/Tools/SearchOptions.php
new file mode 100644 (file)
index 0000000..60e3a9b
--- /dev/null
@@ -0,0 +1,141 @@
+<?php namespace BookStack\Entities\Tools;
+
+use Illuminate\Http\Request;
+
+class SearchOptions
+{
+
+    /**
+     * @var array
+     */
+    public $searches = [];
+
+    /**
+     * @var array
+     */
+    public $exacts = [];
+
+    /**
+     * @var array
+     */
+    public $tags = [];
+
+    /**
+     * @var array
+     */
+    public $filters = [];
+
+    /**
+     * Create a new instance from a search string.
+     */
+    public static function fromString(string $search): SearchOptions
+    {
+        $decoded = static::decode($search);
+        $instance = new static();
+        foreach ($decoded as $type => $value) {
+            $instance->$type = $value;
+        }
+        return $instance;
+    }
+
+    /**
+     * Create a new instance from a request.
+     * Will look for a classic string term and use that
+     * Otherwise we'll use the details from an advanced search form.
+     */
+    public static function fromRequest(Request $request): SearchOptions
+    {
+        if (!$request->has('search') && !$request->has('term')) {
+            return static::fromString('');
+        }
+
+        if ($request->has('term')) {
+            return static::fromString($request->get('term'));
+        }
+
+        $instance = new static();
+        $inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags']);
+        $instance->searches = explode(' ', $inputs['search'] ?? []);
+        $instance->exacts = array_filter($inputs['exact'] ?? []);
+        $instance->tags = array_filter($inputs['tags'] ?? []);
+        foreach (($inputs['filters'] ?? []) as $filterKey => $filterVal) {
+            if (empty($filterVal)) {
+                continue;
+            }
+            $instance->filters[$filterKey] = $filterVal === 'true' ? '' : $filterVal;
+        }
+        if (isset($inputs['types']) && count($inputs['types']) < 4) {
+            $instance->filters['type'] = implode('|', $inputs['types']);
+        }
+        return $instance;
+    }
+
+    /**
+     * Decode a search string into an array of terms.
+     */
+    protected static function decode(string $searchString): array
+    {
+        $terms = [
+            'searches' => [],
+            'exacts' => [],
+            'tags' => [],
+            'filters' => []
+        ];
+
+        $patterns = [
+            'exacts' => '/"(.*?)"/',
+            'tags' => '/\[(.*?)\]/',
+            'filters' => '/\{(.*?)\}/'
+        ];
+
+        // Parse special terms
+        foreach ($patterns as $termType => $pattern) {
+            $matches = [];
+            preg_match_all($pattern, $searchString, $matches);
+            if (count($matches) > 0) {
+                $terms[$termType] = $matches[1];
+                $searchString = preg_replace($pattern, '', $searchString);
+            }
+        }
+
+        // Parse standard terms
+        foreach (explode(' ', trim($searchString)) as $searchTerm) {
+            if ($searchTerm !== '') {
+                $terms['searches'][] = $searchTerm;
+            }
+        }
+
+        // Split filter values out
+        $splitFilters = [];
+        foreach ($terms['filters'] as $filter) {
+            $explodedFilter = explode(':', $filter, 2);
+            $splitFilters[$explodedFilter[0]] = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
+        }
+        $terms['filters'] = $splitFilters;
+
+        return $terms;
+    }
+
+    /**
+     * Encode this instance to a search string.
+     */
+    public function toString(): string
+    {
+        $string = implode(' ', $this->searches ?? []);
+
+        foreach ($this->exacts as $term) {
+            $string .= ' "' . $term . '"';
+        }
+
+        foreach ($this->tags as $term) {
+            $string .= " [{$term}]";
+        }
+
+        foreach ($this->filters as $filterName => $filterVal) {
+            $string .= ' {' . $filterName . ($filterVal ? ':' . $filterVal : '') . '}';
+        }
+
+        return $string;
+    }
+
+}
\ No newline at end of file
similarity index 52%
rename from app/Entities/SearchService.php
rename to app/Entities/Tools/SearchRunner.php
index ee9b87786a57a1e059ed050621f0c694427b17cb..acfe8d9565fdf1ea2884d337e31e0402270f6cb9 100644 (file)
@@ -1,6 +1,8 @@
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Tools;
 
 use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Entity;
 use Illuminate\Database\Connection;
 use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
 use Illuminate\Database\Query\Builder;
@@ -8,12 +10,8 @@ use Illuminate\Database\Query\JoinClause;
 use Illuminate\Support\Collection;
 use Illuminate\Support\Str;
 
-class SearchService
+class SearchRunner
 {
-    /**
-     * @var SearchTerm
-     */
-    protected $searchTerm;
 
     /**
      * @var EntityProvider
@@ -37,49 +35,28 @@ class SearchService
      */
     protected $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!='];
 
-    /**
-     * SearchService constructor.
-     * @param SearchTerm $searchTerm
-     * @param EntityProvider $entityProvider
-     * @param Connection $db
-     * @param PermissionService $permissionService
-     */
-    public function __construct(SearchTerm $searchTerm, EntityProvider $entityProvider, Connection $db, PermissionService $permissionService)
+
+    public function __construct(EntityProvider $entityProvider, Connection $db, PermissionService $permissionService)
     {
-        $this->searchTerm = $searchTerm;
         $this->entityProvider = $entityProvider;
         $this->db = $db;
         $this->permissionService = $permissionService;
     }
 
-    /**
-     * Set the database connection
-     * @param Connection $connection
-     */
-    public function setConnection(Connection $connection)
-    {
-        $this->db = $connection;
-    }
-
     /**
      * Search all entities in the system.
-     * @param string $searchString
-     * @param string $entityType
-     * @param int $page
-     * @param int $count - Count of each entity to search, Total returned could can be larger and not guaranteed.
-     * @param string $action
-     * @return array[int, Collection];
+     * The provided count is for each entity to search,
+     * Total returned could can be larger and not guaranteed.
      */
-    public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20, $action = 'view')
+    public function searchEntities(SearchOptions $searchOpts, string $entityType = 'all', int $page = 1, int $count = 20, string $action = 'view'): array
     {
-        $terms = $this->parseSearchString($searchString);
         $entityTypes = array_keys($this->entityProvider->all());
         $entityTypesToSearch = $entityTypes;
 
         if ($entityType !== 'all') {
             $entityTypesToSearch = $entityType;
-        } else if (isset($terms['filters']['type'])) {
-            $entityTypesToSearch = explode('|', $terms['filters']['type']);
+        } else if (isset($searchOpts->filters['type'])) {
+            $entityTypesToSearch = explode('|', $searchOpts->filters['type']);
         }
 
         $results = collect();
@@ -90,8 +67,8 @@ class SearchService
             if (!in_array($entityType, $entityTypes)) {
                 continue;
             }
-            $search = $this->searchEntityTable($terms, $entityType, $page, $count, $action);
-            $entityTotal = $this->searchEntityTable($terms, $entityType, $page, $count, $action, true);
+            $search = $this->searchEntityTable($searchOpts, $entityType, $page, $count, $action);
+            $entityTotal = $this->searchEntityTable($searchOpts, $entityType, $page, $count, $action, true);
             if ($entityTotal > $page * $count) {
                 $hasMore = true;
             }
@@ -103,60 +80,51 @@ class SearchService
             'total' => $total,
             'count' => count($results),
             'has_more' => $hasMore,
-            'results' => $results->sortByDesc('score')->values()
+            'results' => $results->sortByDesc('score')->values(),
         ];
     }
 
 
     /**
      * Search a book for entities
-     * @param integer $bookId
-     * @param string $searchString
-     * @return Collection
      */
-    public function searchBook($bookId, $searchString)
+    public function searchBook(int $bookId, string $searchString): Collection
     {
-        $terms = $this->parseSearchString($searchString);
+        $opts = SearchOptions::fromString($searchString);
         $entityTypes = ['page', 'chapter'];
-        $entityTypesToSearch = isset($terms['filters']['type']) ? explode('|', $terms['filters']['type']) : $entityTypes;
+        $entityTypesToSearch = isset($opts->filters['type']) ? explode('|', $opts->filters['type']) : $entityTypes;
 
         $results = collect();
         foreach ($entityTypesToSearch as $entityType) {
             if (!in_array($entityType, $entityTypes)) {
                 continue;
             }
-            $search = $this->buildEntitySearchQuery($terms, $entityType)->where('book_id', '=', $bookId)->take(20)->get();
+            $search = $this->buildEntitySearchQuery($opts, $entityType)->where('book_id', '=', $bookId)->take(20)->get();
             $results = $results->merge($search);
         }
+
         return $results->sortByDesc('score')->take(20);
     }
 
     /**
-     * Search a book for entities
-     * @param integer $chapterId
-     * @param string $searchString
-     * @return Collection
+     * Search a chapter for entities
      */
-    public function searchChapter($chapterId, $searchString)
+    public function searchChapter(int $chapterId, string $searchString): Collection
     {
-        $terms = $this->parseSearchString($searchString);
-        $pages = $this->buildEntitySearchQuery($terms, 'page')->where('chapter_id', '=', $chapterId)->take(20)->get();
+        $opts = SearchOptions::fromString($searchString);
+        $pages = $this->buildEntitySearchQuery($opts, 'page')->where('chapter_id', '=', $chapterId)->take(20)->get();
         return $pages->sortByDesc('score');
     }
 
     /**
      * Search across a particular entity type.
-     * @param array $terms
-     * @param string $entityType
-     * @param int $page
-     * @param int $count
-     * @param string $action
-     * @param bool $getCount Return the total count of the search
+     * Setting getCount = true will return the total
+     * matching instead of the items themselves.
      * @return \Illuminate\Database\Eloquent\Collection|int|static[]
      */
-    public function searchEntityTable($terms, $entityType = 'page', $page = 1, $count = 20, $action = 'view', $getCount = false)
+    protected function searchEntityTable(SearchOptions $searchOpts, string $entityType = 'page', int $page = 1, int $count = 20, string $action = 'view', bool $getCount = false)
     {
-        $query = $this->buildEntitySearchQuery($terms, $entityType, $action);
+        $query = $this->buildEntitySearchQuery($searchOpts, $entityType, $action);
         if ($getCount) {
             return $query->count();
         }
@@ -167,50 +135,43 @@ class SearchService
 
     /**
      * Create a search query for an entity
-     * @param array $terms
-     * @param string $entityType
-     * @param string $action
-     * @return EloquentBuilder
      */
-    protected function buildEntitySearchQuery($terms, $entityType = 'page', $action = 'view')
+    protected function buildEntitySearchQuery(SearchOptions $searchOpts, string $entityType = 'page', string $action = 'view'): EloquentBuilder
     {
         $entity = $this->entityProvider->get($entityType);
         $entitySelect = $entity->newQuery();
 
         // Handle normal search terms
-        if (count($terms['search']) > 0) {
-            $subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', \DB::raw('SUM(score) as score'));
+        if (count($searchOpts->searches) > 0) {
+            $rawScoreSum = $this->db->raw('SUM(score) as score');
+            $subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', $rawScoreSum);
             $subQuery->where('entity_type', '=', $entity->getMorphClass());
-            $subQuery->where(function (Builder $query) use ($terms) {
-                foreach ($terms['search'] as $inputTerm) {
+            $subQuery->where(function (Builder $query) use ($searchOpts) {
+                foreach ($searchOpts->searches as $inputTerm) {
                     $query->orWhere('term', 'like', $inputTerm .'%');
                 }
             })->groupBy('entity_type', 'entity_id');
-            $entitySelect->join(\DB::raw('(' . $subQuery->toSql() . ') as s'), function (JoinClause $join) {
+            $entitySelect->join($this->db->raw('(' . $subQuery->toSql() . ') as s'), function (JoinClause $join) {
                 $join->on('id', '=', 'entity_id');
             })->selectRaw($entity->getTable().'.*, s.score')->orderBy('score', 'desc');
             $entitySelect->mergeBindings($subQuery);
         }
 
         // Handle exact term matching
-        if (count($terms['exact']) > 0) {
-            $entitySelect->where(function (EloquentBuilder $query) use ($terms, $entity) {
-                foreach ($terms['exact'] as $inputTerm) {
-                    $query->where(function (EloquentBuilder $query) use ($inputTerm, $entity) {
-                        $query->where('name', 'like', '%'.$inputTerm .'%')
-                            ->orWhere($entity->textField, 'like', '%'.$inputTerm .'%');
-                    });
-                }
+        foreach ($searchOpts->exacts as $inputTerm) {
+            $entitySelect->where(function (EloquentBuilder $query) use ($inputTerm, $entity) {
+                $query->where('name', 'like', '%'.$inputTerm .'%')
+                    ->orWhere($entity->textField, 'like', '%'.$inputTerm .'%');
             });
         }
 
         // Handle tag searches
-        foreach ($terms['tags'] as $inputTerm) {
+        foreach ($searchOpts->tags as $inputTerm) {
             $this->applyTagSearch($entitySelect, $inputTerm);
         }
 
         // Handle filters
-        foreach ($terms['filters'] as $filterTerm => $filterValue) {
+        foreach ($searchOpts->filters as $filterTerm => $filterValue) {
             $functionName = Str::camel('filter_' . $filterTerm);
             if (method_exists($this, $functionName)) {
                 $this->$functionName($entitySelect, $entity, $filterValue);
@@ -220,60 +181,10 @@ class SearchService
         return $this->permissionService->enforceEntityRestrictions($entityType, $entitySelect, $action);
     }
 
-
-    /**
-     * Parse a search string into components.
-     * @param $searchString
-     * @return array
-     */
-    protected function parseSearchString($searchString)
-    {
-        $terms = [
-            'search' => [],
-            'exact' => [],
-            'tags' => [],
-            'filters' => []
-        ];
-
-        $patterns = [
-            'exact' => '/"(.*?)"/',
-            'tags' => '/\[(.*?)\]/',
-            'filters' => '/\{(.*?)\}/'
-        ];
-
-        // Parse special terms
-        foreach ($patterns as $termType => $pattern) {
-            $matches = [];
-            preg_match_all($pattern, $searchString, $matches);
-            if (count($matches) > 0) {
-                $terms[$termType] = $matches[1];
-                $searchString = preg_replace($pattern, '', $searchString);
-            }
-        }
-
-        // Parse standard terms
-        foreach (explode(' ', trim($searchString)) as $searchTerm) {
-            if ($searchTerm !== '') {
-                $terms['search'][] = $searchTerm;
-            }
-        }
-
-        // Split filter values out
-        $splitFilters = [];
-        foreach ($terms['filters'] as $filter) {
-            $explodedFilter = explode(':', $filter, 2);
-            $splitFilters[$explodedFilter[0]] = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
-        }
-        $terms['filters'] = $splitFilters;
-
-        return $terms;
-    }
-
     /**
      * Get the available query operators as a regex escaped list.
-     * @return mixed
      */
-    protected function getRegexEscapedOperators()
+    protected function getRegexEscapedOperators(): string
     {
         $escapedOperators = [];
         foreach ($this->queryOperators as $operator) {
@@ -284,11 +195,8 @@ class SearchService
 
     /**
      * Apply a tag search term onto a entity query.
-     * @param EloquentBuilder $query
-     * @param string $tagTerm
-     * @return mixed
      */
-    protected function applyTagSearch(EloquentBuilder $query, $tagTerm)
+    protected function applyTagSearch(EloquentBuilder $query, string $tagTerm): EloquentBuilder
     {
         preg_match("/^(.*?)((".$this->getRegexEscapedOperators().")(.*?))?$/", $tagTerm, $tagSplit);
         $query->whereHas('tags', function (EloquentBuilder $query) use ($tagSplit) {
@@ -316,103 +224,6 @@ class SearchService
         return $query;
     }
 
-    /**
-     * Index the given entity.
-     * @param Entity $entity
-     */
-    public function indexEntity(Entity $entity)
-    {
-        $this->deleteEntityTerms($entity);
-        $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
-        $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
-        $terms = array_merge($nameTerms, $bodyTerms);
-        foreach ($terms as $index => $term) {
-            $terms[$index]['entity_type'] = $entity->getMorphClass();
-            $terms[$index]['entity_id'] = $entity->id;
-        }
-        $this->searchTerm->newQuery()->insert($terms);
-    }
-
-    /**
-     * Index multiple Entities at once
-     * @param \BookStack\Entities\Entity[] $entities
-     */
-    protected function indexEntities($entities)
-    {
-        $terms = [];
-        foreach ($entities as $entity) {
-            $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
-            $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
-            foreach (array_merge($nameTerms, $bodyTerms) as $term) {
-                $term['entity_id'] = $entity->id;
-                $term['entity_type'] = $entity->getMorphClass();
-                $terms[] = $term;
-            }
-        }
-
-        $chunkedTerms = array_chunk($terms, 500);
-        foreach ($chunkedTerms as $termChunk) {
-            $this->searchTerm->newQuery()->insert($termChunk);
-        }
-    }
-
-    /**
-     * Delete and re-index the terms for all entities in the system.
-     */
-    public function indexAllEntities()
-    {
-        $this->searchTerm->truncate();
-
-        foreach ($this->entityProvider->all() as $entityModel) {
-            $selectFields = ['id', 'name', $entityModel->textField];
-            $entityModel->newQuery()->select($selectFields)->chunk(1000, function ($entities) {
-                $this->indexEntities($entities);
-            });
-        }
-    }
-
-    /**
-     * Delete related Entity search terms.
-     * @param Entity $entity
-     */
-    public function deleteEntityTerms(Entity $entity)
-    {
-        $entity->searchTerms()->delete();
-    }
-
-    /**
-     * Create a scored term array from the given text.
-     * @param $text
-     * @param float|int $scoreAdjustment
-     * @return array
-     */
-    protected function generateTermArrayFromText($text, $scoreAdjustment = 1)
-    {
-        $tokenMap = []; // {TextToken => OccurrenceCount}
-        $splitChars = " \n\t.,!?:;()[]{}<>`'\"";
-        $token = strtok($text, $splitChars);
-
-        while ($token !== false) {
-            if (!isset($tokenMap[$token])) {
-                $tokenMap[$token] = 0;
-            }
-            $tokenMap[$token]++;
-            $token = strtok($splitChars);
-        }
-
-        $terms = [];
-        foreach ($tokenMap as $token => $count) {
-            $terms[] = [
-                'term' => $token,
-                'score' => $count * $scoreAdjustment
-            ];
-        }
-        return $terms;
-    }
-
-
-
-
     /**
      * Custom entity search filters
      */
similarity index 55%
rename from app/Entities/Managers/EntityContext.php
rename to app/Entities/Tools/ShelfContext.php
index 551cd1a100c142f26cea88d8e86aaa282c768fa0..f3849bbb4741e8c075c122bca3274f242f6d0e28 100644 (file)
@@ -1,29 +1,18 @@
-<?php namespace BookStack\Entities\Managers;
+<?php namespace BookStack\Entities\Tools;
 
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use Illuminate\Session\Store;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
 
-class EntityContext
+class ShelfContext
 {
-    protected $session;
-
     protected $KEY_SHELF_CONTEXT_ID = 'context_bookshelf_id';
 
-    /**
-     * EntityContextManager constructor.
-     */
-    public function __construct(Store $session)
-    {
-        $this->session = $session;
-    }
-
     /**
      * Get the current bookshelf context for the given book.
      */
     public function getContextualShelfForBook(Book $book): ?Bookshelf
     {
-        $contextBookshelfId = $this->session->get($this->KEY_SHELF_CONTEXT_ID, null);
+        $contextBookshelfId = session()->get($this->KEY_SHELF_CONTEXT_ID, null);
 
         if (!is_int($contextBookshelfId)) {
             return null;
@@ -37,11 +26,10 @@ class EntityContext
 
     /**
      * Store the current contextual shelf ID.
-     * @param int $shelfId
      */
     public function setShelfContext(int $shelfId)
     {
-        $this->session->put($this->KEY_SHELF_CONTEXT_ID, $shelfId);
+        session()->put($this->KEY_SHELF_CONTEXT_ID, $shelfId);
     }
 
     /**
@@ -49,6 +37,6 @@ class EntityContext
      */
     public function clearShelfContext()
     {
-        $this->session->forget($this->KEY_SHELF_CONTEXT_ID);
+        session()->forget($this->KEY_SHELF_CONTEXT_ID);
     }
 }
diff --git a/app/Entities/Tools/SiblingFetcher.php b/app/Entities/Tools/SiblingFetcher.php
new file mode 100644 (file)
index 0000000..6964fa2
--- /dev/null
@@ -0,0 +1,47 @@
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use Illuminate\Support\Collection;
+
+class SiblingFetcher
+{
+
+    /**
+     * Search among the siblings of the entity of given type and id.
+     */
+    public function fetch(string $entityType, int $entityId): Collection
+    {
+        $entity = (new EntityProvider)->get($entityType)->visible()->findOrFail($entityId);
+        $entities = [];
+
+        // Page in chapter
+        if ($entity->isA('page') && $entity->chapter) {
+            $entities = $entity->chapter->getVisiblePages();
+        }
+
+        // Page in book or chapter
+        if (($entity->isA('page') && !$entity->chapter) || $entity->isA('chapter')) {
+            $entities = $entity->book->getDirectChildren();
+        }
+
+        // Book
+        // Gets just the books in a shelf if shelf is in context
+        if ($entity->isA('book')) {
+            $contextShelf = (new ShelfContext)->getContextualShelfForBook($entity);
+            if ($contextShelf) {
+                $entities = $contextShelf->visibleBooks()->get();
+            } else {
+                $entities = Book::visible()->get();
+            }
+        }
+
+        // Shelve
+        if ($entity->isA('bookshelf')) {
+            $entities = Bookshelf::visible()->get();
+        }
+
+        return $entities;
+    }
+}
diff --git a/app/Entities/Tools/SlugGenerator.php b/app/Entities/Tools/SlugGenerator.php
new file mode 100644 (file)
index 0000000..7075bc7
--- /dev/null
@@ -0,0 +1,52 @@
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\Models\Entity;
+use Illuminate\Support\Str;
+
+class SlugGenerator
+{
+
+    /**
+     * Generate a fresh slug for the given entity.
+     * The slug will generated so it does not conflict within the same parent item.
+     */
+    public function generate(Entity $entity): string
+    {
+        $slug = $this->formatNameAsSlug($entity->name);
+        while ($this->slugInUse($slug, $entity)) {
+            $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
+        }
+        return $slug;
+    }
+
+    /**
+     * Format a name as a url slug.
+     */
+    protected function formatNameAsSlug(string $name): string
+    {
+        $slug = Str::slug($name);
+        if ($slug === "") {
+            $slug = substr(md5(rand(1, 500)), 0, 5);
+        }
+        return $slug;
+    }
+
+    /**
+     * Check if a slug is already in-use for this
+     * type of model within the same parent.
+     */
+    protected function slugInUse(string $slug, Entity $entity): bool
+    {
+        $query = $entity->newQuery()->where('slug', '=', $slug);
+
+        if ($entity instanceof BookChild) {
+            $query->where('book_id', '=', $entity->book_id);
+        }
+
+        if ($entity->id) {
+            $query->where('id', '!=', $entity->id);
+        }
+
+        return $query->count() > 0;
+    }
+}
diff --git a/app/Entities/Tools/TrashCan.php b/app/Entities/Tools/TrashCan.php
new file mode 100644 (file)
index 0000000..d2447ec
--- /dev/null
@@ -0,0 +1,325 @@
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Deletion;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\HasCoverImage;
+use BookStack\Entities\Models\Page;
+use BookStack\Exceptions\NotifyException;
+use BookStack\Facades\Activity;
+use BookStack\Uploads\AttachmentService;
+use BookStack\Uploads\ImageService;
+use Exception;
+use Illuminate\Support\Carbon;
+
+class TrashCan
+{
+
+    /**
+     * Send a shelf to the recycle bin.
+     */
+    public function softDestroyShelf(Bookshelf $shelf)
+    {
+        Deletion::createForEntity($shelf);
+        $shelf->delete();
+    }
+
+    /**
+     * Send a book to the recycle bin.
+     * @throws Exception
+     */
+    public function softDestroyBook(Book $book)
+    {
+        Deletion::createForEntity($book);
+
+        foreach ($book->pages as $page) {
+            $this->softDestroyPage($page, false);
+        }
+
+        foreach ($book->chapters as $chapter) {
+            $this->softDestroyChapter($chapter, false);
+        }
+
+        $book->delete();
+    }
+
+    /**
+     * Send a chapter to the recycle bin.
+     * @throws Exception
+     */
+    public function softDestroyChapter(Chapter $chapter, bool $recordDelete = true)
+    {
+        if ($recordDelete) {
+            Deletion::createForEntity($chapter);
+        }
+
+        if (count($chapter->pages) > 0) {
+            foreach ($chapter->pages as $page) {
+                $this->softDestroyPage($page, false);
+            }
+        }
+
+        $chapter->delete();
+    }
+
+    /**
+     * Send a page to the recycle bin.
+     * @throws Exception
+     */
+    public function softDestroyPage(Page $page, bool $recordDelete = true)
+    {
+        if ($recordDelete) {
+            Deletion::createForEntity($page);
+        }
+
+        // Check if set as custom homepage & remove setting if not used or throw error if active
+        $customHome = setting('app-homepage', '0:');
+        if (intval($page->id) === intval(explode(':', $customHome)[0])) {
+            if (setting('app-homepage-type') === 'page') {
+                throw new NotifyException(trans('errors.page_custom_home_deletion'), $page->getUrl());
+            }
+            setting()->remove('app-homepage');
+        }
+
+        $page->delete();
+    }
+
+    /**
+     * Remove a bookshelf from the system.
+     * @throws Exception
+     */
+    protected function destroyShelf(Bookshelf $shelf): int
+    {
+        $this->destroyCommonRelations($shelf);
+        $shelf->forceDelete();
+        return 1;
+    }
+
+    /**
+     * Remove a book from the system.
+     * Destroys any child chapters and pages.
+     * @throws Exception
+     */
+    protected function destroyBook(Book $book): int
+    {
+        $count = 0;
+        $pages = $book->pages()->withTrashed()->get();
+        foreach ($pages as $page) {
+            $this->destroyPage($page);
+            $count++;
+        }
+
+        $chapters = $book->chapters()->withTrashed()->get();
+        foreach ($chapters as $chapter) {
+            $this->destroyChapter($chapter);
+            $count++;
+        }
+
+        $this->destroyCommonRelations($book);
+        $book->forceDelete();
+        return $count + 1;
+    }
+
+    /**
+     * Remove a chapter from the system.
+     * Destroys all pages within.
+     * @throws Exception
+     */
+    protected function destroyChapter(Chapter $chapter): int
+    {
+        $count = 0;
+        $pages = $chapter->pages()->withTrashed()->get();
+        if (count($pages)) {
+            foreach ($pages as $page) {
+                $this->destroyPage($page);
+                $count++;
+            }
+        }
+
+        $this->destroyCommonRelations($chapter);
+        $chapter->forceDelete();
+        return $count + 1;
+    }
+
+    /**
+     * Remove a page from the system.
+     * @throws Exception
+     */
+    protected function destroyPage(Page $page): int
+    {
+        $this->destroyCommonRelations($page);
+
+        // Delete Attached Files
+        $attachmentService = app(AttachmentService::class);
+        foreach ($page->attachments as $attachment) {
+            $attachmentService->deleteFile($attachment);
+        }
+
+        $page->forceDelete();
+        return 1;
+    }
+
+    /**
+     * Get the total counts of those that have been trashed
+     * but not yet fully deleted (In recycle bin).
+     */
+    public function getTrashedCounts(): array
+    {
+        $counts = [];
+
+        /** @var Entity $instance */
+        foreach ((new EntityProvider)->all() as $key => $instance) {
+            $counts[$key] = $instance->newQuery()->onlyTrashed()->count();
+        }
+
+        return $counts;
+    }
+
+    /**
+     * Destroy all items that have pending deletions.
+     * @throws Exception
+     */
+    public function empty(): int
+    {
+        $deletions = Deletion::all();
+        $deleteCount = 0;
+        foreach ($deletions as $deletion) {
+            $deleteCount += $this->destroyFromDeletion($deletion);
+        }
+        return $deleteCount;
+    }
+
+    /**
+     * Destroy an element from the given deletion model.
+     * @throws Exception
+     */
+    public function destroyFromDeletion(Deletion $deletion): int
+    {
+        // We directly load the deletable element here just to ensure it still
+        // exists in the event it has already been destroyed during this request.
+        $entity = $deletion->deletable()->first();
+        $count = 0;
+        if ($entity) {
+            $count = $this->destroyEntity($deletion->deletable);
+        }
+        $deletion->delete();
+        return $count;
+    }
+
+    /**
+     * Restore the content within the given deletion.
+     * @throws Exception
+     */
+    public function restoreFromDeletion(Deletion $deletion): int
+    {
+        $shouldRestore = true;
+        $restoreCount = 0;
+        $parent = $deletion->deletable->getParent();
+
+        if ($parent && $parent->trashed()) {
+            $shouldRestore = false;
+        }
+
+        if ($shouldRestore) {
+            $restoreCount = $this->restoreEntity($deletion->deletable);
+        }
+
+        $deletion->delete();
+        return $restoreCount;
+    }
+
+    /**
+     * Automatically clear old content from the recycle bin
+     * depending on the configured lifetime.
+     * Returns the total number of deleted elements.
+     * @throws Exception
+     */
+    public function autoClearOld(): int
+    {
+        $lifetime = intval(config('app.recycle_bin_lifetime'));
+        if ($lifetime < 0) {
+            return 0;
+        }
+
+        $clearBeforeDate = Carbon::now()->addSeconds(10)->subDays($lifetime);
+        $deleteCount = 0;
+
+        $deletionsToRemove = Deletion::query()->where('created_at', '<', $clearBeforeDate)->get();
+        foreach ($deletionsToRemove as $deletion) {
+            $deleteCount += $this->destroyFromDeletion($deletion);
+        }
+
+        return $deleteCount;
+    }
+
+    /**
+     * Restore an entity so it is essentially un-deleted.
+     * Deletions on restored child elements will be removed during this restoration.
+     */
+    protected function restoreEntity(Entity $entity): int
+    {
+        $count = 1;
+        $entity->restore();
+
+        $restoreAction = function ($entity) use (&$count) {
+            if ($entity->deletions_count > 0) {
+                $entity->deletions()->delete();
+            }
+
+            $entity->restore();
+            $count++;
+        };
+
+        if ($entity->isA('chapter') || $entity->isA('book')) {
+            $entity->pages()->withTrashed()->withCount('deletions')->get()->each($restoreAction);
+        }
+
+        if ($entity->isA('book')) {
+            $entity->chapters()->withTrashed()->withCount('deletions')->get()->each($restoreAction);
+        }
+
+        return $count;
+    }
+
+    /**
+     * Destroy the given entity.
+     */
+    protected function destroyEntity(Entity $entity): int
+    {
+        if ($entity->isA('page')) {
+            return $this->destroyPage($entity);
+        }
+        if ($entity->isA('chapter')) {
+            return $this->destroyChapter($entity);
+        }
+        if ($entity->isA('book')) {
+            return $this->destroyBook($entity);
+        }
+        if ($entity->isA('shelf')) {
+            return $this->destroyShelf($entity);
+        }
+    }
+
+    /**
+     * Update entity relations to remove or update outstanding connections.
+     */
+    protected function destroyCommonRelations(Entity $entity)
+    {
+        Activity::removeEntity($entity);
+        $entity->views()->delete();
+        $entity->permissions()->delete();
+        $entity->tags()->delete();
+        $entity->comments()->delete();
+        $entity->jointPermissions()->delete();
+        $entity->searchTerms()->delete();
+        $entity->deletions()->delete();
+
+        if ($entity instanceof HasCoverImage && $entity->cover) {
+            $imageService = app()->make(ImageService::class);
+            $imageService->destroy($entity->cover);
+        }
+    }
+}
diff --git a/app/Exceptions/ApiAuthException.php b/app/Exceptions/ApiAuthException.php
new file mode 100644 (file)
index 0000000..cc68ba8
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+namespace BookStack\Exceptions;
+
+class ApiAuthException extends UnauthorizedException {
+
+}
\ No newline at end of file
index 70a53497599d36216ae82bcdd787ab1901247043..57078522b748bbbf905b1836705a4d24c0695bbb 100644 (file)
@@ -7,6 +7,8 @@ use Illuminate\Auth\Access\AuthorizationException;
 use Illuminate\Auth\AuthenticationException;
 use Illuminate\Database\Eloquent\ModelNotFoundException;
 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 use Symfony\Component\HttpKernel\Exception\HttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -23,6 +25,7 @@ class Handler extends ExceptionHandler
         HttpException::class,
         ModelNotFoundException::class,
         ValidationException::class,
+        NotFoundException::class,
     ];
 
     /**
@@ -47,10 +50,17 @@ class Handler extends ExceptionHandler
      */
     public function render($request, Exception $e)
     {
+        if ($this->isApiRequest($request)) {
+            return $this->renderApiException($e);
+        }
+
         // Handle notify exceptions which will redirect to the
         // specified location then show a notification message.
         if ($this->isExceptionType($e, NotifyException::class)) {
-            session()->flash('error', $this->getOriginalMessage($e));
+            $message = $this->getOriginalMessage($e);
+            if (!empty($message)) {
+                session()->flash('error', $message);
+            }
             return redirect($e->redirectLocation);
         }
 
@@ -70,6 +80,41 @@ class Handler extends ExceptionHandler
         return parent::render($request, $e);
     }
 
+    /**
+     * Check if the given request is an API request.
+     */
+    protected function isApiRequest(Request $request): bool
+    {
+        return strpos($request->path(), 'api/') === 0;
+    }
+
+    /**
+     * Render an exception when the API is in use.
+     */
+    protected function renderApiException(Exception $e): JsonResponse
+    {
+        $code = $e->getCode() === 0 ? 500 : $e->getCode();
+        $headers = [];
+        if ($e instanceof HttpException) {
+            $code = $e->getStatusCode();
+            $headers = $e->getHeaders();
+        }
+
+        $responseData = [
+            'error' => [
+                'message' => $e->getMessage(),
+            ]
+        ];
+
+        if ($e instanceof ValidationException) {
+            $responseData['error']['validation'] = $e->errors();
+            $code = $e->status;
+        }
+
+        $responseData['error']['code'] = $code;
+        return new JsonResponse($responseData, $code, $headers);
+    }
+
     /**
      * Check the exception chain to compare against the original exception type.
      * @param Exception $e
diff --git a/app/Exceptions/JsonDebugException.php b/app/Exceptions/JsonDebugException.php
new file mode 100644 (file)
index 0000000..6314533
--- /dev/null
@@ -0,0 +1,25 @@
+<?php namespace BookStack\Exceptions;
+
+use Exception;
+
+class JsonDebugException extends Exception
+{
+
+    protected $data;
+
+    /**
+     * JsonDebugException constructor.
+     */
+    public function __construct($data)
+    {
+        $this->data = $data;
+    }
+
+    /**
+     * Covert this exception into a response.
+     */
+    public function render()
+    {
+        return response()->json($this->data);
+    }
+}
\ No newline at end of file
diff --git a/app/Exceptions/LoginAttemptEmailNeededException.php b/app/Exceptions/LoginAttemptEmailNeededException.php
new file mode 100644 (file)
index 0000000..7ffb0c0
--- /dev/null
@@ -0,0 +1,6 @@
+<?php namespace BookStack\Exceptions;
+
+class LoginAttemptEmailNeededException extends LoginAttemptException
+{
+
+}
diff --git a/app/Exceptions/LoginAttemptException.php b/app/Exceptions/LoginAttemptException.php
new file mode 100644 (file)
index 0000000..22cc980
--- /dev/null
@@ -0,0 +1,6 @@
+<?php namespace BookStack\Exceptions;
+
+class LoginAttemptException extends \Exception
+{
+
+}
index 78ffde05c07e28655ad2cb25ca5a4f04553d2dfc..4f810596099cf2132093677b571ce40bf44a5aa8 100644 (file)
@@ -8,8 +8,6 @@ class NotifyException extends \Exception
 
     /**
      * NotifyException constructor.
-     * @param string $message
-     * @param string    $redirectLocation
      */
     public function __construct(string $message, string $redirectLocation = "/")
     {
similarity index 50%
rename from app/Exceptions/AuthException.php
rename to app/Exceptions/SamlException.php
index 2ab7d4616e5efe8a2b9f95e71334b7a363fb9f00..13db23f27bbd02ddb7fe5479f7892f1e9d4038c8 100644 (file)
@@ -1,6 +1,6 @@
 <?php namespace BookStack\Exceptions;
 
-class AuthException extends PrettyException
+class SamlException extends NotifyException
 {
 
 }
diff --git a/app/Exceptions/UnauthorizedException.php b/app/Exceptions/UnauthorizedException.php
new file mode 100644 (file)
index 0000000..525b431
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+
+namespace BookStack\Exceptions;
+
+use Exception;
+
+class UnauthorizedException extends Exception
+{
+
+    /**
+     * ApiAuthException constructor.
+     */
+    public function __construct($message, $code = 401)
+    {
+        parent::__construct($message, $code);
+    }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/ApiController.php b/app/Http/Controllers/Api/ApiController.php
new file mode 100644 (file)
index 0000000..0a3d894
--- /dev/null
@@ -0,0 +1,30 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Api\ListingResponseBuilder;
+use BookStack\Http\Controllers\Controller;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Http\JsonResponse;
+
+abstract class ApiController extends Controller
+{
+
+    protected $rules = [];
+
+    /**
+     * Provide a paginated listing JSON response in a standard format
+     * taking into account any pagination parameters passed by the user.
+     */
+    protected function apiListingResponse(Builder $query, array $fields): JsonResponse
+    {
+        $listing = new ListingResponseBuilder($query, request(), $fields);
+        return $listing->toResponse();
+    }
+
+    /**
+     * Get the validation rules for this controller.
+     */
+    public function getValdationRules(): array
+    {
+        return $this->rules;
+    }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/ApiDocsController.php b/app/Http/Controllers/Api/ApiDocsController.php
new file mode 100644 (file)
index 0000000..80e86e1
--- /dev/null
@@ -0,0 +1,29 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Api\ApiDocsGenerator;
+
+class ApiDocsController extends ApiController
+{
+
+    /**
+     * Load the docs page for the API.
+     */
+    public function display()
+    {
+        $docs = ApiDocsGenerator::generateConsideringCache();
+        $this->setPageTitle(trans('settings.users_api_tokens_docs'));
+        return view('api-docs.index', [
+            'docs' => $docs,
+        ]);
+    }
+
+    /**
+     * Show a JSON view of the API docs data.
+     */
+    public function json()
+    {
+        $docs = ApiDocsGenerator::generateConsideringCache();
+        return response()->json($docs);
+    }
+
+}
diff --git a/app/Http/Controllers/Api/BookApiController.php b/app/Http/Controllers/Api/BookApiController.php
new file mode 100644 (file)
index 0000000..1b9bddb
--- /dev/null
@@ -0,0 +1,94 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Repos\BookRepo;
+use BookStack\Exceptions\NotifyException;
+use Illuminate\Contracts\Container\BindingResolutionException;
+use Illuminate\Http\Request;
+use Illuminate\Validation\ValidationException;
+
+class BookApiController extends ApiController
+{
+
+    protected $bookRepo;
+
+    protected $rules = [
+        'create' => [
+            'name' => 'required|string|max:255',
+            'description' => 'string|max:1000',
+            'tags' => 'array',
+        ],
+        'update' => [
+            'name' => 'string|min:1|max:255',
+            'description' => 'string|max:1000',
+            'tags' => 'array',
+        ],
+    ];
+
+    public function __construct(BookRepo $bookRepo)
+    {
+        $this->bookRepo = $bookRepo;
+    }
+
+    /**
+     * Get a listing of books visible to the user.
+     */
+    public function list()
+    {
+        $books = Book::visible();
+        return $this->apiListingResponse($books, [
+            'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by', 'image_id',
+        ]);
+    }
+
+    /**
+     * Create a new book in the system.
+     * @throws ValidationException
+     */
+    public function create(Request $request)
+    {
+        $this->checkPermission('book-create-all');
+        $requestData = $this->validate($request, $this->rules['create']);
+
+        $book = $this->bookRepo->create($requestData);
+        return response()->json($book);
+    }
+
+    /**
+     * View the details of a single book.
+     */
+    public function read(string $id)
+    {
+        $book = Book::visible()->with(['tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy'])->findOrFail($id);
+        return response()->json($book);
+    }
+
+    /**
+     * Update the details of a single book.
+     * @throws ValidationException
+     */
+    public function update(Request $request, string $id)
+    {
+        $book = Book::visible()->findOrFail($id);
+        $this->checkOwnablePermission('book-update', $book);
+
+        $requestData = $this->validate($request, $this->rules['update']);
+        $book = $this->bookRepo->update($book, $requestData);
+
+        return response()->json($book);
+    }
+
+    /**
+     * Delete a single book.
+     * This will typically send the book to the recycle bin.
+     * @throws \Exception
+     */
+    public function delete(string $id)
+    {
+        $book = Book::visible()->findOrFail($id);
+        $this->checkOwnablePermission('book-delete', $book);
+
+        $this->bookRepo->destroy($book);
+        return response('', 204);
+    }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/BookExportApiController.php b/app/Http/Controllers/Api/BookExportApiController.php
new file mode 100644 (file)
index 0000000..3d813c4
--- /dev/null
@@ -0,0 +1,47 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\ExportFormatter;
+use Throwable;
+
+class BookExportApiController extends ApiController
+{
+    protected $exportFormatter;
+
+    public function __construct(ExportFormatter $exportFormatter)
+    {
+        $this->exportFormatter = $exportFormatter;
+    }
+
+    /**
+     * Export a book as a PDF file.
+     * @throws Throwable
+     */
+    public function exportPdf(int $id)
+    {
+        $book = Book::visible()->findOrFail($id);
+        $pdfContent = $this->exportFormatter->bookToPdf($book);
+        return $this->downloadResponse($pdfContent, $book->slug . '.pdf');
+    }
+
+    /**
+     * Export a book as a contained HTML file.
+     * @throws Throwable
+     */
+    public function exportHtml(int $id)
+    {
+        $book = Book::visible()->findOrFail($id);
+        $htmlContent = $this->exportFormatter->bookToContainedHtml($book);
+        return $this->downloadResponse($htmlContent, $book->slug . '.html');
+    }
+
+    /**
+     * Export a book as a plain text file.
+     */
+    public function exportPlainText(int $id)
+    {
+        $book = Book::visible()->findOrFail($id);
+        $textContent = $this->exportFormatter->bookToPlainText($book);
+        return $this->downloadResponse($textContent, $book->slug . '.txt');
+    }
+}
diff --git a/app/Http/Controllers/Api/BookshelfApiController.php b/app/Http/Controllers/Api/BookshelfApiController.php
new file mode 100644 (file)
index 0000000..57461fc
--- /dev/null
@@ -0,0 +1,115 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Repos\BookshelfRepo;
+use BookStack\Entities\Models\Bookshelf;
+use Exception;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Http\Request;
+use Illuminate\Validation\ValidationException;
+
+class BookshelfApiController extends ApiController
+{
+
+    /**
+     * @var BookshelfRepo
+     */
+    protected $bookshelfRepo;
+
+    protected $rules = [
+        'create' => [
+            'name' => 'required|string|max:255',
+            'description' => 'string|max:1000',
+            'books' => 'array',
+        ],
+        'update' => [
+            'name' => 'string|min:1|max:255',
+            'description' => 'string|max:1000',
+            'books' => 'array',
+        ],
+    ];
+
+    /**
+     * BookshelfApiController constructor.
+     */
+    public function __construct(BookshelfRepo $bookshelfRepo)
+    {
+        $this->bookshelfRepo = $bookshelfRepo;
+    }
+
+    /**
+     * Get a listing of shelves visible to the user.
+     */
+    public function list()
+    {
+        $shelves = Bookshelf::visible();
+        return $this->apiListingResponse($shelves, [
+            'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by', 'image_id',
+        ]);
+    }
+
+    /**
+     * Create a new shelf in the system.
+     * An array of books IDs can be provided in the request. These
+     * will be added to the shelf in the same order as provided.
+     * @throws ValidationException
+     */
+    public function create(Request $request)
+    {
+        $this->checkPermission('bookshelf-create-all');
+        $requestData = $this->validate($request, $this->rules['create']);
+
+        $bookIds = $request->get('books', []);
+        $shelf = $this->bookshelfRepo->create($requestData, $bookIds);
+
+        return response()->json($shelf);
+    }
+
+    /**
+     * View the details of a single shelf.
+     */
+    public function read(string $id)
+    {
+        $shelf = Bookshelf::visible()->with([
+            'tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy',
+            'books' => function (BelongsToMany $query) {
+                $query->visible()->get(['id', 'name', 'slug']);
+            }
+        ])->findOrFail($id);
+        return response()->json($shelf);
+    }
+
+    /**
+     * Update the details of a single shelf.
+     * An array of books IDs can be provided in the request. These
+     * will be added to the shelf in the same order as provided and overwrite
+     * any existing book assignments.
+     * @throws ValidationException
+     */
+    public function update(Request $request, string $id)
+    {
+        $shelf = Bookshelf::visible()->findOrFail($id);
+        $this->checkOwnablePermission('bookshelf-update', $shelf);
+
+        $requestData = $this->validate($request, $this->rules['update']);
+        $bookIds = $request->get('books', null);
+
+        $shelf = $this->bookshelfRepo->update($shelf, $requestData, $bookIds);
+        return response()->json($shelf);
+    }
+
+
+
+    /**
+     * Delete a single shelf.
+     * This will typically send the shelf to the recycle bin.
+     * @throws Exception
+     */
+    public function delete(string $id)
+    {
+        $shelf = Bookshelf::visible()->findOrFail($id);
+        $this->checkOwnablePermission('bookshelf-delete', $shelf);
+
+        $this->bookshelfRepo->destroy($shelf);
+        return response('', 204);
+    }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/ChapterApiController.php b/app/Http/Controllers/Api/ChapterApiController.php
new file mode 100644 (file)
index 0000000..e58c1c8
--- /dev/null
@@ -0,0 +1,100 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Repos\ChapterRepo;
+use BookStack\Facades\Activity;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Http\Request;
+
+class ChapterApiController extends ApiController
+{
+    protected $chapterRepo;
+
+    protected $rules = [
+        'create' => [
+            'book_id' => 'required|integer',
+            'name' => 'required|string|max:255',
+            'description' => 'string|max:1000',
+            'tags' => 'array',
+        ],
+        'update' => [
+            'book_id' => 'integer',
+            'name' => 'string|min:1|max:255',
+            'description' => 'string|max:1000',
+            'tags' => 'array',
+        ],
+    ];
+
+    /**
+     * ChapterController constructor.
+     */
+    public function __construct(ChapterRepo $chapterRepo)
+    {
+        $this->chapterRepo = $chapterRepo;
+    }
+
+    /**
+     * Get a listing of chapters visible to the user.
+     */
+    public function list()
+    {
+        $chapters = Chapter::visible();
+        return $this->apiListingResponse($chapters, [
+            'id', 'book_id', 'name', 'slug', 'description', 'priority',
+            'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by',
+        ]);
+    }
+
+    /**
+     * Create a new chapter in the system.
+     */
+    public function create(Request $request)
+    {
+        $this->validate($request, $this->rules['create']);
+
+        $bookId = $request->get('book_id');
+        $book = Book::visible()->findOrFail($bookId);
+        $this->checkOwnablePermission('chapter-create', $book);
+
+        $chapter = $this->chapterRepo->create($request->all(), $book);
+        return response()->json($chapter->load(['tags']));
+    }
+
+    /**
+     * View the details of a single chapter.
+     */
+    public function read(string $id)
+    {
+        $chapter = Chapter::visible()->with(['tags', 'createdBy', 'updatedBy', 'ownedBy', 'pages' => function (HasMany $query) {
+            $query->visible()->get(['id', 'name', 'slug']);
+        }])->findOrFail($id);
+        return response()->json($chapter);
+    }
+
+    /**
+     * Update the details of a single chapter.
+     */
+    public function update(Request $request, string $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $this->checkOwnablePermission('chapter-update', $chapter);
+
+        $updatedChapter = $this->chapterRepo->update($chapter, $request->all());
+        return response()->json($updatedChapter->load(['tags']));
+    }
+
+    /**
+     * Delete a chapter.
+     * This will typically send the chapter to the recycle bin.
+     */
+    public function delete(string $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $this->checkOwnablePermission('chapter-delete', $chapter);
+
+        $this->chapterRepo->destroy($chapter);
+        return response('', 204);
+    }
+}
diff --git a/app/Http/Controllers/Api/ChapterExportApiController.php b/app/Http/Controllers/Api/ChapterExportApiController.php
new file mode 100644 (file)
index 0000000..afdfe55
--- /dev/null
@@ -0,0 +1,51 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Tools\ExportFormatter;
+use BookStack\Entities\Repos\BookRepo;
+use Throwable;
+
+class ChapterExportApiController extends ApiController
+{
+    protected $exportFormatter;
+
+    /**
+     * ChapterExportController constructor.
+     */
+    public function __construct(ExportFormatter $exportFormatter)
+    {
+        $this->exportFormatter = $exportFormatter;
+    }
+
+    /**
+     * Export a chapter as a PDF file.
+     * @throws Throwable
+     */
+    public function exportPdf(int $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $pdfContent = $this->exportFormatter->chapterToPdf($chapter);
+        return $this->downloadResponse($pdfContent, $chapter->slug . '.pdf');
+    }
+
+    /**
+     * Export a chapter as a contained HTML file.
+     * @throws Throwable
+     */
+    public function exportHtml(int $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $htmlContent = $this->exportFormatter->chapterToContainedHtml($chapter);
+        return $this->downloadResponse($htmlContent, $chapter->slug . '.html');
+    }
+
+    /**
+     * Export a chapter as a plain text file.
+     */
+    public function exportPlainText(int $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $textContent = $this->exportFormatter->chapterToPlainText($chapter);
+        return $this->downloadResponse($textContent, $chapter->slug . '.txt');
+    }
+}
diff --git a/app/Http/Controllers/Api/PageApiController.php b/app/Http/Controllers/Api/PageApiController.php
new file mode 100644 (file)
index 0000000..a6db058
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+
+namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Repos\PageRepo;
+use BookStack\Exceptions\PermissionsException;
+use Exception;
+use Illuminate\Http\Request;
+
+class PageApiController extends ApiController
+{
+    protected $pageRepo;
+
+    protected $rules = [
+        'create' => [
+            'book_id' => 'required_without:chapter_id|integer',
+            'chapter_id' => 'required_without:book_id|integer',
+            'name' => 'required|string|max:255',
+            'html' => 'required_without:markdown|string',
+            'markdown' => 'required_without:html|string',
+            'tags' => 'array',
+        ],
+        'update' => [
+            'book_id' => 'required|integer',
+            'chapter_id' => 'required|integer',
+            'name' => 'string|min:1|max:255',
+            'html' => 'string',
+            'markdown' => 'string',
+            'tags' => 'array',
+        ],
+    ];
+
+    public function __construct(PageRepo $pageRepo)
+    {
+        $this->pageRepo = $pageRepo;
+    }
+
+    /**
+     * Get a listing of pages visible to the user.
+     */
+    public function list()
+    {
+        $pages = Page::visible();
+        return $this->apiListingResponse($pages, [
+            'id', 'book_id', 'chapter_id', 'name', 'slug', 'priority',
+            'draft', 'template',
+            'created_at', 'updated_at',
+            'created_by', 'updated_by', 'owned_by',
+        ]);
+    }
+
+    /**
+     * Create a new page in the system.
+     *
+     * The ID of a parent book or chapter is required to indicate
+     * where this page should be located.
+     *
+     * Any HTML content provided should be kept to a single-block depth of plain HTML
+     * elements to remain compatible with the BookStack front-end and editors.
+     */
+    public function create(Request $request)
+    {
+        $this->validate($request, $this->rules['create']);
+
+        if ($request->has('chapter_id')) {
+            $parent = Chapter::visible()->findOrFail($request->get('chapter_id'));
+        } else {
+            $parent = Book::visible()->findOrFail($request->get('book_id'));
+        }
+        $this->checkOwnablePermission('page-create', $parent);
+
+        $draft = $this->pageRepo->getNewDraftPage($parent);
+        $this->pageRepo->publishDraft($draft, $request->only(array_keys($this->rules['create'])));
+
+        return response()->json($draft->forJsonDisplay());
+    }
+
+    /**
+     * View the details of a single page.
+     *
+     * Pages will always have HTML content. They may have markdown content
+     * if the markdown editor was used to last update the page.
+     */
+    public function read(string $id)
+    {
+        $page = $this->pageRepo->getById($id, []);
+        return response()->json($page->forJsonDisplay());
+    }
+
+    /**
+     * Update the details of a single page.
+     *
+     * See the 'create' action for details on the provided HTML/Markdown.
+     * Providing a 'book_id' or 'chapter_id' property will essentially move
+     * the page into that parent element if you have permissions to do so.
+     */
+    public function update(Request $request, string $id)
+    {
+        $page = $this->pageRepo->getById($id, []);
+        $this->checkOwnablePermission('page-update', $page);
+
+        $parent = null;
+        if ($request->has('chapter_id')) {
+            $parent = Chapter::visible()->findOrFail($request->get('chapter_id'));
+        } else if ($request->has('book_id')) {
+            $parent = Book::visible()->findOrFail($request->get('book_id'));
+        }
+
+        if ($parent && !$parent->matches($page->getParent())) {
+            $this->checkOwnablePermission('page-delete', $page);
+            try {
+                $this->pageRepo->move($page, $parent->getType() . ':' . $parent->id);
+            } catch (Exception $exception) {
+                if ($exception instanceof  PermissionsException) {
+                    $this->showPermissionError();
+                }
+
+                return $this->jsonError(trans('errors.selected_book_chapter_not_found'));
+            }
+        }
+
+        $updatedPage = $this->pageRepo->update($page, $request->all());
+        return response()->json($updatedPage->forJsonDisplay());
+    }
+
+    /**
+     * Delete a page.
+     * This will typically send the page to the recycle bin.
+     */
+    public function delete(string $id)
+    {
+        $page = $this->pageRepo->getById($id, []);
+        $this->checkOwnablePermission('page-delete', $page);
+
+        $this->pageRepo->destroy($page);
+        return response('', 204);
+    }
+}
diff --git a/app/Http/Controllers/Api/PageExportApiController.php b/app/Http/Controllers/Api/PageExportApiController.php
new file mode 100644 (file)
index 0000000..7563092
--- /dev/null
@@ -0,0 +1,47 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\ExportFormatter;
+use Throwable;
+
+class PageExportApiController extends ApiController
+{
+    protected $exportFormatter;
+
+    public function __construct(ExportFormatter $exportFormatter)
+    {
+        $this->exportFormatter = $exportFormatter;
+    }
+
+    /**
+     * Export a page as a PDF file.
+     * @throws Throwable
+     */
+    public function exportPdf(int $id)
+    {
+        $page = Page::visible()->findOrFail($id);
+        $pdfContent = $this->exportFormatter->pageToPdf($page);
+        return $this->downloadResponse($pdfContent, $page->slug . '.pdf');
+    }
+
+    /**
+     * Export a page as a contained HTML file.
+     * @throws Throwable
+     */
+    public function exportHtml(int $id)
+    {
+        $page = Page::visible()->findOrFail($id);
+        $htmlContent = $this->exportFormatter->pageToContainedHtml($page);
+        return $this->downloadResponse($htmlContent, $page->slug . '.html');
+    }
+
+    /**
+     * Export a page as a plain text file.
+     */
+    public function exportPlainText(int $id)
+    {
+        $page = Page::visible()->findOrFail($id);
+        $textContent = $this->exportFormatter->pageToPlainText($page);
+        return $this->downloadResponse($textContent, $page->slug . '.txt');
+    }
+}
index 8f5da49ed83c10c5979b9e4b8eaa74ac324e14b9..04e89ac5d1a0db18407398aabd3689951822eee8 100644 (file)
@@ -8,6 +8,7 @@ use BookStack\Uploads\AttachmentService;
 use Exception;
 use Illuminate\Contracts\Filesystem\FileNotFoundException;
 use Illuminate\Http\Request;
+use Illuminate\Support\MessageBag;
 use Illuminate\Validation\ValidationException;
 
 class AttachmentController extends Controller
@@ -24,7 +25,6 @@ class AttachmentController extends Controller
         $this->attachmentService = $attachmentService;
         $this->attachment = $attachment;
         $this->pageRepo = $pageRepo;
-        parent::__construct();
     }
 
 
@@ -60,25 +60,17 @@ class AttachmentController extends Controller
     /**
      * Update an uploaded attachment.
      * @throws ValidationException
-     * @throws NotFoundException
      */
     public function uploadUpdate(Request $request, $attachmentId)
     {
         $this->validate($request, [
-            'uploaded_to' => 'required|integer|exists:pages,id',
             'file' => 'required|file'
         ]);
 
-        $pageId = $request->get('uploaded_to');
-        $page = $this->pageRepo->getById($pageId);
-        $attachment = $this->attachment->findOrFail($attachmentId);
-
-        $this->checkOwnablePermission('page-update', $page);
+        $attachment = $this->attachment->newQuery()->findOrFail($attachmentId);
+        $this->checkOwnablePermission('view', $attachment->page);
+        $this->checkOwnablePermission('page-update', $attachment->page);
         $this->checkOwnablePermission('attachment-create', $attachment);
-        
-        if (intval($pageId) !== intval($attachment->uploaded_to)) {
-            return $this->jsonError(trans('errors.attachment_page_mismatch'));
-        }
 
         $uploadedFile = $request->file('file');
 
@@ -92,57 +84,87 @@ class AttachmentController extends Controller
     }
 
     /**
-     * Update the details of an existing file.
-     * @throws ValidationException
-     * @throws NotFoundException
+     * Get the update form for an attachment.
+     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
-    public function update(Request $request, $attachmentId)
+    public function getUpdateForm(string $attachmentId)
     {
-        $this->validate($request, [
-            'uploaded_to' => 'required|integer|exists:pages,id',
-            'name' => 'required|string|min:1|max:255',
-            'link' =>  'string|min:1|max:255'
-        ]);
-
-        $pageId = $request->get('uploaded_to');
-        $page = $this->pageRepo->getById($pageId);
         $attachment = $this->attachment->findOrFail($attachmentId);
 
-        $this->checkOwnablePermission('page-update', $page);
+        $this->checkOwnablePermission('page-update', $attachment->page);
         $this->checkOwnablePermission('attachment-create', $attachment);
 
-        if (intval($pageId) !== intval($attachment->uploaded_to)) {
-            return $this->jsonError(trans('errors.attachment_page_mismatch'));
+        return view('attachments.manager-edit-form', [
+            'attachment' => $attachment,
+        ]);
+    }
+
+    /**
+     * Update the details of an existing file.
+     */
+    public function update(Request $request, string $attachmentId)
+    {
+        $attachment = $this->attachment->newQuery()->findOrFail($attachmentId);
+
+        try {
+            $this->validate($request, [
+                'attachment_edit_name' => 'required|string|min:1|max:255',
+                'attachment_edit_url' =>  'string|min:1|max:255|safe_url'
+            ]);
+        } catch (ValidationException $exception) {
+            return response()->view('attachments.manager-edit-form', array_merge($request->only(['attachment_edit_name', 'attachment_edit_url']), [
+                'attachment' => $attachment,
+                'errors' => new MessageBag($exception->errors()),
+            ]), 422);
         }
 
-        $attachment = $this->attachmentService->updateFile($attachment, $request->all());
-        return response()->json($attachment);
+        $this->checkOwnablePermission('view', $attachment->page);
+        $this->checkOwnablePermission('page-update', $attachment->page);
+        $this->checkOwnablePermission('attachment-create', $attachment);
+
+        $attachment = $this->attachmentService->updateFile($attachment, [
+            'name' => $request->get('attachment_edit_name'),
+            'link' => $request->get('attachment_edit_url'),
+        ]);
+
+        return view('attachments.manager-edit-form', [
+            'attachment' => $attachment,
+        ]);
     }
 
     /**
      * Attach a link to a page.
-     * @throws ValidationException
      * @throws NotFoundException
      */
     public function attachLink(Request $request)
     {
-        $this->validate($request, [
-            'uploaded_to' => 'required|integer|exists:pages,id',
-            'name' => 'required|string|min:1|max:255',
-            'link' =>  'required|string|min:1|max:255'
-        ]);
+        $pageId = $request->get('attachment_link_uploaded_to');
+
+        try {
+            $this->validate($request, [
+                'attachment_link_uploaded_to' => 'required|integer|exists:pages,id',
+                'attachment_link_name' => 'required|string|min:1|max:255',
+                'attachment_link_url' =>  'required|string|min:1|max:255|safe_url'
+            ]);
+        } catch (ValidationException $exception) {
+            return response()->view('attachments.manager-link-form', array_merge($request->only(['attachment_link_name', 'attachment_link_url']), [
+                'pageId' => $pageId,
+                'errors' => new MessageBag($exception->errors()),
+            ]), 422);
+        }
 
-        $pageId = $request->get('uploaded_to');
         $page = $this->pageRepo->getById($pageId);
 
         $this->checkPermission('attachment-create-all');
         $this->checkOwnablePermission('page-update', $page);
 
-        $attachmentName = $request->get('name');
-        $link = $request->get('link');
-        $attachment = $this->attachmentService->saveNewFromLink($attachmentName, $link, $pageId);
+        $attachmentName = $request->get('attachment_link_name');
+        $link = $request->get('attachment_link_url');
+        $attachment = $this->attachmentService->saveNewFromLink($attachmentName, $link, intval($pageId));
 
-        return response()->json($attachment);
+        return view('attachments.manager-link-form', [
+            'pageId' => $pageId,
+        ]);
     }
 
     /**
@@ -152,7 +174,9 @@ class AttachmentController extends Controller
     {
         $page = $this->pageRepo->getById($pageId);
         $this->checkOwnablePermission('page-view', $page);
-        return response()->json($page->attachments);
+        return view('attachments.manager-list', [
+            'attachments' => $page->attachments->all(),
+        ]);
     }
 
     /**
@@ -163,14 +187,13 @@ class AttachmentController extends Controller
     public function sortForPage(Request $request, int $pageId)
     {
         $this->validate($request, [
-            'files' => 'required|array',
-            'files.*.id' => 'required|integer',
+            'order' => 'required|array',
         ]);
         $page = $this->pageRepo->getById($pageId);
         $this->checkOwnablePermission('page-update', $page);
 
-        $attachments = $request->get('files');
-        $this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
+        $attachmentOrder = $request->get('order');
+        $this->attachmentService->updateFileOrderWithinPage($attachmentOrder, $pageId);
         return response()->json(['message' => trans('entities.attachments_order_updated')]);
     }
 
@@ -179,7 +202,7 @@ class AttachmentController extends Controller
      * @throws FileNotFoundException
      * @throws NotFoundException
      */
-    public function get(int $attachmentId)
+    public function get(string $attachmentId)
     {
         $attachment = $this->attachment->findOrFail($attachmentId);
         try {
@@ -200,11 +223,9 @@ class AttachmentController extends Controller
 
     /**
      * Delete a specific attachment in the system.
-     * @param $attachmentId
-     * @return mixed
      * @throws Exception
      */
-    public function delete(int $attachmentId)
+    public function delete(string $attachmentId)
     {
         $attachment = $this->attachment->findOrFail($attachmentId);
         $this->checkOwnablePermission('attachment-delete', $attachment);
diff --git a/app/Http/Controllers/AuditLogController.php b/app/Http/Controllers/AuditLogController.php
new file mode 100644 (file)
index 0000000..eb6eecc
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+namespace BookStack\Http\Controllers;
+
+use BookStack\Actions\Activity;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+
+class AuditLogController extends Controller
+{
+
+    public function index(Request $request)
+    {
+        $this->checkPermission('settings-manage');
+        $this->checkPermission('users-manage');
+
+        $listDetails = [
+            'order' => $request->get('order', 'desc'),
+            'event' => $request->get('event', ''),
+            'sort' => $request->get('sort', 'created_at'),
+            'date_from' => $request->get('date_from', ''),
+            'date_to' => $request->get('date_to', ''),
+        ];
+
+        $query = Activity::query()
+            ->with([
+                'entity' => function ($query) {
+                    $query->withTrashed();
+                },
+                'user'
+            ])
+            ->orderBy($listDetails['sort'], $listDetails['order']);
+
+        if ($listDetails['event']) {
+            $query->where('type', '=', $listDetails['event']);
+        }
+
+        if ($listDetails['date_from']) {
+            $query->where('created_at', '>=', $listDetails['date_from']);
+        }
+        if ($listDetails['date_to']) {
+            $query->where('created_at', '<=', $listDetails['date_to']);
+        }
+
+        $activities = $query->paginate(100);
+        $activities->appends($listDetails);
+
+        $types = DB::table('activities')->select('type')->distinct()->pluck('type');
+        $this->setPageTitle(trans('settings.audit'));
+        return view('settings.audit', [
+            'activities' => $activities,
+            'listDetails' => $listDetails,
+            'activityTypes' => $types,
+        ]);
+    }
+}
index 099558eb77fdce133b307aaac327277bc02510f8..bffeb5f61b2f2f7528b87e6ad506f4b6dded53d2 100644 (file)
@@ -21,15 +21,11 @@ class ConfirmEmailController extends Controller
 
     /**
      * Create a new controller instance.
-     *
-     * @param EmailConfirmationService $emailConfirmationService
-     * @param UserRepo $userRepo
      */
     public function __construct(EmailConfirmationService $emailConfirmationService, UserRepo $userRepo)
     {
         $this->emailConfirmationService = $emailConfirmationService;
         $this->userRepo = $userRepo;
-        parent::__construct();
     }
 
 
index a3c0433a56f9e4af9d574ae0446edac02dbd5c1f..5a033c6aad57d89fe7067b1c65016c200e327439 100644 (file)
@@ -2,10 +2,11 @@
 
 namespace BookStack\Http\Controllers\Auth;
 
+use BookStack\Actions\ActivityType;
 use BookStack\Http\Controllers\Controller;
 use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
 use Illuminate\Http\Request;
-use Password;
+use Illuminate\Support\Facades\Password;
 
 class ForgotPasswordController extends Controller
 {
@@ -30,7 +31,7 @@ class ForgotPasswordController extends Controller
     public function __construct()
     {
         $this->middleware('guest');
-        parent::__construct();
+        $this->middleware('guard:standard');
     }
 
 
@@ -52,7 +53,11 @@ class ForgotPasswordController extends Controller
         );
 
         if ($response === Password::RESET_LINK_SENT) {
-            $message = trans('auth.reset_password_sent_success', ['email' => $request->get('email')]);
+            $this->logActivity(ActivityType::AUTH_PASSWORD_RESET, $request->get('email'));
+        }
+
+        if ($response === Password::RESET_LINK_SENT || $response === Password::INVALID_USER) {
+            $message = trans('auth.reset_password_sent', ['email' => $request->get('email')]);
             $this->showSuccessNotification($message);
             return back()->with('status', trans($response));
         }
index 2b51ab99765d069470f66252b67bf514ff7f8e5a..1252e6217a8b66f1bda3b405af28085a177308de 100644 (file)
@@ -2,12 +2,12 @@
 
 namespace BookStack\Http\Controllers\Auth;
 
-use BookStack\Auth\Access\LdapService;
+use Activity;
+use BookStack\Actions\ActivityType;
 use BookStack\Auth\Access\SocialAuthService;
-use BookStack\Auth\UserRepo;
-use BookStack\Exceptions\AuthException;
+use BookStack\Exceptions\LoginAttemptEmailNeededException;
+use BookStack\Exceptions\LoginAttemptException;
 use BookStack\Http\Controllers\Controller;
-use Illuminate\Contracts\Auth\Authenticatable;
 use Illuminate\Foundation\Auth\AuthenticatesUsers;
 use Illuminate\Http\Request;
 
@@ -27,35 +27,25 @@ class LoginController extends Controller
     use AuthenticatesUsers;
 
     /**
-     * Where to redirect users after login.
-     *
-     * @var string
+     * Redirection paths
      */
     protected $redirectTo = '/';
-
     protected $redirectPath = '/';
     protected $redirectAfterLogout = '/login';
 
     protected $socialAuthService;
-    protected $ldapService;
-    protected $userRepo;
 
     /**
      * Create a new controller instance.
-     *
-     * @param \BookStack\Auth\\BookStack\Auth\Access\SocialAuthService $socialAuthService
-     * @param LdapService $ldapService
-     * @param \BookStack\Auth\UserRepo $userRepo
      */
-    public function __construct(SocialAuthService $socialAuthService, LdapService $ldapService, UserRepo $userRepo)
+    public function __construct(SocialAuthService $socialAuthService)
     {
-        $this->middleware('guest', ['only' => ['getLogin', 'postLogin']]);
+        $this->middleware('guest', ['only' => ['getLogin', 'login']]);
+        $this->middleware('guard:standard,ldap', ['only' => ['login', 'logout']]);
+
         $this->socialAuthService = $socialAuthService;
-        $this->ldapService = $ldapService;
-        $this->userRepo = $userRepo;
         $this->redirectPath = url('/');
         $this->redirectAfterLogout = url('/login');
-        parent::__construct();
     }
 
     public function username()
@@ -64,81 +54,146 @@ class LoginController extends Controller
     }
 
     /**
-     * Overrides the action when a user is authenticated.
-     * If the user authenticated but does not exist in the user table we create them.
-     * @param Request $request
-     * @param Authenticatable $user
-     * @return \Illuminate\Http\RedirectResponse
-     * @throws AuthException
-     * @throws \BookStack\Exceptions\LdapException
+     * Get the needed authorization credentials from the request.
      */
-    protected function authenticated(Request $request, Authenticatable $user)
+    protected function credentials(Request $request)
     {
-        // Explicitly log them out for now if they do no exist.
-        if (!$user->exists) {
-            auth()->logout($user);
+        return $request->only('username', 'email', 'password');
+    }
+
+    /**
+     * Show the application login form.
+     */
+    public function getLogin(Request $request)
+    {
+        $socialDrivers = $this->socialAuthService->getActiveDrivers();
+        $authMethod = config('auth.method');
+
+        if ($request->has('email')) {
+            session()->flashInput([
+                'email' => $request->get('email'),
+                'password' => (config('app.env') === 'demo') ? $request->get('password', '') : ''
+            ]);
         }
 
-        if (!$user->exists && $user->email === null && !$request->filled('email')) {
-            $request->flash();
-            session()->flash('request-email', true);
-            return redirect('/login');
+        // Store the previous location for redirect after login
+        $previous = url()->previous('');
+        if ($previous && $previous !== url('/login') && setting('app-public')) {
+            $isPreviousFromInstance = (strpos($previous, url('/')) === 0);
+            if ($isPreviousFromInstance) {
+                redirect()->setIntendedUrl($previous);
+            }
         }
 
-        if (!$user->exists && $user->email === null && $request->filled('email')) {
-            $user->email = $request->get('email');
+        return view('auth.login', [
+          'socialDrivers' => $socialDrivers,
+          'authMethod' => $authMethod,
+        ]);
+    }
+
+    /**
+     * Handle a login request to the application.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
+     *
+     * @throws \Illuminate\Validation\ValidationException
+     */
+    public function login(Request $request)
+    {
+        $this->validateLogin($request);
+        $username = $request->get($this->username());
+
+        // If the class is using the ThrottlesLogins trait, we can automatically throttle
+        // the login attempts for this application. We'll key this by the username and
+        // the IP address of the client making these requests into this application.
+        if (method_exists($this, 'hasTooManyLoginAttempts') &&
+            $this->hasTooManyLoginAttempts($request)) {
+            $this->fireLockoutEvent($request);
+
+            Activity::logFailedLogin($username);
+            return $this->sendLockoutResponse($request);
         }
 
-        if (!$user->exists) {
-            // Check for users with same email already
-            $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0;
-            if ($alreadyUser) {
-                throw new AuthException(trans('errors.error_user_exists_different_creds', ['email' => $user->email]));
+        try {
+            if ($this->attemptLogin($request)) {
+                return $this->sendLoginResponse($request);
             }
-
-            $user->save();
-            $this->userRepo->attachDefaultRole($user);
-            $this->userRepo->downloadAndAssignUserAvatar($user);
-            auth()->login($user);
+        } catch (LoginAttemptException $exception) {
+            Activity::logFailedLogin($username);
+            return $this->sendLoginAttemptExceptionResponse($exception, $request);
         }
 
-        // Sync LDAP groups if required
-        if ($this->ldapService->shouldSyncGroups()) {
-            $this->ldapService->syncGroups($user, $request->get($this->username()));
+        // If the login attempt was unsuccessful we will increment the number of attempts
+        // to login and redirect the user back to the login form. Of course, when this
+        // user surpasses their maximum number of attempts they will get locked out.
+        $this->incrementLoginAttempts($request);
+
+        Activity::logFailedLogin($username);
+        return $this->sendFailedLoginResponse($request);
+    }
+
+    /**
+     * The user has been authenticated.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  mixed  $user
+     * @return mixed
+     */
+    protected function authenticated(Request $request, $user)
+    {
+        // Authenticate on all session guards if a likely admin
+        if ($user->can('users-manage') && $user->can('user-roles-manage')) {
+            $guards = ['standard', 'ldap', 'saml2'];
+            foreach ($guards as $guard) {
+                auth($guard)->login($user);
+            }
         }
 
-        return redirect()->intended('/');
+        $this->logActivity(ActivityType::AUTH_LOGIN, $user);
+        return redirect()->intended($this->redirectPath());
     }
 
     /**
-     * Show the application login form.
-     * @param Request $request
-     * @return \Illuminate\Http\Response
+     * Validate the user login request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return void
+     *
+     * @throws \Illuminate\Validation\ValidationException
      */
-    public function getLogin(Request $request)
+    protected function validateLogin(Request $request)
     {
-        $socialDrivers = $this->socialAuthService->getActiveDrivers();
+        $rules = ['password' => 'required|string'];
         $authMethod = config('auth.method');
 
-        if ($request->has('email')) {
-            session()->flashInput([
-                'email' => $request->get('email'),
-                'password' => (config('app.env') === 'demo') ? $request->get('password', '') : ''
-            ]);
+        if ($authMethod === 'standard') {
+            $rules['email'] = 'required|email';
+        }
+
+        if ($authMethod === 'ldap') {
+            $rules['username'] = 'required|string';
+            $rules['email'] = 'email';
         }
 
-        return view('auth.login', ['socialDrivers' => $socialDrivers, 'authMethod' => $authMethod]);
+        $request->validate($rules);
     }
 
     /**
-     * Redirect to the relevant social site.
-     * @param $socialDriver
-     * @return \Symfony\Component\HttpFoundation\RedirectResponse
-     * @throws \BookStack\Exceptions\SocialDriverNotConfigured
+     * Send a response when a login attempt exception occurs.
      */
-    public function getSocialLogin($socialDriver)
+    protected function sendLoginAttemptExceptionResponse(LoginAttemptException $exception, Request $request)
     {
-        session()->put('social-callback', 'login');
-        return $this->socialAuthService->startLogIn($socialDriver);
+        if ($exception instanceof LoginAttemptEmailNeededException) {
+            $request->flash();
+            session()->flash('request-email', true);
+        }
+
+        if ($message = $exception->getMessage()) {
+            $this->showWarningNotification($message);
+        }
+
+        return redirect('/login');
     }
+
 }
index 304d3bed2e69999b381e0a8e1b4cb6a22790f492..e3d22264d5301a73c2d2bee41daa1c71209512a9 100644 (file)
@@ -2,25 +2,14 @@
 
 namespace BookStack\Http\Controllers\Auth;
 
-use BookStack\Auth\Access\EmailConfirmationService;
+use BookStack\Auth\Access\RegistrationService;
 use BookStack\Auth\Access\SocialAuthService;
-use BookStack\Auth\SocialAccount;
 use BookStack\Auth\User;
-use BookStack\Auth\UserRepo;
-use BookStack\Exceptions\SocialDriverNotConfigured;
-use BookStack\Exceptions\SocialSignInAccountNotUsed;
-use BookStack\Exceptions\SocialSignInException;
 use BookStack\Exceptions\UserRegistrationException;
 use BookStack\Http\Controllers\Controller;
-use Exception;
 use Illuminate\Foundation\Auth\RegistersUsers;
-use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
-use Illuminate\Http\Response;
-use Illuminate\Routing\Redirector;
 use Illuminate\Support\Facades\Hash;
-use Illuminate\Support\Str;
-use Laravel\Socialite\Contracts\User as SocialUser;
 use Validator;
 
 class RegisterController extends Controller
@@ -39,8 +28,7 @@ class RegisterController extends Controller
     use RegistersUsers;
 
     protected $socialAuthService;
-    protected $emailConfirmationService;
-    protected $userRepo;
+    protected $registrationService;
 
     /**
      * Where to redirect users after login / registration.
@@ -52,26 +40,22 @@ class RegisterController extends Controller
 
     /**
      * Create a new controller instance.
-     *
-     * @param SocialAuthService $socialAuthService
-     * @param EmailConfirmationService $emailConfirmationService
-     * @param UserRepo $userRepo
      */
-    public function __construct(SocialAuthService $socialAuthService, EmailConfirmationService $emailConfirmationService, UserRepo $userRepo)
+    public function __construct(SocialAuthService $socialAuthService, RegistrationService $registrationService)
     {
-        $this->middleware('guest')->only(['getRegister', 'postRegister', 'socialRegister']);
+        $this->middleware('guest');
+        $this->middleware('guard:standard');
+
         $this->socialAuthService = $socialAuthService;
-        $this->emailConfirmationService = $emailConfirmationService;
-        $this->userRepo = $userRepo;
+        $this->registrationService = $registrationService;
+
         $this->redirectTo = url('/');
         $this->redirectPath = url('/');
-        parent::__construct();
     }
 
     /**
      * Get a validator for an incoming registration request.
      *
-     * @param  array $data
      * @return \Illuminate\Contracts\Validation\Validator
      */
     protected function validator(array $data)
@@ -83,42 +67,41 @@ class RegisterController extends Controller
         ]);
     }
 
-    /**
-     * Check whether or not registrations are allowed in the app settings.
-     * @throws UserRegistrationException
-     */
-    protected function checkRegistrationAllowed()
-    {
-        if (!setting('registration-enabled')) {
-            throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login');
-        }
-    }
-
     /**
      * Show the application registration form.
-     * @return Response
      * @throws UserRegistrationException
      */
     public function getRegister()
     {
-        $this->checkRegistrationAllowed();
+        $this->registrationService->ensureRegistrationAllowed();
         $socialDrivers = $this->socialAuthService->getActiveDrivers();
-        return view('auth.register', ['socialDrivers' => $socialDrivers]);
+        return view('auth.register', [
+            'socialDrivers' => $socialDrivers,
+        ]);
     }
 
     /**
      * Handle a registration request for the application.
-     * @param Request|Request $request
-     * @return RedirectResponse|Redirector
      * @throws UserRegistrationException
      */
     public function postRegister(Request $request)
     {
-        $this->checkRegistrationAllowed();
+        $this->registrationService->ensureRegistrationAllowed();
         $this->validator($request->all())->validate();
-
         $userData = $request->all();
-        return $this->registerUser($userData);
+
+        try {
+            $user = $this->registrationService->registerUser($userData);
+            auth()->login($user);
+        } catch (UserRegistrationException $exception) {
+            if ($exception->getMessage()) {
+                $this->showErrorNotification($exception->getMessage());
+            }
+            return redirect($exception->redirectLocation);
+        }
+
+        $this->showSuccessNotification(trans('auth.register_success'));
+        return redirect($this->redirectPath());
     }
 
     /**
@@ -135,136 +118,4 @@ class RegisterController extends Controller
         ]);
     }
 
-    /**
-     * The registrations flow for all users.
-     * @param array $userData
-     * @param bool|false|SocialAccount $socialAccount
-     * @param bool $emailVerified
-     * @return RedirectResponse|Redirector
-     * @throws UserRegistrationException
-     */
-    protected function registerUser(array $userData, $socialAccount = false, $emailVerified = false)
-    {
-        $registrationRestrict = setting('registration-restrict');
-
-        if ($registrationRestrict) {
-            $restrictedEmailDomains = explode(',', str_replace(' ', '', $registrationRestrict));
-            $userEmailDomain = $domain = mb_substr(mb_strrchr($userData['email'], "@"), 1);
-            if (!in_array($userEmailDomain, $restrictedEmailDomains)) {
-                throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), '/register');
-            }
-        }
-
-        $newUser = $this->userRepo->registerNew($userData, $emailVerified);
-        if ($socialAccount) {
-            $newUser->socialAccounts()->save($socialAccount);
-        }
-
-        if ($this->emailConfirmationService->confirmationRequired() && !$emailVerified) {
-            $newUser->save();
-
-            try {
-                $this->emailConfirmationService->sendConfirmation($newUser);
-            } catch (Exception $e) {
-                $this->showErrorNotification(trans('auth.email_confirm_send_error'));
-            }
-
-            return redirect('/register/confirm');
-        }
-
-        auth()->login($newUser);
-        $this->showSuccessNotification(trans('auth.register_success'));
-        return redirect($this->redirectPath());
-    }
-
-    /**
-     * Redirect to the social site for authentication intended to register.
-     * @param $socialDriver
-     * @return mixed
-     * @throws UserRegistrationException
-     * @throws SocialDriverNotConfigured
-     */
-    public function socialRegister($socialDriver)
-    {
-        $this->checkRegistrationAllowed();
-        session()->put('social-callback', 'register');
-        return $this->socialAuthService->startRegister($socialDriver);
-    }
-
-    /**
-     * The callback for social login services.
-     * @param Request $request
-     * @param string $socialDriver
-     * @return RedirectResponse|Redirector
-     * @throws SocialSignInException
-     * @throws UserRegistrationException
-     * @throws SocialDriverNotConfigured
-     */
-    public function socialCallback(Request $request, string $socialDriver)
-    {
-        if (!session()->has('social-callback')) {
-            throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
-        }
-
-        // Check request for error information
-        if ($request->has('error') && $request->has('error_description')) {
-            throw new SocialSignInException(trans('errors.social_login_bad_response', [
-                'socialAccount' => $socialDriver,
-                'error' => $request->get('error_description'),
-            ]), '/login');
-        }
-
-        $action = session()->pull('social-callback');
-
-        // Attempt login or fall-back to register if allowed.
-        $socialUser = $this->socialAuthService->getSocialUser($socialDriver);
-        if ($action == 'login') {
-            try {
-                return $this->socialAuthService->handleLoginCallback($socialDriver, $socialUser);
-            } catch (SocialSignInAccountNotUsed $exception) {
-                if ($this->socialAuthService->driverAutoRegisterEnabled($socialDriver)) {
-                    return $this->socialRegisterCallback($socialDriver, $socialUser);
-                }
-                throw $exception;
-            }
-        }
-
-        if ($action == 'register') {
-            return $this->socialRegisterCallback($socialDriver, $socialUser);
-        }
-
-        return redirect()->back();
-    }
-
-    /**
-     * Detach a social account from a user.
-     * @param $socialDriver
-     * @return RedirectResponse|Redirector
-     */
-    public function detachSocialAccount($socialDriver)
-    {
-        return $this->socialAuthService->detachSocialAccount($socialDriver);
-    }
-
-    /**
-     * Register a new user after a registration callback.
-     * @param string $socialDriver
-     * @param SocialUser $socialUser
-     * @return RedirectResponse|Redirector
-     * @throws UserRegistrationException
-     */
-    protected function socialRegisterCallback(string $socialDriver, SocialUser $socialUser)
-    {
-        $socialUser = $this->socialAuthService->handleRegistrationCallback($socialDriver, $socialUser);
-        $socialAccount = $this->socialAuthService->fillSocialAccount($socialDriver, $socialUser);
-        $emailVerified = $this->socialAuthService->driverAutoConfirmEmailEnabled($socialDriver);
-
-        // Create an array of the user data to create a new user instance
-        $userData = [
-            'name' => $socialUser->getName(),
-            'email' => $socialUser->getEmail(),
-            'password' => Str::random(30)
-        ];
-        return $this->registerUser($userData, $socialAccount, $emailVerified);
-    }
 }
index 4d98eca597c3fffcece65fc79209bae646a49ab9..59e9ab79baa7cb146ed7582e1fc50cd88a7d9e31 100644 (file)
@@ -2,9 +2,11 @@
 
 namespace BookStack\Http\Controllers\Auth;
 
+use BookStack\Actions\ActivityType;
 use BookStack\Http\Controllers\Controller;
 use Illuminate\Foundation\Auth\ResetsPasswords;
 use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Password;
 
 class ResetPasswordController extends Controller
 {
@@ -31,7 +33,7 @@ class ResetPasswordController extends Controller
     public function __construct()
     {
         $this->middleware('guest');
-        parent::__construct();
+        $this->middleware('guard:standard');
     }
 
     /**
@@ -45,7 +47,28 @@ class ResetPasswordController extends Controller
     {
         $message = trans('auth.reset_password_success');
         $this->showSuccessNotification($message);
+        $this->logActivity(ActivityType::AUTH_PASSWORD_RESET_UPDATE, user());
         return redirect($this->redirectPath())
             ->with('status', trans($response));
     }
+
+    /**
+     * Get the response for a failed password reset.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  string  $response
+     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
+     */
+    protected function sendResetFailedResponse(Request $request, $response)
+    {
+        // We show invalid users as invalid tokens as to not leak what
+        // users may exist in the system.
+        if ($response === Password::INVALID_USER) {
+            $response = Password::INVALID_TOKEN;
+        }
+
+        return redirect()->back()
+            ->withInput($request->only('email'))
+            ->withErrors(['email' => trans($response)]);
+    }
 }
diff --git a/app/Http/Controllers/Auth/Saml2Controller.php b/app/Http/Controllers/Auth/Saml2Controller.php
new file mode 100644 (file)
index 0000000..8a3bf06
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+namespace BookStack\Http\Controllers\Auth;
+
+use BookStack\Auth\Access\Saml2Service;
+use BookStack\Http\Controllers\Controller;
+
+class Saml2Controller extends Controller
+{
+
+    protected $samlService;
+
+    /**
+     * Saml2Controller constructor.
+     */
+    public function __construct(Saml2Service $samlService)
+    {
+        $this->samlService = $samlService;
+        $this->middleware('guard:saml2');
+    }
+
+    /**
+     * Start the login flow via SAML2.
+     */
+    public function login()
+    {
+        $loginDetails = $this->samlService->login();
+        session()->flash('saml2_request_id', $loginDetails['id']);
+
+        return redirect($loginDetails['url']);
+    }
+
+    /**
+     * Start the logout flow via SAML2.
+     */
+    public function logout()
+    {
+        $logoutDetails = $this->samlService->logout();
+
+        if ($logoutDetails['id']) {
+            session()->flash('saml2_logout_request_id', $logoutDetails['id']);
+        }
+
+        return redirect($logoutDetails['url']);
+    }
+
+    /*
+     * Get the metadata for this SAML2 service provider.
+     */
+    public function metadata()
+    {
+        $metaData = $this->samlService->metadata();
+        return response()->make($metaData, 200, [
+            'Content-Type' => 'text/xml'
+        ]);
+    }
+
+    /**
+     * Single logout service.
+     * Handle logout requests and responses.
+     */
+    public function sls()
+    {
+        $requestId = session()->pull('saml2_logout_request_id', null);
+        $redirect = $this->samlService->processSlsResponse($requestId) ?? '/';
+        return redirect($redirect);
+    }
+
+    /**
+     * Assertion Consumer Service.
+     * Processes the SAML response from the IDP.
+     */
+    public function acs()
+    {
+        $requestId = session()->pull('saml2_request_id', null);
+
+        $user = $this->samlService->processAcsResponse($requestId);
+        if ($user === null) {
+            $this->showErrorNotification(trans('errors.saml_fail_authed', ['system' => config('saml2.name')]));
+            return redirect('/login');
+        }
+
+        return redirect()->intended();
+    }
+
+}
diff --git a/app/Http/Controllers/Auth/SocialController.php b/app/Http/Controllers/Auth/SocialController.php
new file mode 100644 (file)
index 0000000..0c53c92
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+
+namespace BookStack\Http\Controllers\Auth;
+
+use BookStack\Auth\Access\RegistrationService;
+use BookStack\Auth\Access\SocialAuthService;
+use BookStack\Exceptions\SocialDriverNotConfigured;
+use BookStack\Exceptions\SocialSignInAccountNotUsed;
+use BookStack\Exceptions\SocialSignInException;
+use BookStack\Exceptions\UserRegistrationException;
+use BookStack\Http\Controllers\Controller;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Routing\Redirector;
+use Illuminate\Support\Str;
+use Laravel\Socialite\Contracts\User as SocialUser;
+
+class SocialController extends Controller
+{
+
+    protected $socialAuthService;
+    protected $registrationService;
+
+    /**
+     * SocialController constructor.
+     */
+    public function __construct(SocialAuthService $socialAuthService, RegistrationService $registrationService)
+    {
+        $this->middleware('guest')->only(['getRegister', 'postRegister']);
+        $this->socialAuthService = $socialAuthService;
+        $this->registrationService = $registrationService;
+    }
+
+
+    /**
+     * Redirect to the relevant social site.
+     * @throws \BookStack\Exceptions\SocialDriverNotConfigured
+     */
+    public function getSocialLogin(string $socialDriver)
+    {
+        session()->put('social-callback', 'login');
+        return $this->socialAuthService->startLogIn($socialDriver);
+    }
+
+    /**
+     * Redirect to the social site for authentication intended to register.
+     * @throws SocialDriverNotConfigured
+     * @throws UserRegistrationException
+     */
+    public function socialRegister(string $socialDriver)
+    {
+        $this->registrationService->ensureRegistrationAllowed();
+        session()->put('social-callback', 'register');
+        return $this->socialAuthService->startRegister($socialDriver);
+    }
+
+    /**
+     * The callback for social login services.
+     * @throws SocialSignInException
+     * @throws SocialDriverNotConfigured
+     * @throws UserRegistrationException
+     */
+    public function socialCallback(Request $request, string $socialDriver)
+    {
+        if (!session()->has('social-callback')) {
+            throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
+        }
+
+        // Check request for error information
+        if ($request->has('error') && $request->has('error_description')) {
+            throw new SocialSignInException(trans('errors.social_login_bad_response', [
+                'socialAccount' => $socialDriver,
+                'error' => $request->get('error_description'),
+            ]), '/login');
+        }
+
+        $action = session()->pull('social-callback');
+
+        // Attempt login or fall-back to register if allowed.
+        $socialUser = $this->socialAuthService->getSocialUser($socialDriver);
+        if ($action === 'login') {
+            try {
+                return $this->socialAuthService->handleLoginCallback($socialDriver, $socialUser);
+            } catch (SocialSignInAccountNotUsed $exception) {
+                if ($this->socialAuthService->driverAutoRegisterEnabled($socialDriver)) {
+                    return $this->socialRegisterCallback($socialDriver, $socialUser);
+                }
+                throw $exception;
+            }
+        }
+
+        if ($action === 'register') {
+            return $this->socialRegisterCallback($socialDriver, $socialUser);
+        }
+
+        return redirect()->back();
+    }
+
+    /**
+     * Detach a social account from a user.
+     */
+    public function detachSocialAccount(string $socialDriver)
+    {
+        $this->socialAuthService->detachSocialAccount($socialDriver);
+        session()->flash('success', trans('settings.users_social_disconnected', ['socialAccount' => Str::title($socialDriver)]));
+        return redirect(user()->getEditUrl());
+    }
+
+    /**
+     * Register a new user after a registration callback.
+     * @throws UserRegistrationException
+     */
+    protected function socialRegisterCallback(string $socialDriver, SocialUser $socialUser)
+    {
+        $socialUser = $this->socialAuthService->handleRegistrationCallback($socialDriver, $socialUser);
+        $socialAccount = $this->socialAuthService->fillSocialAccount($socialDriver, $socialUser);
+        $emailVerified = $this->socialAuthService->driverAutoConfirmEmailEnabled($socialDriver);
+
+        // Create an array of the user data to create a new user instance
+        $userData = [
+            'name' => $socialUser->getName(),
+            'email' => $socialUser->getEmail(),
+            'password' => Str::random(32)
+        ];
+
+        // Take name from email address if empty
+        if (!$userData['name']) {
+            $userData['name'] = explode('@', $userData['email'])[0];
+        }
+
+        $user = $this->registrationService->registerUser($userData, $socialAccount, $emailVerified);
+        auth()->login($user);
+
+        $this->showSuccessNotification(trans('auth.register_success'));
+        return redirect('/');
+    }
+}
index c361b0a9b43687101e577db4f3e46f55b86ba397..926458fa613ddc1073305ea5405750c7ce01e085 100644 (file)
@@ -8,11 +8,9 @@ use BookStack\Exceptions\UserTokenExpiredException;
 use BookStack\Exceptions\UserTokenNotFoundException;
 use BookStack\Http\Controllers\Controller;
 use Exception;
-use Illuminate\Contracts\View\Factory;
 use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
 use Illuminate\Routing\Redirector;
-use Illuminate\View\View;
 
 class UserInviteController extends Controller
 {
@@ -21,22 +19,18 @@ class UserInviteController extends Controller
 
     /**
      * Create a new controller instance.
-     *
-     * @param UserInviteService $inviteService
-     * @param UserRepo $userRepo
      */
     public function __construct(UserInviteService $inviteService, UserRepo $userRepo)
     {
+        $this->middleware('guest');
+        $this->middleware('guard:standard');
+
         $this->inviteService = $inviteService;
         $this->userRepo = $userRepo;
-        $this->middleware('guest');
-        parent::__construct();
     }
 
     /**
      * Show the page for the user to set the password for their account.
-     * @param string $token
-     * @return Factory|View|RedirectResponse
      * @throws Exception
      */
     public function showSetPassword(string $token)
@@ -54,9 +48,6 @@ class UserInviteController extends Controller
 
     /**
      * Sets the password for an invited user and then grants them access.
-     * @param Request $request
-     * @param string $token
-     * @return RedirectResponse|Redirector
      * @throws Exception
      */
     public function setPassword(Request $request, string $token)
@@ -85,7 +76,6 @@ class UserInviteController extends Controller
 
     /**
      * Check and validate the exception thrown when checking an invite token.
-     * @param Exception $exception
      * @return RedirectResponse|Redirector
      * @throws Exception
      */
index e7d788d91dde01b78fdd158035fe07fd6cd5f54e..3d695ba85dbdae692f35d24e429e4fc553effbd5 100644 (file)
@@ -1,12 +1,13 @@
 <?php namespace BookStack\Http\Controllers;
 
 use Activity;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Managers\EntityContext;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Tools\PermissionsUpdater;
+use BookStack\Entities\Tools\ShelfContext;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Exceptions\ImageUploadException;
-use BookStack\Exceptions\NotifyException;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 use Throwable;
@@ -18,14 +19,10 @@ class BookController extends Controller
     protected $bookRepo;
     protected $entityContextManager;
 
-    /**
-     * BookController constructor.
-     */
-    public function __construct(EntityContext $entityContextManager, BookRepo $bookRepo)
+    public function __construct(ShelfContext $entityContextManager, BookRepo $bookRepo)
     {
         $this->bookRepo = $bookRepo;
         $this->entityContextManager = $entityContextManager;
-        parent::__construct();
     }
 
     /**
@@ -86,7 +83,7 @@ class BookController extends Controller
         $this->validate($request, [
             'name' => 'required|string|max:255',
             'description' => 'string|max:1000',
-            'image' => $this->getImageValidationRules(),
+            'image' => 'nullable|' . $this->getImageValidationRules(),
         ]);
 
         $bookshelf = null;
@@ -97,11 +94,10 @@ class BookController extends Controller
 
         $book = $this->bookRepo->create($request->all());
         $this->bookRepo->updateCoverImage($book, $request->file('image', null));
-        Activity::add($book, 'book_create', $book->id);
 
         if ($bookshelf) {
             $bookshelf->appendBook($book);
-            Activity::add($bookshelf, 'bookshelf_update');
+            Activity::addForEntity($bookshelf, ActivityType::BOOKSHELF_UPDATE);
         }
 
         return redirect($book->getUrl());
@@ -114,6 +110,7 @@ class BookController extends Controller
     {
         $book = $this->bookRepo->getBySlug($slug);
         $bookChildren = (new BookContents($book))->getTree(true);
+        $bookParentShelves = $book->shelves()->visible()->get();
 
         Views::add($book);
         if ($request->has('shelf')) {
@@ -125,6 +122,7 @@ class BookController extends Controller
             'book' => $book,
             'current' => $book,
             'bookChildren' => $bookChildren,
+            'bookParentShelves' => $bookParentShelves,
             'activity' => Activity::entityActivity($book, 20, 1)
         ]);
     }
@@ -153,15 +151,13 @@ class BookController extends Controller
         $this->validate($request, [
             'name' => 'required|string|max:255',
             'description' => 'string|max:1000',
-            'image' => $this->getImageValidationRules(),
+            'image' => 'nullable|' . $this->getImageValidationRules(),
         ]);
 
         $book = $this->bookRepo->update($book, $request->all());
         $resetCover = $request->has('image_reset');
         $this->bookRepo->updateCoverImage($book, $request->file('image', null), $resetCover);
 
-        Activity::add($book, 'book_update', $book->id);
-
         return redirect($book->getUrl());
     }
 
@@ -179,14 +175,12 @@ class BookController extends Controller
     /**
      * Remove the specified book from the system.
      * @throws Throwable
-     * @throws NotifyException
      */
     public function destroy(string $bookSlug)
     {
         $book = $this->bookRepo->getBySlug($bookSlug);
         $this->checkOwnablePermission('book-delete', $book);
 
-        Activity::addMessage('book_delete', $book->name);
         $this->bookRepo->destroy($book);
 
         return redirect('/books');
@@ -209,14 +203,12 @@ class BookController extends Controller
      * Set the restrictions for this book.
      * @throws Throwable
      */
-    public function permissions(Request $request, string $bookSlug)
+    public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug)
     {
         $book = $this->bookRepo->getBySlug($bookSlug);
         $this->checkOwnablePermission('restrictions-manage', $book);
 
-        $restricted = $request->get('restricted') === 'true';
-        $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
-        $this->bookRepo->updatePermissions($book, $restricted, $permissions);
+        $permissionsUpdater->updateFromPermissionsForm($book, $request);
 
         $this->showSuccessNotification(trans('entities.books_permissions_updated'));
         return redirect($book->getUrl());
index cfa3d6a3a3d162e9eb5d3bf19afa5eb4a4f6b7b8..1c1f124422f962020d31e4f35595b2e65d0f80cf 100644 (file)
@@ -2,7 +2,7 @@
 
 namespace BookStack\Http\Controllers;
 
-use BookStack\Entities\ExportService;
+use BookStack\Entities\Tools\ExportFormatter;
 use BookStack\Entities\Repos\BookRepo;
 use Throwable;
 
@@ -10,16 +10,15 @@ class BookExportController extends Controller
 {
 
     protected $bookRepo;
-    protected $exportService;
+    protected $exportFormatter;
 
     /**
      * BookExportController constructor.
      */
-    public function __construct(BookRepo $bookRepo, ExportService $exportService)
+    public function __construct(BookRepo $bookRepo, ExportFormatter $exportFormatter)
     {
         $this->bookRepo = $bookRepo;
-        $this->exportService = $exportService;
-        parent::__construct();
+        $this->exportFormatter = $exportFormatter;
     }
 
     /**
@@ -29,7 +28,7 @@ class BookExportController extends Controller
     public function pdf(string $bookSlug)
     {
         $book = $this->bookRepo->getBySlug($bookSlug);
-        $pdfContent = $this->exportService->bookToPdf($book);
+        $pdfContent = $this->exportFormatter->bookToPdf($book);
         return $this->downloadResponse($pdfContent, $bookSlug . '.pdf');
     }
 
@@ -40,7 +39,7 @@ class BookExportController extends Controller
     public function html(string $bookSlug)
     {
         $book = $this->bookRepo->getBySlug($bookSlug);
-        $htmlContent = $this->exportService->bookToContainedHtml($book);
+        $htmlContent = $this->exportFormatter->bookToContainedHtml($book);
         return $this->downloadResponse($htmlContent, $bookSlug . '.html');
     }
 
@@ -50,7 +49,7 @@ class BookExportController extends Controller
     public function plainText(string $bookSlug)
     {
         $book = $this->bookRepo->getBySlug($bookSlug);
-        $textContent = $this->exportService->bookToPlainText($book);
+        $textContent = $this->exportFormatter->bookToPlainText($book);
         return $this->downloadResponse($textContent, $bookSlug . '.txt');
     }
 }
index f5fb6f255537c2d16017f7365974863cc402260f..6d3199cbee990fe90b484c2b3a3bc6d4c73a54b8 100644 (file)
@@ -2,8 +2,9 @@
 
 namespace BookStack\Http\Controllers;
 
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\BookContents;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Exceptions\SortOperationException;
 use BookStack\Facades\Activity;
@@ -14,14 +15,9 @@ class BookSortController extends Controller
 
     protected $bookRepo;
 
-    /**
-     * BookSortController constructor.
-     * @param $bookRepo
-     */
     public function __construct(BookRepo $bookRepo)
     {
         $this->bookRepo = $bookRepo;
-        parent::__construct();
     }
 
     /**
@@ -74,7 +70,7 @@ class BookSortController extends Controller
 
         // Rebuild permissions and add activity for involved books.
         $booksInvolved->each(function (Book $book) {
-            Activity::add($book, 'book_sort', $book->id);
+            Activity::addForEntity($book, ActivityType::BOOK_SORT);
         });
 
         return redirect($book->getUrl());
index 57e67dc00e618b53fec9b16eec134242422dd8af..32c22e185fa20116cabc0d3221d621c0e3f70d12 100644 (file)
@@ -1,8 +1,9 @@
 <?php namespace BookStack\Http\Controllers;
 
 use Activity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\EntityContext;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\PermissionsUpdater;
+use BookStack\Entities\Tools\ShelfContext;
 use BookStack\Entities\Repos\BookshelfRepo;
 use BookStack\Exceptions\ImageUploadException;
 use BookStack\Exceptions\NotFoundException;
@@ -19,15 +20,11 @@ class BookshelfController extends Controller
     protected $entityContextManager;
     protected $imageRepo;
 
-    /**
-     * BookController constructor.
-     */
-    public function __construct(BookshelfRepo $bookshelfRepo, EntityContext $entityContextManager, ImageRepo $imageRepo)
+    public function __construct(BookshelfRepo $bookshelfRepo, ShelfContext $entityContextManager, ImageRepo $imageRepo)
     {
         $this->bookshelfRepo = $bookshelfRepo;
         $this->entityContextManager = $entityContextManager;
         $this->imageRepo = $imageRepo;
-        parent::__construct();
     }
 
     /**
@@ -85,14 +82,13 @@ class BookshelfController extends Controller
         $this->validate($request, [
             'name' => 'required|string|max:255',
             'description' => 'string|max:1000',
-            'image' => $this->getImageValidationRules(),
+            'image' => 'nullable|' . $this->getImageValidationRules(),
         ]);
 
         $bookIds = explode(',', $request->get('books', ''));
         $shelf = $this->bookshelfRepo->create($request->all(), $bookIds);
-        $this->bookshelfRepo->updateCoverImage($shelf);
+        $this->bookshelfRepo->updateCoverImage($shelf, $request->file('image', null));
 
-        Activity::add($shelf, 'bookshelf_create');
         return redirect($shelf->getUrl());
     }
 
@@ -107,10 +103,12 @@ class BookshelfController extends Controller
 
         Views::add($shelf);
         $this->entityContextManager->setShelfContext($shelf->id);
+        $view = setting()->getForCurrentUser('bookshelf_view_type', config('app.views.books'));
 
         $this->setPageTitle($shelf->getShortName());
         return view('shelves.show', [
             'shelf' => $shelf,
+            'view' => $view,
             'activity' => Activity::entityActivity($shelf, 20, 1)
         ]);
     }
@@ -146,7 +144,7 @@ class BookshelfController extends Controller
         $this->validate($request, [
             'name' => 'required|string|max:255',
             'description' => 'string|max:1000',
-            'image' => $this->imageRepo->getImageValidationRules(),
+            'image' => 'nullable|' . $this->getImageValidationRules(),
         ]);
 
 
@@ -154,7 +152,6 @@ class BookshelfController extends Controller
         $shelf = $this->bookshelfRepo->update($shelf, $request->all(), $bookIds);
         $resetCover = $request->has('image_reset');
         $this->bookshelfRepo->updateCoverImage($shelf, $request->file('image', null), $resetCover);
-        Activity::add($shelf, 'bookshelf_update');
 
         return redirect($shelf->getUrl());
     }
@@ -180,7 +177,6 @@ class BookshelfController extends Controller
         $shelf = $this->bookshelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('bookshelf-delete', $shelf);
 
-        Activity::addMessage('bookshelf_delete', $shelf->name);
         $this->bookshelfRepo->destroy($shelf);
 
         return redirect('/shelves');
@@ -202,14 +198,12 @@ class BookshelfController extends Controller
     /**
      * Set the permissions for this bookshelf.
      */
-    public function permissions(Request $request, string $slug)
+    public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $slug)
     {
         $shelf = $this->bookshelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('restrictions-manage', $shelf);
 
-        $restricted = $request->get('restricted') === 'true';
-        $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
-        $this->bookshelfRepo->updatePermissions($shelf, $restricted, $permissions);
+        $permissionsUpdater->updateFromPermissionsForm($shelf, $request);
 
         $this->showSuccessNotification(trans('entities.shelves_permissions_updated'));
         return redirect($shelf->getUrl());
index 1355979107eb0181d272e3610511688d5772b7b7..1d69df2a2f6029e148ff59a4f1bb59f04678d8b0 100644 (file)
@@ -1,9 +1,9 @@
 <?php namespace BookStack\Http\Controllers;
 
-use Activity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\BookContents;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Repos\ChapterRepo;
+use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Exceptions\MoveOperationException;
 use BookStack\Exceptions\NotFoundException;
 use Illuminate\Http\Request;
@@ -22,7 +22,6 @@ class ChapterController extends Controller
     public function __construct(ChapterRepo $chapterRepo)
     {
         $this->chapterRepo = $chapterRepo;
-        parent::__construct();
     }
 
     /**
@@ -51,7 +50,6 @@ class ChapterController extends Controller
         $this->checkOwnablePermission('chapter-create', $book);
 
         $chapter = $this->chapterRepo->create($request->all(), $book);
-        Activity::add($chapter, 'chapter_create', $book->id);
 
         return redirect($chapter->getUrl());
     }
@@ -100,7 +98,6 @@ class ChapterController extends Controller
         $this->checkOwnablePermission('chapter-update', $chapter);
 
         $this->chapterRepo->update($chapter, $request->all());
-        Activity::add($chapter, 'chapter_update', $chapter->book->id);
 
         return redirect($chapter->getUrl());
     }
@@ -128,7 +125,6 @@ class ChapterController extends Controller
         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
         $this->checkOwnablePermission('chapter-delete', $chapter);
 
-        Activity::addMessage('chapter_delete', $chapter->name, $chapter->book->id);
         $this->chapterRepo->destroy($chapter);
 
         return redirect($chapter->book->getUrl());
@@ -173,8 +169,6 @@ class ChapterController extends Controller
             return redirect()->back();
         }
 
-        Activity::add($chapter, 'chapter_move', $newBook->id);
-
         $this->showSuccessNotification(trans('entities.chapter_move_success', ['bookName' => $newBook->name]));
         return redirect($chapter->getUrl());
     }
@@ -197,14 +191,12 @@ class ChapterController extends Controller
      * Set the restrictions for this chapter.
      * @throws NotFoundException
      */
-    public function permissions(Request $request, string $bookSlug, string $chapterSlug)
+    public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug, string $chapterSlug)
     {
         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
         $this->checkOwnablePermission('restrictions-manage', $chapter);
 
-        $restricted = $request->get('restricted') === 'true';
-        $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
-        $this->chapterRepo->updatePermissions($chapter, $restricted, $permissions);
+        $permissionsUpdater->updateFromPermissionsForm($chapter, $request);
 
         $this->showSuccessNotification(trans('entities.chapters_permissions_success'));
         return redirect($chapter->getUrl());
index 0c86f854828b70dad5418a9b475c7262aef16612..52d087442ab287eb2d533365ed6cfd8cce5da642 100644 (file)
@@ -1,6 +1,6 @@
 <?php namespace BookStack\Http\Controllers;
 
-use BookStack\Entities\ExportService;
+use BookStack\Entities\Tools\ExportFormatter;
 use BookStack\Entities\Repos\ChapterRepo;
 use BookStack\Exceptions\NotFoundException;
 use Throwable;
@@ -9,16 +9,15 @@ class ChapterExportController extends Controller
 {
 
     protected $chapterRepo;
-    protected $exportService;
+    protected $exportFormatter;
 
     /**
      * ChapterExportController constructor.
      */
-    public function __construct(ChapterRepo $chapterRepo, ExportService $exportService)
+    public function __construct(ChapterRepo $chapterRepo, ExportFormatter $exportFormatter)
     {
         $this->chapterRepo = $chapterRepo;
-        $this->exportService = $exportService;
-        parent::__construct();
+        $this->exportFormatter = $exportFormatter;
     }
 
     /**
@@ -29,7 +28,7 @@ class ChapterExportController extends Controller
     public function pdf(string $bookSlug, string $chapterSlug)
     {
         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
-        $pdfContent = $this->exportService->chapterToPdf($chapter);
+        $pdfContent = $this->exportFormatter->chapterToPdf($chapter);
         return $this->downloadResponse($pdfContent, $chapterSlug . '.pdf');
     }
 
@@ -41,7 +40,7 @@ class ChapterExportController extends Controller
     public function html(string $bookSlug, string $chapterSlug)
     {
         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
-        $containedHtml = $this->exportService->chapterToContainedHtml($chapter);
+        $containedHtml = $this->exportFormatter->chapterToContainedHtml($chapter);
         return $this->downloadResponse($containedHtml, $chapterSlug . '.html');
     }
 
@@ -52,7 +51,7 @@ class ChapterExportController extends Controller
     public function plainText(string $bookSlug, string $chapterSlug)
     {
         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
-        $chapterText = $this->exportService->chapterToPlainText($chapter);
+        $chapterText = $this->exportFormatter->chapterToPlainText($chapter);
         return $this->downloadResponse($chapterText, $chapterSlug . '.txt');
     }
 }
index 068358d72d10af92de45327e0b207093c9c501fd..bf1a76f518f3ce70f2792bd1138bc309ca961d4d 100644 (file)
@@ -1,8 +1,9 @@
 <?php namespace BookStack\Http\Controllers;
 
 use Activity;
+use BookStack\Actions\ActivityType;
 use BookStack\Actions\CommentRepo;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 
@@ -10,24 +11,20 @@ class CommentController extends Controller
 {
     protected $commentRepo;
 
-    /**
-     * CommentController constructor.
-     */
     public function __construct(CommentRepo $commentRepo)
     {
         $this->commentRepo = $commentRepo;
-        parent::__construct();
     }
 
     /**
      * Save a new comment for a Page
      * @throws ValidationException
      */
-    public function savePageComment(Request $request, int $pageId, int $commentId = null)
+    public function savePageComment(Request $request, int $pageId)
     {
         $this->validate($request, [
             'text' => 'required|string',
-            'html' => 'required|string',
+            'parent_id' => 'nullable|integer',
         ]);
 
         $page = Page::visible()->find($pageId);
@@ -35,8 +32,6 @@ class CommentController extends Controller
             return response('Not found', 404);
         }
 
-        $this->checkOwnablePermission('page-view', $page);
-
         // Prevent adding comments to draft pages
         if ($page->draft) {
             return $this->jsonError(trans('errors.cannot_add_comment_to_draft'), 400);
@@ -44,8 +39,7 @@ class CommentController extends Controller
 
         // Create a new comment.
         $this->checkPermission('comment-create-all');
-        $comment = $this->commentRepo->create($page, $request->only(['html', 'text', 'parent_id']));
-        Activity::add($page, 'commented_on', $page->book->id);
+        $comment = $this->commentRepo->create($page, $request->get('text'), $request->get('parent_id'));
         return view('comments.comment', ['comment' => $comment]);
     }
 
@@ -57,14 +51,13 @@ class CommentController extends Controller
     {
         $this->validate($request, [
             'text' => 'required|string',
-            'html' => 'required|string',
         ]);
 
         $comment = $this->commentRepo->getById($commentId);
         $this->checkOwnablePermission('page-view', $comment->entity);
         $this->checkOwnablePermission('comment-update', $comment);
 
-        $comment = $this->commentRepo->update($comment, $request->only(['html', 'text']));
+        $comment = $this->commentRepo->update($comment, $request->get('text'));
         return view('comments.comment', ['comment' => $comment]);
     }
 
index b9576f2febd7923293b8eacb105bab6130f118c1..479d5ac15852be57d44370f433185c15839af226 100644 (file)
@@ -2,25 +2,21 @@
 
 namespace BookStack\Http\Controllers;
 
-use BookStack\Ownable;
+use BookStack\Facades\Activity;
+use BookStack\Interfaces\Loggable;
+use BookStack\HasCreatorAndUpdater;
+use BookStack\Model;
 use Illuminate\Foundation\Bus\DispatchesJobs;
 use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Http\Exceptions\HttpResponseException;
-use Illuminate\Http\Request;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Response;
 use Illuminate\Routing\Controller as BaseController;
 
 abstract class Controller extends BaseController
 {
     use DispatchesJobs, ValidatesRequests;
 
-    /**
-     * Controller constructor.
-     */
-    public function __construct()
-    {
-        //
-    }
-
     /**
      * Check if the current user is signed in.
      */
@@ -42,9 +38,8 @@ abstract class Controller extends BaseController
 
     /**
      * Adds the page title into the view.
-     * @param $title
      */
-    public function setPageTitle($title)
+    public function setPageTitle(string $title)
     {
         view()->share('pageTitle', $title);
     }
@@ -66,96 +61,59 @@ abstract class Controller extends BaseController
     }
 
     /**
-     * Checks for a permission.
-     * @param string $permissionName
-     * @return bool|\Illuminate\Http\RedirectResponse
+     * Checks that the current user has the given permission otherwise throw an exception.
      */
-    protected function checkPermission($permissionName)
+    protected function checkPermission(string $permission): void
     {
-        if (!user() || !user()->can($permissionName)) {
+        if (!user() || !user()->can($permission)) {
             $this->showPermissionError();
         }
-        return true;
     }
 
     /**
-     * Check the current user's permissions against an ownable item.
-     * @param $permission
-     * @param Ownable $ownable
-     * @return bool
+     * Check the current user's permissions against an ownable item otherwise throw an exception.
      */
-    protected function checkOwnablePermission($permission, Ownable $ownable)
+    protected function checkOwnablePermission(string $permission, Model $ownable): void
     {
-        if (userCan($permission, $ownable)) {
-            return true;
+        if (!userCan($permission, $ownable)) {
+            $this->showPermissionError();
         }
-        return $this->showPermissionError();
     }
 
     /**
-     * Check if a user has a permission or bypass if the callback is true.
-     * @param $permissionName
-     * @param $callback
-     * @return bool
+     * Check if a user has a permission or bypass the permission
+     * check if the given callback resolves true.
      */
-    protected function checkPermissionOr($permissionName, $callback)
+    protected function checkPermissionOr(string $permission, callable $callback): void
     {
-        $callbackResult = $callback();
-        if ($callbackResult === false) {
-            $this->checkPermission($permissionName);
+        if ($callback() !== true) {
+            $this->checkPermission($permission);
         }
-        return true;
     }
 
     /**
      * Check if the current user has a permission or bypass if the provided user
      * id matches the current user.
-     * @param string $permissionName
-     * @param int $userId
-     * @return bool
      */
-    protected function checkPermissionOrCurrentUser(string $permissionName, int $userId)
+    protected function checkPermissionOrCurrentUser(string $permission, int $userId): void
     {
-        return $this->checkPermissionOr($permissionName, function () use ($userId) {
+        $this->checkPermissionOr($permission, function () use ($userId) {
             return $userId === user()->id;
         });
     }
 
     /**
      * Send back a json error message.
-     * @param string $messageText
-     * @param int $statusCode
-     * @return mixed
      */
-    protected function jsonError($messageText = "", $statusCode = 500)
+    protected function jsonError(string $messageText = "", int $statusCode = 500): JsonResponse
     {
         return response()->json(['message' => $messageText, 'status' => 'error'], $statusCode);
     }
 
-    /**
-     * Create the response for when a request fails validation.
-     * @param  \Illuminate\Http\Request  $request
-     * @param  array  $errors
-     * @return \Symfony\Component\HttpFoundation\Response
-     */
-    protected function buildFailedValidationResponse(Request $request, array $errors)
-    {
-        if ($request->expectsJson()) {
-            return response()->json(['validation' => $errors], 422);
-        }
-
-        return redirect()->to($this->getRedirectUrl())
-            ->withInput($request->input())
-            ->withErrors($errors, $this->errorBag());
-    }
-
     /**
      * Create a response that forces a download in the browser.
-     * @param string $content
-     * @param string $fileName
-     * @return \Illuminate\Http\Response
      */
-    protected function downloadResponse(string $content, string $fileName)
+    protected function downloadResponse(string $content, string $fileName): Response
     {
         return response()->make($content, 200, [
             'Content-Type'        => 'application/octet-stream',
@@ -165,36 +123,42 @@ abstract class Controller extends BaseController
 
     /**
      * Show a positive, successful notification to the user on next view load.
-     * @param string $message
      */
-    protected function showSuccessNotification(string $message)
+    protected function showSuccessNotification(string $message): void
     {
         session()->flash('success', $message);
     }
 
     /**
      * Show a warning notification to the user on next view load.
-     * @param string $message
      */
-    protected function showWarningNotification(string $message)
+    protected function showWarningNotification(string $message): void
     {
         session()->flash('warning', $message);
     }
 
     /**
      * Show an error notification to the user on next view load.
-     * @param string $message
      */
-    protected function showErrorNotification(string $message)
+    protected function showErrorNotification(string $message): void
     {
         session()->flash('error', $message);
     }
 
+    /**
+     * Log an activity in the system.
+     * @param string|Loggable
+     */
+    protected function logActivity(string $type, $detail = ''): void
+    {
+        Activity::add($type, $detail);
+    }
+
     /**
      * Get the validation rules for image files.
      */
     protected function getImageValidationRules(): string
     {
-        return 'image_extension|no_double_extension|mimes:jpeg,png,gif,bmp,webp,tiff';
+        return 'image_extension|no_double_extension|mimes:jpeg,png,gif,webp';
     }
 }
index 260952fd16eb8e18962bc962ac3db1acc3f13445..3258f43693d5600a26985caa5de675ab04c6f8a5 100644 (file)
@@ -1,9 +1,9 @@
 <?php namespace BookStack\Http\Controllers;
 
 use Activity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\PageContent;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\PageContent;
+use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Entities\Repos\BookshelfRepo;
 use Illuminate\Http\Response;
@@ -14,7 +14,6 @@ class HomeController extends Controller
 
     /**
      * Display the homepage.
-     * @return Response
      */
     public function index()
     {
@@ -22,17 +21,24 @@ class HomeController extends Controller
         $draftPages = [];
 
         if ($this->isSignedIn()) {
-            $draftPages = Page::visible()->where('draft', '=', true)
+            $draftPages = Page::visible()
+                ->where('draft', '=', true)
                 ->where('created_by', '=', user()->id)
-                ->orderBy('updated_at', 'desc')->take(6)->get();
+                ->orderBy('updated_at', 'desc')
+                ->with('book')
+                ->take(6)
+                ->get();
         }
 
         $recentFactor = count($draftPages) > 0 ? 0.5 : 1;
         $recents = $this->isSignedIn() ?
-              Views::getUserRecentlyViewed(12*$recentFactor, 0)
+              Views::getUserRecentlyViewed(12*$recentFactor, 1)
             : Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get();
-        $recentlyUpdatedPages = Page::visible()->where('draft', false)
-            ->orderBy('updated_at', 'desc')->take(12)->get();
+        $recentlyUpdatedPages = Page::visible()->with('book')
+            ->where('draft', false)
+            ->orderBy('updated_at', 'desc')
+            ->take(12)
+            ->get();
 
         $homepageOptions = ['default', 'books', 'bookshelves', 'page'];
         $homepageOption = setting('app-homepage-type', 'default');
@@ -69,11 +75,7 @@ class HomeController extends Controller
         }
 
         if ($homepageOption === 'bookshelves') {
-            $shelfRepo = app(BookshelfRepo::class);
             $shelves = app(BookshelfRepo::class)->getAllPaginated(18, $commonData['sort'], $commonData['order']);
-            foreach ($shelves as $shelf) {
-                $shelf->books = $shelf->visibleBooks;
-            }
             $data = array_merge($commonData, ['shelves' => $shelves]);
             return view('common.home-shelves', $data);
         }
@@ -108,15 +110,16 @@ class HomeController extends Controller
 
     /**
      * Show the view for /robots.txt
-     * @return $this
      */
     public function getRobots()
     {
         $sitePublic = setting('app-public', false);
         $allowRobots = config('app.allow_robots');
+
         if ($allowRobots === null) {
             $allowRobots = $sitePublic;
         }
+
         return response()
             ->view('common.robots', ['allowRobots' => $allowRobots])
             ->header('Content-Type', 'text/plain');
index 3595790f71d5e557760601205ba683468c04d531..462ab68f6f32f66ee44ef4439d2448ff62e2c12e 100644 (file)
@@ -4,6 +4,7 @@ namespace BookStack\Http\Controllers\Images;
 
 use BookStack\Exceptions\ImageUploadException;
 use BookStack\Uploads\ImageRepo;
+use Exception;
 use Illuminate\Http\Request;
 use BookStack\Http\Controllers\Controller;
 
@@ -11,21 +12,14 @@ class DrawioImageController extends Controller
 {
     protected $imageRepo;
 
-    /**
-     * DrawioImageController constructor.
-     * @param ImageRepo $imageRepo
-     */
     public function __construct(ImageRepo $imageRepo)
     {
         $this->imageRepo = $imageRepo;
-        parent::__construct();
     }
 
     /**
      * Get a list of gallery images, in a list.
      * Can be paged and filtered by entity.
-     * @param Request $request
-     * @return \Illuminate\Http\JsonResponse
      */
     public function list(Request $request)
     {
@@ -35,14 +29,15 @@ class DrawioImageController extends Controller
         $parentTypeFilter = $request->get('filter_type', null);
 
         $imgData = $this->imageRepo->getEntityFiltered('drawio', $parentTypeFilter, $page, 24, $uploadedToFilter, $searchTerm);
-        return response()->json($imgData);
+        return view('components.image-manager-list', [
+            'images' => $imgData['images'],
+            'hasMore' => $imgData['has_more'],
+        ]);
     }
 
     /**
      * Store a new gallery image in the system.
-     * @param Request $request
-     * @return Illuminate\Http\JsonResponse
-     * @throws \Exception
+     * @throws Exception
      */
     public function create(Request $request)
     {
@@ -66,8 +61,6 @@ class DrawioImageController extends Controller
 
     /**
      * Get the content of an image based64 encoded.
-     * @param $id
-     * @return \Illuminate\Http\JsonResponse|mixed
      */
     public function getAsBase64($id)
     {
@@ -81,6 +74,7 @@ class DrawioImageController extends Controller
         if ($imageData === null) {
             return $this->jsonError("Image data could not be found");
         }
+
         return response()->json([
             'content' => base64_encode($imageData)
         ]);
index fd52ffd3fb6eaa30466345b2a08e60c0227ba009..c3ad0b7b261fe6d5946de46eacf5f263715e7470 100644 (file)
@@ -6,6 +6,7 @@ use BookStack\Exceptions\ImageUploadException;
 use BookStack\Uploads\ImageRepo;
 use Illuminate\Http\Request;
 use BookStack\Http\Controllers\Controller;
+use Illuminate\Validation\ValidationException;
 
 class GalleryImageController extends Controller
 {
@@ -13,19 +14,15 @@ class GalleryImageController extends Controller
 
     /**
      * GalleryImageController constructor.
-     * @param ImageRepo $imageRepo
      */
     public function __construct(ImageRepo $imageRepo)
     {
         $this->imageRepo = $imageRepo;
-        parent::__construct();
     }
 
     /**
      * Get a list of gallery images, in a list.
      * Can be paged and filtered by entity.
-     * @param Request $request
-     * @return \Illuminate\Http\JsonResponse
      */
     public function list(Request $request)
     {
@@ -35,20 +32,21 @@ class GalleryImageController extends Controller
         $parentTypeFilter = $request->get('filter_type', null);
 
         $imgData = $this->imageRepo->getEntityFiltered('gallery', $parentTypeFilter, $page, 24, $uploadedToFilter, $searchTerm);
-        return response()->json($imgData);
+        return view('components.image-manager-list', [
+            'images' => $imgData['images'],
+            'hasMore' => $imgData['has_more'],
+        ]);
     }
 
     /**
      * Store a new gallery image in the system.
-     * @param Request $request
-     * @return Illuminate\Http\JsonResponse
-     * @throws \Exception
+     * @throws ValidationException
      */
     public function create(Request $request)
     {
         $this->checkPermission('image-create-all');
         $this->validate($request, [
-            'file' => $this->imageRepo->getImageValidationRules()
+            'file' => $this->getImageValidationRules()
         ]);
 
         try {
index 9c67704dd339bd0b21d3471b751206625386869b..ecc36bf67e24ad531f83326ed32d22bf4f97f63d 100644 (file)
@@ -1,13 +1,13 @@
 <?php namespace BookStack\Http\Controllers\Images;
 
-use BookStack\Entities\Page;
 use BookStack\Exceptions\ImageUploadException;
 use BookStack\Http\Controllers\Controller;
-use BookStack\Repos\PageRepo;
 use BookStack\Uploads\Image;
 use BookStack\Uploads\ImageRepo;
+use Exception;
 use Illuminate\Filesystem\Filesystem as File;
 use Illuminate\Http\Request;
+use Illuminate\Validation\ValidationException;
 
 class ImageController extends Controller
 {
@@ -17,22 +17,16 @@ class ImageController extends Controller
 
     /**
      * ImageController constructor.
-     * @param Image $image
-     * @param File $file
-     * @param ImageRepo $imageRepo
      */
     public function __construct(Image $image, File $file, ImageRepo $imageRepo)
     {
         $this->image = $image;
         $this->file = $file;
         $this->imageRepo = $imageRepo;
-        parent::__construct();
     }
 
     /**
      * Provide an image file from storage.
-     * @param string $path
-     * @return mixed
      */
     public function showImage(string $path)
     {
@@ -47,13 +41,10 @@ class ImageController extends Controller
 
     /**
      * Update image details
-     * @param Request $request
-     * @param integer $id
-     * @return \Illuminate\Http\JsonResponse
      * @throws ImageUploadException
-     * @throws \Exception
+     * @throws ValidationException
      */
-    public function update(Request $request, $id)
+    public function update(Request $request, string $id)
     {
         $this->validate($request, [
             'name' => 'required|min:2|string'
@@ -64,47 +55,50 @@ class ImageController extends Controller
         $this->checkOwnablePermission('image-update', $image);
 
         $image = $this->imageRepo->updateImageDetails($image, $request->all());
-        return response()->json($image);
+
+        $this->imageRepo->loadThumbs($image);
+        return view('components.image-manager-form', [
+            'image' => $image,
+            'dependantPages' => null,
+        ]);
     }
 
     /**
-     * Show the usage of an image on pages.
+     * Get the form for editing the given image.
+     * @throws Exception
      */
-    public function usage(int $id)
+    public function edit(Request $request, string $id)
     {
         $image = $this->imageRepo->getById($id);
         $this->checkImagePermission($image);
 
-        $pages = Page::visible()->where('html', 'like', '%' . $image->url . '%')->get(['id', 'name', 'slug', 'book_id']);
-        foreach ($pages as $page) {
-            $page->url = $page->getUrl();
-            $page->html = '';
-            $page->text = '';
+        if ($request->has('delete')) {
+            $dependantPages = $this->imageRepo->getPagesUsingImage($image);
         }
-        $result = count($pages) > 0 ? $pages : false;
 
-        return response()->json($result);
+        $this->imageRepo->loadThumbs($image);
+        return view('components.image-manager-form', [
+            'image' => $image,
+            'dependantPages' => $dependantPages ?? null,
+        ]);
     }
 
     /**
      * Deletes an image and all thumbnail/image files
-     * @param int $id
-     * @return \Illuminate\Http\JsonResponse
-     * @throws \Exception
+     * @throws Exception
      */
-    public function destroy($id)
+    public function destroy(string $id)
     {
         $image = $this->imageRepo->getById($id);
         $this->checkOwnablePermission('image-delete', $image);
         $this->checkImagePermission($image);
 
         $this->imageRepo->destroyImage($image);
-        return response()->json(trans('components.images_deleted'));
+        return response('');
     }
 
     /**
      * Check related page permission and ensure type is drawio or gallery.
-     * @param Image $image
      */
     protected function checkImagePermission(Image $image)
     {
diff --git a/app/Http/Controllers/MaintenanceController.php b/app/Http/Controllers/MaintenanceController.php
new file mode 100644 (file)
index 0000000..3354a14
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+
+namespace BookStack\Http\Controllers;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Tools\TrashCan;
+use BookStack\Notifications\TestEmail;
+use BookStack\Uploads\ImageService;
+use Illuminate\Http\Request;
+
+class MaintenanceController extends Controller
+{
+    /**
+     * Show the page for application maintenance.
+     */
+    public function index()
+    {
+        $this->checkPermission('settings-manage');
+        $this->setPageTitle(trans('settings.maint'));
+
+        // Get application version
+        $version = trim(file_get_contents(base_path('version')));
+
+        // Recycle bin details
+        $recycleStats = (new TrashCan())->getTrashedCounts();
+
+        return view('settings.maintenance', [
+            'version' => $version,
+            'recycleStats' => $recycleStats,
+        ]);
+    }
+
+    /**
+     * Action to clean-up images in the system.
+     */
+    public function cleanupImages(Request $request, ImageService $imageService)
+    {
+        $this->checkPermission('settings-manage');
+        $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'cleanup-images');
+
+        $checkRevisions = !($request->get('ignore_revisions', 'false') === 'true');
+        $dryRun = !($request->has('confirm'));
+
+        $imagesToDelete = $imageService->deleteUnusedImages($checkRevisions, $dryRun);
+        $deleteCount = count($imagesToDelete);
+        if ($deleteCount === 0) {
+            $this->showWarningNotification(trans('settings.maint_image_cleanup_nothing_found'));
+            return redirect('/settings/maintenance')->withInput();
+        }
+
+        if ($dryRun) {
+            session()->flash('cleanup-images-warning', trans('settings.maint_image_cleanup_warning', ['count' => $deleteCount]));
+        } else {
+            $this->showSuccessNotification(trans('settings.maint_image_cleanup_success', ['count' => $deleteCount]));
+        }
+
+        return redirect('/settings/maintenance#image-cleanup')->withInput();
+    }
+
+    /**
+     * Action to send a test e-mail to the current user.
+     */
+    public function sendTestEmail()
+    {
+        $this->checkPermission('settings-manage');
+        $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'send-test-email');
+
+        try {
+            user()->notify(new TestEmail());
+            $this->showSuccessNotification(trans('settings.maint_send_test_email_success', ['address' => user()->email]));
+        } catch (\Exception $exception) {
+            $errorMessage = trans('errors.maintenance_test_email_failure') . "\n" . $exception->getMessage();
+            $this->showErrorNotification($errorMessage);
+        }
+
+        return redirect('/settings/maintenance#image-cleanup')->withInput();
+    }
+}
index 630f888ed3e9dc0d16da96b622a03278b527cfb9..7d8e54382961006db647b5f1b1b4fe9982337a2d 100644 (file)
@@ -1,11 +1,11 @@
 <?php namespace BookStack\Http\Controllers;
 
-use Activity;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Managers\PageContent;
-use BookStack\Entities\Managers\PageEditActivity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Tools\PageContent;
+use BookStack\Entities\Tools\PageEditActivity;
+use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\PageRepo;
+use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\NotifyException;
 use BookStack\Exceptions\PermissionsException;
@@ -26,7 +26,6 @@ class PageController extends Controller
     public function __construct(PageRepo $pageRepo)
     {
         $this->pageRepo = $pageRepo;
-        parent::__construct();
     }
 
     /**
@@ -78,7 +77,7 @@ class PageController extends Controller
     public function editDraft(string $bookSlug, int $pageId)
     {
         $draft = $this->pageRepo->getById($pageId);
-        $this->checkOwnablePermission('page-create', $draft->parent());
+        $this->checkOwnablePermission('page-create', $draft->getParent());
         $this->setPageTitle(trans('entities.pages_edit_draft'));
 
         $draftsEnabled = $this->isSignedIn();
@@ -104,10 +103,9 @@ class PageController extends Controller
             'name' => 'required|string|max:255'
         ]);
         $draftPage = $this->pageRepo->getById($pageId);
-        $this->checkOwnablePermission('page-create', $draftPage->parent());
+        $this->checkOwnablePermission('page-create', $draftPage->getParent());
 
         $page = $this->pageRepo->publishDraft($draftPage, $request->all());
-        Activity::add($page, 'page_create', $draftPage->book->id);
 
         return redirect($page->getUrl());
     }
@@ -163,6 +161,8 @@ class PageController extends Controller
     public function getPageAjax(int $pageId)
     {
         $page = $this->pageRepo->getById($pageId);
+        $page->setHidden(array_diff($page->getHidden(), ['html', 'markdown']));
+        $page->addHidden(['book']);
         return response()->json($page);
     }
 
@@ -222,7 +222,6 @@ class PageController extends Controller
         $this->checkOwnablePermission('page-update', $page);
 
         $this->pageRepo->update($page, $request->all());
-        Activity::add($page, 'page_update', $page->book->id);
 
         return redirect($page->getUrl());
     }
@@ -302,13 +301,11 @@ class PageController extends Controller
     {
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
         $this->checkOwnablePermission('page-delete', $page);
+        $parent = $page->getParent();
 
-        $book = $page->book;
         $this->pageRepo->destroy($page);
-        Activity::addMessage('page_delete', $page->name, $book->id);
 
-        $this->showSuccessNotification(trans('entities.pages_delete_success'));
-        return redirect($book->getUrl());
+        return redirect($parent->getUrl());
     }
 
     /**
@@ -391,7 +388,6 @@ class PageController extends Controller
             return redirect()->back();
         }
 
-        Activity::add($page, 'page_move', $page->book->id);
         $this->showSuccessNotification(trans('entities.pages_move_success', ['parentName' => $parent->name]));
         return redirect($page->getUrl());
     }
@@ -436,8 +432,6 @@ class PageController extends Controller
             return redirect()->back();
         }
 
-        Activity::add($pageCopy, 'page_create', $pageCopy->book->id);
-
         $this->showSuccessNotification(trans('entities.pages_copy_success'));
         return redirect($pageCopy->getUrl());
     }
@@ -460,14 +454,12 @@ class PageController extends Controller
      * @throws NotFoundException
      * @throws Throwable
      */
-    public function permissions(Request $request, string $bookSlug, string $pageSlug)
+    public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug, string $pageSlug)
     {
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
         $this->checkOwnablePermission('restrictions-manage', $page);
 
-        $restricted = $request->get('restricted') === 'true';
-        $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
-        $this->pageRepo->updatePermissions($page, $restricted, $permissions);
+        $permissionsUpdater->updateFromPermissionsForm($page, $request);
 
         $this->showSuccessNotification(trans('entities.pages_permissions_success'));
         return redirect($page->getUrl());
index 3b02ea224716c4f01bc1ceffdd043d5e88702ba4..e5e027fe72cd2f5cec19418d9ea81901238e2eb7 100644 (file)
@@ -2,8 +2,8 @@
 
 namespace BookStack\Http\Controllers;
 
-use BookStack\Entities\ExportService;
-use BookStack\Entities\Managers\PageContent;
+use BookStack\Entities\Tools\ExportFormatter;
+use BookStack\Entities\Tools\PageContent;
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Exceptions\NotFoundException;
 use Throwable;
@@ -12,18 +12,15 @@ class PageExportController extends Controller
 {
 
     protected $pageRepo;
-    protected $exportService;
+    protected $exportFormatter;
 
     /**
      * PageExportController constructor.
-     * @param PageRepo $pageRepo
-     * @param ExportService $exportService
      */
-    public function __construct(PageRepo $pageRepo, ExportService $exportService)
+    public function __construct(PageRepo $pageRepo, ExportFormatter $exportFormatter)
     {
         $this->pageRepo = $pageRepo;
-        $this->exportService = $exportService;
-        parent::__construct();
+        $this->exportFormatter = $exportFormatter;
     }
 
     /**
@@ -36,7 +33,7 @@ class PageExportController extends Controller
     {
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
         $page->html = (new PageContent($page))->render();
-        $pdfContent = $this->exportService->pageToPdf($page);
+        $pdfContent = $this->exportFormatter->pageToPdf($page);
         return $this->downloadResponse($pdfContent, $pageSlug . '.pdf');
     }
 
@@ -49,7 +46,7 @@ class PageExportController extends Controller
     {
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
         $page->html = (new PageContent($page))->render();
-        $containedHtml = $this->exportService->pageToContainedHtml($page);
+        $containedHtml = $this->exportFormatter->pageToContainedHtml($page);
         return $this->downloadResponse($containedHtml, $pageSlug . '.html');
     }
 
@@ -60,7 +57,7 @@ class PageExportController extends Controller
     public function plainText(string $bookSlug, string $pageSlug)
     {
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $pageText = $this->exportService->pageToPlainText($page);
+        $pageText = $this->exportFormatter->pageToPlainText($page);
         return $this->downloadResponse($pageText, $pageSlug . '.txt');
     }
 }
index 3c65b50ac5ae6e2d9c9769ba2f4a4ace572ea51f..4c43330164b743133490dbdf8e764cdfc2836383 100644 (file)
@@ -1,9 +1,9 @@
 <?php namespace BookStack\Http\Controllers;
 
+use BookStack\Entities\Tools\PageContent;
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Exceptions\NotFoundException;
-use BookStack\Facades\Activity;
-use GatherContent\Htmldiff\Htmldiff;
+use Ssddanbrown\HtmlDiff\Diff;
 
 class PageRevisionController extends Controller
 {
@@ -16,7 +16,6 @@ class PageRevisionController extends Controller
     public function __construct(PageRepo $pageRepo)
     {
         $this->pageRepo = $pageRepo;
-        parent::__construct();
     }
 
     /**
@@ -46,6 +45,9 @@ class PageRevisionController extends Controller
         }
 
         $page->fill($revision->toArray());
+        // TODO - Refactor PageContent so we don't need to juggle this
+        $page->html = $revision->html;
+        $page->html = (new PageContent($page))->render();
 
         $this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
         return view('pages.revision', [
@@ -70,9 +72,12 @@ class PageRevisionController extends Controller
 
         $prev = $revision->getPrevious();
         $prevContent = $prev->html ?? '';
-        $diff = (new Htmldiff)->diff($prevContent, $revision->html);
+        $diff = Diff::excecute($prevContent, $revision->html);
 
         $page->fill($revision->toArray());
+        // TODO - Refactor PageContent so we don't need to juggle this
+        $page->html = $revision->html;
+        $page->html = (new PageContent($page))->render();
         $this->setPageTitle(trans('entities.pages_revision_named', ['pageName'=>$page->getShortName()]));
 
         return view('pages.revision', [
@@ -94,7 +99,6 @@ class PageRevisionController extends Controller
 
         $page = $this->pageRepo->restoreRevision($page, $revisionId);
 
-        Activity::add($page, 'page_restore', $page->book->id);
         return redirect($page->getUrl());
     }
 
index eaa1a8ae26ae18f28473c6796062dfb5065dba97..2307bc0d52c09b5bd6ee1411f795ac83c11f8e0a 100644 (file)
@@ -16,7 +16,6 @@ class PageTemplateController extends Controller
     public function __construct(PageRepo $pageRepo)
     {
         $this->pageRepo = $pageRepo;
-        parent::__construct();
     }
 
     /**
diff --git a/app/Http/Controllers/RecycleBinController.php b/app/Http/Controllers/RecycleBinController.php
new file mode 100644 (file)
index 0000000..a644a28
--- /dev/null
@@ -0,0 +1,107 @@
+<?php namespace BookStack\Http\Controllers;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Deletion;
+use BookStack\Entities\Tools\TrashCan;
+
+class RecycleBinController extends Controller
+{
+
+    protected $recycleBinBaseUrl = '/settings/recycle-bin';
+
+    /**
+     * On each request to a method of this controller check permissions
+     * using a middleware closure.
+     */
+    public function __construct()
+    {
+        $this->middleware(function ($request, $next) {
+            $this->checkPermission('settings-manage');
+            $this->checkPermission('restrictions-manage-all');
+            return $next($request);
+        });
+    }
+
+
+    /**
+     * Show the top-level listing for the recycle bin.
+     */
+    public function index()
+    {
+        $deletions = Deletion::query()->with(['deletable', 'deleter'])->paginate(10);
+
+        $this->setPageTitle(trans('settings.recycle_bin'));
+        return view('settings.recycle-bin.index', [
+            'deletions' => $deletions,
+        ]);
+    }
+
+    /**
+     * Show the page to confirm a restore of the deletion of the given id.
+     */
+    public function showRestore(string $id)
+    {
+        /** @var Deletion $deletion */
+        $deletion = Deletion::query()->findOrFail($id);
+
+        return view('settings.recycle-bin.restore', [
+            'deletion' => $deletion,
+        ]);
+    }
+
+    /**
+     * Restore the element attached to the given deletion.
+     * @throws \Exception
+     */
+    public function restore(string $id)
+    {
+        /** @var Deletion $deletion */
+        $deletion = Deletion::query()->findOrFail($id);
+        $this->logActivity(ActivityType::RECYCLE_BIN_RESTORE, $deletion);
+        $restoreCount = (new TrashCan())->restoreFromDeletion($deletion);
+
+        $this->showSuccessNotification(trans('settings.recycle_bin_restore_notification', ['count' => $restoreCount]));
+        return redirect($this->recycleBinBaseUrl);
+    }
+
+    /**
+     * Show the page to confirm a Permanent deletion of the element attached to the deletion of the given id.
+     */
+    public function showDestroy(string $id)
+    {
+        /** @var Deletion $deletion */
+        $deletion = Deletion::query()->findOrFail($id);
+
+        return view('settings.recycle-bin.destroy', [
+            'deletion' => $deletion,
+        ]);
+    }
+
+    /**
+     * Permanently delete the content associated with the given deletion.
+     * @throws \Exception
+     */
+    public function destroy(string $id)
+    {
+        /** @var Deletion $deletion */
+        $deletion = Deletion::query()->findOrFail($id);
+        $this->logActivity(ActivityType::RECYCLE_BIN_DESTROY, $deletion);
+        $deleteCount = (new TrashCan())->destroyFromDeletion($deletion);
+
+        $this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount]));
+        return redirect($this->recycleBinBaseUrl);
+    }
+
+    /**
+     * Empty out the recycle bin.
+     * @throws \Exception
+     */
+    public function empty()
+    {
+        $deleteCount = (new TrashCan())->empty();
+
+        $this->logActivity(ActivityType::RECYCLE_BIN_EMPTY);
+        $this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount]));
+        return redirect($this->recycleBinBaseUrl);
+    }
+}
similarity index 68%
rename from app/Http/Controllers/PermissionController.php
rename to app/Http/Controllers/RoleController.php
index 148ae5cd65a3049e421ea2b331f3bfa34f31ed4d..e16a724a48c49fa419506a997ace27310af607b1 100644 (file)
@@ -2,27 +2,27 @@
 
 use BookStack\Auth\Permissions\PermissionsRepo;
 use BookStack\Exceptions\PermissionsException;
+use Exception;
 use Illuminate\Http\Request;
+use Illuminate\Validation\ValidationException;
 
-class PermissionController extends Controller
+class RoleController extends Controller
 {
 
     protected $permissionsRepo;
 
     /**
      * PermissionController constructor.
-     * @param \BookStack\Auth\Permissions\PermissionsRepo $permissionsRepo
      */
     public function __construct(PermissionsRepo $permissionsRepo)
     {
         $this->permissionsRepo = $permissionsRepo;
-        parent::__construct();
     }
 
     /**
      * Show a listing of the roles in the system.
      */
-    public function listRoles()
+    public function list()
     {
         $this->checkPermission('user-roles-manage');
         $roles = $this->permissionsRepo->getAllRoles();
@@ -31,9 +31,8 @@ class PermissionController extends Controller
 
     /**
      * Show the form to create a new role
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
-    public function createRole()
+    public function create()
     {
         $this->checkPermission('user-roles-manage');
         return view('settings.roles.create');
@@ -41,15 +40,13 @@ class PermissionController extends Controller
 
     /**
      * Store a new role in the system.
-     * @param Request $request
-     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
      */
-    public function storeRole(Request $request)
+    public function store(Request $request)
     {
         $this->checkPermission('user-roles-manage');
         $this->validate($request, [
-            'display_name' => 'required|min:3|max:200',
-            'description' => 'max:250'
+            'display_name' => 'required|min:3|max:180',
+            'description' => 'max:180'
         ]);
 
         $this->permissionsRepo->saveNewRole($request->all());
@@ -59,11 +56,9 @@ class PermissionController extends Controller
 
     /**
      * Show the form for editing a user role.
-     * @param $id
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      * @throws PermissionsException
      */
-    public function editRole($id)
+    public function edit(string $id)
     {
         $this->checkPermission('user-roles-manage');
         $role = $this->permissionsRepo->getRoleById($id);
@@ -75,18 +70,14 @@ class PermissionController extends Controller
 
     /**
      * Updates a user role.
-     * @param Request $request
-     * @param $id
-     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
-     * @throws PermissionsException
-     * @throws \Illuminate\Validation\ValidationException
+     * @throws ValidationException
      */
-    public function updateRole(Request $request, $id)
+    public function update(Request $request, string $id)
     {
         $this->checkPermission('user-roles-manage');
         $this->validate($request, [
-            'display_name' => 'required|min:3|max:200',
-            'description' => 'max:250'
+            'display_name' => 'required|min:3|max:180',
+            'description' => 'max:180'
         ]);
 
         $this->permissionsRepo->updateRole($id, $request->all());
@@ -97,10 +88,8 @@ class PermissionController extends Controller
     /**
      * Show the view to delete a role.
      * Offers the chance to migrate users.
-     * @param $id
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
-    public function showDeleteRole($id)
+    public function showDelete(string $id)
     {
         $this->checkPermission('user-roles-manage');
         $role = $this->permissionsRepo->getRoleById($id);
@@ -113,11 +102,9 @@ class PermissionController extends Controller
     /**
      * Delete a role from the system,
      * Migrate from a previous role if set.
-     * @param Request $request
-     * @param $id
-     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     * @throws Exception
      */
-    public function deleteRole(Request $request, $id)
+    public function delete(Request $request, string $id)
     {
         $this->checkPermission('user-roles-manage');
 
index a5cd7ad6b82224f094173f1d76b2d4a864b48174..21ebea378c06c11f897046575a3fe76a3d0185f9 100644 (file)
@@ -1,31 +1,29 @@
 <?php namespace BookStack\Http\Controllers;
 
 use BookStack\Actions\ViewService;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Managers\EntityContext;
-use BookStack\Entities\SearchService;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Tools\SearchRunner;
+use BookStack\Entities\Tools\ShelfContext;
+use BookStack\Entities\Tools\SearchOptions;
+use BookStack\Entities\Tools\SiblingFetcher;
 use Illuminate\Http\Request;
 
 class SearchController extends Controller
 {
     protected $viewService;
-    protected $searchService;
+    protected $searchRunner;
     protected $entityContextManager;
 
-    /**
-     * SearchController constructor.
-     */
     public function __construct(
         ViewService $viewService,
-        SearchService $searchService,
-        EntityContext $entityContextManager
+        SearchRunner $searchRunner,
+        ShelfContext $entityContextManager
     ) {
         $this->viewService = $viewService;
-        $this->searchService = $searchService;
+        $this->searchRunner = $searchRunner;
         $this->entityContextManager = $entityContextManager;
-        parent::__construct();
     }
 
     /**
@@ -33,31 +31,32 @@ class SearchController extends Controller
      */
     public function search(Request $request)
     {
-        $searchTerm = $request->get('term');
-        $this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
+        $searchOpts = SearchOptions::fromRequest($request);
+        $fullSearchString = $searchOpts->toString();
+        $this->setPageTitle(trans('entities.search_for_term', ['term' => $fullSearchString]));
 
         $page = intval($request->get('page', '0')) ?: 1;
-        $nextPageLink = url('/search?term=' . urlencode($searchTerm) . '&page=' . ($page+1));
+        $nextPageLink = url('/search?term=' . urlencode($fullSearchString) . '&page=' . ($page+1));
 
-        $results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20);
+        $results = $this->searchRunner->searchEntities($searchOpts, 'all', $page, 20);
 
         return view('search.all', [
             'entities'   => $results['results'],
             'totalResults' => $results['total'],
-            'searchTerm' => $searchTerm,
+            'searchTerm' => $fullSearchString,
             'hasNextPage' => $results['has_more'],
-            'nextPageLink' => $nextPageLink
+            'nextPageLink' => $nextPageLink,
+            'options' => $searchOpts,
         ]);
     }
 
-
     /**
      * Searches all entities within a book.
      */
     public function searchBook(Request $request, int $bookId)
     {
         $term = $request->get('term', '');
-        $results = $this->searchService->searchBook($bookId, $term);
+        $results = $this->searchRunner->searchBook($bookId, $term);
         return view('partials.entity-list', ['entities' => $results]);
     }
 
@@ -67,7 +66,7 @@ class SearchController extends Controller
     public function searchChapter(Request $request, int $chapterId)
     {
         $term = $request->get('term', '');
-        $results = $this->searchService->searchChapter($chapterId, $term);
+        $results = $this->searchRunner->searchChapter($chapterId, $term);
         return view('partials.entity-list', ['entities' => $results]);
     }
 
@@ -84,7 +83,7 @@ class SearchController extends Controller
         // Search for entities otherwise show most popular
         if ($searchTerm !== false) {
             $searchTerm .= ' {type:'. implode('|', $entityTypes) .'}';
-            $entities = $this->searchService->searchEntities($searchTerm, 'all', 1, 20, $permission)['results'];
+            $entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20, $permission)['results'];
         } else {
             $entities = $this->viewService->getPopular(20, 0, $entityTypes, $permission);
         }
@@ -100,39 +99,7 @@ class SearchController extends Controller
         $type = $request->get('entity_type', null);
         $id = $request->get('entity_id', null);
 
-        $entity = Entity::getEntityInstance($type)->newQuery()->visible()->find($id);
-        if (!$entity) {
-            return $this->jsonError(trans('errors.entity_not_found'), 404);
-        }
-
-        $entities = [];
-
-        // Page in chapter
-        if ($entity->isA('page') && $entity->chapter) {
-            $entities = $entity->chapter->visiblePages();
-        }
-
-        // Page in book or chapter
-        if (($entity->isA('page') && !$entity->chapter) || $entity->isA('chapter')) {
-            $entities = $entity->book->getDirectChildren();
-        }
-
-        // Book
-        // Gets just the books in a shelf if shelf is in context
-        if ($entity->isA('book')) {
-            $contextShelf = $this->entityContextManager->getContextualShelfForBook($entity);
-            if ($contextShelf) {
-                $entities = $contextShelf->visibleBooks()->get();
-            } else {
-                $entities = Book::visible()->get();
-            }
-        }
-
-        // Shelve
-        if ($entity->isA('bookshelf')) {
-            $entities = Bookshelf::visible()->get();
-        }
-
+        $entities = (new SiblingFetcher)->fetch($type, $id);
         return view('partials.entity-list-basic', ['entities' => $entities, 'style' => 'compact']);
     }
 }
index f0a078300654861de6026ccf5ce9929f456976a0..f02f541bc9ba5b1a9ad14270d900ae053b23900d 100644 (file)
@@ -1,12 +1,9 @@
 <?php namespace BookStack\Http\Controllers;
 
+use BookStack\Actions\ActivityType;
 use BookStack\Auth\User;
-use BookStack\Notifications\TestEmail;
 use BookStack\Uploads\ImageRepo;
-use BookStack\Uploads\ImageService;
 use Illuminate\Http\Request;
-use Illuminate\Http\Response;
-use Setting;
 
 class SettingController extends Controller
 {
@@ -14,18 +11,14 @@ class SettingController extends Controller
 
     /**
      * SettingController constructor.
-     * @param $imageRepo
      */
     public function __construct(ImageRepo $imageRepo)
     {
         $this->imageRepo = $imageRepo;
-        parent::__construct();
     }
 
-
     /**
      * Display a listing of the settings.
-     * @return Response
      */
     public function index()
     {
@@ -43,28 +36,26 @@ class SettingController extends Controller
 
     /**
      * Update the specified settings in storage.
-     * @param  Request $request
-     * @return Response
      */
     public function update(Request $request)
     {
         $this->preventAccessInDemoMode();
         $this->checkPermission('settings-manage');
         $this->validate($request, [
-            'app_logo' => $this->imageRepo->getImageValidationRules(),
+            'app_logo' => 'nullable|' . $this->getImageValidationRules(),
         ]);
 
         // Cycles through posted settings and update them
         foreach ($request->all() as $name => $value) {
+            $key = str_replace('setting-', '', trim($name));
             if (strpos($name, 'setting-') !== 0) {
                 continue;
             }
-            $key = str_replace('setting-', '', trim($name));
             setting()->put($key, $value);
         }
 
         // Update logo image if set
-        if ($request->has('app_logo')) {
+        if ($request->hasFile('app_logo')) {
             $logoFile = $request->file('app_logo');
             $this->imageRepo->destroyByType('system');
             $image = $this->imageRepo->saveNew($logoFile, 'system', 0, null, 86);
@@ -77,67 +68,10 @@ class SettingController extends Controller
             setting()->remove('app-logo');
         }
 
+        $section = $request->get('section', '');
+        $this->logActivity(ActivityType::SETTINGS_UPDATE, $section);
         $this->showSuccessNotification(trans('settings.settings_save_success'));
-        return redirect('/settings');
-    }
-
-    /**
-     * Show the page for application maintenance.
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
-     */
-    public function showMaintenance()
-    {
-        $this->checkPermission('settings-manage');
-        $this->setPageTitle(trans('settings.maint'));
-
-        // Get application version
-        $version = trim(file_get_contents(base_path('version')));
-
-        return view('settings.maintenance', ['version' => $version]);
-    }
-
-    /**
-     * Action to clean-up images in the system.
-     * @param Request $request
-     * @param ImageService $imageService
-     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
-     */
-    public function cleanupImages(Request $request, ImageService $imageService)
-    {
-        $this->checkPermission('settings-manage');
-
-        $checkRevisions = !($request->get('ignore_revisions', 'false') === 'true');
-        $dryRun = !($request->has('confirm'));
-
-        $imagesToDelete = $imageService->deleteUnusedImages($checkRevisions, $dryRun);
-        $deleteCount = count($imagesToDelete);
-        if ($deleteCount === 0) {
-            $this->showWarningNotification(trans('settings.maint_image_cleanup_nothing_found'));
-            return redirect('/settings/maintenance')->withInput();
-        }
-
-        if ($dryRun) {
-            session()->flash('cleanup-images-warning', trans('settings.maint_image_cleanup_warning', ['count' => $deleteCount]));
-        } else {
-            $this->showSuccessNotification(trans('settings.maint_image_cleanup_success', ['count' => $deleteCount]));
-        }
-
-        return redirect('/settings/maintenance#image-cleanup')->withInput();
-    }
-
-    /**
-     * Action to send a test e-mail to the current user.
-     * @param Request $request
-     * @param User $user
-     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
-     */
-    public function sendTestEmail(Request $request)
-    {
-        $this->checkPermission('settings-manage');
-
-        user()->notify(new TestEmail());
-        $this->showSuccessNotification(trans('settings.maint_send_test_email_success', ['address' => user()->email]));
-
-        return redirect('/settings/maintenance#image-cleanup')->withInput();
+        $redirectLocation = '/settings#' . $section;
+        return redirect(rtrim($redirectLocation, '#'));
     }
 }
diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php
new file mode 100644 (file)
index 0000000..9f4ed4d
--- /dev/null
@@ -0,0 +1,47 @@
+<?php namespace BookStack\Http\Controllers;
+
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Session;
+use Illuminate\Support\Str;
+
+class StatusController extends Controller
+{
+
+    /**
+     * Show the system status as a simple json page.
+     */
+    public function show()
+    {
+        $statuses = [
+            'database' => $this->trueWithoutError(function () {
+                return DB::table('migrations')->count() > 0;
+            }),
+            'cache' => $this->trueWithoutError(function () {
+                $rand = Str::random();
+                Cache::set('status_test', $rand);
+                return Cache::get('status_test') === $rand;
+            }),
+            'session' => $this->trueWithoutError(function () {
+                $rand = Str::random();
+                Session::put('status_test', $rand);
+                return Session::get('status_test') === $rand;
+            }),
+        ];
+
+        $hasError = in_array(false, $statuses);
+        return response()->json($statuses, $hasError ? 500 : 200);
+    }
+
+    /**
+     * Check the callable passed returns true and does not throw an exception.
+     */
+    protected function trueWithoutError(callable $test): bool
+    {
+        try {
+            return $test() === true;
+        } catch (\Exception $e) {
+            return false;
+        }
+    }
+}
index 6abbeeebaa451e437e8b150a2ae496f15fe37827..ce84bf4101e4c23f6437915a35c3d014a7c306a5 100644 (file)
@@ -10,47 +10,29 @@ class TagController extends Controller
 
     /**
      * TagController constructor.
-     * @param $tagRepo
      */
     public function __construct(TagRepo $tagRepo)
     {
         $this->tagRepo = $tagRepo;
-        parent::__construct();
-    }
-
-    /**
-     * Get all the Tags for a particular entity
-     * @param $entityType
-     * @param $entityId
-     * @return \Illuminate\Http\JsonResponse
-     */
-    public function getForEntity($entityType, $entityId)
-    {
-        $tags = $this->tagRepo->getForEntity($entityType, $entityId);
-        return response()->json($tags);
     }
 
     /**
      * Get tag name suggestions from a given search term.
-     * @param Request $request
-     * @return \Illuminate\Http\JsonResponse
      */
     public function getNameSuggestions(Request $request)
     {
-        $searchTerm = $request->get('search', false);
+        $searchTerm = $request->get('search', null);
         $suggestions = $this->tagRepo->getNameSuggestions($searchTerm);
         return response()->json($suggestions);
     }
 
     /**
      * Get tag value suggestions from a given search term.
-     * @param Request $request
-     * @return \Illuminate\Http\JsonResponse
      */
     public function getValueSuggestions(Request $request)
     {
-        $searchTerm = $request->get('search', false);
-        $tagName = $request->get('name', false);
+        $searchTerm = $request->get('search', null);
+        $tagName = $request->get('name', null);
         $suggestions = $this->tagRepo->getValueSuggestions($searchTerm, $tagName);
         return response()->json($suggestions);
     }
diff --git a/app/Http/Controllers/UserApiTokenController.php b/app/Http/Controllers/UserApiTokenController.php
new file mode 100644 (file)
index 0000000..ab0e906
--- /dev/null
@@ -0,0 +1,144 @@
+<?php namespace BookStack\Http\Controllers;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Api\ApiToken;
+use BookStack\Auth\User;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Str;
+
+class UserApiTokenController extends Controller
+{
+
+    /**
+     * Show the form to create a new API token.
+     */
+    public function create(int $userId)
+    {
+        // Ensure user is has access-api permission and is the current user or has permission to manage the current user.
+        $this->checkPermission('access-api');
+        $this->checkPermissionOrCurrentUser('users-manage', $userId);
+
+        $user = User::query()->findOrFail($userId);
+        return view('users.api-tokens.create', [
+            'user' => $user,
+        ]);
+    }
+
+    /**
+     * Store a new API token in the system.
+     */
+    public function store(Request $request, int $userId)
+    {
+        $this->checkPermission('access-api');
+        $this->checkPermissionOrCurrentUser('users-manage', $userId);
+
+        $this->validate($request, [
+            'name' => 'required|max:250',
+            'expires_at' => 'date_format:Y-m-d',
+        ]);
+
+        $user = User::query()->findOrFail($userId);
+        $secret = Str::random(32);
+
+        $token = (new ApiToken())->forceFill([
+            'name' => $request->get('name'),
+            'token_id' => Str::random(32),
+            'secret' => Hash::make($secret),
+            'user_id' => $user->id,
+            'expires_at' => $request->get('expires_at') ?: ApiToken::defaultExpiry(),
+        ]);
+
+        while (ApiToken::query()->where('token_id', '=', $token->token_id)->exists()) {
+            $token->token_id = Str::random(32);
+        }
+
+        $token->save();
+
+        session()->flash('api-token-secret:' . $token->id, $secret);
+        $this->showSuccessNotification(trans('settings.user_api_token_create_success'));
+        $this->logActivity(ActivityType::API_TOKEN_CREATE, $token);
+
+        return redirect($user->getEditUrl('/api-tokens/' . $token->id));
+    }
+
+    /**
+     * Show the details for a user API token, with access to edit.
+     */
+    public function edit(int $userId, int $tokenId)
+    {
+        [$user, $token] = $this->checkPermissionAndFetchUserToken($userId, $tokenId);
+        $secret = session()->pull('api-token-secret:' . $token->id, null);
+
+        return view('users.api-tokens.edit', [
+            'user' => $user,
+            'token' => $token,
+            'model' => $token,
+            'secret' => $secret,
+        ]);
+    }
+
+    /**
+     * Update the API token.
+     */
+    public function update(Request $request, int $userId, int $tokenId)
+    {
+        $this->validate($request, [
+            'name' => 'required|max:250',
+            'expires_at' => 'date_format:Y-m-d',
+        ]);
+
+        [$user, $token] = $this->checkPermissionAndFetchUserToken($userId, $tokenId);
+        $token->fill([
+            'name' => $request->get('name'),
+            'expires_at' => $request->get('expires_at') ?: ApiToken::defaultExpiry(),
+        ])->save();
+
+        $this->showSuccessNotification(trans('settings.user_api_token_update_success'));
+        $this->logActivity(ActivityType::API_TOKEN_UPDATE, $token);
+        return redirect($user->getEditUrl('/api-tokens/' . $token->id));
+    }
+
+    /**
+     * Show the delete view for this token.
+     */
+    public function delete(int $userId, int $tokenId)
+    {
+        [$user, $token] = $this->checkPermissionAndFetchUserToken($userId, $tokenId);
+        return view('users.api-tokens.delete', [
+            'user' => $user,
+            'token' => $token,
+        ]);
+    }
+
+    /**
+     * Destroy a token from the system.
+     */
+    public function destroy(int $userId, int $tokenId)
+    {
+        [$user, $token] = $this->checkPermissionAndFetchUserToken($userId, $tokenId);
+        $token->delete();
+
+        $this->showSuccessNotification(trans('settings.user_api_token_delete_success'));
+        $this->logActivity(ActivityType::API_TOKEN_DELETE, $token);
+
+        return redirect($user->getEditUrl('#api_tokens'));
+    }
+
+    /**
+     * Check the permission for the current user and return an array
+     * where the first item is the user in context and the second item is their
+     * API token in context.
+     */
+    protected function checkPermissionAndFetchUserToken(int $userId, int $tokenId): array
+    {
+        $this->checkPermissionOr('users-manage', function () use ($userId) {
+            return $userId === user()->id && userCan('access-api');
+        });
+
+        $user = User::query()->findOrFail($userId);
+        $token = ApiToken::query()->where('user_id', '=', $user->id)->where('id', '=', $tokenId)->firstOrFail();
+        return [$user, $token];
+    }
+
+}
index b55398d2f6f644cb35cec75383df75be9dd40b71..92e1cd8b7766864527f71641efd78cf4e1c6ec39 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace BookStack\Http\Controllers;
 
+use BookStack\Actions\ActivityType;
 use BookStack\Auth\Access\SocialAuthService;
 use BookStack\Auth\Access\UserInviteService;
 use BookStack\Auth\User;
@@ -7,7 +8,6 @@ use BookStack\Auth\UserRepo;
 use BookStack\Exceptions\UserUpdateException;
 use BookStack\Uploads\ImageRepo;
 use Illuminate\Http\Request;
-use Illuminate\Http\Response;
 use Illuminate\Support\Str;
 
 class UserController extends Controller
@@ -20,10 +20,6 @@ class UserController extends Controller
 
     /**
      * UserController constructor.
-     * @param User $user
-     * @param UserRepo $userRepo
-     * @param UserInviteService $inviteService
-     * @param ImageRepo $imageRepo
      */
     public function __construct(User $user, UserRepo $userRepo, UserInviteService $inviteService, ImageRepo $imageRepo)
     {
@@ -31,13 +27,10 @@ class UserController extends Controller
         $this->userRepo = $userRepo;
         $this->inviteService = $inviteService;
         $this->imageRepo = $imageRepo;
-        parent::__construct();
     }
 
     /**
      * Display a listing of the users.
-     * @param Request $request
-     * @return Response
      */
     public function index(Request $request)
     {
@@ -48,6 +41,7 @@ class UserController extends Controller
             'sort' => $request->get('sort', 'name'),
         ];
         $users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails);
+
         $this->setPageTitle(trans('settings.users'));
         $users->appends($listDetails);
         return view('users.index', ['users' => $users, 'listDetails' => $listDetails]);
@@ -55,7 +49,6 @@ class UserController extends Controller
 
     /**
      * Show the form for creating a new user.
-     * @return Response
      */
     public function create()
     {
@@ -67,16 +60,15 @@ class UserController extends Controller
 
     /**
      * Store a newly created user in storage.
-     * @param  Request $request
-     * @return Response
      * @throws UserUpdateException
+     * @throws \Illuminate\Validation\ValidationException
      */
     public function store(Request $request)
     {
         $this->checkPermission('users-manage');
         $validationRules = [
-            'name'             => 'required',
-            'email'            => 'required|email|unique:users,email'
+            'name'  => 'required',
+            'email' => 'required|email|unique:users,email'
         ];
 
         $authMethod = config('auth.method');
@@ -85,7 +77,7 @@ class UserController extends Controller
         if ($authMethod === 'standard' && !$sendInvite) {
             $validationRules['password'] = 'required|min:6';
             $validationRules['password-confirm'] = 'required|same:password';
-        } elseif ($authMethod === 'ldap') {
+        } elseif ($authMethod === 'ldap' || $authMethod === 'saml2') {
             $validationRules['external_auth_id'] = 'required';
         }
         $this->validate($request, $validationRules);
@@ -94,7 +86,7 @@ class UserController extends Controller
 
         if ($authMethod === 'standard') {
             $user->password = bcrypt($request->get('password', Str::random(32)));
-        } elseif ($authMethod === 'ldap') {
+        } elseif ($authMethod === 'ldap' || $authMethod === 'saml2') {
             $user->external_auth_id = $request->get('external_auth_id');
         }
 
@@ -111,38 +103,39 @@ class UserController extends Controller
 
         $this->userRepo->downloadAndAssignUserAvatar($user);
 
+        $this->logActivity(ActivityType::USER_CREATE, $user);
         return redirect('/settings/users');
     }
 
     /**
      * Show the form for editing the specified user.
-     * @param  int              $id
-     * @param \BookStack\Auth\Access\SocialAuthService $socialAuthService
-     * @return Response
      */
-    public function edit($id, SocialAuthService $socialAuthService)
+    public function edit(int $id, SocialAuthService $socialAuthService)
     {
         $this->checkPermissionOrCurrentUser('users-manage', $id);
 
-        $user = $this->user->findOrFail($id);
+        $user = $this->user->newQuery()->with(['apiTokens'])->findOrFail($id);
 
         $authMethod = ($user->system_name) ? 'system' : config('auth.method');
 
         $activeSocialDrivers = $socialAuthService->getActiveDrivers();
         $this->setPageTitle(trans('settings.user_profile'));
         $roles = $this->userRepo->getAllRoles();
-        return view('users.edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]);
+        return view('users.edit', [
+            'user' => $user,
+            'activeSocialDrivers' => $activeSocialDrivers,
+            'authMethod' => $authMethod,
+            'roles' => $roles
+        ]);
     }
 
     /**
      * Update the specified user in storage.
-     * @param Request $request
-     * @param int $id
-     * @return Response
      * @throws UserUpdateException
      * @throws \BookStack\Exceptions\ImageUploadException
+     * @throws \Illuminate\Validation\ValidationException
      */
-    public function update(Request $request, $id)
+    public function update(Request $request, int $id)
     {
         $this->preventAccessInDemoMode();
         $this->checkPermissionOrCurrentUser('users-manage', $id);
@@ -153,7 +146,7 @@ class UserController extends Controller
             'password'         => 'min:6|required_with:password_confirm',
             'password-confirm' => 'same:password|required_with:password',
             'setting'          => 'array',
-            'profile_image'    => $this->imageRepo->getImageValidationRules(),
+            'profile_image'    => 'nullable|' . $this->getImageValidationRules(),
         ]);
 
         $user = $this->userRepo->getById($id);
@@ -189,20 +182,21 @@ class UserController extends Controller
         }
 
         // Save profile image if in request
-        if ($request->has('profile_image')) {
+        if ($request->hasFile('profile_image')) {
             $imageUpload = $request->file('profile_image');
             $this->imageRepo->destroyImage($user->avatar);
             $image = $this->imageRepo->saveNew($imageUpload, 'user', $user->id);
             $user->image_id = $image->id;
         }
 
-        // Delete the profile image if set to
+        // Delete the profile image if reset option is in request
         if ($request->has('profile_image_reset')) {
             $this->imageRepo->destroyImage($user->avatar);
         }
 
         $user->save();
         $this->showSuccessNotification(trans('settings.users_edit_success'));
+        $this->logActivity(ActivityType::USER_UPDATE, $user);
 
         $redirectUrl = userCan('users-manage') ? '/settings/users' : ('/settings/users/' . $user->id);
         return redirect($redirectUrl);
@@ -210,10 +204,8 @@ class UserController extends Controller
 
     /**
      * Show the user delete page.
-     * @param int $id
-     * @return \Illuminate\View\View
      */
-    public function delete($id)
+    public function delete(int $id)
     {
         $this->checkPermissionOrCurrentUser('users-manage', $id);
 
@@ -224,16 +216,15 @@ class UserController extends Controller
 
     /**
      * Remove the specified user from storage.
-     * @param  int $id
-     * @return Response
      * @throws \Exception
      */
-    public function destroy($id)
+    public function destroy(Request $request, int $id)
     {
         $this->preventAccessInDemoMode();
         $this->checkPermissionOrCurrentUser('users-manage', $id);
 
         $user = $this->userRepo->getById($id);
+        $newOwnerId = $request->get('new_owner_id', null);
 
         if ($this->userRepo->isOnlyAdmin($user)) {
             $this->showErrorNotification(trans('errors.users_cannot_delete_only_admin'));
@@ -245,16 +236,15 @@ class UserController extends Controller
             return redirect($user->getEditUrl());
         }
 
-        $this->userRepo->destroy($user);
+        $this->userRepo->destroy($user, $newOwnerId);
         $this->showSuccessNotification(trans('settings.users_delete_success'));
+        $this->logActivity(ActivityType::USER_DELETE, $user);
 
         return redirect('/settings/users');
     }
 
     /**
      * Show the user profile page
-     * @param $id
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
     public function showProfilePage($id)
     {
@@ -274,34 +264,32 @@ class UserController extends Controller
 
     /**
      * Update the user's preferred book-list display setting.
-     * @param Request $request
-     * @param $id
-     * @return \Illuminate\Http\RedirectResponse
      */
-    public function switchBookView(Request $request, $id)
+    public function switchBooksView(Request $request, int $id)
     {
         return $this->switchViewType($id, $request, 'books');
     }
 
     /**
      * Update the user's preferred shelf-list display setting.
-     * @param Request $request
-     * @param $id
-     * @return \Illuminate\Http\RedirectResponse
      */
-    public function switchShelfView(Request $request, $id)
+    public function switchShelvesView(Request $request, int $id)
     {
         return $this->switchViewType($id, $request, 'bookshelves');
     }
 
+    /**
+     * Update the user's preferred shelf-view book list display setting.
+     */
+    public function switchShelfView(Request $request, int $id)
+    {
+        return $this->switchViewType($id, $request, 'bookshelf');
+    }
+
     /**
      * For a type of list, switch with stored view type for a user.
-     * @param integer $userId
-     * @param Request $request
-     * @param string $listName
-     * @return \Illuminate\Http\RedirectResponse
      */
-    protected function switchViewType($userId, Request $request, string $listName)
+    protected function switchViewType(int $userId, Request $request, string $listName)
     {
         $this->checkPermissionOrCurrentUser('users-manage', $userId);
 
@@ -319,10 +307,6 @@ class UserController extends Controller
 
     /**
      * Change the stored sort type for a particular view.
-     * @param Request $request
-     * @param string $id
-     * @param string $type
-     * @return \Illuminate\Http\RedirectResponse
      */
     public function changeSort(Request $request, string $id, string $type)
     {
@@ -333,12 +317,18 @@ class UserController extends Controller
         return $this->changeListSort($id, $request, $type);
     }
 
+    /**
+     * Toggle dark mode for the current user.
+     */
+    public function toggleDarkMode()
+    {
+        $enabled = setting()->getForCurrentUser('dark-mode-enabled', false);
+        setting()->putUser(user(), 'dark-mode-enabled', $enabled ? 'false' : 'true');
+        return redirect()->back();
+    }
+
     /**
      * Update the stored section expansion preference for the given user.
-     * @param Request $request
-     * @param string $id
-     * @param string $key
-     * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
      */
     public function updateExpansionPreference(Request $request, string $id, string $key)
     {
@@ -357,10 +347,6 @@ class UserController extends Controller
 
     /**
      * Changed the stored preference for a list sort order.
-     * @param int $userId
-     * @param Request $request
-     * @param string $listName
-     * @return \Illuminate\Http\RedirectResponse
      */
     protected function changeListSort(int $userId, Request $request, string $listName)
     {
diff --git a/app/Http/Controllers/UserSearchController.php b/app/Http/Controllers/UserSearchController.php
new file mode 100644 (file)
index 0000000..a0dfbd8
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+namespace BookStack\Http\Controllers;
+
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Http\Request;
+
+class UserSearchController extends Controller
+{
+    /**
+     * Search users in the system, with the response formatted
+     * for use in a select-style list.
+     */
+    public function forSelect(Request $request)
+    {
+        $search = $request->get('search', '');
+        $query = User::query()->orderBy('name', 'desc')
+            ->take(20);
+
+        if (!empty($search)) {
+            $query->where(function (Builder $query) use ($search) {
+                $query->where('email', 'like', '%' . $search . '%')
+                    ->orWhere('name', 'like', '%' . $search . '%');
+            });
+        }
+
+        $users = $query->get();
+        return view('components.user-select-list', compact('users'));
+    }
+}
index f9752da09d6492430dd2fe2f4c131dcac301aedf..075c98ec77b7509087d1f66a2981d7ed26a86328 100644 (file)
@@ -6,10 +6,7 @@ class Kernel extends HttpKernel
 {
     /**
      * The application's global HTTP middleware stack.
-     *
      * These middleware are run during every request to your application.
-     *
-     * @var array
      */
     protected $middleware = [
         \BookStack\Http\Middleware\CheckForMaintenanceMode::class,
@@ -25,19 +22,19 @@ class Kernel extends HttpKernel
      */
     protected $middlewareGroups = [
         'web' => [
+            \BookStack\Http\Middleware\ControlIframeSecurity::class,
             \BookStack\Http\Middleware\EncryptCookies::class,
             \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
             \Illuminate\Session\Middleware\StartSession::class,
             \Illuminate\View\Middleware\ShareErrorsFromSession::class,
-            \Illuminate\Routing\Middleware\ThrottleRequests::class,
             \BookStack\Http\Middleware\VerifyCsrfToken::class,
-            \Illuminate\Routing\Middleware\SubstituteBindings::class,
             \BookStack\Http\Middleware\Localization::class,
-            \BookStack\Http\Middleware\GlobalViewData::class,
         ],
         'api' => [
-            'throttle:60,1',
-            'bindings',
+            \BookStack\Http\Middleware\ThrottleApiRequests::class,
+            \BookStack\Http\Middleware\EncryptCookies::class,
+            \BookStack\Http\Middleware\StartSessionIfCookieExists::class,
+            \BookStack\Http\Middleware\ApiAuthenticate::class,
         ],
     ];
 
@@ -48,10 +45,10 @@ class Kernel extends HttpKernel
      */
     protected $routeMiddleware = [
         'auth'       => \BookStack\Http\Middleware\Authenticate::class,
-        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
         'can'        => \Illuminate\Auth\Middleware\Authorize::class,
         'guest'      => \BookStack\Http\Middleware\RedirectIfAuthenticated::class,
-        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
-        'perm'       => \BookStack\Http\Middleware\PermissionMiddleware::class
+        'throttle'   => \Illuminate\Routing\Middleware\ThrottleRequests::class,
+        'perm'       => \BookStack\Http\Middleware\PermissionMiddleware::class,
+        'guard'      => \BookStack\Http\Middleware\CheckGuard::class,
     ];
 }
diff --git a/app/Http/Middleware/ApiAuthenticate.php b/app/Http/Middleware/ApiAuthenticate.php
new file mode 100644 (file)
index 0000000..728057b
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use BookStack\Exceptions\ApiAuthException;
+use BookStack\Exceptions\UnauthorizedException;
+use Closure;
+use Illuminate\Http\Request;
+
+class ApiAuthenticate
+{
+    use ChecksForEmailConfirmation;
+
+    /**
+     * Handle an incoming request.
+     */
+    public function handle(Request $request, Closure $next)
+    {
+        // Validate the token and it's users API access
+        try {
+            $this->ensureAuthorizedBySessionOrToken();
+        } catch (UnauthorizedException $exception) {
+            return $this->unauthorisedResponse($exception->getMessage(), $exception->getCode());
+        }
+
+        return $next($request);
+    }
+
+    /**
+     * Ensure the current user can access authenticated API routes, either via existing session
+     * authentication or via API Token authentication.
+     * @throws UnauthorizedException
+     */
+    protected function ensureAuthorizedBySessionOrToken(): void
+    {
+        // Return if the user is already found to be signed in via session-based auth.
+        // This is to make it easy to browser the API via browser after just logging into the system.
+        if (signedInUser() || session()->isStarted()) {
+            $this->ensureEmailConfirmedIfRequested();
+            if (!user()->can('access-api')) {
+                throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403);
+            }
+            return;
+        }
+
+        // Set our api guard to be the default for this request lifecycle.
+        auth()->shouldUse('api');
+
+        // Validate the token and it's users API access
+        auth()->authenticate();
+        $this->ensureEmailConfirmedIfRequested();
+    }
+
+    /**
+     * Provide a standard API unauthorised response.
+     */
+    protected function unauthorisedResponse(string $message, int $code)
+    {
+        return response()->json([
+            'error' => [
+                'code' => $code,
+                'message' => $message,
+            ]
+        ], $code);
+    }
+}
index d840a9b2e05477c8fca1550dad9e9e81adaa8c2c..df8c44d351cc92784bc8adaec1f642ea0c1719a0 100644 (file)
@@ -3,38 +3,19 @@
 namespace BookStack\Http\Middleware;
 
 use Closure;
-use Illuminate\Contracts\Auth\Guard;
+use Illuminate\Http\Request;
 
 class Authenticate
 {
-    /**
-     * The Guard implementation.
-     * @var Guard
-     */
-    protected $auth;
-
-    /**
-     * Create a new filter instance.
-     * @param  Guard $auth
-     */
-    public function __construct(Guard $auth)
-    {
-        $this->auth = $auth;
-    }
+    use ChecksForEmailConfirmation;
 
     /**
      * Handle an incoming request.
-     * @param  \Illuminate\Http\Request  $request
-     * @param  \Closure  $next
-     * @return mixed
      */
-    public function handle($request, Closure $next)
+    public function handle(Request $request, Closure $next)
     {
-        if ($this->auth->check()) {
-            $requireConfirmation = (setting('registration-confirmation') || setting('registration-restrict'));
-            if ($requireConfirmation && !$this->auth->user()->email_confirmed) {
-                return redirect('/register/confirm/awaiting');
-            }
+        if ($this->awaitingEmailConfirmation()) {
+            return $this->emailConfirmationErrorResponse($request);
         }
 
         if (!hasAppAccess()) {
@@ -47,4 +28,26 @@ class Authenticate
 
         return $next($request);
     }
+
+    /**
+     * Provide an error response for when the current user's email is not confirmed
+     * in a system which requires it.
+     */
+    protected function emailConfirmationErrorResponse(Request $request)
+    {
+        if ($request->wantsJson()) {
+            return response()->json([
+                'error' => [
+                    'code' => 401,
+                    'message' => trans('errors.email_confirmation_awaiting')
+                ]
+            ], 401);
+        }
+
+        if (session()->get('sent-email-confirmation') === true) {
+            return redirect('/register/confirm');
+        }
+
+        return redirect('/register/confirm/awaiting');
+    }
 }
diff --git a/app/Http/Middleware/CheckGuard.php b/app/Http/Middleware/CheckGuard.php
new file mode 100644 (file)
index 0000000..cc73ea6
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use Closure;
+
+class CheckGuard
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @param string $allowedGuards
+     * @return mixed
+     */
+    public function handle($request, Closure $next, ...$allowedGuards)
+    {
+        $activeGuard = config('auth.method');
+        if (!in_array($activeGuard, $allowedGuards)) {
+            session()->flash('error', trans('errors.permission'));
+            return redirect('/');
+        }
+
+        return $next($request);
+    }
+}
diff --git a/app/Http/Middleware/ChecksForEmailConfirmation.php b/app/Http/Middleware/ChecksForEmailConfirmation.php
new file mode 100644 (file)
index 0000000..4b17328
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use BookStack\Exceptions\UnauthorizedException;
+use Illuminate\Http\Request;
+
+trait ChecksForEmailConfirmation
+{
+    /**
+     * Check if the current user has a confirmed email if the instance deems it as required.
+     * Throws if confirmation is required by the user.
+     * @throws UnauthorizedException
+     */
+    protected function ensureEmailConfirmedIfRequested()
+    {
+        if ($this->awaitingEmailConfirmation()) {
+            throw new UnauthorizedException(trans('errors.email_confirmation_awaiting'));
+        }
+    }
+
+    /**
+     * Check if email confirmation is required and the current user is awaiting confirmation.
+     */
+    protected function awaitingEmailConfirmation(): bool
+    {
+        if (auth()->check()) {
+            $requireConfirmation = (setting('registration-confirmation') || setting('registration-restrict'));
+            if ($requireConfirmation && !auth()->user()->email_confirmed) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/app/Http/Middleware/ControlIframeSecurity.php b/app/Http/Middleware/ControlIframeSecurity.php
new file mode 100644 (file)
index 0000000..cc80344
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use Closure;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Sets CSP headers to restrict the hosts that BookStack can be
+ * iframed within. Also adjusts the cookie samesite options
+ * so that cookies will operate in the third-party context.
+ */
+class ControlIframeSecurity
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        $iframeHosts = collect(explode(' ', config('app.iframe_hosts', '')))->filter();
+        if ($iframeHosts->count() > 0) {
+            config()->set('session.same_site', 'none');
+        }
+
+        $iframeHosts->prepend("'self'");
+
+        $response = $next($request);
+        $cspValue = 'frame-ancestors ' . $iframeHosts->join(' ');
+        $response->headers->set('Content-Security-Policy', $cspValue);
+        return $response;
+    }
+}
diff --git a/app/Http/Middleware/GlobalViewData.php b/app/Http/Middleware/GlobalViewData.php
deleted file mode 100644 (file)
index bc132df..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php namespace BookStack\Http\Middleware;
-
-use Closure;
-use Illuminate\Http\Request;
-
-/**
- * Class GlobalViewData
- * Sets up data that is accessible to any view rendered by the web routes.
- */
-class GlobalViewData
-{
-
-    /**
-     * Handle an incoming request.
-     *
-     * @param Request $request
-     * @param Closure $next
-     * @return mixed
-     */
-    public function handle(Request $request, Closure $next)
-    {
-        view()->share('signedIn', auth()->check());
-        view()->share('currentUser', user());
-
-        return $next($request);
-    }
-}
index de48cb19619273ba60eceac771ff79657197d9af..597d2836548286ac8afb20f5677119f5b8ca2be3 100644 (file)
@@ -9,32 +9,37 @@ class Localization
 
     /**
      * Array of right-to-left locales
-     * @var array
      */
-    protected $rtlLocales = ['ar'];
+    protected $rtlLocales = ['ar', 'he'];
 
     /**
      * Map of BookStack locale names to best-estimate system locale names.
-     * @var array
      */
     protected $localeMap = [
         'ar' => 'ar',
+        'bg' => 'bg_BG',
+        'da' => 'da_DK',
         'de' => 'de_DE',
         'de_informal' => 'de_DE',
         'en' => 'en_GB',
         'es' => 'es_ES',
         'es_AR' => 'es_AR',
         'fr' => 'fr_FR',
+        'he' => 'he_IL',
         'it' => 'it_IT',
         'ja' => 'ja',
         'ko' => 'ko_KR',
         'nl' => 'nl_NL',
+        'nb' => 'nb_NO',
         'pl' => 'pl_PL',
+        'pt' => 'pl_PT',
         'pt_BR' => 'pt_BR',
         'ru' => 'ru',
         'sk' => 'sk_SK',
+        'sl' => 'sl_SI',
         'sv' => 'sv_SE',
         'uk' => 'uk_UA',
+        'vi' => 'vi_VN',
         'zh_CN' => 'zh_CN',
         'zh_TW' => 'zh_TW',
         'tr' => 'tr_TR',
@@ -52,12 +57,7 @@ class Localization
         $defaultLang = config('app.locale');
         config()->set('app.default_locale', $defaultLang);
 
-        if (user()->isDefault() && config('app.auto_detect_locale')) {
-            $locale = $this->autoDetectLocale($request, $defaultLang);
-        } else {
-            $locale = setting()->getUser(user(), 'language', $defaultLang);
-        }
-
+        $locale = $this->getUserLocale($request, $defaultLang);
         config()->set('app.lang', str_replace('_', '-', $this->getLocaleIso($locale)));
 
         // Set text direction
@@ -71,14 +71,29 @@ class Localization
         return $next($request);
     }
 
+    /**
+     * Get the locale specifically for the currently logged in user if available.
+     */
+    protected function getUserLocale(Request $request, string $default): string
+    {
+        try {
+            $user = user();
+        } catch (\Exception $exception) {
+            return $default;
+        }
+
+        if ($user->isDefault() && config('app.auto_detect_locale')) {
+            return $this->autoDetectLocale($request, $default);
+        }
+
+        return setting()->getUser($user, 'language', $default);
+    }
+
     /**
      * Autodetect the visitors locale by matching locales in their headers
      * against the locales supported by BookStack.
-     * @param Request $request
-     * @param string $default
-     * @return string
      */
-    protected function autoDetectLocale(Request $request, string $default)
+    protected function autoDetectLocale(Request $request, string $default): string
     {
         $availableLocales = config('app.locales');
         foreach ($request->getLanguages() as $lang) {
@@ -91,10 +106,8 @@ class Localization
 
     /**
      * Get the ISO version of a BookStack language name
-     * @param  string $locale
-     * @return string
      */
-    public function getLocaleIso(string $locale)
+    public function getLocaleIso(string $locale): string
     {
         return $this->localeMap[$locale] ?? $locale;
     }
@@ -102,7 +115,6 @@ class Localization
     /**
      * Set the system date locale for localized date formatting.
      * Will try both the standard locale name and the UTF8 variant.
-     * @param string $locale
      */
     protected function setSystemDateLocale(string $locale)
     {
index 28ffff6438cbb3979aa692d985d24542e5453081..d0bb4f79ef852e9e4b19fdcbd149159a6a05adb4 100644 (file)
@@ -19,7 +19,7 @@ class PermissionMiddleware
     {
 
         if (!$request->user() || !$request->user()->can($permission)) {
-            Session::flash('error', trans('errors.permission'));
+            session()->flash('error', trans('errors.permission'));
             return redirect()->back();
         }
 
diff --git a/app/Http/Middleware/StartSessionIfCookieExists.php b/app/Http/Middleware/StartSessionIfCookieExists.php
new file mode 100644 (file)
index 0000000..456508d
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use Closure;
+use Illuminate\Session\Middleware\StartSession as Middleware;
+
+class StartSessionIfCookieExists extends Middleware
+{
+    /**
+     * Handle an incoming request.
+     */
+    public function handle($request, Closure $next)
+    {
+        $sessionCookieName = config('session.cookie');
+        if ($request->cookies->has($sessionCookieName)) {
+            return parent::handle($request, $next);
+        }
+
+        return $next($request);
+    }
+}
diff --git a/app/Http/Middleware/ThrottleApiRequests.php b/app/Http/Middleware/ThrottleApiRequests.php
new file mode 100644 (file)
index 0000000..d08840c
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use Illuminate\Routing\Middleware\ThrottleRequests as Middleware;
+
+class ThrottleApiRequests extends Middleware
+{
+
+    /**
+     * Resolve the number of attempts if the user is authenticated or not.
+     */
+    protected function resolveMaxAttempts($request, $maxAttempts)
+    {
+        return (int) config('api.requests_per_minute');
+    }
+
+}
\ No newline at end of file
index 878c2f1647e968cfdeb91ce985cb7d5f7f088c78..7b01d0aab0de906a01be4afe31a7f82b937b429e 100644 (file)
@@ -28,7 +28,7 @@ class TrustProxies extends Middleware
      * @param Closure $next
      * @return mixed
      */
-    public function handle($request, Closure $next)
+    public function handle(Request $request, Closure $next)
     {
         $setProxies = config('app.proxies');
         if ($setProxies !== '**' && $setProxies !== '*' && $setProxies !== '') {
index 1a29a2b1d121f35f56dfd9e2eeb5611c84c4b42a..bdeb265540a9bde5c6d53f6ffef8b5e8f1ca3711 100644 (file)
@@ -19,6 +19,6 @@ class VerifyCsrfToken extends Middleware
      * @var array
      */
     protected $except = [
-        //
+        'saml2/*'
     ];
 }
diff --git a/app/Interfaces/Loggable.php b/app/Interfaces/Loggable.php
new file mode 100644 (file)
index 0000000..33e1d7c
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+
+namespace BookStack\Interfaces;
+
+interface Loggable
+{
+    /**
+     * Get the string descriptor for this item.
+     */
+    public function logDescriptor(): string;
+}
\ No newline at end of file
diff --git a/app/Ownable.php b/app/Ownable.php
deleted file mode 100644 (file)
index e660a05..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php namespace BookStack;
-
-use BookStack\Auth\User;
-
-abstract class Ownable extends Model
-{
-    /**
-     * Relation for the user that created this entity.
-     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
-     */
-    public function createdBy()
-    {
-        return $this->belongsTo(User::class, 'created_by');
-    }
-
-    /**
-     * Relation for the user that updated this entity.
-     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
-     */
-    public function updatedBy()
-    {
-        return $this->belongsTo(User::class, 'updated_by');
-    }
-
-    /**
-     * Gets the class name.
-     * @return string
-     */
-    public static function getClassName()
-    {
-        return strtolower(array_slice(explode('\\', static::class), -1, 1)[0]);
-    }
-}
index 3a1b4f42ee63e02118a72d798d8e837f12499018..7673050f8f51bd8d1105ac3a4c6f5e79f7a25885 100644 (file)
@@ -1,19 +1,19 @@
 <?php namespace BookStack\Providers;
 
 use Blade;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
 use BookStack\Entities\BreadcrumbsViewComposer;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
 use BookStack\Settings\Setting;
 use BookStack\Settings\SettingService;
+use Illuminate\Contracts\Cache\Repository;
 use Illuminate\Database\Eloquent\Relations\Relation;
 use Illuminate\Support\Facades\View;
 use Illuminate\Support\ServiceProvider;
 use Schema;
 use URL;
-use Validator;
 
 class AppServiceProvider extends ServiceProvider
 {
@@ -32,30 +32,11 @@ class AppServiceProvider extends ServiceProvider
             URL::forceScheme($isHttps ? 'https' : 'http');
         }
 
-        // Custom validation methods
-        Validator::extend('image_extension', function ($attribute, $value, $parameters, $validator) {
-            $validImageExtensions = ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'tiff', 'webp'];
-            return in_array(strtolower($value->getClientOriginalExtension()), $validImageExtensions);
-        });
-
-        Validator::extend('no_double_extension', function ($attribute, $value, $parameters, $validator) {
-            $uploadName = $value->getClientOriginalName();
-            return substr_count($uploadName, '.') < 2;
-        });
-
         // Custom blade view directives
         Blade::directive('icon', function ($expression) {
             return "<?php echo icon($expression); ?>";
         });
 
-        Blade::directive('exposeTranslations', function ($expression) {
-            return "<?php \$__env->startPush('translations'); ?>" .
-                "<?php foreach({$expression} as \$key): ?>" .
-                '<meta name="translation" key="<?php echo e($key); ?>" value="<?php echo e(trans($key)); ?>">' . "\n" .
-                "<?php endforeach; ?>" .
-                '<?php $__env->stopPush(); ?>';
-        });
-
         // Allow longer string lengths after upgrade to utf8mb4
         Schema::defaultStringLength(191);
 
@@ -79,7 +60,7 @@ class AppServiceProvider extends ServiceProvider
     public function register()
     {
         $this->app->singleton(SettingService::class, function ($app) {
-            return new SettingService($app->make(Setting::class), $app->make('Illuminate\Contracts\Cache\Repository'));
+            return new SettingService($app->make(Setting::class), $app->make(Repository::class));
         });
     }
 }
index 6e5b6ffde7ca10274b1beef274f2e06cd7283472..fe52df1686cec7ac00bcd82ba14a1c74b0b5f3dd 100644 (file)
@@ -3,7 +3,13 @@
 namespace BookStack\Providers;
 
 use Auth;
+use BookStack\Api\ApiTokenGuard;
+use BookStack\Auth\Access\ExternalBaseUserProvider;
+use BookStack\Auth\Access\Guards\LdapSessionGuard;
+use BookStack\Auth\Access\Guards\Saml2SessionGuard;
 use BookStack\Auth\Access\LdapService;
+use BookStack\Auth\Access\RegistrationService;
+use BookStack\Auth\UserRepo;
 use Illuminate\Support\ServiceProvider;
 
 class AuthServiceProvider extends ServiceProvider
@@ -15,7 +21,30 @@ class AuthServiceProvider extends ServiceProvider
      */
     public function boot()
     {
-        //
+        Auth::extend('api-token', function ($app, $name, array $config) {
+            return new ApiTokenGuard($app['request']);
+        });
+
+        Auth::extend('ldap-session', function ($app, $name, array $config) {
+            $provider = Auth::createUserProvider($config['provider']);
+            return new LdapSessionGuard(
+                $name,
+                $provider,
+                $this->app['session.store'],
+                $app[LdapService::class],
+                $app[RegistrationService::class]
+            );
+        });
+
+        Auth::extend('saml2-session', function ($app, $name, array $config) {
+            $provider = Auth::createUserProvider($config['provider']);
+            return new Saml2SessionGuard(
+                $name,
+                $provider,
+                $this->app['session.store'],
+                $app[RegistrationService::class]
+            );
+        });
     }
 
     /**
@@ -25,8 +54,8 @@ class AuthServiceProvider extends ServiceProvider
      */
     public function register()
     {
-        Auth::provider('ldap', function ($app, array $config) {
-            return new LdapUserProvider($config['model'], $app[LdapService::class]);
+        Auth::provider('external-users', function ($app, array $config) {
+            return new ExternalBaseUserProvider($config['model']);
         });
     }
 }
diff --git a/app/Providers/CustomValidationServiceProvider.php b/app/Providers/CustomValidationServiceProvider.php
new file mode 100644 (file)
index 0000000..4a5272b
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+namespace BookStack\Providers;
+
+use Illuminate\Support\Facades\Validator;
+use Illuminate\Support\ServiceProvider;
+
+class CustomValidationServiceProvider extends ServiceProvider
+{
+
+    /**
+     * Register our custom validation rules when the application boots.
+     */
+    public function boot(): void
+    {
+        Validator::extend('image_extension', function ($attribute, $value, $parameters, $validator) {
+            $validImageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'webp'];
+            return in_array(strtolower($value->getClientOriginalExtension()), $validImageExtensions);
+        });
+
+        Validator::extend('no_double_extension', function ($attribute, $value, $parameters, $validator) {
+            $uploadName = $value->getClientOriginalName();
+            return substr_count($uploadName, '.') < 2;
+        });
+
+        Validator::extend('safe_url', function ($attribute, $value, $parameters, $validator) {
+            $cleanLinkName = strtolower(trim($value));
+            $isJs = strpos($cleanLinkName, 'javascript:') === 0;
+            $isData = strpos($cleanLinkName, 'data:') === 0;
+            return !$isJs && !$isData;
+        });
+    }
+}
index c4c39d5345fa74b80631c7e737e4f928407f32a9..a37780e52eb1c25eaef4355afc3b497e786d9f6a 100644 (file)
@@ -34,7 +34,7 @@ class RouteServiceProvider extends ServiceProvider
     public function map()
     {
         $this->mapWebRoutes();
-//        $this->mapApiRoutes();
+        $this->mapApiRoutes();
     }
     /**
      * Define the "web" routes for the application.
@@ -63,7 +63,7 @@ class RouteServiceProvider extends ServiceProvider
     {
         Route::group([
             'middleware' => 'api',
-            'namespace' => $this->namespace,
+            'namespace' => $this->namespace . '\Api',
             'prefix' => 'api',
         ], function ($router) {
             require base_path('routes/api.php');
index dede8fcc418634db821a3d6948b84203e872fd28..1c053b3848ea779d480adea01834dce0059d629d 100644 (file)
@@ -98,12 +98,6 @@ class SettingService
      */
     protected function getValueFromStore($key, $default)
     {
-        // Check for an overriding value
-        $overrideValue = $this->getOverrideValue($key);
-        if ($overrideValue !== null) {
-            return $overrideValue;
-        }
-
         // Check the cache
         $cacheKey = $this->cachePrefix . $key;
         $cacheVal = $this->cache->get($cacheKey, null);
@@ -255,20 +249,4 @@ class SettingService
     {
         return $this->setting->where('setting_key', '=', $key)->first();
     }
-
-
-    /**
-     * Returns an override value for a setting based on certain app conditions.
-     * Used where certain configuration options overrule others.
-     * Returns null if no override value is available.
-     * @param $key
-     * @return bool|null
-     */
-    protected function getOverrideValue($key)
-    {
-        if ($key === 'registration-enabled' && config('auth.method') === 'ldap') {
-            return false;
-        }
-        return null;
-    }
 }
diff --git a/app/Traits/HasCreatorAndUpdater.php b/app/Traits/HasCreatorAndUpdater.php
new file mode 100644 (file)
index 0000000..ad6c303
--- /dev/null
@@ -0,0 +1,28 @@
+<?php namespace BookStack\Traits;
+
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+/**
+ * @property int created_by
+ * @property int updated_by
+ */
+trait HasCreatorAndUpdater
+{
+    /**
+     * Relation for the user that created this entity.
+     */
+    public function createdBy(): BelongsTo
+    {
+        return $this->belongsTo(User::class, 'created_by');
+    }
+
+    /**
+     * Relation for the user that updated this entity.
+     */
+    public function updatedBy(): BelongsTo
+    {
+        return $this->belongsTo(User::class, 'updated_by');
+    }
+
+}
diff --git a/app/Traits/HasOwner.php b/app/Traits/HasOwner.php
new file mode 100644 (file)
index 0000000..9d1eb3d
--- /dev/null
@@ -0,0 +1,19 @@
+<?php namespace BookStack\Traits;
+
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+/**
+ * @property int owned_by
+ */
+trait HasOwner
+{
+    /**
+     * Relation for the user that owns this entity.
+     */
+    public function ownedBy(): BelongsTo
+    {
+        return $this->belongsTo(User::class, 'owned_by');
+    }
+
+}
index 3f0b447df7388a573602396d052bce2e5c5bca7a..d1060477d085d3cda5c23b7363c5d54067de7b3d 100644 (file)
@@ -1,10 +1,20 @@
 <?php namespace BookStack\Uploads;
 
-use BookStack\Entities\Page;
-use BookStack\Ownable;
+use BookStack\Entities\Models\Page;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
 
-class Attachment extends Ownable
+/**
+ * @property int id
+ * @property string name
+ * @property string path
+ * @property string extension
+ * @property bool external
+ */
+class Attachment extends Model
 {
+    use HasCreatorAndUpdater;
+
     protected $fillable = ['name', 'order'];
 
     /**
@@ -30,13 +40,28 @@ class Attachment extends Ownable
 
     /**
      * Get the url of this file.
-     * @return string
      */
-    public function getUrl()
+    public function getUrl(): string
     {
         if ($this->external && strpos($this->path, 'http') !== 0) {
             return $this->path;
         }
         return url('/attachments/' . $this->id);
     }
+
+    /**
+     * Generate a HTML link to this attachment.
+     */
+    public function htmlLink(): string
+    {
+        return '<a target="_blank" href="'.e($this->getUrl()).'">'.e($this->name).'</a>';
+    }
+
+    /**
+     * Generate a markdown link to this attachment.
+     */
+    public function markdownLink(): string
+    {
+        return '['. $this->name .']('. $this->getUrl() .')';
+    }
 }
index ae4fb6e967160787e1c46dde0e442228386f9af9..b14f49473709647e23fd03afeea406d39c752361 100644 (file)
@@ -2,17 +2,29 @@
 
 use BookStack\Exceptions\FileUploadException;
 use Exception;
+use Illuminate\Contracts\Filesystem\Factory as FileSystem;
+use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
 use Illuminate\Support\Str;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
 
-class AttachmentService extends UploadService
+class AttachmentService
 {
 
+    protected $fileSystem;
+
+    /**
+     * AttachmentService constructor.
+     */
+    public function __construct(FileSystem $fileSystem)
+    {
+        $this->fileSystem = $fileSystem;
+    }
+
+
     /**
      * Get the storage that will be used for storing files.
-     * @return \Illuminate\Contracts\Filesystem\Filesystem
      */
-    protected function getStorage()
+    protected function getStorage(): FileSystemInstance
     {
         $storageType = config('filesystems.attachments');
 
@@ -88,12 +100,8 @@ class AttachmentService extends UploadService
 
     /**
      * Save a new File attachment from a given link and name.
-     * @param string $name
-     * @param string $link
-     * @param int $page_id
-     * @return Attachment
      */
-    public function saveNewFromLink($name, $link, $page_id)
+    public function saveNewFromLink(string $name, string $link, int $page_id): Attachment
     {
         $largestExistingOrder = Attachment::where('uploaded_to', '=', $page_id)->max('order');
         return Attachment::forceCreate([
@@ -109,27 +117,25 @@ class AttachmentService extends UploadService
     }
 
     /**
-     * Updates the file ordering for a listing of attached files.
-     * @param array $attachmentList
-     * @param $pageId
+     * Updates the ordering for a listing of attached files.
      */
-    public function updateFileOrderWithinPage($attachmentList, $pageId)
+    public function updateFileOrderWithinPage(array $attachmentOrder, string $pageId)
     {
-        foreach ($attachmentList as $index => $attachment) {
-            Attachment::where('uploaded_to', '=', $pageId)->where('id', '=', $attachment['id'])->update(['order' => $index]);
+        foreach ($attachmentOrder as $index => $attachmentId) {
+            Attachment::query()->where('uploaded_to', '=', $pageId)
+                ->where('id', '=', $attachmentId)
+                ->update(['order' => $index]);
         }
     }
 
 
     /**
      * Update the details of a file.
-     * @param Attachment $attachment
-     * @param $requestData
-     * @return Attachment
      */
-    public function updateFile(Attachment $attachment, $requestData)
+    public function updateFile(Attachment $attachment, array $requestData): Attachment
     {
         $attachment->name = $requestData['name'];
+
         if (isset($requestData['link']) && trim($requestData['link']) !== '') {
             $attachment->path = $requestData['link'];
             if (!$attachment->external) {
@@ -137,6 +143,7 @@ class AttachmentService extends UploadService
                 $attachment->external = true;
             }
         }
+
         $attachment->save();
         return $attachment;
     }
index 6fa5db2a562e703fa0d18aa12b5abb753de0e80b..dc26af002ab5e29de70d3679d9a56282b39bb458 100644 (file)
@@ -1,13 +1,16 @@
 <?php namespace BookStack\Uploads;
 
-use BookStack\Entities\Page;
-use BookStack\Ownable;
+use BookStack\Entities\Models\Page;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
 use Images;
 
-class Image extends Ownable
+class Image extends Model
 {
+    use HasCreatorAndUpdater;
 
     protected $fillable = ['name'];
+    protected $hidden = [];
 
     /**
      * Get a thumbnail for this image.
index da0b7d379eddd2fb4a227dd071af248c2ba44d07..b4d743b73447a2cc99dade9b367f78e489020279 100644 (file)
@@ -1,7 +1,9 @@
 <?php namespace BookStack\Uploads;
 
 use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
+use BookStack\Exceptions\ImageUploadException;
+use Exception;
 use Illuminate\Database\Eloquent\Builder;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
 
@@ -15,10 +17,6 @@ class ImageRepo
 
     /**
      * ImageRepo constructor.
-     * @param Image $image
-     * @param ImageService $imageService
-     * @param \BookStack\Auth\Permissions\PermissionService $permissionService
-     * @param \BookStack\Entities\Page $page
      */
     public function __construct(
         Image $image,
@@ -35,10 +33,8 @@ class ImageRepo
 
     /**
      * Get an image with the given id.
-     * @param $id
-     * @return Image
      */
-    public function getById($id)
+    public function getById($id): Image
     {
         return $this->image->findOrFail($id);
     }
@@ -46,13 +42,8 @@ class ImageRepo
     /**
      * Execute a paginated query, returning in a standard format.
      * Also runs the query through the restriction system.
-     * @param $query
-     * @param int $page
-     * @param int $pageSize
-     * @param bool $filterOnPage
-     * @return array
      */
-    private function returnPaginated($query, $page = 1, $pageSize = 24)
+    private function returnPaginated($query, $page = 1, $pageSize = 24): array
     {
         $images = $query->orderBy('created_at', 'desc')->skip($pageSize * ($page - 1))->take($pageSize + 1)->get();
         $hasMore = count($images) > $pageSize;
@@ -71,13 +62,6 @@ class ImageRepo
     /**
      * Fetch a list of images in a paginated format, filtered by image type.
      * Can be filtered by uploaded to and also by name.
-     * @param string $type
-     * @param int $page
-     * @param int $pageSize
-     * @param int $uploadedTo
-     * @param string|null $search
-     * @param callable|null $whereClause
-     * @return array
      */
     public function getPaginatedByType(
         string $type,
@@ -86,7 +70,8 @@ class ImageRepo
         int $uploadedTo = null,
         string $search = null,
         callable $whereClause = null
-    ) {
+    ): array
+    {
         $imageQuery = $this->image->newQuery()->where('type', '=', strtolower($type));
 
         if ($uploadedTo !== null) {
@@ -109,13 +94,6 @@ class ImageRepo
 
     /**
      * Get paginated gallery images within a specific page or book.
-     * @param string $type
-     * @param string $filterType
-     * @param int $page
-     * @param int $pageSize
-     * @param int|null $uploadedTo
-     * @param string|null $search
-     * @return array
      */
     public function getEntityFiltered(
         string $type,
@@ -124,7 +102,8 @@ class ImageRepo
         int $pageSize = 24,
         int $uploadedTo = null,
         string $search = null
-    ) {
+    ): array
+    {
         $contextPage = $this->page->findOrFail($uploadedTo);
         $parentFilter = null;
 
@@ -133,7 +112,7 @@ class ImageRepo
                 if ($filterType === 'page') {
                     $query->where('uploaded_to', '=', $contextPage->id);
                 } elseif ($filterType === 'book') {
-                    $validPageIds = $contextPage->book->pages()->get(['id'])->pluck('id')->toArray();
+                    $validPageIds = $contextPage->book->pages()->visible()->get(['id'])->pluck('id')->toArray();
                     $query->whereIn('uploaded_to', $validPageIds);
                 }
             };
@@ -144,16 +123,9 @@ class ImageRepo
 
     /**
      * Save a new image into storage and return the new image.
-     * @param UploadedFile $uploadFile
-     * @param string $type
-     * @param int $uploadedTo
-     * @param int|null $resizeWidth
-     * @param int|null $resizeHeight
-     * @param bool $keepRatio
-     * @return Image
-     * @throws \BookStack\Exceptions\ImageUploadException
+     * @throws ImageUploadException
      */
-    public function saveNew(UploadedFile $uploadFile, $type, $uploadedTo = 0, int $resizeWidth = null, int $resizeHeight = null, bool $keepRatio = true)
+    public function saveNew(UploadedFile $uploadFile, string $type, int $uploadedTo = 0, int $resizeWidth = null, int $resizeHeight = null, bool $keepRatio = true): Image
     {
         $image = $this->imageService->saveNewFromUpload($uploadFile, $type, $uploadedTo, $resizeWidth, $resizeHeight, $keepRatio);
         $this->loadThumbs($image);
@@ -161,29 +133,22 @@ class ImageRepo
     }
 
     /**
-     * Save a drawing the the database;
-     * @param string $base64Uri
-     * @param int $uploadedTo
-     * @return Image
-     * @throws \BookStack\Exceptions\ImageUploadException
+     * Save a drawing the the database.
+     * @throws ImageUploadException
      */
-    public function saveDrawing(string $base64Uri, int $uploadedTo)
+    public function saveDrawing(string $base64Uri, int $uploadedTo): Image
     {
-        $name = 'Drawing-' . user()->getShortName(40) . '-' . strval(time()) . '.png';
-        $image = $this->imageService->saveNewFromBase64Uri($base64Uri, $name, 'drawio', $uploadedTo);
-        return $image;
+        $name = 'Drawing-' . strval(user()->id) . '-' . strval(time()) . '.png';
+        return $this->imageService->saveNewFromBase64Uri($base64Uri, $name, 'drawio', $uploadedTo);
     }
 
 
     /**
      * Update the details of an image via an array of properties.
-     * @param Image $image
-     * @param array $updateDetails
-     * @return Image
-     * @throws \BookStack\Exceptions\ImageUploadException
-     * @throws \Exception
+     * @throws ImageUploadException
+     * @throws Exception
      */
-    public function updateImageDetails(Image $image, $updateDetails)
+    public function updateImageDetails(Image $image, $updateDetails): Image
     {
         $image->fill($updateDetails);
         $image->save();
@@ -191,14 +156,11 @@ class ImageRepo
         return $image;
     }
 
-
     /**
      * Destroys an Image object along with its revisions, files and thumbnails.
-     * @param Image $image
-     * @return bool
-     * @throws \Exception
+     * @throws Exception
      */
-    public function destroyImage(Image $image = null)
+    public function destroyImage(Image $image = null): bool
     {
         if ($image) {
             $this->imageService->destroy($image);
@@ -208,8 +170,7 @@ class ImageRepo
 
     /**
      * Destroy all images of a certain type.
-     * @param string $imageType
-     * @throws \Exception
+     * @throws Exception
      */
     public function destroyByType(string $imageType)
     {
@@ -222,11 +183,9 @@ class ImageRepo
 
     /**
      * Load thumbnails onto an image object.
-     * @param Image $image
-     * @throws \BookStack\Exceptions\ImageUploadException
-     * @throws \Exception
+     * @throws Exception
      */
-    protected function loadThumbs(Image $image)
+    public function loadThumbs(Image $image)
     {
         $image->thumbs = [
             'gallery' => $this->getThumbnail($image, 150, 150, false),
@@ -238,43 +197,42 @@ class ImageRepo
      * Get the thumbnail for an image.
      * If $keepRatio is true only the width will be used.
      * Checks the cache then storage to avoid creating / accessing the filesystem on every check.
-     * @param Image $image
-     * @param int $width
-     * @param int $height
-     * @param bool $keepRatio
-     * @return string
-     * @throws \BookStack\Exceptions\ImageUploadException
-     * @throws \Exception
+     * @throws Exception
      */
-    protected function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)
+    protected function getThumbnail(Image $image, ?int $width = 220, ?int $height = 220, bool $keepRatio = false): ?string
     {
         try {
             return $this->imageService->getThumbnail($image, $width, $height, $keepRatio);
-        } catch (\Exception $exception) {
+        } catch (Exception $exception) {
             return null;
         }
     }
 
     /**
      * Get the raw image data from an Image.
-     * @param Image $image
-     * @return null|string
      */
-    public function getImageData(Image $image)
+    public function getImageData(Image $image): ?string
     {
         try {
             return $this->imageService->getImageData($image);
-        } catch (\Exception $exception) {
+        } catch (Exception $exception) {
             return null;
         }
     }
 
     /**
-     * Get the validation rules for image files.
-     * @return string
+     * Get the user visible pages using the given image.
      */
-    public function getImageValidationRules()
+    public function getPagesUsingImage(Image $image): array
     {
-        return 'image_extension|no_double_extension|mimes:jpeg,png,gif,bmp,webp,tiff';
+        $pages = Page::visible()
+            ->where('html', 'like', '%' . $image->url . '%')
+            ->get(['id', 'name', 'slug', 'book_id']);
+
+        foreach ($pages as $page) {
+            $page->url = $page->getUrl();
+        }
+
+        return $pages->all();
     }
 }
index e7668471bd64c174c858eef0ac6c9c36a16e8880..92c3994a71e0386a792951dac75b6ad5542b9ec7 100644 (file)
@@ -1,50 +1,41 @@
 <?php namespace BookStack\Uploads;
 
-use BookStack\Auth\User;
-use BookStack\Exceptions\HttpFetchException;
 use BookStack\Exceptions\ImageUploadException;
 use DB;
+use ErrorException;
 use Exception;
 use Illuminate\Contracts\Cache\Repository as Cache;
 use Illuminate\Contracts\Filesystem\Factory as FileSystem;
+use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
+use Illuminate\Contracts\Filesystem\FileNotFoundException;
 use Illuminate\Support\Str;
 use Intervention\Image\Exception\NotSupportedException;
 use Intervention\Image\ImageManager;
-use phpDocumentor\Reflection\Types\Integer;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
 
-class ImageService extends UploadService
+class ImageService
 {
-
     protected $imageTool;
     protected $cache;
     protected $storageUrl;
     protected $image;
-    protected $http;
+    protected $fileSystem;
 
     /**
      * ImageService constructor.
-     * @param Image $image
-     * @param ImageManager $imageTool
-     * @param FileSystem $fileSystem
-     * @param Cache $cache
-     * @param HttpFetcher $http
      */
-    public function __construct(Image $image, ImageManager $imageTool, FileSystem $fileSystem, Cache $cache, HttpFetcher $http)
+    public function __construct(Image $image, ImageManager $imageTool, FileSystem $fileSystem, Cache $cache)
     {
         $this->image = $image;
         $this->imageTool = $imageTool;
+        $this->fileSystem = $fileSystem;
         $this->cache = $cache;
-        $this->http = $http;
-        parent::__construct($fileSystem);
     }
 
     /**
      * Get the storage that will be used for storing images.
-     * @param string $type
-     * @return \Illuminate\Contracts\Filesystem\Filesystem
      */
-    protected function getStorage($type = '')
+    protected function getStorage(string $type = ''): FileSystemInstance
     {
         $storageType = config('filesystems.images');
 
@@ -58,12 +49,6 @@ class ImageService extends UploadService
 
     /**
      * Saves a new image from an upload.
-     * @param UploadedFile $uploadedFile
-     * @param string $type
-     * @param int $uploadedTo
-     * @param int|null $resizeWidth
-     * @param int|null $resizeHeight
-     * @param bool $keepRatio
      * @return mixed
      * @throws ImageUploadException
      */
@@ -87,14 +72,9 @@ class ImageService extends UploadService
 
     /**
      * Save a new image from a uri-encoded base64 string of data.
-     * @param string $base64Uri
-     * @param string $name
-     * @param string $type
-     * @param int $uploadedTo
-     * @return Image
      * @throws ImageUploadException
      */
-    public function saveNewFromBase64Uri(string $base64Uri, string $name, string $type, $uploadedTo = 0)
+    public function saveNewFromBase64Uri(string $base64Uri, string $name, string $type, int $uploadedTo = 0): Image
     {
         $splitData = explode(';base64,', $base64Uri);
         if (count($splitData) < 2) {
@@ -105,48 +85,24 @@ class ImageService extends UploadService
     }
 
     /**
-     * Gets an image from url and saves it to the database.
-     * @param             $url
-     * @param string      $type
-     * @param bool|string $imageName
-     * @return mixed
-     * @throws \Exception
-     */
-    private function saveNewFromUrl($url, $type, $imageName = false)
-    {
-        $imageName = $imageName ? $imageName : basename($url);
-        try {
-            $imageData = $this->http->fetch($url);
-        } catch (HttpFetchException $exception) {
-            throw new \Exception(trans('errors.cannot_get_image_from_url', ['url' => $url]));
-        }
-        return $this->saveNew($imageName, $imageData, $type);
-    }
-
-    /**
-     * Saves a new image
-     * @param string $imageName
-     * @param string $imageData
-     * @param string $type
-     * @param int $uploadedTo
-     * @return Image
+     * Save a new image into storage.
      * @throws ImageUploadException
      */
-    private function saveNew($imageName, $imageData, $type, $uploadedTo = 0)
+    public function saveNew(string $imageName, string $imageData, string $type, int $uploadedTo = 0): Image
     {
         $storage = $this->getStorage($type);
         $secureUploads = setting('app-secure-images');
-        $imageName = str_replace(' ', '-', $imageName);
+        $fileName = $this->cleanImageFileName($imageName);
 
         $imagePath = '/uploads/images/' . $type . '/' . Date('Y-m') . '/';
 
-        while ($storage->exists($imagePath . $imageName)) {
-            $imageName = Str::random(3) . $imageName;
+        while ($storage->exists($imagePath . $fileName)) {
+            $fileName = Str::random(3) . $fileName;
         }
 
-        $fullPath = $imagePath . $imageName;
+        $fullPath = $imagePath . $fileName;
         if ($secureUploads) {
-            $fullPath = $imagePath . Str::random(16) . '-' . $imageName;
+            $fullPath = $imagePath . Str::random(16) . '-' . $fileName;
         }
 
         try {
@@ -157,10 +113,10 @@ class ImageService extends UploadService
         }
 
         $imageDetails = [
-            'name'       => $imageName,
-            'path'       => $fullPath,
-            'url'        => $this->getPublicUrl($fullPath),
-            'type'       => $type,
+            'name' => $imageName,
+            'path' => $fullPath,
+            'url' => $this->getPublicUrl($fullPath),
+            'type' => $type,
             'uploaded_to' => $uploadedTo
         ];
 
@@ -175,13 +131,28 @@ class ImageService extends UploadService
         return $image;
     }
 
+    /**
+     * Clean up an image file name to be both URL and storage safe.
+     */
+    protected function cleanImageFileName(string $name): string
+    {
+        $name = str_replace(' ', '-', $name);
+        $nameParts = explode('.', $name);
+        $extension = array_pop($nameParts);
+        $name = implode('.', $nameParts);
+        $name = Str::slug($name);
+
+        if (strlen($name) === 0) {
+            $name = Str::random(10);
+        }
+
+        return $name . '.' . $extension;
+    }
 
     /**
      * Checks if the image is a gif. Returns true if it is, else false.
-     * @param Image $image
-     * @return boolean
      */
-    protected function isGif(Image $image)
+    protected function isGif(Image $image): bool
     {
         return strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) === 'gif';
     }
@@ -223,6 +194,7 @@ class ImageService extends UploadService
         $storage->setVisibility($thumbFilePath, 'public');
         $this->cache->put('images-' . $image->id . '-' . $thumbFilePath, $thumbFilePath, 60 * 60 * 72);
 
+
         return $this->getPublicUrl($thumbFilePath);
     }
 
@@ -240,7 +212,7 @@ class ImageService extends UploadService
         try {
             $thumb = $this->imageTool->make($imageData);
         } catch (Exception $e) {
-            if ($e instanceof \ErrorException || $e instanceof NotSupportedException) {
+            if ($e instanceof ErrorException || $e instanceof NotSupportedException) {
                 throw new ImageUploadException(trans('errors.cannot_create_thumbs'));
             }
             throw $e;
@@ -254,16 +226,23 @@ class ImageService extends UploadService
         } else {
             $thumb->fit($width, $height);
         }
-        return (string)$thumb->encode();
+
+        $thumbData = (string)$thumb->encode();
+
+        // Use original image data if we're keeping the ratio
+        // and the resizing does not save any space.
+        if ($keepRatio && strlen($thumbData) > strlen($imageData)) {
+            return $imageData;
+        }
+
+        return $thumbData;
     }
 
     /**
      * Get the raw data content from an image.
-     * @param Image $image
-     * @return string
-     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
+     * @throws FileNotFoundException
      */
-    public function getImageData(Image $image)
+    public function getImageData(Image $image): string
     {
         $imagePath = $image->path;
         $storage = $this->getStorage();
@@ -272,7 +251,6 @@ class ImageService extends UploadService
 
     /**
      * Destroy an image along with its revisions, thumbnails and remaining folders.
-     * @param Image $image
      * @throws Exception
      */
     public function destroy(Image $image)
@@ -283,11 +261,9 @@ class ImageService extends UploadService
 
     /**
      * Destroys an image at the given path.
-     * Searches for image thumbnails in addition to main provided path..
-     * @param string $path
-     * @return bool
+     * Searches for image thumbnails in addition to main provided path.
      */
-    protected function destroyImagesFromPath(string $path)
+    protected function destroyImagesFromPath(string $path): bool
     {
         $storage = $this->getStorage();
 
@@ -297,15 +273,14 @@ class ImageService extends UploadService
 
         // Delete image files
         $imagesToDelete = $allImages->filter(function ($imagePath) use ($imageFileName) {
-            $expectedIndex = strlen($imagePath) - strlen($imageFileName);
-            return strpos($imagePath, $imageFileName) === $expectedIndex;
+            return basename($imagePath) === $imageFileName;
         });
         $storage->delete($imagesToDelete->all());
 
         // Cleanup of empty folders
         $foldersInvolved = array_merge([$imageFolder], $storage->directories($imageFolder));
         foreach ($foldersInvolved as $directory) {
-            if ($this->isFolderEmpty($directory)) {
+            if ($this->isFolderEmpty($storage, $directory)) {
                 $storage->deleteDirectory($directory);
             }
         }
@@ -314,57 +289,13 @@ class ImageService extends UploadService
     }
 
     /**
-     * Save an avatar image from an external service.
-     * @param \BookStack\Auth\User $user
-     * @param int $size
-     * @return Image
-     * @throws Exception
-     */
-    public function saveUserAvatar(User $user, $size = 500)
-    {
-        $avatarUrl = $this->getAvatarUrl();
-        $email = strtolower(trim($user->email));
-
-        $replacements = [
-            '${hash}' => md5($email),
-            '${size}' => $size,
-            '${email}' => urlencode($email),
-        ];
-
-        $userAvatarUrl = strtr($avatarUrl, $replacements);
-        $imageName = str_replace(' ', '-', $user->name . '-avatar.png');
-        $image = $this->saveNewFromUrl($userAvatarUrl, 'user', $imageName);
-        $image->created_by = $user->id;
-        $image->updated_by = $user->id;
-        $image->uploaded_to = $user->id;
-        $image->save();
-
-        return $image;
-    }
-
-    /**
-     * Check if fetching external avatars is enabled.
-     * @return bool
+     * Check whether or not a folder is empty.
      */
-    public function avatarFetchEnabled()
+    protected function isFolderEmpty(FileSystemInstance $storage, string $path): bool
     {
-        $fetchUrl = $this->getAvatarUrl();
-        return is_string($fetchUrl) && strpos($fetchUrl, 'http') === 0;
-    }
-
-    /**
-     * Get the URL to fetch avatars from.
-     * @return string|mixed
-     */
-    protected function getAvatarUrl()
-    {
-        $url = trim(config('services.avatar_url'));
-
-        if (empty($url) && !config('services.disable_services')) {
-            $url = 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon';
-        }
-
-        return $url;
+        $files = $storage->files($path);
+        $folders = $storage->directories($path);
+        return (count($files) === 0 && count($folders) === 0);
     }
 
     /**
@@ -373,26 +304,23 @@ class ImageService extends UploadService
      * Could be much improved to be more specific but kept it generic for now to be safe.
      *
      * Returns the path of the images that would be/have been deleted.
-     * @param bool $checkRevisions
-     * @param bool $dryRun
-     * @param array $types
-     * @return array
      */
-    public function deleteUnusedImages($checkRevisions = true, $dryRun = true, $types = ['gallery', 'drawio'])
+    public function deleteUnusedImages(bool $checkRevisions = true, bool $dryRun = true)
     {
-        $types = array_intersect($types, ['gallery', 'drawio']);
+        $types = ['gallery', 'drawio'];
         $deletedPaths = [];
 
         $this->image->newQuery()->whereIn('type', $types)
-            ->chunk(1000, function ($images) use ($types, $checkRevisions, &$deletedPaths, $dryRun) {
+            ->chunk(1000, function ($images) use ($checkRevisions, &$deletedPaths, $dryRun) {
                 foreach ($images as $image) {
                     $searchQuery = '%' . basename($image->path) . '%';
                     $inPage = DB::table('pages')
-                         ->where('html', 'like', $searchQuery)->count() > 0;
+                            ->where('html', 'like', $searchQuery)->count() > 0;
+
                     $inRevision = false;
                     if ($checkRevisions) {
-                        $inRevision =  DB::table('page_revisions')
-                             ->where('html', 'like', $searchQuery)->count() > 0;
+                        $inRevision = DB::table('page_revisions')
+                                ->where('html', 'like', $searchQuery)->count() > 0;
                     }
 
                     if (!$inPage && !$inRevision) {
@@ -408,38 +336,25 @@ class ImageService extends UploadService
 
     /**
      * Convert a image URI to a Base64 encoded string.
-     * Attempts to find locally via set storage method first.
-     * @param string $uri
-     * @return null|string
-     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
+     * Attempts to convert the URL to a system storage url then
+     * fetch the data from the disk or storage location.
+     * Returns null if the image data cannot be fetched from storage.
+     * @throws FileNotFoundException
      */
-    public function imageUriToBase64(string $uri)
+    public function imageUriToBase64(string $uri): ?string
     {
-        $isLocal = strpos(trim($uri), 'http') !== 0;
-
-        // Attempt to find local files even if url not absolute
-        $base = url('/');
-        if (!$isLocal && strpos($uri, $base) === 0) {
-            $isLocal = true;
-            $uri = str_replace($base, '', $uri);
+        $storagePath = $this->imageUrlToStoragePath($uri);
+        if (empty($uri) || is_null($storagePath)) {
+            return null;
         }
 
+        $storage = $this->getStorage();
         $imageData = null;
-
-        if ($isLocal) {
-            $uri = trim($uri, '/');
-            $storage = $this->getStorage();
-            if ($storage->exists($uri)) {
-                $imageData = $storage->get($uri);
-            }
-        } else {
-            try {
-                $imageData = $this->http->fetch($uri);
-            } catch (\Exception $e) {
-            }
+        if ($storage->exists($storagePath)) {
+            $imageData = $storage->get($storagePath);
         }
 
-        if ($imageData === null) {
+        if (is_null($imageData)) {
             return null;
         }
 
@@ -451,12 +366,45 @@ class ImageService extends UploadService
         return 'data:image/' . $extension . ';base64,' . base64_encode($imageData);
     }
 
+    /**
+     * Get a storage path for the given image URL.
+     * Ensures the path will start with "uploads/images".
+     * Returns null if the url cannot be resolved to a local URL.
+     */
+    private function imageUrlToStoragePath(string $url): ?string
+    {
+        $url = ltrim(trim($url), '/');
+
+        // Handle potential relative paths
+        $isRelative = strpos($url, 'http') !== 0;
+        if ($isRelative) {
+            if (strpos(strtolower($url), 'uploads/images') === 0) {
+                return trim($url, '/');
+            }
+            return null;
+        }
+
+        // Handle local images based on paths on the same domain
+        $potentialHostPaths = [
+            url('uploads/images/'),
+            $this->getPublicUrl('/uploads/images/'),
+        ];
+
+        foreach ($potentialHostPaths as $potentialBasePath) {
+            $potentialBasePath = strtolower($potentialBasePath);
+            if (strpos(strtolower($url), $potentialBasePath) === 0) {
+                return 'uploads/images/' . trim(substr($url, strlen($potentialBasePath)), '/');
+            }
+        }
+
+        return null;
+    }
+
     /**
      * Gets a public facing url for an image by checking relevant environment variables.
-     * @param string $filePath
-     * @return string
+     * If s3-style store is in use it will default to guessing a public bucket URL.
      */
-    private function getPublicUrl($filePath)
+    private function getPublicUrl(string $filePath): string
     {
         if ($this->storageUrl === null) {
             $storageUrl = config('filesystems.url');
diff --git a/app/Uploads/UploadService.php b/app/Uploads/UploadService.php
deleted file mode 100644 (file)
index 292e61e..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php namespace BookStack\Uploads;
-
-use Illuminate\Contracts\Filesystem\Factory as FileSystem;
-use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
-
-abstract class UploadService
-{
-
-    /**
-     * @var FileSystem
-     */
-    protected $fileSystem;
-
-
-    /**
-     * FileService constructor.
-     * @param $fileSystem
-     */
-    public function __construct(FileSystem $fileSystem)
-    {
-        $this->fileSystem = $fileSystem;
-    }
-
-    /**
-     * Get the storage that will be used for storing images.
-     * @return FileSystemInstance
-     */
-    protected function getStorage()
-    {
-        $storageType = config('filesystems.default');
-        return $this->fileSystem->disk($storageType);
-    }
-
-    /**
-     * Check whether or not a folder is empty.
-     * @param $path
-     * @return bool
-     */
-    protected function isFolderEmpty($path)
-    {
-        $files = $this->getStorage()->files($path);
-        $folders = $this->getStorage()->directories($path);
-        return (count($files) === 0 && count($folders) === 0);
-    }
-}
diff --git a/app/Uploads/UserAvatars.php b/app/Uploads/UserAvatars.php
new file mode 100644 (file)
index 0000000..b3b9d59
--- /dev/null
@@ -0,0 +1,101 @@
+<?php namespace BookStack\Uploads;
+
+use BookStack\Auth\User;
+use BookStack\Exceptions\HttpFetchException;
+use Exception;
+use Illuminate\Support\Facades\Log;
+
+class UserAvatars
+{
+    protected $imageService;
+    protected $http;
+
+    public function __construct(ImageService $imageService, HttpFetcher $http)
+    {
+        $this->imageService = $imageService;
+        $this->http = $http;
+    }
+
+    /**
+     * Fetch and assign an avatar image to the given user.
+     */
+    public function fetchAndAssignToUser(User $user): void
+    {
+        if (!$this->avatarFetchEnabled()) {
+            return;
+        }
+
+        try {
+            $avatar = $this->saveAvatarImage($user);
+            $user->avatar()->associate($avatar);
+            $user->save();
+        } catch (Exception $e) {
+            Log::error('Failed to save user avatar image');
+        }
+    }
+
+    /**
+     * Save an avatar image from an external service.
+     * @throws Exception
+     */
+    protected function saveAvatarImage(User $user, int $size = 500): Image
+    {
+        $avatarUrl = $this->getAvatarUrl();
+        $email = strtolower(trim($user->email));
+
+        $replacements = [
+            '${hash}' => md5($email),
+            '${size}' => $size,
+            '${email}' => urlencode($email),
+        ];
+
+        $userAvatarUrl = strtr($avatarUrl, $replacements);
+        $imageName = str_replace(' ', '-', $user->id . '-avatar.png');
+        $imageData = $this->getAvatarImageData($userAvatarUrl);
+
+        $image = $this->imageService->saveNew($imageName, $imageData, 'user', $user->id);
+        $image->created_by = $user->id;
+        $image->updated_by = $user->id;
+        $image->save();
+
+        return $image;
+    }
+
+    /**
+     * Gets an image from url and returns it as a string of image data.
+     * @throws Exception
+     */
+    protected function getAvatarImageData(string $url): string
+    {
+        try {
+            $imageData = $this->http->fetch($url);
+        } catch (HttpFetchException $exception) {
+            throw new Exception(trans('errors.cannot_get_image_from_url', ['url' => $url]));
+        }
+        return $imageData;
+    }
+
+    /**
+     * Check if fetching external avatars is enabled.
+     */
+    protected function avatarFetchEnabled(): bool
+    {
+        $fetchUrl = $this->getAvatarUrl();
+        return is_string($fetchUrl) && strpos($fetchUrl, 'http') === 0;
+    }
+
+    /**
+     * Get the URL to fetch avatars from.
+     */
+    protected function getAvatarUrl(): string
+    {
+        $url = trim(config('services.avatar_url'));
+
+        if (empty($url) && !config('services.disable_services')) {
+            $url = 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon';
+        }
+
+        return $url;
+    }
+
+}
\ No newline at end of file
index 6211f41be0c4ef1a866a4c043287b2d76c1f3f60..c090bfd055acc400e1ad4c46e7059b6c19820f60 100644 (file)
@@ -2,14 +2,11 @@
 
 use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Auth\User;
-use BookStack\Ownable;
+use BookStack\Model;
 use BookStack\Settings\SettingService;
 
 /**
  * Get the path to a versioned file.
- *
- * @param  string $file
- * @return string
  * @throws Exception
  */
 function versioned_asset(string $file = ''): string
@@ -33,7 +30,6 @@ function versioned_asset(string $file = ''): string
 /**
  * Helper method to get the current User.
  * Defaults to public 'Guest' user if not logged in.
- * @return User
  */
 function user(): User
 {
@@ -42,7 +38,6 @@ function user(): User
 
 /**
  * Check if current user is a signed in user.
- * @return bool
  */
 function signedInUser(): bool
 {
@@ -51,7 +46,6 @@ function signedInUser(): bool
 
 /**
  * Check if the current user has general access.
- * @return bool
  */
 function hasAppAccess(): bool
 {
@@ -59,14 +53,10 @@ function hasAppAccess(): bool
 }
 
 /**
- * Check if the current user has a permission.
- * If an ownable element is passed in the jointPermissions are checked against
- * that particular item.
- * @param string $permission
- * @param Ownable $ownable
- * @return bool
+ * Check if the current user has a permission. If an ownable element
+ * is passed in the jointPermissions are checked against that particular item.
  */
-function userCan(string $permission, Ownable $ownable = null): bool
+function userCan(string $permission, Model $ownable = null): bool
 {
     if ($ownable === null) {
         return user() && user()->can($permission);
@@ -80,9 +70,6 @@ function userCan(string $permission, Ownable $ownable = null): bool
 /**
  * Check if the current user has the given permission
  * on any item in the system.
- * @param string $permission
- * @param string|null $entityClass
- * @return bool
  */
 function userCanOnAny(string $permission, string $entityClass = null): bool
 {
@@ -92,27 +79,26 @@ function userCanOnAny(string $permission, string $entityClass = null): bool
 
 /**
  * Helper to access system settings.
- * @param string $key
- * @param $default
  * @return bool|string|SettingService
  */
 function setting(string $key = null, $default = false)
 {
     $settingService = resolve(SettingService::class);
+
     if (is_null($key)) {
         return $settingService;
     }
+
     return $settingService->get($key, $default);
 }
 
 /**
  * Get a path to a theme resource.
- * @param string $path
- * @return string
  */
 function theme_path(string $path = ''): string
 {
     $theme = config('view.theme');
+
     if (!$theme) {
         return '';
     }
@@ -126,9 +112,6 @@ function theme_path(string $path = ''): string
  * to the 'resources/assets/icons' folder.
  *
  * Returns an empty string if icon file not found.
- * @param $name
- * @param array $attrs
- * @return mixed
  */
 function icon(string $name, array $attrs = []): string
 {
@@ -144,6 +127,7 @@ function icon(string $name, array $attrs = []): string
 
     $iconPath = resource_path('icons/' . $name . '.svg');
     $themeIconPath = theme_path('icons/' . $name . '.svg');
+
     if ($themeIconPath && file_exists($themeIconPath)) {
         $iconPath = $themeIconPath;
     } else if (!file_exists($iconPath)) {
@@ -158,10 +142,6 @@ function icon(string $name, array $attrs = []): string
  * Generate a url with multiple parameters for sorting purposes.
  * Works out the logic to set the correct sorting direction
  * Discards empty parameters and allows overriding.
- * @param string $path
- * @param array $data
- * @param array $overrideData
- * @return string
  */
 function sortUrl(string $path, array $data, array $overrideData = []): string
 {
@@ -171,7 +151,7 @@ function sortUrl(string $path, array $data, array $overrideData = []): string
     // Change sorting direction is already sorted on current attribute
     if (isset($overrideData['sort']) && $overrideData['sort'] === $data['sort']) {
         $queryData['order'] = ($data['order'] === 'asc') ? 'desc' : 'asc';
-    } else {
+    } elseif (isset($overrideData['sort'])) {
         $queryData['order'] = 'asc';
     }
 
diff --git a/artisan b/artisan
index dad16dcdefdee1989b99de8cddffffe06d10a381..d5c6aaf98542479db38a44dce76de952dbc34de6 100755 (executable)
--- a/artisan
+++ b/artisan
@@ -5,15 +5,17 @@ define('LARAVEL_START', microtime(true));
 
 /*
 |--------------------------------------------------------------------------
-| Initialize The App
+| Register The Auto Loader
 |--------------------------------------------------------------------------
 |
-| We need to get things going before we start up the app.
-| The init file loads everything in, in the correct order.
+| Composer provides a convenient, automatically generated class loader
+| for our application. We just need to utilize it! We'll require it
+| into the script here so that we do not have to worry about the
+| loading of any our classes "manually". Feels great to relax.
 |
 */
 
-require __DIR__.'/bootstrap/init.php';
+require __DIR__.'/vendor/autoload.php';
 
 $app = require_once __DIR__.'/bootstrap/app.php';
 
diff --git a/bootstrap/init.php b/bootstrap/init.php
deleted file mode 100644 (file)
index 7d9e43f..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-/*
-|--------------------------------------------------------------------------
-| Load Our Own Helpers
-|--------------------------------------------------------------------------
-|
-| This custom function loads any helpers, before the Laravel Framework
-| is built so we can override any helpers as we please.
-|
-*/
-require __DIR__.'/../app/helpers.php';
-
-/*
-|--------------------------------------------------------------------------
-| Register The Composer Auto Loader
-|--------------------------------------------------------------------------
-|
-| Composer provides a convenient, automatically generated class loader
-| for our application. We just need to utilize it! We'll require it
-| into the script here so that we do not have to worry about the
-| loading of any our classes "manually". Feels great to relax.
-|
-*/
-require __DIR__.'/../vendor/autoload.php';
\ No newline at end of file
index a8b9456a1856c01fb2bf65e14ea32de0f7c55a15..df2439f85415ba9113929226e913e46a004663d6 100644 (file)
@@ -5,42 +5,42 @@
     "license": "MIT",
     "type": "project",
     "require": {
-        "php": "^7.2",
+        "php": "^7.2.5",
         "ext-curl": "*",
         "ext-dom": "*",
         "ext-gd": "*",
         "ext-json": "*",
         "ext-mbstring": "*",
-        "ext-tidy": "*",
         "ext-xml": "*",
-        "barryvdh/laravel-dompdf": "^0.8.5",
-        "barryvdh/laravel-snappy": "^0.4.5",
+        "barryvdh/laravel-dompdf": "^0.8.7",
+        "barryvdh/laravel-snappy": "^0.4.8",
         "doctrine/dbal": "^2.9",
-        "fideloper/proxy": "^4.0",
-        "gathercontent/htmldiff": "^0.2.1",
-        "intervention/image": "^2.5",
-        "laravel/framework": "^6.0",
-        "laravel/socialite": "^4.2",
-        "league/flysystem-aws-s3-v3": "^1.0",
-        "predis/predis": "^1.1",
-        "socialiteproviders/discord": "^2.0",
-        "socialiteproviders/gitlab": "^3.0",
-        "socialiteproviders/microsoft-azure": "^3.0",
-        "socialiteproviders/okta": "^1.0",
-        "socialiteproviders/slack": "^3.0",
-        "socialiteproviders/twitch": "^5.0"
+        "facade/ignition": "^1.16.4",
+        "fideloper/proxy": "^4.4.1",
+        "intervention/image": "^2.5.1",
+        "laravel/framework": "^6.20.12",
+        "laravel/socialite": "^5.1",
+        "league/commonmark": "^1.5",
+        "league/flysystem-aws-s3-v3": "^1.0.29",
+        "nunomaduro/collision": "^3.1",
+        "onelogin/php-saml": "^3.3",
+        "predis/predis": "^1.1.6",
+        "socialiteproviders/discord": "^4.1",
+        "socialiteproviders/gitlab": "^4.1",
+        "socialiteproviders/microsoft-azure": "^4.1",
+        "socialiteproviders/okta": "^4.1",
+        "socialiteproviders/slack": "^4.1",
+        "socialiteproviders/twitch": "^5.3",
+        "ssddanbrown/htmldiff": "^v1.0.1"
     },
     "require-dev": {
-        "barryvdh/laravel-debugbar": "^3.2.8",
-        "barryvdh/laravel-ide-helper": "^2.6.4",
-        "facade/ignition": "^1.4",
-        "fzaninotto/faker": "^1.4",
-        "laravel/browser-kit-testing": "^5.1",
-        "mockery/mockery": "^1.0",
-        "nunomaduro/collision": "^3.0",
+        "barryvdh/laravel-debugbar": "^3.5.1",
+        "barryvdh/laravel-ide-helper": "^2.8.2",
+        "fakerphp/faker": "^1.9.1",
+        "laravel/browser-kit-testing": "^5.2",
+        "mockery/mockery": "^1.3.3",
         "phpunit/phpunit": "^8.0",
-        "squizlabs/php_codesniffer": "^3.4",
-        "wnx/laravel-stats": "^2.0"
+        "squizlabs/php_codesniffer": "^3.5.8"
     },
     "autoload": {
         "classmap": [
         ],
         "psr-4": {
             "BookStack\\": "app/"
-        }
+        },
+               "files": [
+                       "app/helpers.php"
+               ]
     },
     "autoload-dev": {
         "psr-4": {
         "post-create-project-cmd": [
             "@php artisan key:generate --ansi"
         ],
-        "pre-update-cmd": [
-            "@php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"",
-            "@php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\""
-        ],
         "pre-install-cmd": [
-            "@php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"",
-            "@php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\""
+            "@php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\""
         ],
         "post-install-cmd": [
             "@php artisan cache:clear",
@@ -89,7 +87,7 @@
         "preferred-install": "dist",
         "sort-packages": true,
         "platform": {
-            "php": "7.2.0"
+            "php": "7.2.5"
         }
     },
     "extra": {
index 3ec106ded4f0c57c9ca3556222dff16874606bc8..d959ed97eda5f495d981f8130acb7c7dfe8f502a 100644 (file)
@@ -1,33 +1,33 @@
 {
     "_readme": [
         "This file locks the dependencies of your project to a known state",
-        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "c156e1738dbab2a57f9a926d9a9a5a6a",
+    "content-hash": "df6e5d5d5debaaeab590da55c7d50ec2",
     "packages": [
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.112.0",
+            "version": "3.172.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "1e21446c6780a3b9b5e4315bd6d4347d2c3381eb"
+                "reference": "14fd73f12fc70d5f48e034f5dfc33136136adb61"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/1e21446c6780a3b9b5e4315bd6d4347d2c3381eb",
-                "reference": "1e21446c6780a3b9b5e4315bd6d4347d2c3381eb",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/14fd73f12fc70d5f48e034f5dfc33136136adb61",
+                "reference": "14fd73f12fc70d5f48e034f5dfc33136136adb61",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "ext-pcre": "*",
                 "ext-simplexml": "*",
-                "guzzlehttp/guzzle": "^5.3.3|^6.2.1",
-                "guzzlehttp/promises": "~1.0",
+                "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0",
+                "guzzlehttp/promises": "^1.0",
                 "guzzlehttp/psr7": "^1.4.1",
-                "mtdowling/jmespath.php": "~2.2",
+                "mtdowling/jmespath.php": "^2.5",
                 "php": ">=5.5"
             },
             "require-dev": {
                 "ext-pcntl": "*",
                 "ext-sockets": "*",
                 "nette/neon": "^2.3",
+                "paragonie/random_compat": ">= 2",
                 "phpunit/phpunit": "^4.8.35|^5.4.3",
                 "psr/cache": "^1.0",
-                "psr/simple-cache": "^1.0"
+                "psr/simple-cache": "^1.0",
+                "sebastian/comparator": "^1.2.3"
             },
             "suggest": {
                 "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
                 "s3",
                 "sdk"
             ],
-            "time": "2019-09-12T18:09:53+00:00"
+            "support": {
+                "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
+                "issues": "https://github.com/aws/aws-sdk-php/issues",
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.172.3"
+            },
+            "time": "2021-01-28T19:14:55+00:00"
         },
         {
             "name": "barryvdh/laravel-dompdf",
-            "version": "v0.8.5",
+            "version": "v0.8.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-dompdf.git",
-                "reference": "7393732b2f3a3ee357974cbb0c46c9b65b84dad1"
+                "reference": "30310e0a675462bf2aa9d448c8dcbf57fbcc517d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/7393732b2f3a3ee357974cbb0c46c9b65b84dad1",
-                "reference": "7393732b2f3a3ee357974cbb0c46c9b65b84dad1",
+                "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/30310e0a675462bf2aa9d448c8dcbf57fbcc517d",
+                "reference": "30310e0a675462bf2aa9d448c8dcbf57fbcc517d",
                 "shasum": ""
             },
             "require": {
                 "dompdf/dompdf": "^0.8",
-                "illuminate/support": "^5.5|^6",
+                "illuminate/support": "^5.5|^6|^7|^8",
                 "php": ">=7"
             },
             "type": "library",
                 "laravel",
                 "pdf"
             ],
-            "time": "2019-08-23T14:30:33+00:00"
+            "support": {
+                "issues": "https://github.com/barryvdh/laravel-dompdf/issues",
+                "source": "https://github.com/barryvdh/laravel-dompdf/tree/master"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/barryvdh",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-07T11:50:18+00:00"
         },
         {
             "name": "barryvdh/laravel-snappy",
-            "version": "v0.4.5",
+            "version": "v0.4.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-snappy.git",
-                "reference": "9be767fc7a082665a84945f36c70b0cbead91ce9"
+                "reference": "1903ab84171072b6bff8d98eb58d38b2c9aaf645"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/9be767fc7a082665a84945f36c70b0cbead91ce9",
-                "reference": "9be767fc7a082665a84945f36c70b0cbead91ce9",
+                "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/1903ab84171072b6bff8d98eb58d38b2c9aaf645",
+                "reference": "1903ab84171072b6bff8d98eb58d38b2c9aaf645",
                 "shasum": ""
             },
             "require": {
-                "illuminate/filesystem": "5.5.x|5.6.x|5.7.x|5.8.x|6.0.*",
-                "illuminate/support": "5.5.x|5.6.x|5.7.x|5.8.x|6.0.*",
+                "illuminate/filesystem": "^5.5|^6|^7|^8",
+                "illuminate/support": "^5.5|^6|^7|^8",
                 "knplabs/knp-snappy": "^1",
                 "php": ">=7"
             },
                 "wkhtmltoimage",
                 "wkhtmltopdf"
             ],
-            "time": "2019-08-30T16:12:23+00:00"
-        },
-        {
-            "name": "cogpowered/finediff",
-            "version": "0.3.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/cogpowered/FineDiff.git",
-                "reference": "339ddc8c3afb656efed4f2f0a80e5c3d026f8ea8"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/cogpowered/FineDiff/zipball/339ddc8c3afb656efed4f2f0a80e5c3d026f8ea8",
-                "reference": "339ddc8c3afb656efed4f2f0a80e5c3d026f8ea8",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.3.0"
-            },
-            "require-dev": {
-                "mockery/mockery": "*",
-                "phpunit/phpunit": "*"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-0": {
-                    "cogpowered\\FineDiff": "src/"
-                }
+            "support": {
+                "issues": "https://github.com/barryvdh/laravel-snappy/issues",
+                "source": "https://github.com/barryvdh/laravel-snappy/tree/master"
             },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Rob Crowe",
-                    "email": "[email protected]"
-                },
-                {
-                    "name": "Raymond Hill"
-                }
-            ],
-            "description": "PHP implementation of a Fine granularity Diff engine",
-            "homepage": "https://github.com/cogpowered/FineDiff",
-            "keywords": [
-                "diff",
-                "finediff",
-                "opcode",
-                "string",
-                "text"
-            ],
-            "time": "2014-05-19T10:25:02+00:00"
+            "time": "2020-09-07T12:33:10+00:00"
         },
         {
             "name": "doctrine/cache",
-            "version": "v1.8.0",
+            "version": "1.10.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/cache.git",
-                "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57"
+                "reference": "13e3381b25847283a91948d04640543941309727"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57",
-                "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57",
+                "url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727",
+                "reference": "13e3381b25847283a91948d04640543941309727",
                 "shasum": ""
             },
             "require": {
-                "php": "~7.1"
+                "php": "~7.1 || ^8.0"
             },
             "conflict": {
                 "doctrine/common": ">2.2,<2.4"
             },
             "require-dev": {
                 "alcaeus/mongo-php-adapter": "^1.1",
-                "doctrine/coding-standard": "^4.0",
+                "doctrine/coding-standard": "^6.0",
                 "mongodb/mongodb": "^1.1",
                 "phpunit/phpunit": "^7.0",
                 "predis/predis": "~1.0"
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.8.x-dev"
+                    "dev-master": "1.9.x-dev"
                 }
             },
             "autoload": {
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Roman Borschel",
                     "email": "[email protected]"
                     "name": "Benjamin Eberlei",
                     "email": "[email protected]"
                 },
-                {
-                    "name": "Guilherme Blanco",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Jonathan Wage",
                     "email": "[email protected]"
                     "email": "[email protected]"
                 }
             ],
-            "description": "Caching library offering an object-oriented API for many cache backends",
-            "homepage": "https://www.doctrine-project.org",
+            "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.",
+            "homepage": "https://www.doctrine-project.org/projects/cache.html",
             "keywords": [
+                "abstraction",
+                "apcu",
                 "cache",
-                "caching"
+                "caching",
+                "couchdb",
+                "memcached",
+                "php",
+                "redis",
+                "xcache"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/cache/issues",
+                "source": "https://github.com/doctrine/cache/tree/1.10.x"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache",
+                    "type": "tidelift"
+                }
             ],
-            "time": "2018-08-21T18:01:43+00:00"
+            "time": "2020-07-07T18:54:01+00:00"
         },
         {
             "name": "doctrine/dbal",
-            "version": "v2.9.2",
+            "version": "2.10.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/dbal.git",
-                "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9"
+                "reference": "47433196b6390d14409a33885ee42b6208160643"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9",
-                "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/47433196b6390d14409a33885ee42b6208160643",
+                "reference": "47433196b6390d14409a33885ee42b6208160643",
                 "shasum": ""
             },
             "require": {
                 "doctrine/cache": "^1.0",
                 "doctrine/event-manager": "^1.0",
                 "ext-pdo": "*",
-                "php": "^7.1"
+                "php": "^7.2"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^5.0",
-                "jetbrains/phpstorm-stubs": "^2018.1.2",
-                "phpstan/phpstan": "^0.10.1",
-                "phpunit/phpunit": "^7.4",
-                "symfony/console": "^2.0.5|^3.0|^4.0",
-                "symfony/phpunit-bridge": "^3.4.5|^4.0.5"
+                "doctrine/coding-standard": "^8.1",
+                "jetbrains/phpstorm-stubs": "^2019.1",
+                "nikic/php-parser": "^4.4",
+                "phpstan/phpstan": "^0.12.40",
+                "phpunit/phpunit": "^8.5.5",
+                "psalm/plugin-phpunit": "^0.10.0",
+                "symfony/console": "^2.0.5|^3.0|^4.0|^5.0",
+                "vimeo/psalm": "^3.14.2"
             },
             "suggest": {
                 "symfony/console": "For helpful console commands such as SQL execution and import of files."
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.9.x-dev",
+                    "dev-master": "2.10.x-dev",
                     "dev-develop": "3.0.x-dev"
                 }
             },
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Roman Borschel",
                     "email": "[email protected]"
                     "name": "Benjamin Eberlei",
                     "email": "[email protected]"
                 },
-                {
-                    "name": "Guilherme Blanco",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Jonathan Wage",
                     "email": "[email protected]"
             "keywords": [
                 "abstraction",
                 "database",
+                "db2",
                 "dbal",
+                "mariadb",
+                "mssql",
                 "mysql",
-                "persistence",
+                "oci8",
+                "oracle",
+                "pdo",
                 "pgsql",
-                "php",
-                "queryobject"
+                "postgresql",
+                "queryobject",
+                "sasql",
+                "sql",
+                "sqlanywhere",
+                "sqlite",
+                "sqlserver",
+                "sqlsrv"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/dbal/issues",
+                "source": "https://github.com/doctrine/dbal/tree/2.10.4"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal",
+                    "type": "tidelift"
+                }
             ],
-            "time": "2018-12-31T03:27:51+00:00"
+            "time": "2020-09-12T21:20:41+00:00"
         },
         {
             "name": "doctrine/event-manager",
-            "version": "v1.0.0",
+            "version": "1.1.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/event-manager.git",
-                "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3"
+                "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3",
-                "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3",
+                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f",
+                "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": "^7.1 || ^8.0"
             },
             "conflict": {
                 "doctrine/common": "<2.9@dev"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^4.0",
+                "doctrine/coding-standard": "^6.0",
                 "phpunit/phpunit": "^7.0"
             },
             "type": "library",
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Roman Borschel",
                     "email": "[email protected]"
                     "name": "Benjamin Eberlei",
                     "email": "[email protected]"
                 },
-                {
-                    "name": "Guilherme Blanco",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Jonathan Wage",
                     "email": "[email protected]"
                     "email": "[email protected]"
                 }
             ],
-            "description": "Doctrine Event Manager component",
+            "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.",
             "homepage": "https://www.doctrine-project.org/projects/event-manager.html",
             "keywords": [
                 "event",
-                "eventdispatcher",
-                "eventmanager"
+                "event dispatcher",
+                "event manager",
+                "event system",
+                "events"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/event-manager/issues",
+                "source": "https://github.com/doctrine/event-manager/tree/1.1.x"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager",
+                    "type": "tidelift"
+                }
             ],
-            "time": "2018-06-11T11:59:03+00:00"
+            "time": "2020-05-29T18:28:51+00:00"
         },
         {
             "name": "doctrine/inflector",
-            "version": "v1.3.0",
+            "version": "2.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/inflector.git",
-                "reference": "5527a48b7313d15261292c149e55e26eae771b0a"
+                "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a",
-                "reference": "5527a48b7313d15261292c149e55e26eae771b0a",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/9cf661f4eb38f7c881cac67c75ea9b00bf97b210",
+                "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": "^7.2 || ^8.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^6.2"
+                "doctrine/coding-standard": "^7.0",
+                "phpstan/phpstan": "^0.11",
+                "phpstan/phpstan-phpunit": "^0.11",
+                "phpstan/phpstan-strict-rules": "^0.11",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.3.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "autoload": {
                 "psr-4": {
-                    "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector"
+                    "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Roman Borschel",
                     "email": "[email protected]"
                     "name": "Benjamin Eberlei",
                     "email": "[email protected]"
                 },
-                {
-                    "name": "Guilherme Blanco",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Jonathan Wage",
                     "email": "[email protected]"
                     "email": "[email protected]"
                 }
             ],
-            "description": "Common String Manipulations with regard to casing and singular/plural rules.",
-            "homepage": "http://www.doctrine-project.org",
+            "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+            "homepage": "https://www.doctrine-project.org/projects/inflector.html",
             "keywords": [
                 "inflection",
-                "pluralize",
-                "singularize",
-                "string"
+                "inflector",
+                "lowercase",
+                "manipulation",
+                "php",
+                "plural",
+                "singular",
+                "strings",
+                "uppercase",
+                "words"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/inflector/issues",
+                "source": "https://github.com/doctrine/inflector/tree/2.0.x"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+                    "type": "tidelift"
+                }
             ],
-            "time": "2018-01-09T20:05:19+00:00"
+            "time": "2020-05-29T15:13:26+00:00"
         },
         {
             "name": "doctrine/lexer",
-            "version": "1.1.0",
+            "version": "1.2.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/lexer.git",
-                "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea"
+                "reference": "e864bbf5904cb8f5bb334f99209b48018522f042"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea",
-                "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042",
+                "reference": "e864bbf5904cb8f5bb334f99209b48018522f042",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2"
+                "php": "^7.2 || ^8.0"
             },
             "require-dev": {
                 "doctrine/coding-standard": "^6.0",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.1.x-dev"
+                    "dev-master": "1.2.x-dev"
                 }
             },
             "autoload": {
                 "parser",
                 "php"
             ],
-            "time": "2019-07-30T19:33:28+00:00"
+            "support": {
+                "issues": "https://github.com/doctrine/lexer/issues",
+                "source": "https://github.com/doctrine/lexer/tree/1.2.1"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-25T17:44:05+00:00"
         },
         {
             "name": "dompdf/dompdf",
-            "version": "v0.8.3",
+            "version": "v0.8.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/dompdf/dompdf.git",
-                "reference": "75f13c700009be21a1965dc2c5b68a8708c22ba2"
+                "reference": "db91d81866c69a42dad1d2926f61515a1e3f42c5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/dompdf/dompdf/zipball/75f13c700009be21a1965dc2c5b68a8708c22ba2",
-                "reference": "75f13c700009be21a1965dc2c5b68a8708c22ba2",
+                "url": "https://api.github.com/repos/dompdf/dompdf/zipball/db91d81866c69a42dad1d2926f61515a1e3f42c5",
+                "reference": "db91d81866c69a42dad1d2926f61515a1e3f42c5",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-mbstring": "*",
-                "phenx/php-font-lib": "0.5.*",
-                "phenx/php-svg-lib": "0.3.*",
-                "php": ">=5.4.0"
+                "phenx/php-font-lib": "^0.5.2",
+                "phenx/php-svg-lib": "^0.3.3",
+                "php": "^7.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8|^5.5|^6.5",
-                "squizlabs/php_codesniffer": "2.*"
+                "mockery/mockery": "^1.3",
+                "phpunit/phpunit": "^7.5",
+                "squizlabs/php_codesniffer": "^3.5"
             },
             "suggest": {
                 "ext-gd": "Needed to process images",
                 "ext-gmagick": "Improves image processing performance",
-                "ext-imagick": "Improves image processing performance"
+                "ext-imagick": "Improves image processing performance",
+                "ext-zlib": "Needed for pdf stream compression"
             },
             "type": "library",
             "extra": {
             ],
             "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
             "homepage": "https://github.com/dompdf/dompdf",
-            "time": "2018-12-14T02:40:31+00:00"
+            "support": {
+                "issues": "https://github.com/dompdf/dompdf/issues",
+                "source": "https://github.com/dompdf/dompdf/tree/master"
+            },
+            "time": "2020-08-30T22:54:22+00:00"
         },
         {
             "name": "dragonmantank/cron-expression",
-            "version": "v2.3.0",
+            "version": "v2.3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/dragonmantank/cron-expression.git",
-                "reference": "72b6fbf76adb3cf5bc0db68559b33d41219aba27"
+                "reference": "65b2d8ee1f10915efb3b55597da3404f096acba2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/72b6fbf76adb3cf5bc0db68559b33d41219aba27",
-                "reference": "72b6fbf76adb3cf5bc0db68559b33d41219aba27",
+                "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/65b2d8ee1f10915efb3b55597da3404f096acba2",
+                "reference": "65b2d8ee1f10915efb3b55597da3404f096acba2",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.0"
+                "php": "^7.0|^8.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^6.4|^7.0"
+                "phpunit/phpunit": "^6.4|^7.0|^8.0|^9.0"
             },
             "type": "library",
             "extra": {
                 "cron",
                 "schedule"
             ],
-            "time": "2019-03-31T00:38:28+00:00"
+            "support": {
+                "issues": "https://github.com/dragonmantank/cron-expression/issues",
+                "source": "https://github.com/dragonmantank/cron-expression/tree/v2.3.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/dragonmantank",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-13T00:52:37+00:00"
         },
         {
             "name": "egulias/email-validator",
-            "version": "2.1.11",
+            "version": "2.1.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/egulias/EmailValidator.git",
-                "reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23"
+                "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/92dd169c32f6f55ba570c309d83f5209cefb5e23",
-                "reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23",
+                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0dbf5d78455d4d6a41d186da50adc1122ec066f4",
+                "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4",
                 "shasum": ""
             },
             "require": {
                 "doctrine/lexer": "^1.0.1",
-                "php": ">= 5.5"
+                "php": ">=5.5",
+                "symfony/polyfill-intl-idn": "^1.10"
             },
             "require-dev": {
-                "dominicsayers/isemail": "dev-master",
-                "phpunit/phpunit": "^4.8.35||^5.7||^6.0",
-                "satooshi/php-coveralls": "^1.0.1",
-                "symfony/phpunit-bridge": "^4.4@dev"
+                "dominicsayers/isemail": "^3.0.7",
+                "phpunit/phpunit": "^4.8.36|^7.5.15",
+                "satooshi/php-coveralls": "^1.0.1"
             },
             "suggest": {
                 "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
             },
             "autoload": {
                 "psr-4": {
-                    "Egulias\\EmailValidator\\": "EmailValidator"
+                    "Egulias\\EmailValidator\\": "src"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
                 "validation",
                 "validator"
             ],
-            "time": "2019-08-13T17:33:27+00:00"
+            "support": {
+                "issues": "https://github.com/egulias/EmailValidator/issues",
+                "source": "https://github.com/egulias/EmailValidator/tree/2.1.25"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/egulias",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-12-29T14:50:06+00:00"
         },
         {
-            "name": "erusev/parsedown",
-            "version": "1.7.3",
+            "name": "facade/flare-client-php",
+            "version": "1.3.7",
             "source": {
                 "type": "git",
-                "url": "https://github.com/erusev/parsedown.git",
-                "reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7"
+                "url": "https://github.com/facade/flare-client-php.git",
+                "reference": "fd688d3c06658f2b3b5f7bb19f051ee4ddf02492"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/erusev/parsedown/zipball/6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
-                "reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
+                "url": "https://api.github.com/repos/facade/flare-client-php/zipball/fd688d3c06658f2b3b5f7bb19f051ee4ddf02492",
+                "reference": "fd688d3c06658f2b3b5f7bb19f051ee4ddf02492",
                 "shasum": ""
             },
             "require": {
-                "ext-mbstring": "*",
-                "php": ">=5.3.0"
+                "facade/ignition-contracts": "~1.0",
+                "illuminate/pipeline": "^5.5|^6.0|^7.0|^8.0",
+                "php": "^7.1|^8.0",
+                "symfony/http-foundation": "^3.3|^4.1|^5.0",
+                "symfony/mime": "^3.4|^4.0|^5.1",
+                "symfony/var-dumper": "^3.4|^4.0|^5.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35"
+                "friendsofphp/php-cs-fixer": "^2.14",
+                "phpunit/phpunit": "^7.5.16",
+                "spatie/phpunit-snapshot-assertions": "^2.0"
             },
             "type": "library",
-            "autoload": {
-                "psr-0": {
-                    "Parsedown": ""
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
                 }
             },
+            "autoload": {
+                "psr-4": {
+                    "Facade\\FlareClient\\": "src"
+                },
+                "files": [
+                    "src/helpers.php"
+                ]
+            },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
                 "MIT"
             ],
-            "authors": [
+            "description": "Send PHP errors to Flare",
+            "homepage": "https://github.com/facade/flare-client-php",
+            "keywords": [
+                "exception",
+                "facade",
+                "flare",
+                "reporting"
+            ],
+            "support": {
+                "issues": "https://github.com/facade/flare-client-php/issues",
+                "source": "https://github.com/facade/flare-client-php/tree/1.3.7"
+            },
+            "funding": [
                 {
-                    "name": "Emanuil Rusev",
-                    "email": "[email protected]",
-                    "homepage": "http://erusev.com"
+                    "url": "https://github.com/spatie",
+                    "type": "github"
                 }
             ],
-            "description": "Parser for Markdown.",
-            "homepage": "http://parsedown.org",
-            "keywords": [
-                "markdown",
-                "parser"
-            ],
-            "time": "2019-03-17T18:48:37+00:00"
+            "time": "2020-10-21T16:02:39+00:00"
         },
         {
-            "name": "fideloper/proxy",
-            "version": "4.2.1",
+            "name": "facade/ignition",
+            "version": "1.16.4",
             "source": {
                 "type": "git",
-                "url": "https://github.com/fideloper/TrustedProxy.git",
-                "reference": "03085e58ec7bee24773fa5a8850751a6e61a7e8a"
+                "url": "https://github.com/facade/ignition.git",
+                "reference": "1da1705e7f6b24ed45af05461463228da424e14f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/03085e58ec7bee24773fa5a8850751a6e61a7e8a",
-                "reference": "03085e58ec7bee24773fa5a8850751a6e61a7e8a",
+                "url": "https://api.github.com/repos/facade/ignition/zipball/1da1705e7f6b24ed45af05461463228da424e14f",
+                "reference": "1da1705e7f6b24ed45af05461463228da424e14f",
                 "shasum": ""
             },
             "require": {
-                "illuminate/contracts": "^5.0|^6.0|^7.0",
-                "php": ">=5.4.0"
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "facade/flare-client-php": "^1.3",
+                "facade/ignition-contracts": "^1.0",
+                "filp/whoops": "^2.4",
+                "illuminate/support": "~5.5.0 || ~5.6.0 || ~5.7.0 || ~5.8.0 || ^6.0",
+                "monolog/monolog": "^1.12 || ^2.0",
+                "php": "^7.1|^8.0",
+                "scrivo/highlight.php": "^9.15",
+                "symfony/console": "^3.4 || ^4.0",
+                "symfony/var-dumper": "^3.4 || ^4.0"
             },
             "require-dev": {
-                "illuminate/http": "^5.0|^6.0|^7.0",
-                "mockery/mockery": "^1.0",
-                "phpunit/phpunit": "^6.0"
+                "mockery/mockery": "~1.3.3|^1.4.2",
+                "orchestra/testbench": "^3.5 || ^3.6 || ^3.7 || ^3.8 || ^4.0"
+            },
+            "suggest": {
+                "laravel/telescope": "^2.0"
             },
             "type": "library",
             "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                },
                 "laravel": {
                     "providers": [
-                        "Fideloper\\Proxy\\TrustedProxyServiceProvider"
-                    ]
+                        "Facade\\Ignition\\IgnitionServiceProvider"
+                    ],
+                    "aliases": {
+                        "Flare": "Facade\\Ignition\\Facades\\Flare"
+                    }
                 }
             },
             "autoload": {
                 "psr-4": {
-                    "Fideloper\\Proxy\\": "src/"
-                }
+                    "Facade\\Ignition\\": "src"
+                },
+                "files": [
+                    "src/helpers.php"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
                 "MIT"
             ],
-            "authors": [
-                {
-                    "name": "Chris Fidao",
-                    "email": "[email protected]"
-                }
-            ],
-            "description": "Set trusted proxies for Laravel",
+            "description": "A beautiful error page for Laravel applications.",
+            "homepage": "https://github.com/facade/ignition",
             "keywords": [
-                "load balancing",
-                "proxy",
-                "trusted proxy"
+                "error",
+                "flare",
+                "laravel",
+                "page"
             ],
-            "time": "2019-09-03T16:45:42+00:00"
+            "support": {
+                "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction",
+                "forum": "https://twitter.com/flareappio",
+                "issues": "https://github.com/facade/ignition/issues",
+                "source": "https://github.com/facade/ignition"
+            },
+            "time": "2020-10-30T13:40:01+00:00"
         },
         {
-            "name": "gathercontent/htmldiff",
-            "version": "0.2.1",
+            "name": "facade/ignition-contracts",
+            "version": "1.0.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/gathercontent/htmldiff.git",
-                "reference": "24674a62315f64330134b4a4c5b01a7b59193c93"
+                "url": "https://github.com/facade/ignition-contracts.git",
+                "reference": "aeab1ce8b68b188a43e81758e750151ad7da796b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/gathercontent/htmldiff/zipball/24674a62315f64330134b4a4c5b01a7b59193c93",
-                "reference": "24674a62315f64330134b4a4c5b01a7b59193c93",
+                "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/aeab1ce8b68b188a43e81758e750151ad7da796b",
+                "reference": "aeab1ce8b68b188a43e81758e750151ad7da796b",
                 "shasum": ""
             },
             "require": {
-                "cogpowered/finediff": "0.3.1",
-                "ext-tidy": "*"
+                "php": "^7.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "4.*",
-                "squizlabs/php_codesniffer": "1.*"
+                "friendsofphp/php-cs-fixer": "^2.14",
+                "phpunit/phpunit": "^7.5|^8.0",
+                "vimeo/psalm": "^3.12"
             },
             "type": "library",
             "autoload": {
-                "psr-0": {
-                    "GatherContent\\Htmldiff": "src/"
+                "psr-4": {
+                    "Facade\\IgnitionContracts\\": "src"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
             ],
             "authors": [
                 {
-                    "name": "Andrew Cairns",
-                    "email": "[email protected]"
-                },
-                {
-                    "name": "Mathew Chapman",
-                    "email": "[email protected]"
-                },
-                {
-                    "name": "Peter Legierski",
-                    "email": "[email protected]"
+                    "name": "Freek Van der Herten",
+                    "email": "[email protected]",
+                    "homepage": "https://flareapp.io",
+                    "role": "Developer"
                 }
             ],
-            "description": "Compare two HTML strings",
-            "time": "2015-04-15T15:39:46+00:00"
+            "description": "Solution contracts for Ignition",
+            "homepage": "https://github.com/facade/ignition-contracts",
+            "keywords": [
+                "contracts",
+                "flare",
+                "ignition"
+            ],
+            "support": {
+                "issues": "https://github.com/facade/ignition-contracts/issues",
+                "source": "https://github.com/facade/ignition-contracts/tree/1.0.1"
+            },
+            "time": "2020-07-14T10:10:28+00:00"
         },
         {
-            "name": "guzzlehttp/guzzle",
-            "version": "6.3.3",
+            "name": "fideloper/proxy",
+            "version": "4.4.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
+                "url": "https://github.com/fideloper/TrustedProxy.git",
+                "reference": "c073b2bd04d1c90e04dc1b787662b558dd65ade0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
-                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+                "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/c073b2bd04d1c90e04dc1b787662b558dd65ade0",
+                "reference": "c073b2bd04d1c90e04dc1b787662b558dd65ade0",
                 "shasum": ""
             },
             "require": {
-                "guzzlehttp/promises": "^1.0",
-                "guzzlehttp/psr7": "^1.4",
-                "php": ">=5.5"
+                "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0",
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "illuminate/http": "^5.0|^6.0|^7.0|^8.0|^9.0",
+                "mockery/mockery": "^1.0",
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Fideloper\\Proxy\\TrustedProxyServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Fideloper\\Proxy\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Chris Fidao",
+                    "email": "[email protected]"
+                }
+            ],
+            "description": "Set trusted proxies for Laravel",
+            "keywords": [
+                "load balancing",
+                "proxy",
+                "trusted proxy"
+            ],
+            "support": {
+                "issues": "https://github.com/fideloper/TrustedProxy/issues",
+                "source": "https://github.com/fideloper/TrustedProxy/tree/4.4.1"
+            },
+            "time": "2020-10-22T13:48:01+00:00"
+        },
+        {
+            "name": "filp/whoops",
+            "version": "2.9.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/filp/whoops.git",
+                "reference": "df7933820090489623ce0be5e85c7e693638e536"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/filp/whoops/zipball/df7933820090489623ce0be5e85c7e693638e536",
+                "reference": "df7933820090489623ce0be5e85c7e693638e536",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9 || ^7.0 || ^8.0",
+                "psr/log": "^1.0.1"
+            },
+            "require-dev": {
+                "mockery/mockery": "^0.9 || ^1.0",
+                "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
+                "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
+            },
+            "suggest": {
+                "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
+                "whoops/soap": "Formats errors as SOAP responses"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.7-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Whoops\\": "src/Whoops/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Filipe Dobreira",
+                    "homepage": "https://github.com/filp",
+                    "role": "Developer"
+                }
+            ],
+            "description": "php error handling for cool kids",
+            "homepage": "https://filp.github.io/whoops/",
+            "keywords": [
+                "error",
+                "exception",
+                "handling",
+                "library",
+                "throwable",
+                "whoops"
+            ],
+            "support": {
+                "issues": "https://github.com/filp/whoops/issues",
+                "source": "https://github.com/filp/whoops/tree/2.9.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/denis-sokolov",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-01-24T12:00:00+00:00"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "7.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0aa74dfb41ae110835923ef10a9d803a22d50e79",
+                "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "guzzlehttp/promises": "^1.4",
+                "guzzlehttp/psr7": "^1.7",
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-client": "^1.0"
+            },
+            "provide": {
+                "psr/http-client-implementation": "1.0"
             },
             "require-dev": {
                 "ext-curl": "*",
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
-                "psr/log": "^1.0"
+                "php-http/client-integration-tests": "^3.0",
+                "phpunit/phpunit": "^8.5.5 || ^9.3.5",
+                "psr/log": "^1.1"
             },
             "suggest": {
+                "ext-curl": "Required for CURL handler support",
+                "ext-intl": "Required for Internationalized Domain Name (IDN) support",
                 "psr/log": "Required for using the Log middleware"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "6.3-dev"
+                    "dev-master": "7.1-dev"
                 }
             },
             "autoload": {
-                "files": [
-                    "src/functions_include.php"
-                ],
                 "psr-4": {
                     "GuzzleHttp\\": "src/"
-                }
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
                     "name": "Michael Dowling",
                     "email": "[email protected]",
                     "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "[email protected]",
+                    "homepage": "https://sagikazarmark.hu"
                 }
             ],
             "description": "Guzzle is a PHP HTTP client library",
                 "framework",
                 "http",
                 "http client",
+                "psr-18",
+                "psr-7",
                 "rest",
                 "web service"
             ],
-            "time": "2018-04-22T15:46:56+00:00"
+            "support": {
+                "issues": "https://github.com/guzzle/guzzle/issues",
+                "source": "https://github.com/guzzle/guzzle/tree/7.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/alexeyshockov",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/gmponos",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-10T11:47:56+00:00"
         },
         {
             "name": "guzzlehttp/promises",
-            "version": "v1.3.1",
+            "version": "1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/promises.git",
-                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
+                "reference": "60d379c243457e073cff02bc323a2a86cb355631"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
-                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631",
+                "reference": "60d379c243457e073cff02bc323a2a86cb355631",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5.0"
+                "php": ">=5.5"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.0"
+                "symfony/phpunit-bridge": "^4.4 || ^5.1"
             },
             "type": "library",
             "extra": {
             "keywords": [
                 "promise"
             ],
-            "time": "2016-12-20T10:07:11+00:00"
+            "support": {
+                "issues": "https://github.com/guzzle/promises/issues",
+                "source": "https://github.com/guzzle/promises/tree/1.4.0"
+            },
+            "time": "2020-09-30T07:37:28+00:00"
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "1.6.1",
+            "version": "1.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "239400de7a173fe9901b9ac7c06497751f00727a"
+                "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
-                "reference": "239400de7a173fe9901b9ac7c06497751f00727a",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3",
+                "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3",
                 "shasum": ""
             },
             "require": {
             },
             "require-dev": {
                 "ext-zlib": "*",
-                "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
+                "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
             },
             "suggest": {
-                "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
+                "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.6-dev"
+                    "dev-master": "1.7-dev"
                 }
             },
             "autoload": {
                 "uri",
                 "url"
             ],
-            "time": "2019-07-01T23:21:34+00:00"
+            "support": {
+                "issues": "https://github.com/guzzle/psr7/issues",
+                "source": "https://github.com/guzzle/psr7/tree/1.7.0"
+            },
+            "time": "2020-09-30T07:37:11+00:00"
         },
         {
             "name": "intervention/image",
-            "version": "2.5.0",
+            "version": "2.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Intervention/image.git",
-                "reference": "39eaef720d082ecc54c64bf54541c55f10db546d"
+                "reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Intervention/image/zipball/39eaef720d082ecc54c64bf54541c55f10db546d",
-                "reference": "39eaef720d082ecc54c64bf54541c55f10db546d",
+                "url": "https://api.github.com/repos/Intervention/image/zipball/abbf18d5ab8367f96b3205ca3c89fb2fa598c69e",
+                "reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e",
                 "shasum": ""
             },
             "require": {
                 "thumbnail",
                 "watermark"
             ],
-            "time": "2019-06-24T14:06:31+00:00"
+            "support": {
+                "issues": "https://github.com/Intervention/image/issues",
+                "source": "https://github.com/Intervention/image/tree/master"
+            },
+            "time": "2019-11-02T09:15:47+00:00"
+        },
+        {
+            "name": "jakub-onderka/php-console-color",
+            "version": "v0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/JakubOnderka/PHP-Console-Color.git",
+                "reference": "d5deaecff52a0d61ccb613bb3804088da0307191"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Color/zipball/d5deaecff52a0d61ccb613bb3804088da0307191",
+                "reference": "d5deaecff52a0d61ccb613bb3804088da0307191",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "jakub-onderka/php-code-style": "1.0",
+                "jakub-onderka/php-parallel-lint": "1.0",
+                "jakub-onderka/php-var-dump-check": "0.*",
+                "phpunit/phpunit": "~4.3",
+                "squizlabs/php_codesniffer": "1.*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "JakubOnderka\\PhpConsoleColor\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jakub Onderka",
+                    "email": "[email protected]"
+                }
+            ],
+            "support": {
+                "issues": "https://github.com/JakubOnderka/PHP-Console-Color/issues",
+                "source": "https://github.com/JakubOnderka/PHP-Console-Color/tree/master"
+            },
+            "abandoned": "php-parallel-lint/php-console-color",
+            "time": "2018-09-29T17:23:10+00:00"
+        },
+        {
+            "name": "jakub-onderka/php-console-highlighter",
+            "version": "v0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git",
+                "reference": "9f7a229a69d52506914b4bc61bfdb199d90c5547"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/9f7a229a69d52506914b4bc61bfdb199d90c5547",
+                "reference": "9f7a229a69d52506914b4bc61bfdb199d90c5547",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "jakub-onderka/php-console-color": "~0.2",
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "jakub-onderka/php-code-style": "~1.0",
+                "jakub-onderka/php-parallel-lint": "~1.0",
+                "jakub-onderka/php-var-dump-check": "~0.1",
+                "phpunit/phpunit": "~4.0",
+                "squizlabs/php_codesniffer": "~1.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "JakubOnderka\\PhpConsoleHighlighter\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jakub Onderka",
+                    "email": "[email protected]",
+                    "homepage": "http://www.acci.cz/"
+                }
+            ],
+            "description": "Highlight PHP code in terminal",
+            "support": {
+                "issues": "https://github.com/JakubOnderka/PHP-Console-Highlighter/issues",
+                "source": "https://github.com/JakubOnderka/PHP-Console-Highlighter/tree/master"
+            },
+            "abandoned": "php-parallel-lint/php-console-highlighter",
+            "time": "2018-09-29T18:48:56+00:00"
         },
         {
             "name": "knplabs/knp-snappy",
-            "version": "v1.1.0",
+            "version": "v1.2.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/KnpLabs/snappy.git",
-                "reference": "ea037298d3c613454da77ecb9588cf0397d695e1"
+                "reference": "7bac60fb729147b7ccd8532c07df3f52a4afa8a4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/KnpLabs/snappy/zipball/ea037298d3c613454da77ecb9588cf0397d695e1",
-                "reference": "ea037298d3c613454da77ecb9588cf0397d695e1",
+                "url": "https://api.github.com/repos/KnpLabs/snappy/zipball/7bac60fb729147b7ccd8532c07df3f52a4afa8a4",
+                "reference": "7bac60fb729147b7ccd8532c07df3f52a4afa8a4",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.1",
                 "psr/log": "^1.0",
-                "symfony/process": "~3.4||~4.1"
+                "symfony/process": "~3.4||~4.3||~5.0"
             },
             "require-dev": {
                 "phpunit/phpunit": "~7.4"
                 "thumbnail",
                 "wkhtmltopdf"
             ],
-            "time": "2018-12-14T14:59:37+00:00"
+            "support": {
+                "issues": "https://github.com/KnpLabs/snappy/issues",
+                "source": "https://github.com/KnpLabs/snappy/tree/master"
+            },
+            "time": "2020-01-20T08:30:30+00:00"
         },
         {
             "name": "laravel/framework",
-            "version": "v6.0.3",
+            "version": "v6.20.15",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "56789e9dec750e0fbe8e9e6ae90a01a4e6887902"
+                "reference": "b42c2d845cdd827ac5a53cacf16af4a0b5dd8be1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/56789e9dec750e0fbe8e9e6ae90a01a4e6887902",
-                "reference": "56789e9dec750e0fbe8e9e6ae90a01a4e6887902",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/b42c2d845cdd827ac5a53cacf16af4a0b5dd8be1",
+                "reference": "b42c2d845cdd827ac5a53cacf16af4a0b5dd8be1",
                 "shasum": ""
             },
             "require": {
-                "doctrine/inflector": "^1.1",
-                "dragonmantank/cron-expression": "^2.0",
+                "doctrine/inflector": "^1.4|^2.0",
+                "dragonmantank/cron-expression": "^2.3.1",
                 "egulias/email-validator": "^2.1.10",
-                "erusev/parsedown": "^1.7",
                 "ext-json": "*",
                 "ext-mbstring": "*",
                 "ext-openssl": "*",
-                "league/flysystem": "^1.0.8",
+                "league/commonmark": "^1.3",
+                "league/flysystem": "^1.1",
                 "monolog/monolog": "^1.12|^2.0",
-                "nesbot/carbon": "^2.0",
-                "opis/closure": "^3.1",
-                "php": "^7.2",
+                "nesbot/carbon": "^2.31",
+                "opis/closure": "^3.6",
+                "php": "^7.2.5|^8.0",
                 "psr/container": "^1.0",
                 "psr/simple-cache": "^1.0",
                 "ramsey/uuid": "^3.7",
                 "symfony/finder": "^4.3.4",
                 "symfony/http-foundation": "^4.3.4",
                 "symfony/http-kernel": "^4.3.4",
+                "symfony/polyfill-php73": "^1.17",
                 "symfony/process": "^4.3.4",
                 "symfony/routing": "^4.3.4",
                 "symfony/var-dumper": "^4.3.4",
                 "illuminate/view": "self.version"
             },
             "require-dev": {
-                "aws/aws-sdk-php": "^3.0",
+                "aws/aws-sdk-php": "^3.155",
                 "doctrine/dbal": "^2.6",
-                "filp/whoops": "^2.4",
-                "guzzlehttp/guzzle": "^6.3",
+                "filp/whoops": "^2.8",
+                "guzzlehttp/guzzle": "^6.3.1|^7.0.1",
                 "league/flysystem-cached-adapter": "^1.0",
-                "mockery/mockery": "^1.2.3",
+                "mockery/mockery": "~1.3.3|^1.4.2",
                 "moontoast/math": "^1.1",
-                "orchestra/testbench-core": "^4.0",
+                "orchestra/testbench-core": "^4.8",
                 "pda/pheanstalk": "^4.0",
-                "phpunit/phpunit": "^8.3",
+                "phpunit/phpunit": "^7.5.15|^8.4|^9.3.3",
                 "predis/predis": "^1.1.1",
-                "symfony/cache": "^4.3",
-                "true/punycode": "^2.1"
+                "symfony/cache": "^4.3.4"
             },
             "suggest": {
-                "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.0).",
+                "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.155).",
                 "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6).",
+                "ext-ftp": "Required to use the Flysystem FTP driver.",
                 "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
                 "ext-memcached": "Required to use the memcache cache driver.",
                 "ext-pcntl": "Required to use all features of the queue worker.",
                 "ext-posix": "Required to use all features of the queue worker.",
-                "ext-redis": "Required to use the Redis cache and queue drivers.",
-                "filp/whoops": "Required for friendly error pages in development (^2.4).",
-                "fzaninotto/faker": "Required to use the eloquent factory builder (^1.4).",
-                "guzzlehttp/guzzle": "Required to use the Mailgun mail driver and the ping methods on schedules (^6.0).",
-                "laravel/tinker": "Required to use the tinker console command (^1.0).",
+                "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).",
+                "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
+                "filp/whoops": "Required for friendly error pages in development (^2.8).",
+                "guzzlehttp/guzzle": "Required to use the Mailgun mail driver and the ping methods on schedules (^6.3.1|^7.0.1).",
+                "laravel/tinker": "Required to use the tinker console command (^2.0).",
                 "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).",
                 "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).",
                 "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
                 "moontoast/math": "Required to use ordered UUIDs (^1.1).",
+                "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
                 "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
-                "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^3.0).",
+                "predis/predis": "Required to use the predis connector (^1.1.2).",
+                "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
+                "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0).",
                 "symfony/cache": "Required to PSR-6 cache bridge (^4.3.4).",
                 "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^1.2).",
                 "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)."
                 "framework",
                 "laravel"
             ],
-            "time": "2019-09-10T18:46:24+00:00"
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "time": "2021-01-26T14:39:47+00:00"
         },
         {
             "name": "laravel/socialite",
-            "version": "v4.2.0",
+            "version": "v5.1.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/socialite.git",
-                "reference": "f509d06e1e7323997b804c5152874f8aad4508e9"
+                "reference": "2e6beafe911a09f2300353c102d882e9d63f1f72"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/socialite/zipball/f509d06e1e7323997b804c5152874f8aad4508e9",
-                "reference": "f509d06e1e7323997b804c5152874f8aad4508e9",
+                "url": "https://api.github.com/repos/laravel/socialite/zipball/2e6beafe911a09f2300353c102d882e9d63f1f72",
+                "reference": "2e6beafe911a09f2300353c102d882e9d63f1f72",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
-                "guzzlehttp/guzzle": "~6.0",
-                "illuminate/http": "~5.7.0|~5.8.0|^6.0|^7.0",
-                "illuminate/support": "~5.7.0|~5.8.0|^6.0|^7.0",
-                "league/oauth1-client": "~1.0",
-                "php": "^7.1.3"
+                "guzzlehttp/guzzle": "^6.0|^7.0",
+                "illuminate/http": "^6.0|^7.0|^8.0",
+                "illuminate/support": "^6.0|^7.0|^8.0",
+                "league/oauth1-client": "^1.0",
+                "php": "^7.2|^8.0"
             },
             "require-dev": {
-                "illuminate/contracts": "~5.7.0|~5.8.0|^6.0|^7.0",
+                "illuminate/contracts": "^6.0|^7.0",
                 "mockery/mockery": "^1.0",
-                "phpunit/phpunit": "^7.0|^8.0"
+                "orchestra/testbench": "^4.0|^5.0|^6.0",
+                "phpunit/phpunit": "^8.0|^9.3"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.0-dev"
+                    "dev-master": "5.x-dev"
                 },
                 "laravel": {
                     "providers": [
                 "laravel",
                 "oauth"
             ],
-            "time": "2019-09-03T15:27:17+00:00"
+            "support": {
+                "issues": "https://github.com/laravel/socialite/issues",
+                "source": "https://github.com/laravel/socialite"
+            },
+            "time": "2021-01-05T17:02:09+00:00"
         },
         {
-            "name": "league/flysystem",
-            "version": "1.0.55",
+            "name": "league/commonmark",
+            "version": "1.5.7",
             "source": {
                 "type": "git",
-                "url": "https://github.com/thephpleague/flysystem.git",
-                "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6"
+                "url": "https://github.com/thephpleague/commonmark.git",
+                "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/33c91155537c6dc899eacdc54a13ac6303f156e6",
-                "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6",
+                "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/11df9b36fd4f1d2b727a73bf14931d81373b9a54",
+                "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54",
                 "shasum": ""
             },
             "require": {
-                "ext-fileinfo": "*",
-                "php": ">=5.5.9"
+                "ext-mbstring": "*",
+                "php": "^7.1 || ^8.0"
             },
             "conflict": {
-                "league/flysystem-sftp": "<1.0.6"
+                "scrutinizer/ocular": "1.7.*"
             },
             "require-dev": {
-                "phpspec/phpspec": "^3.4",
-                "phpunit/phpunit": "^5.7.10"
-            },
-            "suggest": {
-                "ext-fileinfo": "Required for MimeType",
-                "ext-ftp": "Allows you to use FTP server storage",
-                "ext-openssl": "Allows you to use FTPS server storage",
-                "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
-                "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
-                "league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
-                "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
-                "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
-                "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
-                "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
-                "league/flysystem-webdav": "Allows you to use WebDAV storage",
-                "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
-                "spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
-                "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
+                "cebe/markdown": "~1.0",
+                "commonmark/commonmark.js": "0.29.2",
+                "erusev/parsedown": "~1.0",
+                "ext-json": "*",
+                "github/gfm": "0.29.0",
+                "michelf/php-markdown": "~1.4",
+                "mikehaertl/php-shellcommand": "^1.4",
+                "phpstan/phpstan": "^0.12",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.2",
+                "scrutinizer/ocular": "^1.5",
+                "symfony/finder": "^4.2"
+            },
+            "bin": [
+                "bin/commonmark"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\CommonMark\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Colin O'Dell",
+                    "email": "[email protected]",
+                    "homepage": "https://www.colinodell.com",
+                    "role": "Lead Developer"
+                }
+            ],
+            "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and Github-Flavored Markdown (GFM)",
+            "homepage": "https://commonmark.thephpleague.com",
+            "keywords": [
+                "commonmark",
+                "flavored",
+                "gfm",
+                "github",
+                "github-flavored",
+                "markdown",
+                "md",
+                "parser"
+            ],
+            "support": {
+                "docs": "https://commonmark.thephpleague.com/",
+                "issues": "https://github.com/thephpleague/commonmark/issues",
+                "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+                "source": "https://github.com/thephpleague/commonmark"
+            },
+            "funding": [
+                {
+                    "url": "https://enjoy.gitstore.app/repositories/thephpleague/commonmark",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.colinodell.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.paypal.me/colinpodell/10.00",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/colinodell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/colinodell",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-10-31T13:49:32+00:00"
+        },
+        {
+            "name": "league/flysystem",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/flysystem.git",
+                "reference": "9be3b16c877d477357c015cec057548cf9b2a14a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a",
+                "reference": "9be3b16c877d477357c015cec057548cf9b2a14a",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "league/mime-type-detection": "^1.3",
+                "php": "^7.2.5 || ^8.0"
+            },
+            "conflict": {
+                "league/flysystem-sftp": "<1.0.6"
+            },
+            "require-dev": {
+                "phpspec/prophecy": "^1.11.1",
+                "phpunit/phpunit": "^8.5.8"
+            },
+            "suggest": {
+                "ext-fileinfo": "Required for MimeType",
+                "ext-ftp": "Allows you to use FTP server storage",
+                "ext-openssl": "Allows you to use FTPS server storage",
+                "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
+                "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
+                "league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
+                "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
+                "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
+                "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
+                "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
+                "league/flysystem-webdav": "Allows you to use WebDAV storage",
+                "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
+                "spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
+                "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
             },
             "type": "library",
             "extra": {
                 "sftp",
                 "storage"
             ],
-            "time": "2019-08-24T11:17:19+00:00"
+            "support": {
+                "issues": "https://github.com/thephpleague/flysystem/issues",
+                "source": "https://github.com/thephpleague/flysystem/tree/1.x"
+            },
+            "funding": [
+                {
+                    "url": "https://offset.earth/frankdejonge",
+                    "type": "other"
+                }
+            ],
+            "time": "2020-08-23T07:39:11+00:00"
         },
         {
             "name": "league/flysystem-aws-s3-v3",
-            "version": "1.0.23",
+            "version": "1.0.29",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
-                "reference": "15b0cdeab7240bf8e8bffa85ae5275bbc3692bf4"
+                "reference": "4e25cc0582a36a786c31115e419c6e40498f6972"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/15b0cdeab7240bf8e8bffa85ae5275bbc3692bf4",
-                "reference": "15b0cdeab7240bf8e8bffa85ae5275bbc3692bf4",
+                "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4e25cc0582a36a786c31115e419c6e40498f6972",
+                "reference": "4e25cc0582a36a786c31115e419c6e40498f6972",
                 "shasum": ""
             },
             "require": {
-                "aws/aws-sdk-php": "^3.0.0",
+                "aws/aws-sdk-php": "^3.20.0",
                 "league/flysystem": "^1.0.40",
                 "php": ">=5.5.0"
             },
                 }
             ],
             "description": "Flysystem adapter for the AWS S3 SDK v3.x",
-            "time": "2019-06-05T17:18:29+00:00"
+            "support": {
+                "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
+                "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/1.0.29"
+            },
+            "time": "2020-10-08T18:58:37+00:00"
         },
         {
-            "name": "league/oauth1-client",
+            "name": "league/mime-type-detection",
             "version": "1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/mime-type-detection.git",
+                "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3",
+                "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.18",
+                "phpstan/phpstan": "^0.12.68",
+                "phpunit/phpunit": "^8.5.8 || ^9.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\MimeTypeDetection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "[email protected]"
+                }
+            ],
+            "description": "Mime-type detection for Flysystem",
+            "support": {
+                "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+                "source": "https://github.com/thephpleague/mime-type-detection/tree/1.7.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/frankdejonge",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-18T20:58:21+00:00"
+        },
+        {
+            "name": "league/oauth1-client",
+            "version": "v1.9.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/oauth1-client.git",
-                "reference": "fca5f160650cb74d23fc11aa570dd61f86dcf647"
+                "reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/fca5f160650cb74d23fc11aa570dd61f86dcf647",
-                "reference": "fca5f160650cb74d23fc11aa570dd61f86dcf647",
+                "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
+                "reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
                 "shasum": ""
             },
             "require": {
-                "guzzlehttp/guzzle": "^6.0",
-                "php": ">=5.5.0"
+                "ext-json": "*",
+                "ext-openssl": "*",
+                "guzzlehttp/guzzle": "^6.0|^7.0",
+                "php": ">=7.1||>=8.0"
             },
             "require-dev": {
-                "mockery/mockery": "^0.9",
-                "phpunit/phpunit": "^4.0",
-                "squizlabs/php_codesniffer": "^2.0"
+                "ext-simplexml": "*",
+                "friendsofphp/php-cs-fixer": "^2.17",
+                "mockery/mockery": "^1.3.3",
+                "phpstan/phpstan": "^0.12.42",
+                "phpunit/phpunit": "^7.5||9.5"
+            },
+            "suggest": {
+                "ext-simplexml": "For decoding XML-based responses."
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0-dev"
+                    "dev-master": "1.0-dev",
+                    "dev-develop": "2.0-dev"
                 }
             },
             "autoload": {
                 "psr-4": {
-                    "League\\OAuth1\\": "src/"
+                    "League\\OAuth1\\Client\\": "src/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
                 "tumblr",
                 "twitter"
             ],
-            "time": "2016-08-17T00:36:58+00:00"
+            "support": {
+                "issues": "https://github.com/thephpleague/oauth1-client/issues",
+                "source": "https://github.com/thephpleague/oauth1-client/tree/v1.9.0"
+            },
+            "time": "2021-01-20T01:40:53+00:00"
         },
         {
             "name": "monolog/monolog",
-            "version": "2.0.0",
+            "version": "2.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Seldaek/monolog.git",
-                "reference": "68545165e19249013afd1d6f7485aecff07a2d22"
+                "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/68545165e19249013afd1d6f7485aecff07a2d22",
-                "reference": "68545165e19249013afd1d6f7485aecff07a2d22",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1cb1cde8e8dd0f70cc0fe51354a59acad9302084",
+                "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2",
+                "php": ">=7.2",
                 "psr/log": "^1.0.1"
             },
             "provide": {
             "require-dev": {
                 "aws/aws-sdk-php": "^2.4.9 || ^3.0",
                 "doctrine/couchdb": "~1.0@dev",
-                "elasticsearch/elasticsearch": "^6.0",
+                "elasticsearch/elasticsearch": "^7",
                 "graylog2/gelf-php": "^1.4.2",
-                "jakub-onderka/php-parallel-lint": "^0.9",
+                "mongodb/mongodb": "^1.8",
                 "php-amqplib/php-amqplib": "~2.4",
                 "php-console/php-console": "^3.1.3",
                 "phpspec/prophecy": "^1.6.1",
-                "phpunit/phpunit": "^8.3",
+                "phpstan/phpstan": "^0.12.59",
+                "phpunit/phpunit": "^8.5",
                 "predis/predis": "^1.1",
                 "rollbar/rollbar": "^1.3",
-                "ruflin/elastica": ">=0.90 <3.0",
+                "ruflin/elastica": ">=0.90 <7.0.1",
                 "swiftmailer/swiftmailer": "^5.3|^6.0"
             },
             "suggest": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.x-dev"
+                    "dev-main": "2.x-dev"
                 }
             },
             "autoload": {
                 {
                     "name": "Jordi Boggiano",
                     "email": "[email protected]",
-                    "homepage": "http://seld.be"
+                    "homepage": "https://seld.be"
                 }
             ],
             "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
-            "homepage": "http://github.com/Seldaek/monolog",
+            "homepage": "https://github.com/Seldaek/monolog",
             "keywords": [
                 "log",
                 "logging",
                 "psr-3"
             ],
-            "time": "2019-08-30T09:56:44+00:00"
+            "support": {
+                "issues": "https://github.com/Seldaek/monolog/issues",
+                "source": "https://github.com/Seldaek/monolog/tree/2.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/Seldaek",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-12-14T13:15:25+00:00"
         },
         {
             "name": "mtdowling/jmespath.php",
-            "version": "2.4.0",
+            "version": "2.6.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/jmespath/jmespath.php.git",
-                "reference": "adcc9531682cf87dfda21e1fd5d0e7a41d292fac"
+                "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/adcc9531682cf87dfda21e1fd5d0e7a41d292fac",
-                "reference": "adcc9531682cf87dfda21e1fd5d0e7a41d292fac",
+                "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/42dae2cbd13154083ca6d70099692fef8ca84bfb",
+                "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.4.0"
+                "php": "^5.4 || ^7.0 || ^8.0",
+                "symfony/polyfill-mbstring": "^1.17"
             },
             "require-dev": {
-                "phpunit/phpunit": "~4.0"
+                "composer/xdebug-handler": "^1.4",
+                "phpunit/phpunit": "^4.8.36 || ^7.5.15"
             },
             "bin": [
                 "bin/jp.php"
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.0-dev"
+                    "dev-master": "2.6-dev"
                 }
             },
             "autoload": {
                 "json",
                 "jsonpath"
             ],
-            "time": "2016-12-03T22:08:25+00:00"
+            "support": {
+                "issues": "https://github.com/jmespath/jmespath.php/issues",
+                "source": "https://github.com/jmespath/jmespath.php/tree/2.6.0"
+            },
+            "time": "2020-07-31T21:01:56+00:00"
         },
         {
             "name": "nesbot/carbon",
-            "version": "2.24.0",
+            "version": "2.44.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29"
+                "reference": "e6ef33cb1f67a4bed831ed6d0f7e156739a5d8cd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/934459c5ac0658bc765ad1e53512c7c77adcac29",
-                "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e6ef33cb1f67a4bed831ed6d0f7e156739a5d8cd",
+                "reference": "e6ef33cb1f67a4bed831ed6d0f7e156739a5d8cd",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "php": "^7.1.8 || ^8.0",
-                "symfony/translation": "^3.4 || ^4.0"
+                "symfony/polyfill-mbstring": "^1.0",
+                "symfony/translation": "^3.4 || ^4.0 || ^5.0"
             },
             "require-dev": {
+                "doctrine/orm": "^2.7",
                 "friendsofphp/php-cs-fixer": "^2.14 || ^3.0",
-                "kylekatarnls/multi-tester": "^1.1",
-                "phpmd/phpmd": "dev-php-7.1-compatibility",
-                "phpstan/phpstan": "^0.11",
-                "phpunit/phpunit": "^7.5 || ^8.0",
+                "kylekatarnls/multi-tester": "^2.0",
+                "phpmd/phpmd": "^2.9",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^0.12.54",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.14",
                 "squizlabs/php_codesniffer": "^3.4"
             },
             "bin": [
             ],
             "type": "library",
             "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev",
+                    "dev-3.x": "3.x-dev"
+                },
                 "laravel": {
                     "providers": [
                         "Carbon\\Laravel\\ServiceProvider"
                     ]
+                },
+                "phpstan": {
+                    "includes": [
+                        "extension.neon"
+                    ]
                 }
             },
             "autoload": {
                     "homepage": "http://github.com/kylekatarnls"
                 }
             ],
-            "description": "A API extension for DateTime that supports 281 different languages.",
+            "description": "An API extension for DateTime that supports 281 different languages.",
             "homepage": "http://carbon.nesbot.com",
             "keywords": [
                 "date",
                 "datetime",
                 "time"
             ],
-            "time": "2019-08-31T16:37:55+00:00"
+            "support": {
+                "issues": "https://github.com/briannesbitt/Carbon/issues",
+                "source": "https://github.com/briannesbitt/Carbon"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/Carbon",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-26T20:46:41+00:00"
+        },
+        {
+            "name": "nunomaduro/collision",
+            "version": "v3.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nunomaduro/collision.git",
+                "reference": "88b58b5bd9bdcc54756480fb3ce87234696544ee"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nunomaduro/collision/zipball/88b58b5bd9bdcc54756480fb3ce87234696544ee",
+                "reference": "88b58b5bd9bdcc54756480fb3ce87234696544ee",
+                "shasum": ""
+            },
+            "require": {
+                "filp/whoops": "^2.1.4",
+                "jakub-onderka/php-console-highlighter": "0.3.*|0.4.*",
+                "php": "^7.1 || ^8.0",
+                "symfony/console": "~2.8|~3.3|~4.0"
+            },
+            "require-dev": {
+                "laravel/framework": "^6.0",
+                "phpunit/phpunit": "^8.0 || ^9.0"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "NunoMaduro\\Collision\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nuno Maduro",
+                    "email": "[email protected]"
+                }
+            ],
+            "description": "Cli error handling for console/command-line PHP applications.",
+            "keywords": [
+                "artisan",
+                "cli",
+                "command-line",
+                "console",
+                "error",
+                "handling",
+                "laravel",
+                "laravel-zero",
+                "php",
+                "symfony"
+            ],
+            "support": {
+                "issues": "https://github.com/nunomaduro/collision/issues",
+                "source": "https://github.com/nunomaduro/collision"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/nunomaduro",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/nunomaduro",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2020-10-29T16:05:21+00:00"
+        },
+        {
+            "name": "onelogin/php-saml",
+            "version": "3.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/onelogin/php-saml.git",
+                "reference": "593aca859b67d607923fe50d8ad7315373f5b6dd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/onelogin/php-saml/zipball/593aca859b67d607923fe50d8ad7315373f5b6dd",
+                "reference": "593aca859b67d607923fe50d8ad7315373f5b6dd",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4",
+                "robrichards/xmlseclibs": ">=3.1.1"
+            },
+            "require-dev": {
+                "pdepend/pdepend": "^2.5.0",
+                "php-coveralls/php-coveralls": "^1.0.2 || ^2.0",
+                "phploc/phploc": "^2.1 || ^3.0 || ^4.0",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1",
+                "sebastian/phpcpd": "^2.0 || ^3.0 || ^4.0",
+                "squizlabs/php_codesniffer": "^3.1.1"
+            },
+            "suggest": {
+                "ext-curl": "Install curl lib to be able to use the IdPMetadataParser for parsing remote XMLs",
+                "ext-gettext": "Install gettext and php5-gettext libs to handle translations",
+                "ext-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "OneLogin\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "OneLogin PHP SAML Toolkit",
+            "homepage": "https://developers.onelogin.com/saml/php",
+            "keywords": [
+                "SAML2",
+                "onelogin",
+                "saml"
+            ],
+            "support": {
+                "email": "[email protected]",
+                "issues": "https://github.com/onelogin/php-saml/issues",
+                "source": "https://github.com/onelogin/php-saml/"
+            },
+            "time": "2020-12-03T20:08:41+00:00"
         },
         {
             "name": "opis/closure",
-            "version": "3.4.0",
+            "version": "3.6.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/opis/closure.git",
-                "reference": "60a97fff133b1669a5b1776aa8ab06db3f3962b7"
+                "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/opis/closure/zipball/60a97fff133b1669a5b1776aa8ab06db3f3962b7",
-                "reference": "60a97fff133b1669a5b1776aa8ab06db3f3962b7",
+                "url": "https://api.github.com/repos/opis/closure/zipball/943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
+                "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.4 || ^7.0"
+                "php": "^5.4 || ^7.0 || ^8.0"
             },
             "require-dev": {
                 "jeremeamia/superclosure": "^2.0",
-                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.3.x-dev"
+                    "dev-master": "3.6.x-dev"
                 }
             },
             "autoload": {
                 "serialization",
                 "serialize"
             ],
-            "time": "2019-09-02T21:07:33+00:00"
+            "support": {
+                "issues": "https://github.com/opis/closure/issues",
+                "source": "https://github.com/opis/closure/tree/3.6.1"
+            },
+            "time": "2020-11-07T02:01:34+00:00"
         },
         {
             "name": "paragonie/random_compat",
                 "pseudorandom",
                 "random"
             ],
+            "support": {
+                "email": "[email protected]",
+                "issues": "https://github.com/paragonie/random_compat/issues",
+                "source": "https://github.com/paragonie/random_compat"
+            },
             "time": "2018-07-02T15:55:56+00:00"
         },
         {
             "name": "phenx/php-font-lib",
-            "version": "0.5.1",
+            "version": "0.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/PhenX/php-font-lib.git",
-                "reference": "760148820110a1ae0936e5cc35851e25a938bc97"
+                "reference": "ca6ad461f032145fff5971b5985e5af9e7fa88d8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/760148820110a1ae0936e5cc35851e25a938bc97",
-                "reference": "760148820110a1ae0936e5cc35851e25a938bc97",
+                "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/ca6ad461f032145fff5971b5985e5af9e7fa88d8",
+                "reference": "ca6ad461f032145fff5971b5985e5af9e7fa88d8",
                 "shasum": ""
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8"
+                "phpunit/phpunit": "^4.8.35 || ^5 || ^6 || ^7"
             },
             "type": "library",
             "autoload": {
             ],
             "description": "A library to read, parse, export and make subsets of different types of font files.",
             "homepage": "https://github.com/PhenX/php-font-lib",
-            "time": "2017-09-13T16:14:37+00:00"
+            "support": {
+                "issues": "https://github.com/PhenX/php-font-lib/issues",
+                "source": "https://github.com/PhenX/php-font-lib/tree/0.5.2"
+            },
+            "time": "2020-03-08T15:31:32+00:00"
         },
         {
             "name": "phenx/php-svg-lib",
             ],
             "description": "A library to read, parse and export to PDF SVG files.",
             "homepage": "https://github.com/PhenX/php-svg-lib",
+            "support": {
+                "issues": "https://github.com/PhenX/php-svg-lib/issues",
+                "source": "https://github.com/PhenX/php-svg-lib/tree/master"
+            },
             "time": "2019-09-11T20:02:13+00:00"
         },
         {
             "name": "phpoption/phpoption",
-            "version": "1.5.0",
+            "version": "1.7.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/schmittjoh/php-option.git",
-                "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed"
+                "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed",
-                "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525",
+                "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": "^5.5.9 || ^7.0 || ^8.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "4.7.*"
+                "bamarni/composer-bin-plugin": "^1.4.1",
+                "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.3-dev"
+                    "dev-master": "1.7-dev"
                 }
             },
             "autoload": {
-                "psr-0": {
-                    "PhpOption\\": "src/"
+                "psr-4": {
+                    "PhpOption\\": "src/PhpOption/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "Apache2"
+                "Apache-2.0"
             ],
             "authors": [
                 {
                     "name": "Johannes M. Schmitt",
                     "email": "[email protected]"
+                },
+                {
+                    "name": "Graham Campbell",
+                    "email": "[email protected]"
                 }
             ],
             "description": "Option Type for PHP",
                 "php",
                 "type"
             ],
-            "time": "2015-07-25T16:39:46+00:00"
+            "support": {
+                "issues": "https://github.com/schmittjoh/php-option/issues",
+                "source": "https://github.com/schmittjoh/php-option/tree/1.7.5"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-20T17:29:33+00:00"
         },
         {
             "name": "predis/predis",
-            "version": "v1.1.1",
+            "version": "v1.1.6",
             "source": {
                 "type": "git",
-                "url": "https://github.com/nrk/predis.git",
-                "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1"
+                "url": "https://github.com/predis/predis.git",
+                "reference": "9930e933c67446962997b05201c69c2319bf26de"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1",
-                "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1",
+                "url": "https://api.github.com/repos/predis/predis/zipball/9930e933c67446962997b05201c69c2319bf26de",
+                "reference": "9930e933c67446962997b05201c69c2319bf26de",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.9"
             },
             "require-dev": {
+                "cweagans/composer-patches": "^1.6",
                 "phpunit/phpunit": "~4.8"
             },
             "suggest": {
                 "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol"
             },
             "type": "library",
+            "extra": {
+                "composer-exit-on-patch-failure": true,
+                "patches": {
+                    "phpunit/phpunit-mock-objects": {
+                        "Fix PHP 7 and 8 compatibility": "./tests/phpunit_mock_objects.patch"
+                    },
+                    "phpunit/phpunit": {
+                        "Fix PHP 7 compatibility": "./tests/phpunit_php7.patch",
+                        "Fix PHP 8 compatibility": "./tests/phpunit_php8.patch"
+                    }
+                }
+            },
             "autoload": {
                 "psr-4": {
                     "Predis\\": "src/"
                 {
                     "name": "Daniele Alessandri",
                     "email": "[email protected]",
-                    "homepage": "http://clorophilla.net"
+                    "homepage": "http://clorophilla.net",
+                    "role": "Creator & Maintainer"
+                },
+                {
+                    "name": "Till Krüss",
+                    "homepage": "https://till.im",
+                    "role": "Maintainer"
                 }
             ],
             "description": "Flexible and feature-complete Redis client for PHP and HHVM",
-            "homepage": "http://github.com/nrk/predis",
+            "homepage": "http://github.com/predis/predis",
             "keywords": [
                 "nosql",
                 "predis",
                 "redis"
             ],
-            "time": "2016-06-16T16:22:20+00:00"
-        },
-        {
-            "name": "psr/container",
+            "support": {
+                "issues": "https://github.com/predis/predis/issues",
+                "source": "https://github.com/predis/predis/tree/v1.1.6"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/tillkruss",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-11T19:18:05+00:00"
+        },
+        {
+            "name": "psr/container",
             "version": "1.0.0",
             "source": {
                 "type": "git",
                 "container-interop",
                 "psr"
             ],
+            "support": {
+                "issues": "https://github.com/php-fig/container/issues",
+                "source": "https://github.com/php-fig/container/tree/master"
+            },
             "time": "2017-02-14T16:28:37+00:00"
         },
+        {
+            "name": "psr/http-client",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-client.git",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0",
+                "psr/http-message": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Client\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP clients",
+            "homepage": "https://github.com/php-fig/http-client",
+            "keywords": [
+                "http",
+                "http-client",
+                "psr",
+                "psr-18"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-client/tree/master"
+            },
+            "time": "2020-06-29T06:28:15+00:00"
+        },
         {
             "name": "psr/http-message",
             "version": "1.0.1",
                 "request",
                 "response"
             ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/master"
+            },
             "time": "2016-08-06T14:39:51+00:00"
         },
         {
             "name": "psr/log",
-            "version": "1.1.0",
+            "version": "1.1.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/log.git",
-                "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
+                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
-                "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
+                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "1.1.x-dev"
                 }
             },
             "autoload": {
                 "psr",
                 "psr-3"
             ],
-            "time": "2018-11-20T15:27:04+00:00"
+            "support": {
+                "source": "https://github.com/php-fig/log/tree/1.1.3"
+            },
+            "time": "2020-03-23T09:12:05+00:00"
         },
         {
             "name": "psr/simple-cache",
                 "psr-16",
                 "simple-cache"
             ],
+            "support": {
+                "source": "https://github.com/php-fig/simple-cache/tree/master"
+            },
             "time": "2017-10-23T01:57:42+00:00"
         },
         {
                 }
             ],
             "description": "A polyfill for getallheaders.",
+            "support": {
+                "issues": "https://github.com/ralouphie/getallheaders/issues",
+                "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+            },
             "time": "2019-03-08T08:55:37+00:00"
         },
         {
             "name": "ramsey/uuid",
-            "version": "3.8.0",
+            "version": "3.9.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/ramsey/uuid.git",
-                "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3"
+                "reference": "7e1633a6964b48589b142d60542f9ed31bd37a92"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3",
-                "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3",
+                "url": "https://api.github.com/repos/ramsey/uuid/zipball/7e1633a6964b48589b142d60542f9ed31bd37a92",
+                "reference": "7e1633a6964b48589b142d60542f9ed31bd37a92",
                 "shasum": ""
             },
             "require": {
-                "paragonie/random_compat": "^1.0|^2.0|9.99.99",
-                "php": "^5.4 || ^7.0",
+                "ext-json": "*",
+                "paragonie/random_compat": "^1 | ^2 | 9.99.99",
+                "php": "^5.4 | ^7 | ^8",
                 "symfony/polyfill-ctype": "^1.8"
             },
             "replace": {
                 "rhumsaa/uuid": "self.version"
             },
             "require-dev": {
-                "codeception/aspect-mock": "^1.0 | ~2.0.0",
-                "doctrine/annotations": "~1.2.0",
-                "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0",
-                "ircmaxell/random-lib": "^1.1",
-                "jakub-onderka/php-parallel-lint": "^0.9.0",
-                "mockery/mockery": "^0.9.9",
+                "codeception/aspect-mock": "^1 | ^2",
+                "doctrine/annotations": "^1.2",
+                "goaop/framework": "1.0.0-alpha.2 | ^1 | ^2.1",
+                "jakub-onderka/php-parallel-lint": "^1",
+                "mockery/mockery": "^0.9.11 | ^1",
                 "moontoast/math": "^1.1",
-                "php-mock/php-mock-phpunit": "^0.3|^1.1",
-                "phpunit/phpunit": "^4.7|^5.0|^6.5",
-                "squizlabs/php_codesniffer": "^2.3"
+                "paragonie/random-lib": "^2",
+                "php-mock/php-mock-phpunit": "^0.3 | ^1.1",
+                "phpunit/phpunit": "^4.8 | ^5.4 | ^6.5",
+                "squizlabs/php_codesniffer": "^3.5"
             },
             "suggest": {
                 "ext-ctype": "Provides support for PHP Ctype functions",
                 "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator",
+                "ext-openssl": "Provides the OpenSSL extension for use with the OpenSslGenerator",
                 "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator",
-                "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
                 "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).",
+                "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
                 "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid",
                 "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
             },
             "autoload": {
                 "psr-4": {
                     "Ramsey\\Uuid\\": "src/"
-                }
+                },
+                "files": [
+                    "src/functions.php"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Ben Ramsey",
+                    "email": "[email protected]",
+                    "homepage": "https://benramsey.com"
+                },
                 {
                     "name": "Marijn Huizendveld",
                     "email": "[email protected]"
                 {
                     "name": "Thibaud Fabre",
                     "email": "[email protected]"
-                },
-                {
-                    "name": "Ben Ramsey",
-                    "email": "[email protected]",
-                    "homepage": "https://benramsey.com"
                 }
             ],
             "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).",
                 "identifier",
                 "uuid"
             ],
-            "time": "2018-07-19T23:38:55+00:00"
+            "support": {
+                "issues": "https://github.com/ramsey/uuid/issues",
+                "rss": "https://github.com/ramsey/uuid/releases.atom",
+                "source": "https://github.com/ramsey/uuid",
+                "wiki": "https://github.com/ramsey/uuid/wiki"
+            },
+            "time": "2020-02-21T04:36:14+00:00"
+        },
+        {
+            "name": "robrichards/xmlseclibs",
+            "version": "3.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/robrichards/xmlseclibs.git",
+                "reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/f8f19e58f26cdb42c54b214ff8a820760292f8df",
+                "reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df",
+                "shasum": ""
+            },
+            "require": {
+                "ext-openssl": "*",
+                "php": ">= 5.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "RobRichards\\XMLSecLibs\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "A PHP library for XML Security",
+            "homepage": "https://github.com/robrichards/xmlseclibs",
+            "keywords": [
+                "security",
+                "signature",
+                "xml",
+                "xmldsig"
+            ],
+            "support": {
+                "issues": "https://github.com/robrichards/xmlseclibs/issues",
+                "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.1"
+            },
+            "time": "2020-09-05T13:00:25+00:00"
         },
         {
             "name": "sabberworm/php-css-parser",
-            "version": "8.3.0",
+            "version": "8.3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
-                "reference": "91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f"
+                "reference": "d217848e1396ef962fb1997cf3e2421acba7f796"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f",
-                "reference": "91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f",
+                "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/d217848e1396ef962fb1997cf3e2421acba7f796",
+                "reference": "d217848e1396ef962fb1997cf3e2421acba7f796",
                 "shasum": ""
             },
             "require": {
                 "parser",
                 "stylesheet"
             ],
-            "time": "2019-02-22T07:42:52+00:00"
+            "support": {
+                "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues",
+                "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.3.1"
+            },
+            "time": "2020-06-01T09:10:00+00:00"
+        },
+        {
+            "name": "scrivo/highlight.php",
+            "version": "v9.18.1.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/scrivo/highlight.php.git",
+                "reference": "44a3d4136edb5ad8551590bf90f437db80b2d466"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/44a3d4136edb5ad8551590bf90f437db80b2d466",
+                "reference": "44a3d4136edb5ad8551590bf90f437db80b2d466",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "php": ">=5.4"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8|^5.7",
+                "sabberworm/php-css-parser": "^8.3",
+                "symfony/finder": "^2.8|^3.4",
+                "symfony/var-dumper": "^2.8|^3.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Highlight\\": "",
+                    "HighlightUtilities\\": ""
+                },
+                "files": [
+                    "HighlightUtilities/functions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Geert Bergman",
+                    "homepage": "http://www.scrivo.org/",
+                    "role": "Project Author"
+                },
+                {
+                    "name": "Vladimir Jimenez",
+                    "homepage": "https://allejo.io",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "Martin Folkers",
+                    "homepage": "https://twobrain.io",
+                    "role": "Contributor"
+                }
+            ],
+            "description": "Server side syntax highlighter that supports 185 languages. It's a PHP port of highlight.js",
+            "keywords": [
+                "code",
+                "highlight",
+                "highlight.js",
+                "highlight.php",
+                "syntax"
+            ],
+            "support": {
+                "issues": "https://github.com/scrivo/highlight.php/issues",
+                "source": "https://github.com/scrivo/highlight.php"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/allejo",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-12-22T19:20:29+00:00"
         },
         {
             "name": "socialiteproviders/discord",
-            "version": "v2.0.2",
+            "version": "4.1.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Discord.git",
-                "reference": "e0cd8895f321943b36f533e7bf21ad29bcdece9a"
+                "reference": "c6eddeb07ace7473e82d02d4db852dfacf5ef574"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Discord/zipball/e0cd8895f321943b36f533e7bf21ad29bcdece9a",
-                "reference": "e0cd8895f321943b36f533e7bf21ad29bcdece9a",
+                "url": "https://api.github.com/repos/SocialiteProviders/Discord/zipball/c6eddeb07ace7473e82d02d4db852dfacf5ef574",
+                "reference": "c6eddeb07ace7473e82d02d4db852dfacf5ef574",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.6 || ^7.0",
-                "socialiteproviders/manager": "~2.0 || ~3.0"
+                "ext-json": "*",
+                "php": "^7.2 || ^8.0",
+                "socialiteproviders/manager": "~4.0"
             },
             "type": "library",
             "autoload": {
                 }
             ],
             "description": "Discord OAuth2 Provider for Laravel Socialite",
-            "time": "2018-05-26T03:40:07+00:00"
+            "support": {
+                "source": "https://github.com/SocialiteProviders/Discord/tree/4.1.1"
+            },
+            "time": "2021-01-05T22:03:58+00:00"
         },
         {
             "name": "socialiteproviders/gitlab",
-            "version": "v3.1",
+            "version": "4.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/GitLab.git",
-                "reference": "69e537f6192ca15483e98b8662495384f44299ca"
+                "reference": "a8f67d3b02c9ee8c70c25c6728417c0eddcbbb9d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/GitLab/zipball/69e537f6192ca15483e98b8662495384f44299ca",
-                "reference": "69e537f6192ca15483e98b8662495384f44299ca",
+                "url": "https://api.github.com/repos/SocialiteProviders/GitLab/zipball/a8f67d3b02c9ee8c70c25c6728417c0eddcbbb9d",
+                "reference": "a8f67d3b02c9ee8c70c25c6728417c0eddcbbb9d",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.6 || ^7.0",
-                "socialiteproviders/manager": "~2.0 || ~3.0"
+                "ext-json": "*",
+                "php": "^7.2 || ^8.0",
+                "socialiteproviders/manager": "~4.0"
             },
             "type": "library",
             "autoload": {
                 }
             ],
             "description": "GitLab OAuth2 Provider for Laravel Socialite",
-            "time": "2018-06-27T05:10:32+00:00"
+            "support": {
+                "source": "https://github.com/SocialiteProviders/GitLab/tree/4.1.0"
+            },
+            "time": "2020-12-01T23:10:59+00:00"
         },
         {
             "name": "socialiteproviders/manager",
-            "version": "v3.4.2",
+            "version": "4.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Manager.git",
-                "reference": "e3e8e78b9a3060801cd008941a0894a0a0c479e1"
+                "reference": "0f5e82af0404df0080bdc5c105cef936c1711524"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/e3e8e78b9a3060801cd008941a0894a0a0c479e1",
-                "reference": "e3e8e78b9a3060801cd008941a0894a0a0c479e1",
+                "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/0f5e82af0404df0080bdc5c105cef936c1711524",
+                "reference": "0f5e82af0404df0080bdc5c105cef936c1711524",
                 "shasum": ""
             },
             "require": {
-                "illuminate/support": "~5.4|~5.7.0|~5.8.0|^6.0",
-                "laravel/socialite": "~3.0|~4.0",
-                "php": "^5.6 || ^7.0"
+                "illuminate/support": "^6.0|^7.0|^8.0",
+                "laravel/socialite": "~4.0|~5.0",
+                "php": "^7.2 || ^8.0"
             },
             "require-dev": {
-                "mockery/mockery": "^0.9.4",
-                "phpunit/phpunit": "^5.0"
+                "mockery/mockery": "^1.2",
+                "phpunit/phpunit": "^9.0"
             },
             "type": "library",
             "extra": {
                 {
                     "name": "Miguel Piedrafita",
                     "email": "[email protected]"
+                },
+                {
+                    "name": "atymic",
+                    "email": "[email protected]",
+                    "homepage": "https://atymic.dev"
                 }
             ],
             "description": "Easily add new or override built-in providers in Laravel Socialite.",
-            "time": "2019-09-09T03:07:52+00:00"
+            "homepage": "https://socialiteproviders.com/",
+            "support": {
+                "issues": "https://github.com/SocialiteProviders/Manager/issues",
+                "source": "https://github.com/SocialiteProviders/Manager/tree/4.0.1"
+            },
+            "time": "2020-12-01T23:09:06+00:00"
         },
         {
             "name": "socialiteproviders/microsoft-azure",
-            "version": "v3.0.0",
+            "version": "4.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Microsoft-Azure.git",
-                "reference": "d7a703a782eb9f7eae0db803beaa3ddec19ef372"
+                "reference": "7808764f777a01df88be9ca6b14d683e50aaf88a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/d7a703a782eb9f7eae0db803beaa3ddec19ef372",
-                "reference": "d7a703a782eb9f7eae0db803beaa3ddec19ef372",
+                "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/7808764f777a01df88be9ca6b14d683e50aaf88a",
+                "reference": "7808764f777a01df88be9ca6b14d683e50aaf88a",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.6 || ^7.0",
-                "socialiteproviders/manager": "~3.0"
+                "ext-json": "*",
+                "php": "^7.2 || ^8.0",
+                "socialiteproviders/manager": "~4.0"
             },
             "type": "library",
             "autoload": {
                 }
             ],
             "description": "Microsoft Azure OAuth2 Provider for Laravel Socialite",
-            "time": "2017-01-25T09:48:29+00:00"
+            "support": {
+                "source": "https://github.com/SocialiteProviders/Microsoft-Azure/tree/4.2.0"
+            },
+            "time": "2020-12-01T23:10:59+00:00"
         },
         {
             "name": "socialiteproviders/okta",
-            "version": "v1.0.0",
+            "version": "4.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Okta.git",
-                "reference": "dcda13432c80060cd84d4cb5f2af422d280ab895"
+                "reference": "60f88b8e8c88508889c61346af83290131b72fe7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/dcda13432c80060cd84d4cb5f2af422d280ab895",
-                "reference": "dcda13432c80060cd84d4cb5f2af422d280ab895",
+                "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/60f88b8e8c88508889c61346af83290131b72fe7",
+                "reference": "60f88b8e8c88508889c61346af83290131b72fe7",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.6 || ^7.0",
-                "socialiteproviders/manager": "~2.0 || ~3.0"
+                "ext-json": "*",
+                "php": "^7.2 || ^8.0",
+                "socialiteproviders/manager": "~4.0"
             },
             "type": "library",
             "autoload": {
                 }
             ],
             "description": "Okta OAuth2 Provider for Laravel Socialite",
-            "time": "2017-11-21T05:31:47+00:00"
+            "support": {
+                "source": "https://github.com/SocialiteProviders/Okta/tree/4.1.0"
+            },
+            "time": "2020-12-01T23:10:59+00:00"
         },
         {
             "name": "socialiteproviders/slack",
-            "version": "v3.0.3",
+            "version": "4.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Slack.git",
-                "reference": "8d5d0c0c916adf2af6b406679130441db0afc387"
+                "reference": "8efb25c71d98bedf4010a829d1e41ff9fe449bcc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/8d5d0c0c916adf2af6b406679130441db0afc387",
-                "reference": "8d5d0c0c916adf2af6b406679130441db0afc387",
+                "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/8efb25c71d98bedf4010a829d1e41ff9fe449bcc",
+                "reference": "8efb25c71d98bedf4010a829d1e41ff9fe449bcc",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.6 || ^7.0",
-                "socialiteproviders/manager": "~2.0 || ~3.0"
+                "ext-json": "*",
+                "php": "^7.2|^8.0",
+                "socialiteproviders/manager": "~4.0"
             },
             "type": "library",
             "autoload": {
                 }
             ],
             "description": "Slack OAuth2 Provider for Laravel Socialite",
-            "time": "2017-04-10T05:10:48+00:00"
+            "support": {
+                "source": "https://github.com/SocialiteProviders/Slack/tree/4.1.0"
+            },
+            "time": "2020-11-26T17:57:15+00:00"
         },
         {
             "name": "socialiteproviders/twitch",
-            "version": "v5.0.0",
+            "version": "5.3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Twitch.git",
-                "reference": "8c19b26ff24c40cc019413042a5492c5ed21a658"
+                "reference": "7accf30ae7a3139b757b4ca8f34989c09a3dbee7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/8c19b26ff24c40cc019413042a5492c5ed21a658",
-                "reference": "8c19b26ff24c40cc019413042a5492c5ed21a658",
+                "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/7accf30ae7a3139b757b4ca8f34989c09a3dbee7",
+                "reference": "7accf30ae7a3139b757b4ca8f34989c09a3dbee7",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.6 || ^7.0",
-                "socialiteproviders/manager": "~2.0 || ~3.0"
+                "ext-json": "*",
+                "php": "^7.2 || ^8.0",
+                "socialiteproviders/manager": "~4.0"
             },
             "type": "library",
             "autoload": {
                 }
             ],
             "description": "Twitch OAuth2 Provider for Laravel Socialite",
-            "time": "2018-06-20T10:59:51+00:00"
+            "support": {
+                "source": "https://github.com/SocialiteProviders/Twitch/tree/5.3.1"
+            },
+            "time": "2020-12-01T23:10:59+00:00"
+        },
+        {
+            "name": "ssddanbrown/htmldiff",
+            "version": "v1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ssddanbrown/HtmlDiff.git",
+                "reference": "f60d5cc278b60305ab980a6665f46117c5b589c0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ssddanbrown/HtmlDiff/zipball/f60d5cc278b60305ab980a6665f46117c5b589c0",
+                "reference": "f60d5cc278b60305ab980a6665f46117c5b589c0",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "php": ">=7.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5|^9.4.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Ssddanbrown\\HtmlDiff\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Dan Brown",
+                    "email": "[email protected]",
+                    "role": "Developer"
+                }
+            ],
+            "description": "HTML Content Diff Generator",
+            "homepage": "https://github.com/ssddanbrown/htmldiff",
+            "support": {
+                "issues": "https://github.com/ssddanbrown/HtmlDiff/issues",
+                "source": "https://github.com/ssddanbrown/HtmlDiff/tree/v1.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/ssddanbrown",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-01-24T18:51:30+00:00"
         },
         {
             "name": "swiftmailer/swiftmailer",
-            "version": "v6.2.1",
+            "version": "v6.2.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/swiftmailer/swiftmailer.git",
-                "reference": "5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a"
+                "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a",
-                "reference": "5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a",
+                "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/698a6a9f54d7eb321274de3ad19863802c879fb7",
+                "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7",
                 "shasum": ""
             },
             "require": {
-                "egulias/email-validator": "~2.0",
+                "egulias/email-validator": "^2.0",
                 "php": ">=7.0.0",
                 "symfony/polyfill-iconv": "^1.0",
                 "symfony/polyfill-intl-idn": "^1.10",
                 "symfony/polyfill-mbstring": "^1.0"
             },
             "require-dev": {
-                "mockery/mockery": "~0.9.1",
-                "symfony/phpunit-bridge": "^3.4.19|^4.1.8"
+                "mockery/mockery": "^1.0",
+                "symfony/phpunit-bridge": "^4.4|^5.0"
             },
             "suggest": {
-                "ext-intl": "Needed to support internationalized email addresses",
-                "true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed"
+                "ext-intl": "Needed to support internationalized email addresses"
             },
             "type": "library",
             "extra": {
                 "mail",
                 "mailer"
             ],
-            "time": "2019-04-21T09:21:45+00:00"
+            "support": {
+                "issues": "https://github.com/swiftmailer/swiftmailer/issues",
+                "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.5"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-12T09:35:59+00:00"
         },
         {
             "name": "symfony/console",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "de63799239b3881b8a08f8481b22348f77ed7b36"
+                "reference": "24026c44fc37099fa145707fecd43672831b837a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/de63799239b3881b8a08f8481b22348f77ed7b36",
-                "reference": "de63799239b3881b8a08f8481b22348f77ed7b36",
+                "url": "https://api.github.com/repos/symfony/console/zipball/24026c44fc37099fa145707fecd43672831b837a",
+                "reference": "24026c44fc37099fa145707fecd43672831b837a",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-mbstring": "~1.0",
                 "symfony/polyfill-php73": "^1.8",
-                "symfony/service-contracts": "^1.1"
+                "symfony/polyfill-php80": "^1.15",
+                "symfony/service-contracts": "^1.1|^2"
             },
             "conflict": {
                 "symfony/dependency-injection": "<3.4",
-                "symfony/event-dispatcher": "<4.3",
+                "symfony/event-dispatcher": "<4.3|>=5",
+                "symfony/lock": "<4.4",
                 "symfony/process": "<3.3"
             },
             "provide": {
             },
             "require-dev": {
                 "psr/log": "~1.0",
-                "symfony/config": "~3.4|~4.0",
-                "symfony/dependency-injection": "~3.4|~4.0",
+                "symfony/config": "^3.4|^4.0|^5.0",
+                "symfony/dependency-injection": "^3.4|^4.0|^5.0",
                 "symfony/event-dispatcher": "^4.3",
-                "symfony/lock": "~3.4|~4.0",
-                "symfony/process": "~3.4|~4.0",
-                "symfony/var-dumper": "^4.3"
+                "symfony/lock": "^4.4|^5.0",
+                "symfony/process": "^3.4|^4.0|^5.0",
+                "symfony/var-dumper": "^4.3|^5.0"
             },
             "suggest": {
                 "psr/log": "For using the console logger",
                 "symfony/process": ""
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\Console\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony Console Component",
+            "description": "Eases the creation of beautiful and testable command line interfaces",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:26:39+00:00"
+            "support": {
+                "source": "https://github.com/symfony/console/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
             "name": "symfony/css-selector",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
-                "reference": "c6e5e2a00db768c92c3ae131532af4e1acc7bd03"
+                "reference": "f907d3e53ecb2a5fad8609eb2f30525287a734c8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/css-selector/zipball/c6e5e2a00db768c92c3ae131532af4e1acc7bd03",
-                "reference": "c6e5e2a00db768c92c3ae131532af4e1acc7bd03",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/f907d3e53ecb2a5fad8609eb2f30525287a734c8",
+                "reference": "f907d3e53ecb2a5fad8609eb2f30525287a734c8",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\CssSelector\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony CssSelector Component",
+            "description": "Converts CSS selectors to XPath expressions",
             "homepage": "https://symfony.com",
-            "time": "2019-08-20T14:07:54+00:00"
+            "support": {
+                "source": "https://github.com/symfony/css-selector/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
             "name": "symfony/debug",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
-                "reference": "afcdea44a2e399c1e4b52246ec8d54c715393ced"
+                "reference": "af4987aa4a5630e9615be9d9c3ed1b0f24ca449c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/debug/zipball/afcdea44a2e399c1e4b52246ec8d54c715393ced",
-                "reference": "afcdea44a2e399c1e4b52246ec8d54c715393ced",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/af4987aa4a5630e9615be9d9c3ed1b0f24ca449c",
+                "reference": "af4987aa4a5630e9615be9d9c3ed1b0f24ca449c",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
-                "psr/log": "~1.0"
+                "php": ">=7.1.3",
+                "psr/log": "~1.0",
+                "symfony/polyfill-php80": "^1.15"
             },
             "conflict": {
                 "symfony/http-kernel": "<3.4"
             },
             "require-dev": {
-                "symfony/http-kernel": "~3.4|~4.0"
+                "symfony/http-kernel": "^3.4|^4.0|^5.0"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\Debug\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony Debug Component",
+            "description": "Provides tools to ease debugging PHP code",
             "homepage": "https://symfony.com",
-            "time": "2019-08-20T14:27:59+00:00"
+            "support": {
+                "source": "https://github.com/symfony/debug/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
-            "name": "symfony/event-dispatcher",
-            "version": "v4.3.4",
+            "name": "symfony/deprecation-contracts",
+            "version": "v2.2.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "429d0a1451d4c9c4abe1959b2986b88794b9b7d2"
+                "url": "https://github.com/symfony/deprecation-contracts.git",
+                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/429d0a1451d4c9c4abe1959b2986b88794b9b7d2",
-                "reference": "429d0a1451d4c9c4abe1959b2986b88794b9b7d2",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
-                "symfony/event-dispatcher-contracts": "^1.1"
+                "php": ">=7.1"
             },
-            "conflict": {
-                "symfony/dependency-injection": "<3.4"
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.2-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "function.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "A generic function and convention to trigger deprecation notices",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/deprecation-contracts/tree/master"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-07T11:33:47+00:00"
+        },
+        {
+            "name": "symfony/error-handler",
+            "version": "v4.4.19",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/error-handler.git",
+                "reference": "d603654eaeb713503bba3e308b9e748e5a6d3f2e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/error-handler/zipball/d603654eaeb713503bba3e308b9e748e5a6d3f2e",
+                "reference": "d603654eaeb713503bba3e308b9e748e5a6d3f2e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "psr/log": "~1.0",
+                "symfony/debug": "^4.4.5",
+                "symfony/polyfill-php80": "^1.15",
+                "symfony/var-dumper": "^4.4|^5.0"
+            },
+            "require-dev": {
+                "symfony/http-kernel": "^4.4|^5.0",
+                "symfony/serializer": "^4.4|^5.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\ErrorHandler\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools to manage errors and ease debugging PHP code",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/error-handler/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher",
+            "version": "v4.4.19",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher.git",
+                "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c352647244bd376bf7d31efbd5401f13f50dad0c",
+                "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "symfony/event-dispatcher-contracts": "^1.1"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<3.4"
             },
             "provide": {
                 "psr/event-dispatcher-implementation": "1.0",
             },
             "require-dev": {
                 "psr/log": "~1.0",
-                "symfony/config": "~3.4|~4.0",
-                "symfony/dependency-injection": "~3.4|~4.0",
-                "symfony/expression-language": "~3.4|~4.0",
-                "symfony/http-foundation": "^3.4|^4.0",
-                "symfony/service-contracts": "^1.1",
-                "symfony/stopwatch": "~3.4|~4.0"
+                "symfony/config": "^3.4|^4.0|^5.0",
+                "symfony/dependency-injection": "^3.4|^4.0|^5.0",
+                "symfony/error-handler": "~3.4|~4.4",
+                "symfony/expression-language": "^3.4|^4.0|^5.0",
+                "symfony/http-foundation": "^3.4|^4.0|^5.0",
+                "symfony/service-contracts": "^1.1|^2",
+                "symfony/stopwatch": "^3.4|^4.0|^5.0"
             },
             "suggest": {
                 "symfony/dependency-injection": "",
                 "symfony/http-kernel": ""
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\EventDispatcher\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony EventDispatcher Component",
+            "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:55:16+00:00"
+            "support": {
+                "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
             "name": "symfony/event-dispatcher-contracts",
-            "version": "v1.1.5",
+            "version": "v1.1.9",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher-contracts.git",
-                "reference": "c61766f4440ca687de1084a5c00b08e167a2575c"
+                "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c61766f4440ca687de1084a5c00b08e167a2575c",
-                "reference": "c61766f4440ca687de1084a5c00b08e167a2575c",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+                "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "suggest": {
                 "psr/event-dispatcher": "",
             "extra": {
                 "branch-alias": {
                     "dev-master": "1.1-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "autoload": {
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-06-20T06:46:26+00:00"
+            "support": {
+                "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.9"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-06T13:19:58+00:00"
         },
         {
             "name": "symfony/finder",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "86c1c929f0a4b24812e1eb109262fc3372c8e9f2"
+                "reference": "25d79cfccfc12e84e7a63a248c3f0720fdd92db6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/86c1c929f0a4b24812e1eb109262fc3372c8e9f2",
-                "reference": "86c1c929f0a4b24812e1eb109262fc3372c8e9f2",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/25d79cfccfc12e84e7a63a248c3f0720fdd92db6",
+                "reference": "25d79cfccfc12e84e7a63a248c3f0720fdd92db6",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\Finder\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony Finder Component",
+            "description": "Finds files and directories via an intuitive fluent interface",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/finder/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
+        },
+        {
+            "name": "symfony/http-client-contracts",
+            "version": "v2.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-client-contracts.git",
+                "reference": "41db680a15018f9c1d4b23516059633ce280ca33"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/41db680a15018f9c1d4b23516059633ce280ca33",
+                "reference": "41db680a15018f9c1d4b23516059633ce280ca33",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5"
+            },
+            "suggest": {
+                "symfony/http-client-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-version": "2.3",
+                "branch-alias": {
+                    "dev-main": "2.3-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\HttpClient\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to HTTP clients",
             "homepage": "https://symfony.com",
-            "time": "2019-08-14T12:26:46+00:00"
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/http-client-contracts/tree/v2.3.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-10-14T17:08:19+00:00"
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "d804bea118ff340a12e22a79f9c7e7eb56b35adc"
+                "reference": "8888741b633f6c3d1e572b7735ad2cae3e03f9c5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d804bea118ff340a12e22a79f9c7e7eb56b35adc",
-                "reference": "d804bea118ff340a12e22a79f9c7e7eb56b35adc",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8888741b633f6c3d1e572b7735ad2cae3e03f9c5",
+                "reference": "8888741b633f6c3d1e572b7735ad2cae3e03f9c5",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
-                "symfony/mime": "^4.3",
-                "symfony/polyfill-mbstring": "~1.1"
+                "php": ">=7.1.3",
+                "symfony/mime": "^4.3|^5.0",
+                "symfony/polyfill-mbstring": "~1.1",
+                "symfony/polyfill-php80": "^1.15"
             },
             "require-dev": {
                 "predis/predis": "~1.0",
-                "symfony/expression-language": "~3.4|~4.0"
+                "symfony/expression-language": "^3.4|^4.0|^5.0"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\HttpFoundation\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony HttpFoundation Component",
+            "description": "Defines an object-oriented layer for the HTTP specification",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:55:16+00:00"
+            "support": {
+                "source": "https://github.com/symfony/http-foundation/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "5e0fc71be03d52cd00c423061cfd300bd6f92a52"
+                "reference": "07ea794a327d7c8c5d76e3058fde9fec6a711cb4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/5e0fc71be03d52cd00c423061cfd300bd6f92a52",
-                "reference": "5e0fc71be03d52cd00c423061cfd300bd6f92a52",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/07ea794a327d7c8c5d76e3058fde9fec6a711cb4",
+                "reference": "07ea794a327d7c8c5d76e3058fde9fec6a711cb4",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "psr/log": "~1.0",
-                "symfony/debug": "~3.4|~4.0",
-                "symfony/event-dispatcher": "^4.3",
-                "symfony/http-foundation": "^4.1.1",
-                "symfony/polyfill-ctype": "~1.8",
-                "symfony/polyfill-php73": "^1.9"
+                "symfony/error-handler": "^4.4",
+                "symfony/event-dispatcher": "^4.4",
+                "symfony/http-client-contracts": "^1.1|^2",
+                "symfony/http-foundation": "^4.4|^5.0",
+                "symfony/polyfill-ctype": "^1.8",
+                "symfony/polyfill-php73": "^1.9",
+                "symfony/polyfill-php80": "^1.15"
             },
             "conflict": {
                 "symfony/browser-kit": "<4.3",
                 "symfony/config": "<3.4",
+                "symfony/console": ">=5",
                 "symfony/dependency-injection": "<4.3",
                 "symfony/translation": "<4.2",
-                "symfony/var-dumper": "<4.1.1",
-                "twig/twig": "<1.34|<2.4,>=2"
+                "twig/twig": "<1.43|<2.13,>=2"
             },
             "provide": {
                 "psr/log-implementation": "1.0"
             },
             "require-dev": {
                 "psr/cache": "~1.0",
-                "symfony/browser-kit": "^4.3",
-                "symfony/config": "~3.4|~4.0",
-                "symfony/console": "~3.4|~4.0",
-                "symfony/css-selector": "~3.4|~4.0",
-                "symfony/dependency-injection": "^4.3",
-                "symfony/dom-crawler": "~3.4|~4.0",
-                "symfony/expression-language": "~3.4|~4.0",
-                "symfony/finder": "~3.4|~4.0",
-                "symfony/process": "~3.4|~4.0",
-                "symfony/routing": "~3.4|~4.0",
-                "symfony/stopwatch": "~3.4|~4.0",
-                "symfony/templating": "~3.4|~4.0",
-                "symfony/translation": "~4.2",
-                "symfony/translation-contracts": "^1.1",
-                "symfony/var-dumper": "^4.1.1",
-                "twig/twig": "^1.34|^2.4"
+                "symfony/browser-kit": "^4.3|^5.0",
+                "symfony/config": "^3.4|^4.0|^5.0",
+                "symfony/console": "^3.4|^4.0",
+                "symfony/css-selector": "^3.4|^4.0|^5.0",
+                "symfony/dependency-injection": "^4.3|^5.0",
+                "symfony/dom-crawler": "^3.4|^4.0|^5.0",
+                "symfony/expression-language": "^3.4|^4.0|^5.0",
+                "symfony/finder": "^3.4|^4.0|^5.0",
+                "symfony/process": "^3.4|^4.0|^5.0",
+                "symfony/routing": "^3.4|^4.0|^5.0",
+                "symfony/stopwatch": "^3.4|^4.0|^5.0",
+                "symfony/templating": "^3.4|^4.0|^5.0",
+                "symfony/translation": "^4.2|^5.0",
+                "symfony/translation-contracts": "^1.1|^2",
+                "twig/twig": "^1.43|^2.13|^3.0.4"
             },
             "suggest": {
                 "symfony/browser-kit": "",
                 "symfony/config": "",
                 "symfony/console": "",
-                "symfony/dependency-injection": "",
-                "symfony/var-dumper": ""
+                "symfony/dependency-injection": ""
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\HttpKernel\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony HttpKernel Component",
+            "description": "Provides a structured process for converting a Request into a Response",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T16:47:42+00:00"
+            "support": {
+                "source": "https://github.com/symfony/http-kernel/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T13:50:53+00:00"
         },
         {
             "name": "symfony/mime",
-            "version": "v4.3.4",
+            "version": "v5.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/mime.git",
-                "reference": "987a05df1c6ac259b34008b932551353f4f408df"
+                "reference": "37bade585ea100d235c031b258eff93b5b6bb9a9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/mime/zipball/987a05df1c6ac259b34008b932551353f4f408df",
-                "reference": "987a05df1c6ac259b34008b932551353f4f408df",
+                "url": "https://api.github.com/repos/symfony/mime/zipball/37bade585ea100d235c031b258eff93b5b6bb9a9",
+                "reference": "37bade585ea100d235c031b258eff93b5b6bb9a9",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.2.5",
+                "symfony/deprecation-contracts": "^2.1",
                 "symfony/polyfill-intl-idn": "^1.10",
-                "symfony/polyfill-mbstring": "^1.0"
+                "symfony/polyfill-mbstring": "^1.0",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "conflict": {
+                "phpdocumentor/reflection-docblock": "<3.2.2",
+                "phpdocumentor/type-resolver": "<1.4.0",
+                "symfony/mailer": "<4.4"
             },
             "require-dev": {
                 "egulias/email-validator": "^2.1.10",
-                "symfony/dependency-injection": "~3.4|^4.1"
+                "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+                "symfony/dependency-injection": "^4.4|^5.0",
+                "symfony/property-access": "^4.4|^5.1",
+                "symfony/property-info": "^4.4|^5.1",
+                "symfony/serializer": "^5.2"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\Mime\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "A library to manipulate MIME messages",
+            "description": "Allows manipulating MIME messages",
             "homepage": "https://symfony.com",
             "keywords": [
                 "mime",
                 "mime-type"
             ],
-            "time": "2019-08-22T08:16:11+00:00"
+            "support": {
+                "source": "https://github.com/symfony/mime/tree/v5.2.2"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-25T14:08:25+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
-            "version": "v1.12.0",
+            "version": "v1.22.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-ctype.git",
-                "reference": "550ebaac289296ce228a706d0867afc34687e3f4"
+                "reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4",
-                "reference": "550ebaac289296ce228a706d0867afc34687e3f4",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
+                "reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": ">=7.1"
             },
             "suggest": {
                 "ext-ctype": "For best performance"
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.12-dev"
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                 "polyfill",
                 "portable"
             ],
-            "time": "2019-08-06T08:03:45+00:00"
+            "support": {
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
         },
         {
             "name": "symfony/polyfill-iconv",
-            "version": "v1.12.0",
+            "version": "v1.22.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-iconv.git",
-                "reference": "685968b11e61a347c18bf25db32effa478be610f"
+                "reference": "b34bfb8c4c22650ac080d2662ae3502e5f2f4ae6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/685968b11e61a347c18bf25db32effa478be610f",
-                "reference": "685968b11e61a347c18bf25db32effa478be610f",
+                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/b34bfb8c4c22650ac080d2662ae3502e5f2f4ae6",
+                "reference": "b34bfb8c4c22650ac080d2662ae3502e5f2f4ae6",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": ">=7.1"
             },
             "suggest": {
                 "ext-iconv": "For best performance"
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.12-dev"
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                 "portable",
                 "shim"
             ],
-            "time": "2019-08-06T08:03:45+00:00"
+            "support": {
+                "source": "https://github.com/symfony/polyfill-iconv/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
         },
         {
             "name": "symfony/polyfill-intl-idn",
-            "version": "v1.12.0",
+            "version": "v1.22.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-intl-idn.git",
-                "reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2"
+                "reference": "0eb8293dbbcd6ef6bf81404c9ce7d95bcdf34f44"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6af626ae6fa37d396dc90a399c0ff08e5cfc45b2",
-                "reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/0eb8293dbbcd6ef6bf81404c9ce7d95bcdf34f44",
+                "reference": "0eb8293dbbcd6ef6bf81404c9ce7d95bcdf34f44",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3",
-                "symfony/polyfill-mbstring": "^1.3",
-                "symfony/polyfill-php72": "^1.9"
+                "php": ">=7.1",
+                "symfony/polyfill-intl-normalizer": "^1.10",
+                "symfony/polyfill-php72": "^1.10"
             },
             "suggest": {
                 "ext-intl": "For best performance"
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.12-dev"
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                     "name": "Laurent Bassin",
                     "email": "[email protected]"
                 },
+                {
+                    "name": "Trevor Rowbotham",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Symfony Community",
                     "homepage": "https://symfony.com/contributors"
                 "portable",
                 "shim"
             ],
-            "time": "2019-08-06T08:03:45+00:00"
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-normalizer",
+            "version": "v1.22.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+                "reference": "6e971c891537eb617a00bb07a43d182a6915faba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/6e971c891537eb617a00bb07a43d182a6915faba",
+                "reference": "6e971c891537eb617a00bb07a43d182a6915faba",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's Normalizer class and related functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "intl",
+                "normalizer",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T17:09:11+00:00"
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.12.0",
+            "version": "v1.22.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17"
+                "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17",
-                "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
+                "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": ">=7.1"
             },
             "suggest": {
                 "ext-mbstring": "For best performance"
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.12-dev"
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                 "portable",
                 "shim"
             ],
-            "time": "2019-08-06T08:03:45+00:00"
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
         },
         {
             "name": "symfony/polyfill-php72",
-            "version": "v1.12.0",
+            "version": "v1.22.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php72.git",
-                "reference": "04ce3335667451138df4307d6a9b61565560199e"
+                "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e",
-                "reference": "04ce3335667451138df4307d6a9b61565560199e",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
+                "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": ">=7.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.12-dev"
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                 "portable",
                 "shim"
             ],
-            "time": "2019-08-06T08:03:45+00:00"
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
         },
         {
             "name": "symfony/polyfill-php73",
-            "version": "v1.12.0",
+            "version": "v1.22.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php73.git",
-                "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188"
+                "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/2ceb49eaccb9352bff54d22570276bb75ba4a188",
-                "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188",
+                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+                "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": ">=7.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.12-dev"
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                 "portable",
                 "shim"
             ],
-            "time": "2019-08-06T08:03:45+00:00"
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
         },
         {
-            "name": "symfony/process",
-            "version": "v4.3.4",
+            "name": "symfony/polyfill-php80",
+            "version": "v1.22.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/symfony/process.git",
-                "reference": "e89969c00d762349f078db1128506f7f3dcc0d4a"
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/e89969c00d762349f078db1128506f7f3dcc0d4a",
-                "reference": "e89969c00d762349f078db1128506f7f3dcc0d4a",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
+                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.3-dev"
+                    "dev-main": "1.22-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
                 }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-07T16:49:33+00:00"
+        },
+        {
+            "name": "symfony/process",
+            "version": "v4.4.19",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/process.git",
+                "reference": "7e950b6366d4da90292c2e7fa820b3c1842b965a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/process/zipball/7e950b6366d4da90292c2e7fa820b3c1842b965a",
+                "reference": "7e950b6366d4da90292c2e7fa820b3c1842b965a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.3"
             },
+            "type": "library",
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\Process\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony Process Component",
+            "description": "Executes commands in sub-processes",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:26:39+00:00"
+            "support": {
+                "source": "https://github.com/symfony/process/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
             "name": "symfony/routing",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
-                "reference": "ff1049f6232dc5b6023b1ff1c6de56f82bcd264f"
+                "reference": "87529f6e305c7acb162840d1ea57922038072425"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/routing/zipball/ff1049f6232dc5b6023b1ff1c6de56f82bcd264f",
-                "reference": "ff1049f6232dc5b6023b1ff1c6de56f82bcd264f",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/87529f6e305c7acb162840d1ea57922038072425",
+                "reference": "87529f6e305c7acb162840d1ea57922038072425",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "conflict": {
                 "symfony/config": "<4.2",
                 "symfony/yaml": "<3.4"
             },
             "require-dev": {
-                "doctrine/annotations": "~1.2",
+                "doctrine/annotations": "^1.10.4",
                 "psr/log": "~1.0",
-                "symfony/config": "~4.2",
-                "symfony/dependency-injection": "~3.4|~4.0",
-                "symfony/expression-language": "~3.4|~4.0",
-                "symfony/http-foundation": "~3.4|~4.0",
-                "symfony/yaml": "~3.4|~4.0"
+                "symfony/config": "^4.2|^5.0",
+                "symfony/dependency-injection": "^3.4|^4.0|^5.0",
+                "symfony/expression-language": "^3.4|^4.0|^5.0",
+                "symfony/http-foundation": "^3.4|^4.0|^5.0",
+                "symfony/yaml": "^3.4|^4.0|^5.0"
             },
             "suggest": {
                 "doctrine/annotations": "For using the annotation loader",
                 "symfony/yaml": "For using the YAML loader"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\Routing\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony Routing Component",
+            "description": "Maps an HTTP request to a set of configuration variables",
             "homepage": "https://symfony.com",
             "keywords": [
                 "router",
                 "uri",
                 "url"
             ],
-            "time": "2019-08-26T08:26:39+00:00"
+            "support": {
+                "source": "https://github.com/symfony/routing/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
             "name": "symfony/service-contracts",
-            "version": "v1.1.6",
+            "version": "v2.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/service-contracts.git",
-                "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3"
+                "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ea7263d6b6d5f798b56a45a5b8d686725f2719a3",
-                "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1",
+                "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.2.5",
                 "psr/container": "^1.0"
             },
             "suggest": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.1-dev"
+                    "dev-master": "2.2-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "autoload": {
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-08-20T14:44:19+00:00"
+            "support": {
+                "source": "https://github.com/symfony/service-contracts/tree/master"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-07T11:33:47+00:00"
         },
         {
             "name": "symfony/translation",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "28498169dd334095fa981827992f3a24d50fed0f"
+                "reference": "e1d0c67167a553556d9f974b5fa79c2448df317a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/28498169dd334095fa981827992f3a24d50fed0f",
-                "reference": "28498169dd334095fa981827992f3a24d50fed0f",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/e1d0c67167a553556d9f974b5fa79c2448df317a",
+                "reference": "e1d0c67167a553556d9f974b5fa79c2448df317a",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-mbstring": "~1.0",
-                "symfony/translation-contracts": "^1.1.6"
+                "symfony/translation-contracts": "^1.1.6|^2"
             },
             "conflict": {
                 "symfony/config": "<3.4",
                 "symfony/dependency-injection": "<3.4",
+                "symfony/http-kernel": "<4.4",
                 "symfony/yaml": "<3.4"
             },
             "provide": {
             },
             "require-dev": {
                 "psr/log": "~1.0",
-                "symfony/config": "~3.4|~4.0",
-                "symfony/console": "~3.4|~4.0",
-                "symfony/dependency-injection": "~3.4|~4.0",
-                "symfony/finder": "~2.8|~3.0|~4.0",
-                "symfony/http-kernel": "~3.4|~4.0",
-                "symfony/intl": "~3.4|~4.0",
-                "symfony/service-contracts": "^1.1.2",
-                "symfony/var-dumper": "~3.4|~4.0",
-                "symfony/yaml": "~3.4|~4.0"
+                "symfony/config": "^3.4|^4.0|^5.0",
+                "symfony/console": "^3.4|^4.0|^5.0",
+                "symfony/dependency-injection": "^3.4|^4.0|^5.0",
+                "symfony/finder": "~2.8|~3.0|~4.0|^5.0",
+                "symfony/http-kernel": "^4.4",
+                "symfony/intl": "^3.4|^4.0|^5.0",
+                "symfony/service-contracts": "^1.1.2|^2",
+                "symfony/yaml": "^3.4|^4.0|^5.0"
             },
             "suggest": {
                 "psr/log-implementation": "To use logging capability in translator",
                 "symfony/yaml": ""
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\Translation\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony Translation Component",
+            "description": "Provides tools to internationalize your application",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:55:16+00:00"
+            "support": {
+                "source": "https://github.com/symfony/translation/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v1.1.6",
+            "version": "v2.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a"
+                "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/325b17c24f3ee23cbecfa63ba809c6d89b5fa04a",
-                "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/e2eaa60b558f26a4b0354e1bbb25636efaaad105",
+                "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.2.5"
             },
             "suggest": {
                 "symfony/translation-implementation": ""
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.1-dev"
+                    "dev-master": "2.3-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "autoload": {
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-08-02T12:15:04+00:00"
+            "support": {
+                "source": "https://github.com/symfony/translation-contracts/tree/v2.3.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-28T13:05:58+00:00"
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "641043e0f3e615990a0f29479f9c117e8a6698c6"
+                "reference": "a1eab2f69906dc83c5ddba4632180260d0ab4f7f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/641043e0f3e615990a0f29479f9c117e8a6698c6",
-                "reference": "641043e0f3e615990a0f29479f9c117e8a6698c6",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a1eab2f69906dc83c5ddba4632180260d0ab4f7f",
+                "reference": "a1eab2f69906dc83c5ddba4632180260d0ab4f7f",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-mbstring": "~1.0",
-                "symfony/polyfill-php72": "~1.5"
+                "symfony/polyfill-php72": "~1.5",
+                "symfony/polyfill-php80": "^1.15"
             },
             "conflict": {
                 "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
             },
             "require-dev": {
                 "ext-iconv": "*",
-                "symfony/console": "~3.4|~4.0",
-                "symfony/process": "~3.4|~4.0",
-                "twig/twig": "~1.34|~2.4"
+                "symfony/console": "^3.4|^4.0|^5.0",
+                "symfony/process": "^4.4|^5.0",
+                "twig/twig": "^1.43|^2.13|^3.0.4"
             },
             "suggest": {
                 "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
                 "Resources/bin/var-dump-server"
             ],
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "files": [
                     "Resources/functions/dump.php"
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony mechanism for exploring and dumping PHP variables",
+            "description": "Provides mechanisms for walking through any arbitrary PHP variable",
             "homepage": "https://symfony.com",
             "keywords": [
                 "debug",
                 "dump"
             ],
-            "time": "2019-08-26T08:26:39+00:00"
+            "support": {
+                "source": "https://github.com/symfony/var-dumper/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
             "name": "tijsverkoyen/css-to-inline-styles",
-            "version": "2.2.1",
+            "version": "2.2.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
-                "reference": "0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757"
+                "reference": "b43b05cf43c1b6d849478965062b6ef73e223bb5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757",
-                "reference": "0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757",
+                "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/b43b05cf43c1b6d849478965062b6ef73e223bb5",
+                "reference": "b43b05cf43c1b6d849478965062b6ef73e223bb5",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.5 || ^7.0",
-                "symfony/css-selector": "^2.7 || ^3.0 || ^4.0"
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "php": "^5.5 || ^7.0 || ^8.0",
+                "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5"
             },
             "type": "library",
             "extra": {
             ],
             "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
             "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
-            "time": "2017-11-27T11:13:29+00:00"
+            "support": {
+                "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
+                "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.3"
+            },
+            "time": "2020-07-13T06:12:54+00:00"
         },
         {
             "name": "vlucas/phpdotenv",
-            "version": "v3.6.0",
+            "version": "v3.6.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/vlucas/phpdotenv.git",
-                "reference": "1bdf24f065975594f6a117f0f1f6cabf1333b156"
+                "reference": "5e679f7616db829358341e2d5cccbd18773bdab8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1bdf24f065975594f6a117f0f1f6cabf1333b156",
-                "reference": "1bdf24f065975594f6a117f0f1f6cabf1333b156",
+                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/5e679f7616db829358341e2d5cccbd18773bdab8",
+                "reference": "5e679f7616db829358341e2d5cccbd18773bdab8",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.4 || ^7.0",
-                "phpoption/phpoption": "^1.5",
-                "symfony/polyfill-ctype": "^1.9"
+                "php": "^5.4 || ^7.0 || ^8.0",
+                "phpoption/phpoption": "^1.5.2",
+                "symfony/polyfill-ctype": "^1.17"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
+                "ext-filter": "*",
+                "ext-pcre": "*",
+                "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20"
+            },
+            "suggest": {
+                "ext-filter": "Required to use the boolean validator.",
+                "ext-pcre": "Required to use most of the library."
             },
             "type": "library",
             "extra": {
                 "env",
                 "environment"
             ],
-            "time": "2019-09-10T21:37:39+00:00"
+            "support": {
+                "issues": "https://github.com/vlucas/phpdotenv/issues",
+                "source": "https://github.com/vlucas/phpdotenv/tree/v3.6.8"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-20T14:39:46+00:00"
         }
     ],
     "packages-dev": [
         {
             "name": "barryvdh/laravel-debugbar",
-            "version": "v3.2.8",
+            "version": "v3.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-debugbar.git",
-                "reference": "18208d64897ab732f6c04a19b319fe8f1d57a9c0"
+                "reference": "cae0a8d1cb89b0f0522f65e60465e16d738e069b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/18208d64897ab732f6c04a19b319fe8f1d57a9c0",
-                "reference": "18208d64897ab732f6c04a19b319fe8f1d57a9c0",
+                "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/cae0a8d1cb89b0f0522f65e60465e16d738e069b",
+                "reference": "cae0a8d1cb89b0f0522f65e60465e16d738e069b",
                 "shasum": ""
             },
             "require": {
-                "illuminate/routing": "^5.5|^6",
-                "illuminate/session": "^5.5|^6",
-                "illuminate/support": "^5.5|^6",
-                "maximebf/debugbar": "~1.15.0",
-                "php": ">=7.0",
-                "symfony/debug": "^3|^4",
-                "symfony/finder": "^3|^4"
+                "illuminate/routing": "^6|^7|^8",
+                "illuminate/session": "^6|^7|^8",
+                "illuminate/support": "^6|^7|^8",
+                "maximebf/debugbar": "^1.16.3",
+                "php": ">=7.2",
+                "symfony/debug": "^4.3|^5",
+                "symfony/finder": "^4.3|^5"
             },
             "require-dev": {
-                "laravel/framework": "5.5.x"
+                "mockery/mockery": "^1.3.3",
+                "orchestra/testbench-dusk": "^4|^5|^6",
+                "phpunit/phpunit": "^8.5|^9.0",
+                "squizlabs/php_codesniffer": "^3.5"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.5-dev"
                 },
                 "laravel": {
                     "providers": [
                 "profiler",
                 "webprofiler"
             ],
-            "time": "2019-08-29T07:01:03+00:00"
+            "support": {
+                "issues": "https://github.com/barryvdh/laravel-debugbar/issues",
+                "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.5.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/barryvdh",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-01-06T14:21:44+00:00"
         },
         {
             "name": "barryvdh/laravel-ide-helper",
-            "version": "v2.6.5",
+            "version": "v2.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-ide-helper.git",
-                "reference": "8740a9a158d3dd5cfc706a9d4cc1bf7a518f99f3"
+                "reference": "5515cabea39b9cf55f98980d0f269dc9d85cfcca"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/8740a9a158d3dd5cfc706a9d4cc1bf7a518f99f3",
-                "reference": "8740a9a158d3dd5cfc706a9d4cc1bf7a518f99f3",
+                "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/5515cabea39b9cf55f98980d0f269dc9d85cfcca",
+                "reference": "5515cabea39b9cf55f98980d0f269dc9d85cfcca",
                 "shasum": ""
             },
             "require": {
                 "barryvdh/reflection-docblock": "^2.0.6",
-                "composer/composer": "^1.6",
+                "composer/composer": "^1.6 || ^2",
                 "doctrine/dbal": "~2.3",
-                "illuminate/console": "^5.5|^6",
-                "illuminate/filesystem": "^5.5|^6",
-                "illuminate/support": "^5.5|^6",
-                "php": ">=7"
+                "ext-json": "*",
+                "illuminate/console": "^6 || ^7 || ^8",
+                "illuminate/filesystem": "^6 || ^7 || ^8",
+                "illuminate/support": "^6 || ^7 || ^8",
+                "php": ">=7.2",
+                "phpdocumentor/type-resolver": "^1.1.0"
             },
             "require-dev": {
-                "illuminate/config": "^5.5|^6",
-                "illuminate/view": "^5.5|^6",
-                "phpro/grumphp": "^0.14",
-                "phpunit/phpunit": "4.*",
-                "scrutinizer/ocular": "~1.1",
-                "squizlabs/php_codesniffer": "^3"
+                "ext-pdo_sqlite": "*",
+                "friendsofphp/php-cs-fixer": "^2",
+                "illuminate/config": "^6 || ^7 || ^8",
+                "illuminate/view": "^6 || ^7 || ^8",
+                "mockery/mockery": "^1.3.3",
+                "orchestra/testbench": "^4 || ^5 || ^6",
+                "phpunit/phpunit": "^8.5 || ^9",
+                "spatie/phpunit-snapshot-assertions": "^1.4 || ^2.2 || ^3 || ^4",
+                "vimeo/psalm": "^3.12"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.6-dev"
+                    "dev-master": "2.8-dev"
                 },
                 "laravel": {
                     "providers": [
                 "phpstorm",
                 "sublime"
             ],
-            "time": "2019-09-08T09:56:38+00:00"
+            "support": {
+                "issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
+                "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.8.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/barryvdh",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-12-06T08:55:05+00:00"
         },
         {
             "name": "barryvdh/reflection-docblock",
                     "email": "[email protected]"
                 }
             ],
+            "support": {
+                "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.0.6"
+            },
             "time": "2018-12-13T10:34:14+00:00"
         },
         {
             "name": "composer/ca-bundle",
-            "version": "1.2.4",
+            "version": "1.2.9",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/ca-bundle.git",
-                "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527"
+                "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/ca-bundle/zipball/10bb96592168a0f8e8f6dcde3532d9fa50b0b527",
-                "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527",
+                "url": "https://api.github.com/repos/composer/ca-bundle/zipball/78a0e288fdcebf92aa2318a8d3656168da6ac1a5",
+                "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5",
                 "shasum": ""
             },
             "require": {
                 "php": "^5.3.2 || ^7.0 || ^8.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8",
+                "phpstan/phpstan": "^0.12.55",
                 "psr/log": "^1.0",
-                "symfony/process": "^2.5 || ^3.0 || ^4.0"
+                "symfony/phpunit-bridge": "^4.2 || ^5",
+                "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.x-dev"
+                    "dev-main": "1.x-dev"
                 }
             },
             "autoload": {
                 "ssl",
                 "tls"
             ],
-            "time": "2019-08-30T08:44:50+00:00"
+            "support": {
+                "irc": "irc://irc.freenode.org/composer",
+                "issues": "https://github.com/composer/ca-bundle/issues",
+                "source": "https://github.com/composer/ca-bundle/tree/1.2.9"
+            },
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-12T12:10:35+00:00"
         },
         {
             "name": "composer/composer",
-            "version": "1.9.0",
+            "version": "2.0.9",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/composer.git",
-                "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5"
+                "reference": "591c2c155cac0d2d7f34af41d3b1e29bcbfc685e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/composer/zipball/314aa57fdcfc942065996f59fb73a8b3f74f3fa5",
-                "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5",
+                "url": "https://api.github.com/repos/composer/composer/zipball/591c2c155cac0d2d7f34af41d3b1e29bcbfc685e",
+                "reference": "591c2c155cac0d2d7f34af41d3b1e29bcbfc685e",
                 "shasum": ""
             },
             "require": {
                 "composer/ca-bundle": "^1.0",
-                "composer/semver": "^1.0",
+                "composer/semver": "^3.0",
                 "composer/spdx-licenses": "^1.2",
                 "composer/xdebug-handler": "^1.1",
-                "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
-                "php": "^5.3.2 || ^7.0",
+                "justinrainbow/json-schema": "^5.2.10",
+                "php": "^5.3.2 || ^7.0 || ^8.0",
                 "psr/log": "^1.0",
+                "react/promise": "^1.2 || ^2.7",
                 "seld/jsonlint": "^1.4",
                 "seld/phar-utils": "^1.0",
-                "symfony/console": "^2.7 || ^3.0 || ^4.0",
-                "symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
-                "symfony/finder": "^2.7 || ^3.0 || ^4.0",
-                "symfony/process": "^2.7 || ^3.0 || ^4.0"
-            },
-            "conflict": {
-                "symfony/console": "2.8.38"
+                "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
+                "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
+                "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
+                "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.7",
-                "phpunit/phpunit-mock-objects": "^2.3 || ^3.0"
+                "phpspec/prophecy": "^1.10",
+                "symfony/phpunit-bridge": "^4.2 || ^5.0"
             },
             "suggest": {
                 "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.9-dev"
+                    "dev-master": "2.0-dev"
                 }
             },
             "autoload": {
                 {
                     "name": "Nils Adermann",
                     "email": "[email protected]",
-                    "homepage": "http://www.naderman.de"
+                    "homepage": "https://www.naderman.de"
                 },
                 {
                     "name": "Jordi Boggiano",
                     "email": "[email protected]",
-                    "homepage": "http://seld.be"
+                    "homepage": "https://seld.be"
                 }
             ],
             "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.",
                 "dependency",
                 "package"
             ],
-            "time": "2019-08-02T18:55:33+00:00"
+            "support": {
+                "irc": "irc://irc.freenode.org/composer",
+                "issues": "https://github.com/composer/composer/issues",
+                "source": "https://github.com/composer/composer/tree/2.0.9"
+            },
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T15:09:27+00:00"
         },
         {
             "name": "composer/semver",
-            "version": "1.5.0",
+            "version": "3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/semver.git",
-                "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e"
+                "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
-                "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
+                "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
+                "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3.2 || ^7.0"
+                "php": "^5.3.2 || ^7.0 || ^8.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.5 || ^5.0.5",
-                "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
+                "phpstan/phpstan": "^0.12.54",
+                "symfony/phpunit-bridge": "^4.2 || ^5"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.x-dev"
+                    "dev-main": "3.x-dev"
                 }
             },
             "autoload": {
                 "validation",
                 "versioning"
             ],
-            "time": "2019-03-19T17:25:45+00:00"
+            "support": {
+                "irc": "irc://irc.freenode.org/composer",
+                "issues": "https://github.com/composer/semver/issues",
+                "source": "https://github.com/composer/semver/tree/3.2.4"
+            },
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-11-13T08:59:24+00:00"
         },
         {
             "name": "composer/spdx-licenses",
-            "version": "1.5.2",
+            "version": "1.5.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/spdx-licenses.git",
-                "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5"
+                "reference": "de30328a7af8680efdc03e396aad24befd513200"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7ac1e6aec371357df067f8a688c3d6974df68fa5",
-                "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5",
+                "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/de30328a7af8680efdc03e396aad24befd513200",
+                "reference": "de30328a7af8680efdc03e396aad24befd513200",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.x-dev"
+                    "dev-main": "1.x-dev"
                 }
             },
             "autoload": {
                 "spdx",
                 "validator"
             ],
-            "time": "2019-07-29T10:31:59+00:00"
+            "support": {
+                "irc": "irc://irc.freenode.org/composer",
+                "issues": "https://github.com/composer/spdx-licenses/issues",
+                "source": "https://github.com/composer/spdx-licenses/tree/1.5.5"
+            },
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-12-03T16:04:16+00:00"
         },
         {
             "name": "composer/xdebug-handler",
-            "version": "1.3.3",
+            "version": "1.4.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/xdebug-handler.git",
-                "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f"
+                "reference": "f28d44c286812c714741478d968104c5e604a1d4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f",
-                "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f",
+                "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f28d44c286812c714741478d968104c5e604a1d4",
+                "reference": "f28d44c286812c714741478d968104c5e604a1d4",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3.2 || ^7.0",
+                "php": "^5.3.2 || ^7.0 || ^8.0",
                 "psr/log": "^1.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8"
             },
             "type": "library",
             "autoload": {
                     "email": "[email protected]"
                 }
             ],
-            "description": "Restarts a process without xdebug.",
+            "description": "Restarts a process without Xdebug.",
             "keywords": [
                 "Xdebug",
                 "performance"
             ],
-            "time": "2019-05-27T17:52:04+00:00"
+            "support": {
+                "irc": "irc://irc.freenode.org/composer",
+                "issues": "https://github.com/composer/xdebug-handler/issues",
+                "source": "https://github.com/composer/xdebug-handler/tree/1.4.5"
+            },
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-11-13T08:04:11+00:00"
         },
         {
             "name": "doctrine/instantiator",
-            "version": "1.2.0",
+            "version": "1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/instantiator.git",
-                "reference": "a2c590166b2133a4633738648b6b064edae0814a"
+                "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a",
-                "reference": "a2c590166b2133a4633738648b6b064edae0814a",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b",
+                "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": "^7.1 || ^8.0"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^6.0",
+                "doctrine/coding-standard": "^8.0",
                 "ext-pdo": "*",
                 "ext-phar": "*",
-                "phpbench/phpbench": "^0.13",
-                "phpstan/phpstan-phpunit": "^0.11",
-                "phpstan/phpstan-shim": "^0.11",
-                "phpunit/phpunit": "^7.0"
+                "phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
+                "phpstan/phpstan": "^0.12",
+                "phpstan/phpstan-phpunit": "^0.12",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.2.x-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
                 {
                     "name": "Marco Pivetta",
                     "email": "[email protected]",
-                    "homepage": "http://ocramius.github.com/"
+                    "homepage": "https://ocramius.github.io/"
                 }
             ],
             "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
                 "constructor",
                 "instantiate"
             ],
-            "time": "2019-03-17T17:37:11+00:00"
-        },
-        {
-            "name": "facade/flare-client-php",
-            "version": "1.0.4",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/facade/flare-client-php.git",
-                "reference": "7128b251b48f24ef64e5cddd7f8d40cc3a06fd3e"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/facade/flare-client-php/zipball/7128b251b48f24ef64e5cddd7f8d40cc3a06fd3e",
-                "reference": "7128b251b48f24ef64e5cddd7f8d40cc3a06fd3e",
-                "shasum": ""
-            },
-            "require": {
-                "facade/ignition-contracts": "~1.0",
-                "illuminate/pipeline": "~5.5|~5.6|~5.7|~5.8|^6.0",
-                "php": "^7.1",
-                "symfony/http-foundation": "~3.3|~4.1",
-                "symfony/var-dumper": "^3.4|^4.0"
-            },
-            "require-dev": {
-                "larapack/dd": "^1.1",
-                "phpunit/phpunit": "^7.0",
-                "spatie/phpunit-snapshot-assertions": "^2.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Facade\\FlareClient\\": "src"
-                },
-                "files": [
-                    "src/helpers.php"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "description": "Send PHP errors to Flare",
-            "homepage": "https://github.com/facade/flare-client-php",
-            "keywords": [
-                "exception",
-                "facade",
-                "flare",
-                "reporting"
-            ],
-            "time": "2019-09-11T14:19:56+00:00"
-        },
-        {
-            "name": "facade/ignition",
-            "version": "1.6.5",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/facade/ignition.git",
-                "reference": "97244f6d511332f3574acab8242c09ddcfda892b"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/facade/ignition/zipball/97244f6d511332f3574acab8242c09ddcfda892b",
-                "reference": "97244f6d511332f3574acab8242c09ddcfda892b",
-                "shasum": ""
+            "support": {
+                "issues": "https://github.com/doctrine/instantiator/issues",
+                "source": "https://github.com/doctrine/instantiator/tree/1.4.0"
             },
-            "require": {
-                "ext-json": "*",
-                "ext-mbstring": "*",
-                "facade/flare-client-php": "^1.0.4",
-                "facade/ignition-contracts": "^1.0",
-                "filp/whoops": "^2.4",
-                "illuminate/support": "~5.5.0 || ~5.6.0 || ~5.7.0 || ~5.8.0 || ^6.0",
-                "monolog/monolog": "^1.12 || ^2.0",
-                "php": "^7.1",
-                "scrivo/highlight.php": "^9.15",
-                "symfony/console": "^3.4 || ^4.0",
-                "symfony/var-dumper": "^3.4 || ^4.0"
-            },
-            "require-dev": {
-                "friendsofphp/php-cs-fixer": "^2.14",
-                "mockery/mockery": "^1.2",
-                "orchestra/testbench": "^3.5 || ^3.6 || ^3.7 || ^3.8 || ^4.0"
-            },
-            "suggest": {
-                "laravel/telescope": "^2.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0-dev"
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
                 },
-                "laravel": {
-                    "providers": [
-                        "Facade\\Ignition\\IgnitionServiceProvider"
-                    ],
-                    "aliases": {
-                        "Flare": "Facade\\Ignition\\Facades\\Flare"
-                    }
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Facade\\Ignition\\": "src"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "description": "A beautiful error page for Laravel applications.",
-            "homepage": "https://github.com/facade/ignition",
-            "keywords": [
-                "error",
-                "flare",
-                "laravel",
-                "page"
-            ],
-            "time": "2019-09-13T13:38:04+00:00"
-        },
-        {
-            "name": "facade/ignition-contracts",
-            "version": "1.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/facade/ignition-contracts.git",
-                "reference": "f445db0fb86f48e205787b2592840dd9c80ded28"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/f445db0fb86f48e205787b2592840dd9c80ded28",
-                "reference": "f445db0fb86f48e205787b2592840dd9c80ded28",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.1"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "Facade\\IgnitionContracts\\": "src"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
                 {
-                    "name": "Freek Van der Herten",
-                    "email": "[email protected]",
-                    "homepage": "https://flareapp.io",
-                    "role": "Developer"
-                }
-            ],
-            "description": "Solution contracts for Ignition",
-            "homepage": "https://github.com/facade/ignition-contracts",
-            "keywords": [
-                "contracts",
-                "flare",
-                "ignition"
-            ],
-            "time": "2019-08-30T14:06:08+00:00"
-        },
-        {
-            "name": "filp/whoops",
-            "version": "2.5.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/filp/whoops.git",
-                "reference": "cde50e6720a39fdacb240159d3eea6865d51fd96"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/filp/whoops/zipball/cde50e6720a39fdacb240159d3eea6865d51fd96",
-                "reference": "cde50e6720a39fdacb240159d3eea6865d51fd96",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.5.9 || ^7.0",
-                "psr/log": "^1.0.1"
-            },
-            "require-dev": {
-                "mockery/mockery": "^0.9 || ^1.0",
-                "phpunit/phpunit": "^4.8.35 || ^5.7",
-                "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0"
-            },
-            "suggest": {
-                "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
-                "whoops/soap": "Formats errors as SOAP responses"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.2-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Whoops\\": "src/Whoops/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
                 {
-                    "name": "Filipe Dobreira",
-                    "role": "Developer",
-                    "homepage": "https://github.com/filp"
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+                    "type": "tidelift"
                 }
             ],
-            "description": "php error handling for cool kids",
-            "homepage": "https://filp.github.io/whoops/",
-            "keywords": [
-                "error",
-                "exception",
-                "handling",
-                "library",
-                "throwable",
-                "whoops"
-            ],
-            "time": "2019-08-07T09:00:00+00:00"
+            "time": "2020-11-10T18:47:58+00:00"
         },
         {
-            "name": "fzaninotto/faker",
-            "version": "v1.8.0",
+            "name": "fakerphp/faker",
+            "version": "v1.13.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/fzaninotto/Faker.git",
-                "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de"
+                "url": "https://github.com/FakerPHP/Faker.git",
+                "reference": "ab3f5364d01f2c2c16113442fb987d26e4004913"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de",
-                "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de",
+                "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/ab3f5364d01f2c2c16113442fb987d26e4004913",
+                "reference": "ab3f5364d01f2c2c16113442fb987d26e4004913",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3.3 || ^7.0"
+                "php": "^7.1 || ^8.0"
+            },
+            "conflict": {
+                "fzaninotto/faker": "*"
             },
             "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.4.1",
                 "ext-intl": "*",
-                "phpunit/phpunit": "^4.8.35 || ^5.7",
-                "squizlabs/php_codesniffer": "^1.5"
+                "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.4.2"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.8-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Faker\\": "src/Faker/"
                 "faker",
                 "fixtures"
             ],
-            "time": "2018-07-12T10:23:15+00:00"
+            "support": {
+                "issues": "https://github.com/FakerPHP/Faker/issues",
+                "source": "https://github.com/FakerPHP/Faker/tree/v1.13.0"
+            },
+            "time": "2020-12-18T16:50:48+00:00"
         },
         {
             "name": "hamcrest/hamcrest-php",
-            "version": "v2.0.0",
+            "version": "v2.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/hamcrest/hamcrest-php.git",
-                "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad"
+                "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad",
-                "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad",
+                "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+                "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3|^7.0"
+                "php": "^5.3|^7.0|^8.0"
             },
             "replace": {
                 "cordoval/hamcrest-php": "*",
                 "kodova/hamcrest-php": "*"
             },
             "require-dev": {
-                "phpunit/php-file-iterator": "1.3.3",
-                "phpunit/phpunit": "~4.0",
-                "satooshi/php-coveralls": "^1.0"
+                "phpunit/php-file-iterator": "^1.4 || ^2.0",
+                "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.0-dev"
+                    "dev-master": "2.1-dev"
                 }
             },
             "autoload": {
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD"
+                "BSD-3-Clause"
             ],
             "description": "This is the PHP port of Hamcrest Matchers",
             "keywords": [
                 "test"
             ],
-            "time": "2016-01-20T08:20:44+00:00"
-        },
-        {
-            "name": "jakub-onderka/php-console-color",
-            "version": "v0.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/JakubOnderka/PHP-Console-Color.git",
-                "reference": "d5deaecff52a0d61ccb613bb3804088da0307191"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Color/zipball/d5deaecff52a0d61ccb613bb3804088da0307191",
-                "reference": "d5deaecff52a0d61ccb613bb3804088da0307191",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.4.0"
-            },
-            "require-dev": {
-                "jakub-onderka/php-code-style": "1.0",
-                "jakub-onderka/php-parallel-lint": "1.0",
-                "jakub-onderka/php-var-dump-check": "0.*",
-                "phpunit/phpunit": "~4.3",
-                "squizlabs/php_codesniffer": "1.*"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "JakubOnderka\\PhpConsoleColor\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-2-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Jakub Onderka",
-                    "email": "[email protected]"
-                }
-            ],
-            "time": "2018-09-29T17:23:10+00:00"
-        },
-        {
-            "name": "jakub-onderka/php-console-highlighter",
-            "version": "v0.4",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git",
-                "reference": "9f7a229a69d52506914b4bc61bfdb199d90c5547"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/9f7a229a69d52506914b4bc61bfdb199d90c5547",
-                "reference": "9f7a229a69d52506914b4bc61bfdb199d90c5547",
-                "shasum": ""
-            },
-            "require": {
-                "ext-tokenizer": "*",
-                "jakub-onderka/php-console-color": "~0.2",
-                "php": ">=5.4.0"
-            },
-            "require-dev": {
-                "jakub-onderka/php-code-style": "~1.0",
-                "jakub-onderka/php-parallel-lint": "~1.0",
-                "jakub-onderka/php-var-dump-check": "~0.1",
-                "phpunit/phpunit": "~4.0",
-                "squizlabs/php_codesniffer": "~1.5"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "JakubOnderka\\PhpConsoleHighlighter\\": "src/"
-                }
+            "support": {
+                "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+                "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
             },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Jakub Onderka",
-                    "email": "[email protected]",
-                    "homepage": "http://www.acci.cz/"
-                }
-            ],
-            "description": "Highlight PHP code in terminal",
-            "time": "2018-09-29T18:48:56+00:00"
+            "time": "2020-07-09T08:09:16+00:00"
         },
         {
             "name": "justinrainbow/json-schema",
-            "version": "5.2.8",
+            "version": "5.2.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/justinrainbow/json-schema.git",
-                "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4"
+                "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/dcb6e1006bb5fd1e392b4daa68932880f37550d4",
-                "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4",
+                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b",
+                "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
             "require-dev": {
-                "friendsofphp/php-cs-fixer": "~2.2.20",
+                "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1",
                 "json-schema/json-schema-test-suite": "1.2.0",
                 "phpunit/phpunit": "^4.8.35"
             },
                 "json",
                 "schema"
             ],
-            "time": "2019-01-14T23:55:14+00:00"
+            "support": {
+                "issues": "https://github.com/justinrainbow/json-schema/issues",
+                "source": "https://github.com/justinrainbow/json-schema/tree/5.2.10"
+            },
+            "time": "2020-05-27T16:41:55+00:00"
         },
         {
             "name": "laravel/browser-kit-testing",
-            "version": "v5.1.3",
+            "version": "v5.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/browser-kit-testing.git",
-                "reference": "cb0cf22cf38fe8796842adc8b9ad550ded2a1377"
+                "reference": "fa0efb279c009e2a276f934f8aff946caf66edc7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/browser-kit-testing/zipball/cb0cf22cf38fe8796842adc8b9ad550ded2a1377",
-                "reference": "cb0cf22cf38fe8796842adc8b9ad550ded2a1377",
+                "url": "https://api.github.com/repos/laravel/browser-kit-testing/zipball/fa0efb279c009e2a276f934f8aff946caf66edc7",
+                "reference": "fa0efb279c009e2a276f934f8aff946caf66edc7",
                 "shasum": ""
             },
             "require": {
                 "illuminate/http": "~5.7.0|~5.8.0|^6.0",
                 "illuminate/support": "~5.7.0|~5.8.0|^6.0",
                 "mockery/mockery": "^1.0",
-                "php": ">=7.1.3",
-                "phpunit/phpunit": "^7.0|^8.0",
+                "php": "^7.1.3|^8.0",
+                "phpunit/phpunit": "^7.5|^8.0|^9.3",
                 "symfony/console": "^4.2",
                 "symfony/css-selector": "^4.2",
                 "symfony/dom-crawler": "^4.2",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "5.0-dev"
+                    "dev-master": "5.x-dev"
                 }
             },
             "autoload": {
                 "laravel",
                 "testing"
             ],
-            "time": "2019-07-30T14:57:44+00:00"
+            "support": {
+                "issues": "https://github.com/laravel/browser-kit-testing/issues",
+                "source": "https://github.com/laravel/browser-kit-testing/tree/v5.2.0"
+            },
+            "time": "2020-10-30T08:49:09+00:00"
         },
         {
             "name": "maximebf/debugbar",
-            "version": "v1.15.0",
+            "version": "v1.16.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/maximebf/php-debugbar.git",
-                "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07"
+                "reference": "6d51ee9e94cff14412783785e79a4e7ef97b9d62"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/30e7d60937ee5f1320975ca9bc7bcdd44d500f07",
-                "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07",
+                "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/6d51ee9e94cff14412783785e79a4e7ef97b9d62",
+                "reference": "6d51ee9e94cff14412783785e79a4e7ef97b9d62",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0",
+                "php": "^7.1|^8",
                 "psr/log": "^1.0",
-                "symfony/var-dumper": "^2.6|^3.0|^4.0"
+                "symfony/var-dumper": "^2.6|^3|^4|^5"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.0|^5.0"
+                "phpunit/phpunit": "^7.5.20 || ^9.4.2"
             },
             "suggest": {
                 "kriswallsmith/assetic": "The best way to manage assets",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.14-dev"
+                    "dev-master": "1.16-dev"
                 }
             },
             "autoload": {
                 "debug",
                 "debugbar"
             ],
-            "time": "2017-12-15T11:13:46+00:00"
+            "support": {
+                "issues": "https://github.com/maximebf/php-debugbar/issues",
+                "source": "https://github.com/maximebf/php-debugbar/tree/v1.16.5"
+            },
+            "time": "2020-12-07T11:07:24+00:00"
         },
         {
             "name": "mockery/mockery",
-            "version": "1.2.3",
+            "version": "1.3.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/mockery/mockery.git",
-                "reference": "4eff936d83eb809bde2c57a3cea0ee9643769031"
+                "reference": "60fa2f67f6e4d3634bb4a45ff3171fa52215800d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/mockery/mockery/zipball/4eff936d83eb809bde2c57a3cea0ee9643769031",
-                "reference": "4eff936d83eb809bde2c57a3cea0ee9643769031",
+                "url": "https://api.github.com/repos/mockery/mockery/zipball/60fa2f67f6e4d3634bb4a45ff3171fa52215800d",
+                "reference": "60fa2f67f6e4d3634bb4a45ff3171fa52215800d",
                 "shasum": ""
             },
             "require": {
-                "hamcrest/hamcrest-php": "~2.0",
+                "hamcrest/hamcrest-php": "^2.0.1",
                 "lib-pcre": ">=7.0",
                 "php": ">=5.6.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "~5.7.10|~6.5|~7.0|~8.0"
+                "phpunit/phpunit": "^5.7.10|^6.5|^7.5|^8.5|^9.3"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "1.3.x-dev"
                 }
             },
             "autoload": {
                 "test double",
                 "testing"
             ],
-            "time": "2019-08-07T15:01:07+00:00"
+            "support": {
+                "issues": "https://github.com/mockery/mockery/issues",
+                "source": "https://github.com/mockery/mockery/tree/1.3.3"
+            },
+            "time": "2020-08-11T18:10:21+00:00"
         },
         {
             "name": "myclabs/deep-copy",
-            "version": "1.9.3",
+            "version": "1.10.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/myclabs/DeepCopy.git",
-                "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea"
+                "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea",
-                "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220",
+                "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": "^7.1 || ^8.0"
             },
             "replace": {
                 "myclabs/deep-copy": "self.version"
                 "object",
                 "object graph"
             ],
-            "time": "2019-08-09T12:45:53+00:00"
-        },
-        {
-            "name": "nunomaduro/collision",
-            "version": "v3.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/nunomaduro/collision.git",
-                "reference": "af42d339fe2742295a54f6fdd42aaa6f8c4aca68"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/nunomaduro/collision/zipball/af42d339fe2742295a54f6fdd42aaa6f8c4aca68",
-                "reference": "af42d339fe2742295a54f6fdd42aaa6f8c4aca68",
-                "shasum": ""
+            "support": {
+                "issues": "https://github.com/myclabs/DeepCopy/issues",
+                "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
             },
-            "require": {
-                "filp/whoops": "^2.1.4",
-                "jakub-onderka/php-console-highlighter": "0.3.*|0.4.*",
-                "php": "^7.1",
-                "symfony/console": "~2.8|~3.3|~4.0"
-            },
-            "require-dev": {
-                "laravel/framework": "5.8.*",
-                "nunomaduro/larastan": "^0.3.0",
-                "phpstan/phpstan": "^0.11",
-                "phpunit/phpunit": "~8.0"
-            },
-            "type": "library",
-            "extra": {
-                "laravel": {
-                    "providers": [
-                        "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
-                    ]
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "NunoMaduro\\Collision\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
+            "funding": [
                 {
-                    "name": "Nuno Maduro",
-                    "email": "[email protected]"
+                    "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+                    "type": "tidelift"
                 }
             ],
-            "description": "Cli error handling for console/command-line PHP applications.",
-            "keywords": [
-                "artisan",
-                "cli",
-                "command-line",
-                "console",
-                "error",
-                "handling",
-                "laravel",
-                "laravel-zero",
-                "php",
-                "symfony"
-            ],
-            "time": "2019-03-07T21:35:13+00:00"
+            "time": "2020-11-13T09:40:50+00:00"
         },
         {
             "name": "phar-io/manifest",
-            "version": "1.0.3",
+            "version": "2.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phar-io/manifest.git",
-                "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
+                "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
-                "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
+                "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
+                "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-phar": "*",
-                "phar-io/version": "^2.0",
-                "php": "^5.6 || ^7.0"
+                "ext-xmlwriter": "*",
+                "phar-io/version": "^3.0.1",
+                "php": "^7.2 || ^8.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "autoload": {
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "role": "Developer",
-                    "email": "[email protected]"
+                    "email": "[email protected]",
+                    "role": "Developer"
                 },
                 {
                     "name": "Sebastian Heuer",
-                    "role": "Developer",
-                    "email": "[email protected]"
+                    "email": "[email protected]",
+                    "role": "Developer"
                 },
                 {
                     "name": "Sebastian Bergmann",
-                    "role": "Developer",
-                    "email": "[email protected]"
+                    "email": "[email protected]",
+                    "role": "Developer"
                 }
             ],
             "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
-            "time": "2018-07-08T19:23:20+00:00"
+            "support": {
+                "issues": "https://github.com/phar-io/manifest/issues",
+                "source": "https://github.com/phar-io/manifest/tree/master"
+            },
+            "time": "2020-06-27T14:33:11+00:00"
         },
         {
             "name": "phar-io/version",
-            "version": "2.0.1",
+            "version": "3.0.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phar-io/version.git",
-                "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6"
+                "reference": "e4782611070e50613683d2b9a57730e9a3ba5451"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6",
-                "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6",
+                "url": "https://api.github.com/repos/phar-io/version/zipball/e4782611070e50613683d2b9a57730e9a3ba5451",
+                "reference": "e4782611070e50613683d2b9a57730e9a3ba5451",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.6 || ^7.0"
+                "php": "^7.2 || ^8.0"
             },
             "type": "library",
             "autoload": {
                 }
             ],
             "description": "Library for handling version information and constraints",
-            "time": "2018-07-08T19:19:57+00:00"
+            "support": {
+                "issues": "https://github.com/phar-io/version/issues",
+                "source": "https://github.com/phar-io/version/tree/3.0.4"
+            },
+            "time": "2020-12-13T23:18:30+00:00"
         },
         {
             "name": "phpdocumentor/reflection-common",
-            "version": "2.0.0",
+            "version": "2.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
-                "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a"
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a",
-                "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.1"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "~6"
+                "php": "^7.2 || ^8.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.x-dev"
+                    "dev-2.x": "2.x-dev"
                 }
             },
             "autoload": {
                 "reflection",
                 "static analysis"
             ],
-            "time": "2018-08-07T13:53:10+00:00"
+            "support": {
+                "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+                "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+            },
+            "time": "2020-06-27T09:03:43+00:00"
         },
         {
             "name": "phpdocumentor/reflection-docblock",
-            "version": "4.3.2",
+            "version": "5.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
-                "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
+                "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
-                "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
+                "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.0",
-                "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0",
-                "phpdocumentor/type-resolver": "~0.4 || ^1.0.0",
-                "webmozart/assert": "^1.0"
+                "ext-filter": "*",
+                "php": "^7.2 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.2",
+                "phpdocumentor/type-resolver": "^1.3",
+                "webmozart/assert": "^1.9.1"
             },
             "require-dev": {
-                "doctrine/instantiator": "^1.0.5",
-                "mockery/mockery": "^1.0",
-                "phpunit/phpunit": "^6.4"
+                "mockery/mockery": "~1.3.2"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.x-dev"
+                    "dev-master": "5.x-dev"
                 }
             },
             "autoload": {
                 "psr-4": {
-                    "phpDocumentor\\Reflection\\": [
-                        "src/"
-                    ]
+                    "phpDocumentor\\Reflection\\": "src"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
                 {
                     "name": "Mike van Riel",
                     "email": "[email protected]"
+                },
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "[email protected]"
                 }
             ],
             "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
-            "time": "2019-09-12T14:27:41+00:00"
+            "support": {
+                "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+                "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master"
+            },
+            "time": "2020-09-03T19:13:55+00:00"
         },
         {
             "name": "phpdocumentor/type-resolver",
-            "version": "1.0.1",
+            "version": "1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/TypeResolver.git",
-                "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9"
+                "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
-                "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
+                "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1",
+                "php": "^7.2 || ^8.0",
                 "phpdocumentor/reflection-common": "^2.0"
             },
             "require-dev": {
-                "ext-tokenizer": "^7.1",
-                "mockery/mockery": "~1",
-                "phpunit/phpunit": "^7.0"
+                "ext-tokenizer": "*"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.x-dev"
+                    "dev-1.x": "1.x-dev"
                 }
             },
             "autoload": {
                 }
             ],
             "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
-            "time": "2019-08-22T18:11:29+00:00"
-        },
-        {
-            "name": "phploc/phploc",
-            "version": "5.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/phploc.git",
-                "reference": "5b714ccb7cb8ca29ccf9caf6eb1aed0131d3a884"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/5b714ccb7cb8ca29ccf9caf6eb1aed0131d3a884",
-                "reference": "5b714ccb7cb8ca29ccf9caf6eb1aed0131d3a884",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.2",
-                "sebastian/finder-facade": "^1.1",
-                "sebastian/version": "^2.0",
-                "symfony/console": "^4.0"
+            "support": {
+                "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0"
             },
-            "bin": [
-                "phploc"
-            ],
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "5.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "[email protected]",
-                    "role": "lead"
-                }
-            ],
-            "description": "A tool for quickly measuring the size of a PHP project.",
-            "homepage": "https://github.com/sebastianbergmann/phploc",
-            "time": "2019-03-16T10:41:19+00:00"
+            "time": "2020-09-17T18:55:26+00:00"
         },
         {
             "name": "phpspec/prophecy",
-            "version": "1.8.1",
+            "version": "1.12.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpspec/prophecy.git",
-                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76"
+                "reference": "245710e971a030f42e08f4912863805570f23d39"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
-                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/245710e971a030f42e08f4912863805570f23d39",
+                "reference": "245710e971a030f42e08f4912863805570f23d39",
                 "shasum": ""
             },
             "require": {
-                "doctrine/instantiator": "^1.0.2",
-                "php": "^5.3|^7.0",
-                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
-                "sebastian/comparator": "^1.1|^2.0|^3.0",
-                "sebastian/recursion-context": "^1.0|^2.0|^3.0"
+                "doctrine/instantiator": "^1.2",
+                "php": "^7.2 || ~8.0, <8.1",
+                "phpdocumentor/reflection-docblock": "^5.2",
+                "sebastian/comparator": "^3.0 || ^4.0",
+                "sebastian/recursion-context": "^3.0 || ^4.0"
             },
             "require-dev": {
-                "phpspec/phpspec": "^2.5|^3.2",
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
+                "phpspec/phpspec": "^6.0",
+                "phpunit/phpunit": "^8.0 || ^9.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.8.x-dev"
+                    "dev-master": "1.11.x-dev"
                 }
             },
             "autoload": {
                 "spy",
                 "stub"
             ],
-            "time": "2019-06-13T12:50:23+00:00"
+            "support": {
+                "issues": "https://github.com/phpspec/prophecy/issues",
+                "source": "https://github.com/phpspec/prophecy/tree/1.12.2"
+            },
+            "time": "2020-12-19T10:15:11+00:00"
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "7.0.7",
+            "version": "7.0.14",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "7743bbcfff2a907e9ee4a25be13d0f8ec5e73800"
+                "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7743bbcfff2a907e9ee4a25be13d0f8ec5e73800",
-                "reference": "7743bbcfff2a907e9ee4a25be13d0f8ec5e73800",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bb7c9a210c72e4709cdde67f8b7362f672f2225c",
+                "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-xmlwriter": "*",
-                "php": "^7.2",
+                "php": ">=7.2",
                 "phpunit/php-file-iterator": "^2.0.2",
                 "phpunit/php-text-template": "^1.2.1",
-                "phpunit/php-token-stream": "^3.1.0",
+                "phpunit/php-token-stream": "^3.1.1 || ^4.0",
                 "sebastian/code-unit-reverse-lookup": "^1.0.1",
                 "sebastian/environment": "^4.2.2",
                 "sebastian/version": "^2.0.1",
                 "testing",
                 "xunit"
             ],
-            "time": "2019-07-25T05:31:54+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.14"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-12-02T13:39:03+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
-            "version": "2.0.2",
+            "version": "2.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
-                "reference": "050bedf145a257b1ff02746c31894800e5122946"
+                "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946",
-                "reference": "050bedf145a257b1ff02746c31894800e5122946",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4b49fb70f067272b659ef0174ff9ca40fdaa6357",
+                "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": ">=7.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^7.1"
+                "phpunit/phpunit": "^8.5"
             },
             "type": "library",
             "extra": {
                 "filesystem",
                 "iterator"
             ],
-            "time": "2018-09-13T20:33:42+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+                "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T08:25:21+00:00"
         },
         {
             "name": "phpunit/php-text-template",
             "keywords": [
                 "template"
             ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+                "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1"
+            },
             "time": "2015-06-21T13:50:34+00:00"
         },
         {
             "name": "phpunit/php-timer",
-            "version": "2.1.2",
+            "version": "2.1.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-timer.git",
-                "reference": "1038454804406b0b5f5f520358e78c1c2f71501e"
+                "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e",
-                "reference": "1038454804406b0b5f5f520358e78c1c2f71501e",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662",
+                "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": ">=7.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^7.0"
+                "phpunit/phpunit": "^8.5"
             },
             "type": "library",
             "extra": {
             "keywords": [
                 "timer"
             ],
-            "time": "2019-06-07T04:22:29+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+                "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T08:20:02+00:00"
         },
         {
             "name": "phpunit/php-token-stream",
-            "version": "3.1.0",
+            "version": "3.1.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-token-stream.git",
-                "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a"
+                "reference": "472b687829041c24b25f475e14c2f38a09edf1c2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e899757bb3df5ff6e95089132f32cd59aac2220a",
-                "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/472b687829041c24b25f475e14c2f38a09edf1c2",
+                "reference": "472b687829041c24b25f475e14c2f38a09edf1c2",
                 "shasum": ""
             },
             "require": {
                 "ext-tokenizer": "*",
-                "php": "^7.1"
+                "php": ">=7.1"
             },
             "require-dev": {
                 "phpunit/phpunit": "^7.0"
             "keywords": [
                 "tokenizer"
             ],
-            "time": "2019-07-25T05:29:42+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-token-stream/issues",
+                "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "abandoned": true,
+            "time": "2020-11-30T08:38:46+00:00"
         },
         {
             "name": "phpunit/phpunit",
-            "version": "8.3.5",
+            "version": "8.5.14",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "302faed7059fde575cf3403a78c730c5e3a62750"
+                "reference": "c25f79895d27b6ecd5abfa63de1606b786a461a3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/302faed7059fde575cf3403a78c730c5e3a62750",
-                "reference": "302faed7059fde575cf3403a78c730c5e3a62750",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c25f79895d27b6ecd5abfa63de1606b786a461a3",
+                "reference": "c25f79895d27b6ecd5abfa63de1606b786a461a3",
                 "shasum": ""
             },
             "require": {
-                "doctrine/instantiator": "^1.2.0",
+                "doctrine/instantiator": "^1.3.1",
                 "ext-dom": "*",
                 "ext-json": "*",
                 "ext-libxml": "*",
                 "ext-mbstring": "*",
                 "ext-xml": "*",
                 "ext-xmlwriter": "*",
-                "myclabs/deep-copy": "^1.9.1",
-                "phar-io/manifest": "^1.0.3",
-                "phar-io/version": "^2.0.1",
-                "php": "^7.2",
-                "phpspec/prophecy": "^1.8.1",
-                "phpunit/php-code-coverage": "^7.0.7",
+                "myclabs/deep-copy": "^1.10.0",
+                "phar-io/manifest": "^2.0.1",
+                "phar-io/version": "^3.0.2",
+                "php": ">=7.2",
+                "phpspec/prophecy": "^1.10.3",
+                "phpunit/php-code-coverage": "^7.0.12",
                 "phpunit/php-file-iterator": "^2.0.2",
                 "phpunit/php-text-template": "^1.2.1",
                 "phpunit/php-timer": "^2.1.2",
                 "sebastian/comparator": "^3.0.2",
                 "sebastian/diff": "^3.0.2",
-                "sebastian/environment": "^4.2.2",
-                "sebastian/exporter": "^3.1.1",
+                "sebastian/environment": "^4.2.3",
+                "sebastian/exporter": "^3.1.2",
                 "sebastian/global-state": "^3.0.0",
                 "sebastian/object-enumerator": "^3.0.3",
                 "sebastian/resource-operations": "^2.0.1",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "8.3-dev"
+                    "dev-master": "8.5-dev"
                 }
             },
             "autoload": {
                 "testing",
                 "xunit"
             ],
-            "time": "2019-09-14T09:12:03+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.14"
+            },
+            "funding": [
+                {
+                    "url": "https://phpunit.de/donate.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-01-17T07:37:30+00:00"
         },
         {
-            "name": "scrivo/highlight.php",
-            "version": "v9.15.10.0",
+            "name": "react/promise",
+            "version": "v2.8.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/scrivo/highlight.php.git",
-                "reference": "9ad3adb4456dc91196327498dbbce6aa1ba1239e"
+                "url": "https://github.com/reactphp/promise.git",
+                "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/9ad3adb4456dc91196327498dbbce6aa1ba1239e",
-                "reference": "9ad3adb4456dc91196327498dbbce6aa1ba1239e",
+                "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4",
+                "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4",
                 "shasum": ""
             },
             "require": {
-                "ext-json": "*",
-                "ext-mbstring": "*",
-                "php": ">=5.4"
+                "php": ">=5.4.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8|^5.7",
-                "symfony/finder": "^2.8"
-            },
-            "suggest": {
-                "ext-dom": "Needed to make use of the features in the utilities namespace"
+                "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36"
             },
             "type": "library",
             "autoload": {
-                "psr-0": {
-                    "Highlight\\": "",
-                    "HighlightUtilities\\": ""
+                "psr-4": {
+                    "React\\Promise\\": "src/"
                 },
                 "files": [
-                    "HighlightUtilities/functions.php"
+                    "src/functions_include.php"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD-3-Clause"
+                "MIT"
             ],
             "authors": [
                 {
-                    "name": "Geert Bergman",
-                    "role": "Project Author",
-                    "homepage": "http://www.scrivo.org/"
-                },
-                {
-                    "name": "Vladimir Jimenez",
-                    "role": "Contributor",
-                    "homepage": "https://allejo.io"
-                },
-                {
-                    "name": "Martin Folkers",
-                    "role": "Contributor",
-                    "homepage": "https://twobrain.io"
+                    "name": "Jan Sorgalla",
+                    "email": "[email protected]"
                 }
             ],
-            "description": "Server side syntax highlighter that supports 185 languages. It's a PHP port of highlight.js",
+            "description": "A lightweight implementation of CommonJS Promises/A for PHP",
             "keywords": [
-                "code",
-                "highlight",
-                "highlight.js",
-                "highlight.php",
-                "syntax"
+                "promise",
+                "promises"
             ],
-            "time": "2019-08-27T04:27:48+00:00"
+            "support": {
+                "issues": "https://github.com/reactphp/promise/issues",
+                "source": "https://github.com/reactphp/promise/tree/v2.8.0"
+            },
+            "time": "2020-05-12T15:16:56+00:00"
         },
         {
             "name": "sebastian/code-unit-reverse-lookup",
-            "version": "1.0.1",
+            "version": "1.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
-                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
+                "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
-                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619",
+                "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.6 || ^7.0"
+                "php": ">=5.6"
             },
             "require-dev": {
-                "phpunit/phpunit": "^5.7 || ^6.0"
+                "phpunit/phpunit": "^8.5"
             },
             "type": "library",
             "extra": {
             ],
             "description": "Looks up which function or method a line of code belongs to",
             "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
-            "time": "2017-03-04T06:30:41+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+                "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T08:15:22+00:00"
         },
         {
             "name": "sebastian/comparator",
-            "version": "3.0.2",
+            "version": "3.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/comparator.git",
-                "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
+                "reference": "1071dfcef776a57013124ff35e1fc41ccd294758"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
-                "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758",
+                "reference": "1071dfcef776a57013124ff35e1fc41ccd294758",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1",
+                "php": ">=7.1",
                 "sebastian/diff": "^3.0",
                 "sebastian/exporter": "^3.1"
             },
             "require-dev": {
-                "phpunit/phpunit": "^7.1"
+                "phpunit/phpunit": "^8.5"
             },
             "type": "library",
             "extra": {
                 "BSD-3-Clause"
             ],
             "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Jeff Welch",
                     "email": "[email protected]"
                 {
                     "name": "Bernhard Schussek",
                     "email": "[email protected]"
-                },
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "[email protected]"
                 }
             ],
             "description": "Provides the functionality to compare PHP values for equality",
                 "compare",
                 "equality"
             ],
-            "time": "2018-07-12T15:12:46+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/comparator/issues",
+                "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T08:04:30+00:00"
         },
         {
             "name": "sebastian/diff",
-            "version": "3.0.2",
+            "version": "3.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/diff.git",
-                "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
+                "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
-                "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
+                "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": ">=7.1"
             },
             "require-dev": {
                 "phpunit/phpunit": "^7.5 || ^8.0",
                 "BSD-3-Clause"
             ],
             "authors": [
-                {
-                    "name": "Kore Nordmann",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Sebastian Bergmann",
                     "email": "[email protected]"
+                },
+                {
+                    "name": "Kore Nordmann",
+                    "email": "[email protected]"
                 }
             ],
             "description": "Diff implementation",
                 "unidiff",
                 "unified diff"
             ],
-            "time": "2019-02-04T06:01:07+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/diff/issues",
+                "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T07:59:04+00:00"
         },
         {
             "name": "sebastian/environment",
-            "version": "4.2.2",
+            "version": "4.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/environment.git",
-                "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404"
+                "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404",
-                "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
+                "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": ">=7.1"
             },
             "require-dev": {
                 "phpunit/phpunit": "^7.5"
                 "environment",
                 "hhvm"
             ],
-            "time": "2019-05-05T09:05:15+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/environment/issues",
+                "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T07:53:42+00:00"
         },
         {
             "name": "sebastian/exporter",
-            "version": "3.1.2",
+            "version": "3.1.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/exporter.git",
-                "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e"
+                "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e",
-                "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e",
+                "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.0",
+                "php": ">=7.0",
                 "sebastian/recursion-context": "^3.0"
             },
             "require-dev": {
                 "export",
                 "exporter"
             ],
-            "time": "2019-09-14T09:02:43+00:00"
-        },
-        {
-            "name": "sebastian/finder-facade",
-            "version": "1.2.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/finder-facade.git",
-                "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f",
-                "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f",
-                "shasum": ""
-            },
-            "require": {
-                "symfony/finder": "~2.3|~3.0|~4.0",
-                "theseer/fdomdocument": "~1.3"
-            },
-            "type": "library",
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/exporter/issues",
+                "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3"
             },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
+            "funding": [
                 {
-                    "name": "Sebastian Bergmann",
-                    "email": "[email protected]",
-                    "role": "lead"
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
                 }
             ],
-            "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.",
-            "homepage": "https://github.com/sebastianbergmann/finder-facade",
-            "time": "2017-11-18T17:31:49+00:00"
+            "time": "2020-11-30T07:47:53+00:00"
         },
         {
             "name": "sebastian/global-state",
-            "version": "3.0.0",
+            "version": "3.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/global-state.git",
-                "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4"
+                "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4",
-                "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b",
+                "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2",
+                "php": ">=7.2",
                 "sebastian/object-reflector": "^1.1.1",
                 "sebastian/recursion-context": "^3.0"
             },
             "keywords": [
                 "global state"
             ],
-            "time": "2019-02-01T05:30:01+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/global-state/issues",
+                "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T07:43:24+00:00"
         },
         {
             "name": "sebastian/object-enumerator",
-            "version": "3.0.3",
+            "version": "3.0.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/object-enumerator.git",
-                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
+                "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
-                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
+                "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.0",
+                "php": ">=7.0",
                 "sebastian/object-reflector": "^1.1.1",
                 "sebastian/recursion-context": "^3.0"
             },
             ],
             "description": "Traverses array structures and object graphs to enumerate all referenced objects",
             "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
-            "time": "2017-08-03T12:35:26+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+                "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T07:40:27+00:00"
         },
         {
             "name": "sebastian/object-reflector",
-            "version": "1.1.1",
+            "version": "1.1.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/object-reflector.git",
-                "reference": "773f97c67f28de00d397be301821b06708fca0be"
+                "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
-                "reference": "773f97c67f28de00d397be301821b06708fca0be",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
+                "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.0"
+                "php": ">=7.0"
             },
             "require-dev": {
                 "phpunit/phpunit": "^6.0"
             ],
             "description": "Allows reflection of object attributes, including inherited and non-public ones",
             "homepage": "https://github.com/sebastianbergmann/object-reflector/",
-            "time": "2017-03-29T09:07:27+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+                "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T07:37:18+00:00"
         },
         {
             "name": "sebastian/recursion-context",
-            "version": "3.0.0",
+            "version": "3.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/recursion-context.git",
-                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
+                "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
-                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb",
+                "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.0"
+                "php": ">=7.0"
             },
             "require-dev": {
                 "phpunit/phpunit": "^6.0"
                 "BSD-3-Clause"
             ],
             "authors": [
-                {
-                    "name": "Jeff Welch",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Sebastian Bergmann",
                     "email": "[email protected]"
                 },
+                {
+                    "name": "Jeff Welch",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Adam Harvey",
                     "email": "[email protected]"
             ],
             "description": "Provides functionality to recursively process PHP variables",
             "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
-            "time": "2017-03-03T06:23:57+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+                "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T07:34:24+00:00"
         },
         {
             "name": "sebastian/resource-operations",
-            "version": "2.0.1",
+            "version": "2.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/resource-operations.git",
-                "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9"
+                "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
-                "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
+                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3",
+                "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": ">=7.1"
             },
             "type": "library",
             "extra": {
             ],
             "description": "Provides a list of PHP built-in functions that operate on resources",
             "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
-            "time": "2018-10-04T04:07:39+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
+                "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T07:30:19+00:00"
         },
         {
             "name": "sebastian/type",
-            "version": "1.1.3",
+            "version": "1.1.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/type.git",
-                "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3"
+                "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3",
-                "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3",
+                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4",
+                "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2"
+                "php": ">=7.2"
             },
             "require-dev": {
                 "phpunit/phpunit": "^8.2"
             ],
             "description": "Collection of value objects that represent the types of the PHP type system",
             "homepage": "https://github.com/sebastianbergmann/type",
-            "time": "2019-07-02T08:10:15+00:00"
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/type/issues",
+                "source": "https://github.com/sebastianbergmann/type/tree/1.1.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-30T07:25:11+00:00"
         },
         {
             "name": "sebastian/version",
             ],
             "description": "Library that helps with managing the version number of Git-hosted PHP projects",
             "homepage": "https://github.com/sebastianbergmann/version",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/version/issues",
+                "source": "https://github.com/sebastianbergmann/version/tree/master"
+            },
             "time": "2016-10-03T07:35:21+00:00"
         },
         {
             "name": "seld/jsonlint",
-            "version": "1.7.1",
+            "version": "1.8.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Seldaek/jsonlint.git",
-                "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38"
+                "reference": "9ad6ce79c342fbd44df10ea95511a1b24dee5b57"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38",
-                "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38",
+                "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9ad6ce79c342fbd44df10ea95511a1b24dee5b57",
+                "reference": "9ad6ce79c342fbd44df10ea95511a1b24dee5b57",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3 || ^7.0"
+                "php": "^5.3 || ^7.0 || ^8.0"
             },
             "require-dev": {
                 "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
                 "parser",
                 "validator"
             ],
-            "time": "2018-01-24T12:46:19+00:00"
+            "support": {
+                "issues": "https://github.com/Seldaek/jsonlint/issues",
+                "source": "https://github.com/Seldaek/jsonlint/tree/1.8.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/Seldaek",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-11-11T09:19:24+00:00"
         },
         {
             "name": "seld/phar-utils",
-            "version": "1.0.1",
+            "version": "1.1.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Seldaek/phar-utils.git",
-                "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a"
+                "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a",
-                "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a",
+                "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8674b1d84ffb47cc59a101f5d5a3b61e87d23796",
+                "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "PHAR file format utilities, for when PHP phars you up",
             "keywords": [
-                "phra"
+                "phar"
             ],
-            "time": "2015-10-13T18:44:15+00:00"
+            "support": {
+                "issues": "https://github.com/Seldaek/phar-utils/issues",
+                "source": "https://github.com/Seldaek/phar-utils/tree/master"
+            },
+            "time": "2020-07-07T18:42:57+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",
-            "version": "3.4.2",
+            "version": "3.5.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8"
+                "reference": "9d583721a7157ee997f235f327de038e7ea6dac4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8",
-                "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8",
+                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4",
+                "reference": "9d583721a7157ee997f235f327de038e7ea6dac4",
                 "shasum": ""
             },
             "require": {
                 "phpcs",
                 "standards"
             ],
-            "time": "2019-04-10T23:49:02+00:00"
+            "support": {
+                "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
+                "source": "https://github.com/squizlabs/PHP_CodeSniffer",
+                "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
+            },
+            "time": "2020-10-23T02:01:07+00:00"
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v4.3.4",
+            "version": "v4.4.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
-                "reference": "cc686552948d627528c0e2e759186dff67c2610e"
+                "reference": "21032c566558255e551d23f4a516434c9e3a9a78"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/cc686552948d627528c0e2e759186dff67c2610e",
-                "reference": "cc686552948d627528c0e2e759186dff67c2610e",
+                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/21032c566558255e551d23f4a516434c9e3a9a78",
+                "reference": "21032c566558255e551d23f4a516434c9e3a9a78",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-ctype": "~1.8",
                 "symfony/polyfill-mbstring": "~1.0"
             },
             },
             "require-dev": {
                 "masterminds/html5": "^2.6",
-                "symfony/css-selector": "~3.4|~4.0"
+                "symfony/css-selector": "^3.4|^4.0|^5.0"
             },
             "suggest": {
                 "symfony/css-selector": ""
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\DomCrawler\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony DomCrawler Component",
+            "description": "Eases DOM navigation for HTML and XML documents",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:26:39+00:00"
+            "support": {
+                "source": "https://github.com/symfony/dom-crawler/tree/v4.4.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-01-27T09:09:26+00:00"
         },
         {
             "name": "symfony/filesystem",
-            "version": "v4.3.4",
+            "version": "v5.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263"
+                "reference": "262d033b57c73e8b59cd6e68a45c528318b15038"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263",
-                "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/262d033b57c73e8b59cd6e68a45c528318b15038",
+                "reference": "262d033b57c73e8b59cd6e68a45c528318b15038",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.2.5",
                 "symfony/polyfill-ctype": "~1.8"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Symfony\\Component\\Filesystem\\": ""
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony Filesystem Component",
+            "description": "Provides basic utilities for the filesystem",
             "homepage": "https://symfony.com",
-            "time": "2019-08-20T14:07:54+00:00"
-        },
-        {
-            "name": "theseer/fdomdocument",
-            "version": "1.6.6",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/theseer/fDOMDocument.git",
-                "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/6e8203e40a32a9c770bcb62fe37e68b948da6dca",
-                "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca",
-                "shasum": ""
-            },
-            "require": {
-                "ext-dom": "*",
-                "lib-libxml": "*",
-                "php": ">=5.3.3"
-            },
-            "type": "library",
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
+            "support": {
+                "source": "https://github.com/symfony/filesystem/tree/v5.2.2"
             },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
+            "funding": [
                 {
-                    "name": "Arne Blankerts",
-                    "role": "lead",
-                    "email": "[email protected]"
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
                 }
             ],
-            "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.",
-            "homepage": "https://github.com/theseer/fDOMDocument",
-            "time": "2017-06-30T11:53:12+00:00"
+            "time": "2021-01-27T10:01:46+00:00"
         },
         {
             "name": "theseer/tokenizer",
-            "version": "1.1.3",
+            "version": "1.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/theseer/tokenizer.git",
-                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
+                "reference": "75a63c33a8577608444246075ea0af0d052e452a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
-                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a",
+                "reference": "75a63c33a8577608444246075ea0af0d052e452a",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-tokenizer": "*",
                 "ext-xmlwriter": "*",
-                "php": "^7.0"
+                "php": "^7.2 || ^8.0"
             },
             "type": "library",
             "autoload": {
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "role": "Developer",
-                    "email": "[email protected]"
+                    "email": "[email protected]",
+                    "role": "Developer"
                 }
             ],
             "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
-            "time": "2019-06-13T22:48:21+00:00"
+            "support": {
+                "issues": "https://github.com/theseer/tokenizer/issues",
+                "source": "https://github.com/theseer/tokenizer/tree/master"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/theseer",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-07-12T23:59:07+00:00"
         },
         {
             "name": "webmozart/assert",
-            "version": "1.5.0",
+            "version": "1.9.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/webmozart/assert.git",
-                "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4"
+                "url": "https://github.com/webmozarts/assert.git",
+                "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4",
-                "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4",
+                "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
+                "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3.3 || ^7.0",
+                "php": "^5.3.3 || ^7.0 || ^8.0",
                 "symfony/polyfill-ctype": "^1.8"
             },
+            "conflict": {
+                "phpstan/phpstan": "<0.12.20",
+                "vimeo/psalm": "<3.9.1"
+            },
             "require-dev": {
                 "phpunit/phpunit": "^4.8.36 || ^7.5.13"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.3-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Webmozart\\Assert\\": "src/"
                 "check",
                 "validate"
             ],
-            "time": "2019-08-24T08:43:50+00:00"
-        },
-        {
-            "name": "wnx/laravel-stats",
-            "version": "v2.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/stefanzweifel/laravel-stats.git",
-                "reference": "1b3c60bfbf81233973cbc2a63be4e6f83b2d6205"
+            "support": {
+                "issues": "https://github.com/webmozarts/assert/issues",
+                "source": "https://github.com/webmozarts/assert/tree/1.9.1"
             },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/stefanzweifel/laravel-stats/zipball/1b3c60bfbf81233973cbc2a63be4e6f83b2d6205",
-                "reference": "1b3c60bfbf81233973cbc2a63be4e6f83b2d6205",
-                "shasum": ""
-            },
-            "require": {
-                "illuminate/console": "~5.8.0|^6.0",
-                "illuminate/support": "~5.8.0|^6.0",
-                "php": ">=7.2.0",
-                "phploc/phploc": "~4.0|~5.0",
-                "symfony/finder": "~3.3|~4.0"
-            },
-            "require-dev": {
-                "laravel/browser-kit-testing": "~2.0|~3.0|~4.0|~5.0",
-                "laravel/dusk": "~3.0|~4.0|~5.0",
-                "mockery/mockery": "^1.1",
-                "orchestra/testbench": "^3.8",
-                "phpunit/phpunit": "6.*|7.*|8.*"
-            },
-            "type": "library",
-            "extra": {
-                "laravel": {
-                    "providers": [
-                        "Wnx\\LaravelStats\\StatsServiceProvider"
-                    ]
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Wnx\\LaravelStats\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Stefan Zweifel",
-                    "email": "[email protected]",
-                    "homepage": "https://stefanzweifel.io",
-                    "role": "Developer"
-                }
-            ],
-            "description": "Get insights about your Laravel Project",
-            "homepage": "https://github.com/stefanzweifel/laravel-stats",
-            "keywords": [
-                "laravel",
-                "statistics",
-                "stats",
-                "wnx"
-            ],
-            "time": "2019-09-01T14:18:49+00:00"
+            "time": "2020-07-08T17:02:28+00:00"
         }
     ],
     "aliases": [],
     "prefer-stable": true,
     "prefer-lowest": false,
     "platform": {
-        "php": "^7.2",
+        "php": "^7.2.5",
         "ext-curl": "*",
         "ext-dom": "*",
         "ext-gd": "*",
         "ext-json": "*",
         "ext-mbstring": "*",
-        "ext-tidy": "*",
         "ext-xml": "*"
     },
     "platform-dev": [],
     "platform-overrides": {
-        "php": "7.2.0"
-    }
+        "php": "7.2.5"
+    },
+    "plugin-api-version": "2.0.0"
 }
index ddf3c295d22e09023fad7e62eaefc5c198f454a1..405e5fcf4490a062408dd79569fd41099847ead3 100644 (file)
@@ -21,7 +21,7 @@ $factory->define(\BookStack\Auth\User::class, function ($faker) {
     ];
 });
 
-$factory->define(\BookStack\Entities\Bookshelf::class, function ($faker) {
+$factory->define(\BookStack\Entities\Models\Bookshelf::class, function ($faker) {
     return [
         'name' => $faker->sentence,
         'slug' => Str::random(10),
@@ -29,7 +29,7 @@ $factory->define(\BookStack\Entities\Bookshelf::class, function ($faker) {
     ];
 });
 
-$factory->define(\BookStack\Entities\Book::class, function ($faker) {
+$factory->define(\BookStack\Entities\Models\Book::class, function ($faker) {
     return [
         'name' => $faker->sentence,
         'slug' => Str::random(10),
@@ -37,7 +37,7 @@ $factory->define(\BookStack\Entities\Book::class, function ($faker) {
     ];
 });
 
-$factory->define(\BookStack\Entities\Chapter::class, function ($faker) {
+$factory->define(\BookStack\Entities\Models\Chapter::class, function ($faker) {
     return [
         'name' => $faker->sentence,
         'slug' => Str::random(10),
@@ -45,7 +45,7 @@ $factory->define(\BookStack\Entities\Chapter::class, function ($faker) {
     ];
 });
 
-$factory->define(\BookStack\Entities\Page::class, function ($faker) {
+$factory->define(\BookStack\Entities\Models\Page::class, function ($faker) {
     $html = '<p>' . implode('</p>', $faker->paragraphs(5)) . '</p>';
     return [
         'name' => $faker->sentence,
index eab3216bbdfa6cc02e2b180ff094683a73f5cfdd..9efba0071c3689c6fe5ce13cdc8faa59ae0f7142 100644 (file)
@@ -119,11 +119,11 @@ class CreateBookshelvesTable extends Migration
         Schema::dropIfExists('bookshelves');
 
         // Drop related polymorphic items
-        DB::table('activities')->where('entity_type', '=', 'BookStack\Entities\Bookshelf')->delete();
-        DB::table('views')->where('viewable_type', '=', 'BookStack\Entities\Bookshelf')->delete();
-        DB::table('entity_permissions')->where('restrictable_type', '=', 'BookStack\Entities\Bookshelf')->delete();
-        DB::table('tags')->where('entity_type', '=', 'BookStack\Entities\Bookshelf')->delete();
-        DB::table('search_terms')->where('entity_type', '=', 'BookStack\Entities\Bookshelf')->delete();
-        DB::table('comments')->where('entity_type', '=', 'BookStack\Entities\Bookshelf')->delete();
+        DB::table('activities')->where('entity_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+        DB::table('views')->where('viewable_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+        DB::table('entity_permissions')->where('restrictable_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+        DB::table('tags')->where('entity_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+        DB::table('search_terms')->where('entity_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+        DB::table('comments')->where('entity_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
     }
 }
index a545081986a71e1baca7c2c512b4dd006efd4688..3fcc6822751ea926985fbe9573a5a688ba71b7f0 100644 (file)
@@ -46,9 +46,9 @@ class AddTemplateSupport extends Migration
 
         // Remove templates-manage permission
         $templatesManagePermission = DB::table('role_permissions')
-            ->where('name', '=', 'templates_manage')->first();
+            ->where('name', '=', 'templates-manage')->first();
 
         DB::table('permission_role')->where('permission_id', '=', $templatesManagePermission->id)->delete();
-        DB::table('role_permissions')->where('name', '=', 'templates_manage')->delete();
+        DB::table('role_permissions')->where('name', '=', 'templates-manage')->delete();
     }
 }
diff --git a/database/migrations/2019_12_29_120917_add_api_auth.php b/database/migrations/2019_12_29_120917_add_api_auth.php
new file mode 100644 (file)
index 0000000..eff8824
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\Schema;
+
+class AddApiAuth extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+
+        // Add API tokens table
+        Schema::create('api_tokens', function(Blueprint $table) {
+            $table->increments('id');
+            $table->string('name');
+            $table->string('token_id')->unique();
+            $table->string('secret');
+            $table->integer('user_id')->unsigned()->index();
+            $table->date('expires_at')->index();
+            $table->nullableTimestamps();
+        });
+
+        // Add access-api permission
+        $adminRoleId = DB::table('roles')->where('system_name', '=', 'admin')->first()->id;
+        $permissionId = DB::table('role_permissions')->insertGetId([
+            'name' => 'access-api',
+            'display_name' => 'Access system API',
+            'created_at' => Carbon::now()->toDateTimeString(),
+            'updated_at' => Carbon::now()->toDateTimeString()
+        ]);
+        DB::table('permission_role')->insert([
+            'role_id' => $adminRoleId,
+            'permission_id' => $permissionId
+        ]);
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        // Remove API tokens table
+        Schema::dropIfExists('api_tokens');
+
+        // Remove access-api permission
+        $apiAccessPermission = DB::table('role_permissions')
+            ->where('name', '=', 'access-api')->first();
+
+        DB::table('permission_role')->where('permission_id', '=', $apiAccessPermission->id)->delete();
+        DB::table('role_permissions')->where('name', '=', 'access-api')->delete();
+    }
+}
diff --git a/database/migrations/2020_08_04_111754_drop_joint_permissions_id.php b/database/migrations/2020_08_04_111754_drop_joint_permissions_id.php
new file mode 100644 (file)
index 0000000..bb953a5
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class DropJointPermissionsId extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->dropColumn('id');
+            $table->primary(['role_id', 'entity_type', 'entity_id', 'action'], 'joint_primary');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->dropPrimary(['role_id', 'entity_type', 'entity_id', 'action']);
+        });
+
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->increments('id')->unsigned();
+        });
+    }
+}
diff --git a/database/migrations/2020_08_04_131052_remove_role_name_field.php b/database/migrations/2020_08_04_131052_remove_role_name_field.php
new file mode 100644 (file)
index 0000000..f3cafb7
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+class RemoveRoleNameField extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('roles', function (Blueprint $table) {
+            $table->dropColumn('name');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('roles', function (Blueprint $table) {
+            $table->string('name')->index();
+        });
+
+        DB::table('roles')->update([
+            "name" => DB::raw("lower(replace(`display_name`, ' ', '-'))"),
+        ]);
+    }
+}
diff --git a/database/migrations/2020_09_19_094251_add_activity_indexes.php b/database/migrations/2020_09_19_094251_add_activity_indexes.php
new file mode 100644 (file)
index 0000000..7d6a270
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddActivityIndexes extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('activities', function(Blueprint $table) {
+            $table->index('key');
+            $table->index('created_at');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('activities', function(Blueprint $table) {
+            $table->dropIndex('activities_key_index');
+            $table->dropIndex('activities_created_at_index');
+        });
+    }
+}
diff --git a/database/migrations/2020_09_27_210059_add_entity_soft_deletes.php b/database/migrations/2020_09_27_210059_add_entity_soft_deletes.php
new file mode 100644 (file)
index 0000000..d2b63e8
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddEntitySoftDeletes extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('bookshelves', function(Blueprint  $table) {
+            $table->softDeletes();
+        });
+        Schema::table('books', function(Blueprint  $table) {
+            $table->softDeletes();
+        });
+        Schema::table('chapters', function(Blueprint  $table) {
+            $table->softDeletes();
+        });
+        Schema::table('pages', function(Blueprint  $table) {
+            $table->softDeletes();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('bookshelves', function(Blueprint  $table) {
+            $table->dropSoftDeletes();
+        });
+        Schema::table('books', function(Blueprint  $table) {
+            $table->dropSoftDeletes();
+        });
+        Schema::table('chapters', function(Blueprint  $table) {
+            $table->dropSoftDeletes();
+        });
+        Schema::table('pages', function(Blueprint  $table) {
+            $table->dropSoftDeletes();
+        });
+    }
+}
diff --git a/database/migrations/2020_09_27_210528_create_deletions_table.php b/database/migrations/2020_09_27_210528_create_deletions_table.php
new file mode 100644 (file)
index 0000000..c38a935
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateDeletionsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('deletions', function (Blueprint $table) {
+            $table->increments('id');
+            $table->integer('deleted_by');
+            $table->string('deletable_type', 100);
+            $table->integer('deletable_id');
+            $table->timestamps();
+
+            $table->index('deleted_by');
+            $table->index('deletable_type');
+            $table->index('deletable_id');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('deletions');
+    }
+}
diff --git a/database/migrations/2020_11_07_232321_simplify_activities_table.php b/database/migrations/2020_11_07_232321_simplify_activities_table.php
new file mode 100644 (file)
index 0000000..828dbc6
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+class SimplifyActivitiesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('activities', function (Blueprint $table) {
+            $table->renameColumn('key', 'type');
+            $table->renameColumn('extra', 'detail');
+            $table->dropColumn('book_id');
+            $table->integer('entity_id')->nullable()->change();
+            $table->string('entity_type', 191)->nullable()->change();
+        });
+
+        DB::table('activities')
+            ->where('entity_id', '=', 0)
+            ->update([
+                'entity_id' => null,
+                'entity_type' => null,
+            ]);
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        DB::table('activities')
+            ->whereNull('entity_id')
+            ->update([
+                'entity_id' => 0,
+                'entity_type' => '',
+            ]);
+
+        Schema::table('activities', function (Blueprint $table) {
+            $table->renameColumn('type', 'key');
+            $table->renameColumn('detail', 'extra');
+            $table->integer('book_id');
+
+            $table->integer('entity_id')->change();
+            $table->string('entity_type', 191)->change();
+
+            $table->index('book_id');
+        });
+    }
+}
diff --git a/database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php b/database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php
new file mode 100644 (file)
index 0000000..bf8bf28
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+class AddOwnedByFieldToEntities extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        $tables = ['pages', 'books', 'chapters', 'bookshelves'];
+        foreach ($tables as $table) {
+            Schema::table($table, function (Blueprint $table) {
+                $table->integer('owned_by')->unsigned()->index();
+            });
+
+            DB::table($table)->update(['owned_by' => DB::raw('`created_by`')]);
+        }
+
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->renameColumn('created_by', 'owned_by');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        $tables = ['pages', 'books', 'chapters', 'bookshelves'];
+        foreach ($tables as $table) {
+            Schema::table($table, function (Blueprint $table) {
+                $table->dropColumn('owned_by');
+            });
+        }
+
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->renameColumn('owned_by', 'created_by');
+        });
+    }
+}
index deb1aa11c3b3b196add089e024404eb7c73555a8..611c05246244b426c3c8d711b29909bb8ed6256b 100644 (file)
@@ -1,12 +1,14 @@
 <?php
 
+use BookStack\Api\ApiToken;
 use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\RolePermission;
 use BookStack\Auth\Role;
 use BookStack\Auth\User;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
-use BookStack\Entities\SearchService;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\SearchIndex;
 use Illuminate\Database\Seeder;
 use Illuminate\Support\Str;
 
@@ -29,9 +31,9 @@ class DummyContentSeeder extends Seeder
         $role = Role::getRole('viewer');
         $viewerUser->attachRole($role);
 
-        $byData = ['created_by' => $editorUser->id, 'updated_by' => $editorUser->id];
+        $byData = ['created_by' => $editorUser->id, 'updated_by' => $editorUser->id, 'owned_by' => $editorUser->id];
 
-        factory(\BookStack\Entities\Book::class, 5)->create($byData)
+        factory(\BookStack\Entities\Models\Book::class, 5)->create($byData)
             ->each(function($book) use ($editorUser, $byData) {
                 $chapters = factory(Chapter::class, 3)->create($byData)
                     ->each(function($chapter) use ($editorUser, $book, $byData){
@@ -43,7 +45,7 @@ class DummyContentSeeder extends Seeder
                 $book->pages()->saveMany($pages);
             });
 
-        $largeBook = factory(\BookStack\Entities\Book::class)->create(array_merge($byData, ['name' => 'Large book' . Str::random(10)]));
+        $largeBook = factory(\BookStack\Entities\Models\Book::class)->create(array_merge($byData, ['name' => 'Large book' . Str::random(10)]));
         $pages = factory(Page::class, 200)->make($byData);
         $chapters = factory(Chapter::class, 50)->make($byData);
         $largeBook->pages()->saveMany($pages);
@@ -52,7 +54,19 @@ class DummyContentSeeder extends Seeder
         $shelves = factory(Bookshelf::class, 10)->create($byData);
         $largeBook->shelves()->attach($shelves->pluck('id'));
 
+        // Assign API permission to editor role and create an API key
+        $apiPermission = RolePermission::getByName('access-api');
+        $editorRole->attachPermission($apiPermission);
+        $token = (new ApiToken())->forceFill([
+            'user_id' => $editorUser->id,
+            'name' => 'Testing API key',
+            'expires_at' => ApiToken::defaultExpiry(),
+            'secret' => Hash::make('password'),
+            'token_id' => 'apitoken',
+        ]);
+        $token->save();
+
         app(PermissionService::class)->buildJointPermissions();
-        app(SearchService::class)->indexAllEntities();
+        app(SearchIndex::class)->indexAllEntities();
     }
 }
index 4db10395adf037a48aac650a19b2cc02748f3f84..535626b8f794e8c74fb3d28a8b0bfe6ce7612975 100644 (file)
@@ -3,9 +3,9 @@
 use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Auth\Role;
 use BookStack\Auth\User;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
-use BookStack\Entities\SearchService;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\SearchIndex;
 use Illuminate\Database\Seeder;
 use Illuminate\Support\Str;
 
@@ -23,12 +23,12 @@ class LargeContentSeeder extends Seeder
         $editorRole = Role::getRole('editor');
         $editorUser->attachRole($editorRole);
 
-        $largeBook = factory(\BookStack\Entities\Book::class)->create(['name' => 'Large book' . Str::random(10), 'created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
+        $largeBook = factory(\BookStack\Entities\Models\Book::class)->create(['name' => 'Large book' . Str::random(10), 'created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
         $pages = factory(Page::class, 200)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
         $chapters = factory(Chapter::class, 50)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
         $largeBook->pages()->saveMany($pages);
         $largeBook->chapters()->saveMany($chapters);
         app(PermissionService::class)->buildJointPermissions();
-        app(SearchService::class)->indexAllEntities();
+        app(SearchIndex::class)->indexAllEntities();
     }
 }
diff --git a/dev/api/requests/books-create.json b/dev/api/requests/books-create.json
new file mode 100644 (file)
index 0000000..4a66266
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "name": "My own book",
+  "description": "This is my own little book"
+}
\ No newline at end of file
diff --git a/dev/api/requests/books-update.json b/dev/api/requests/books-update.json
new file mode 100644 (file)
index 0000000..fc67d5f
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "name": "My updated book",
+  "description": "This is my book with updated details"
+}
\ No newline at end of file
diff --git a/dev/api/requests/chapters-create.json b/dev/api/requests/chapters-create.json
new file mode 100644 (file)
index 0000000..ca06fc2
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "book_id": 1,
+  "name": "My fantastic new chapter",
+  "description": "This is a great new chapter that I've created via the API",
+  "tags": [
+    {"name": "Category", "value": "Top Content"},
+    {"name": "Rating", "value": "Highest"}
+  ]
+}
\ No newline at end of file
diff --git a/dev/api/requests/chapters-update.json b/dev/api/requests/chapters-update.json
new file mode 100644 (file)
index 0000000..6bd3a3e
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "book_id": 1,
+  "name": "My fantastic updated chapter",
+  "description": "This is an updated chapter that I've altered via the API",
+  "tags": [
+    {"name": "Category", "value": "Kinda Good Content"},
+    {"name": "Rating", "value": "Medium"}
+  ]
+}
\ No newline at end of file
diff --git a/dev/api/requests/pages-create.json b/dev/api/requests/pages-create.json
new file mode 100644 (file)
index 0000000..1f53b42
--- /dev/null
@@ -0,0 +1,9 @@
+{
+       "book_id": 1,
+       "name": "My API Page",
+       "html": "<p>my new API page</p>",
+       "tags": [
+               {"name": "Category", "value": "Not Bad Content"},
+               {"name": "Rating", "value": "Average"}
+       ]
+}
\ No newline at end of file
diff --git a/dev/api/requests/pages-update.json b/dev/api/requests/pages-update.json
new file mode 100644 (file)
index 0000000..b9bfeb6
--- /dev/null
@@ -0,0 +1,9 @@
+{
+       "chapter_id": 1,
+       "name": "My updated API Page",
+       "html": "<p>my new API page - Updated</p>",
+       "tags": [
+               {"name": "Category", "value": "API Examples"},
+               {"name": "Rating", "value": "Alright"}
+       ]
+}
\ No newline at end of file
diff --git a/dev/api/requests/shelves-create.json b/dev/api/requests/shelves-create.json
new file mode 100644 (file)
index 0000000..39b88af
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "name": "My shelf",
+  "description": "This is my shelf with some books",
+  "books": [5,1,3]
+}
\ No newline at end of file
diff --git a/dev/api/requests/shelves-update.json b/dev/api/requests/shelves-update.json
new file mode 100644 (file)
index 0000000..df5f573
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "name": "My updated shelf",
+  "description": "This is my update shelf with some books",
+  "books": [5,1,3]
+}
\ No newline at end of file
diff --git a/dev/api/responses/books-create.json b/dev/api/responses/books-create.json
new file mode 100644 (file)
index 0000000..124305c
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "name": "My new book",
+  "description": "This is a book created via the API",
+  "created_by": 1,
+  "updated_by": 1,
+  "owned_by": 1,
+  "slug": "my-new-book",
+  "updated_at": "2020-01-12 14:05:11",
+  "created_at": "2020-01-12 14:05:11",
+  "id": 15
+}
\ No newline at end of file
diff --git a/dev/api/responses/books-list.json b/dev/api/responses/books-list.json
new file mode 100644 (file)
index 0000000..9900b5b
--- /dev/null
@@ -0,0 +1,29 @@
+{
+  "data": [
+    {
+      "id": 1,
+      "name": "BookStack User Guide",
+      "slug": "bookstack-user-guide",
+      "description": "This is a general guide on using BookStack on a day-to-day basis.",
+      "created_at": "2019-05-05 21:48:46",
+      "updated_at": "2019-12-11 20:57:31",
+      "created_by": 1,
+      "updated_by": 1,
+      "owned_by": 1,
+      "image_id": 3
+    },
+    {
+      "id": 2,
+      "name": "Inventore inventore quia voluptatem.",
+      "slug": "inventore-inventore-quia-voluptatem",
+      "description": "Veniam nihil voluptas enim laborum corporis quos sint. Ab rerum voluptas ut iste voluptas magni quibusdam ut. Amet omnis enim voluptate neque facilis.",
+      "created_at": "2019-05-05 22:10:14",
+      "updated_at": "2019-12-11 20:57:23",
+      "created_by": 4,
+      "updated_by": 3,
+      "owned_by": 3,
+      "image_id": 34
+    }
+  ],
+  "total": 14
+}
\ No newline at end of file
diff --git a/dev/api/responses/books-read.json b/dev/api/responses/books-read.json
new file mode 100644 (file)
index 0000000..0b0bce4
--- /dev/null
@@ -0,0 +1,40 @@
+{
+  "id": 16,
+  "name": "My own book",
+  "slug": "my-own-book",
+  "description": "This is my own little book",
+  "created_at": "2020-01-12 14:09:59",
+  "updated_at": "2020-01-12 14:11:51",
+  "created_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "updated_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "owned_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "tags": [
+    {
+      "id": 13,
+      "name": "Category",
+      "value": "Guide",
+      "order": 0
+    }
+  ],
+  "cover": {
+    "id": 452,
+    "name": "sjovall_m117hUWMu40.jpg",
+    "url": "https:\/\/p.rizon.top:443\/http\/bookstack.local\/uploads\/images\/cover_book\/2020-01\/sjovall_m117hUWMu40.jpg",
+    "created_at": "2020-01-12 14:11:51",
+    "updated_at": "2020-01-12 14:11:51",
+    "created_by": 1,
+    "updated_by": 1,
+    "path": "\/uploads\/images\/cover_book\/2020-01\/sjovall_m117hUWMu40.jpg",
+    "type": "cover_book",
+    "uploaded_to": 16
+  }
+}
\ No newline at end of file
diff --git a/dev/api/responses/books-update.json b/dev/api/responses/books-update.json
new file mode 100644 (file)
index 0000000..fd93dc9
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "id": 16,
+  "name": "My own book",
+  "slug": "my-own-book",
+  "description": "This is my own little book - updated",
+  "created_at": "2020-01-12 14:09:59",
+  "updated_at": "2020-01-12 14:16:10",
+  "created_by": 1,
+  "updated_by": 1,
+  "owned_by": 1,
+  "image_id": 452
+}
\ No newline at end of file
diff --git a/dev/api/responses/chapters-create.json b/dev/api/responses/chapters-create.json
new file mode 100644 (file)
index 0000000..a990f27
--- /dev/null
@@ -0,0 +1,39 @@
+{
+  "book_id": 1,
+  "priority": 6,
+  "name": "My fantastic new chapter",
+  "description": "This is a great new chapter that I've created via the API",
+  "created_by": 1,
+  "updated_by": 1,
+  "owned_by": 1,
+  "slug": "my-fantastic-new-chapter",
+  "updated_at": "2020-05-22 22:59:55",
+  "created_at": "2020-05-22 22:59:55",
+  "id": 74,
+  "book": {
+    "id": 1,
+    "name": "BookStack User Guide",
+    "slug": "bookstack-user-guide",
+    "description": "This is a general guide on using BookStack on a day-to-day basis.",
+    "created_at": "2019-05-05 21:48:46",
+    "updated_at": "2019-12-11 20:57:31",
+    "created_by": 1,
+    "updated_by": 1
+  },
+  "tags": [
+    {
+      "name": "Category",
+      "value": "Top Content",
+      "order": 0,
+      "created_at": "2020-05-22 22:59:55",
+      "updated_at": "2020-05-22 22:59:55"
+    },
+    {
+      "name": "Rating",
+      "value": "Highest",
+      "order": 0,
+      "created_at": "2020-05-22 22:59:55",
+      "updated_at": "2020-05-22 22:59:55"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/dev/api/responses/chapters-list.json b/dev/api/responses/chapters-list.json
new file mode 100644 (file)
index 0000000..72ed753
--- /dev/null
@@ -0,0 +1,31 @@
+{
+  "data": [
+    {
+      "id": 1,
+      "book_id": 1,
+      "name": "Content Creation",
+      "slug": "content-creation",
+      "description": "How to create documentation on whatever subject you need to write about.",
+      "priority": 3,
+      "created_at": "2019-05-05 21:49:56",
+      "updated_at": "2019-09-28 11:24:23",
+      "created_by": 1,
+      "updated_by": 1,
+      "owned_by": 1
+    },
+    {
+      "id": 2,
+      "book_id": 1,
+      "name": "Managing Content",
+      "slug": "managing-content",
+      "description": "How to keep things organised and orderly in the system for easier navigation and better user experience.",
+      "priority": 5,
+      "created_at": "2019-05-05 21:58:07",
+      "updated_at": "2019-10-17 15:05:34",
+      "created_by": 3,
+      "updated_by": 3,
+      "owned_by": 3
+    }
+  ],
+  "total": 40
+}
\ No newline at end of file
diff --git a/dev/api/responses/chapters-read.json b/dev/api/responses/chapters-read.json
new file mode 100644 (file)
index 0000000..41fed80
--- /dev/null
@@ -0,0 +1,61 @@
+{
+  "id": 1,
+  "book_id": 1,
+  "slug": "content-creation",
+  "name": "Content Creation",
+  "description": "How to create documentation on whatever subject you need to write about.",
+  "priority": 3,
+  "created_at": "2019-05-05 21:49:56",
+  "updated_at": "2019-09-28 11:24:23",
+  "created_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "updated_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "owned_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "tags": [
+    {
+      "name": "Category",
+      "value": "Guide",
+      "order": 0
+    }
+  ],
+  "pages": [
+    {
+      "id": 1,
+      "book_id": 1,
+      "chapter_id": 1,
+      "name": "How to create page content",
+      "slug": "how-to-create-page-content",
+      "priority": 0,
+      "created_at": "2019-05-05 21:49:58",
+      "updated_at": "2019-08-26 14:32:59",
+      "created_by": 1,
+      "updated_by": 1,
+      "draft": false,
+      "revision_count": 2,
+      "template": false
+    },
+    {
+      "id": 7,
+      "book_id": 1,
+      "chapter_id": 1,
+      "name": "Good book structure",
+      "slug": "good-book-structure",
+      "priority": 1,
+      "created_at": "2019-05-05 22:01:55",
+      "updated_at": "2019-06-06 12:03:04",
+      "created_by": 3,
+      "updated_by": 3,
+      "draft": false,
+      "revision_count": 1,
+      "template": false
+    }
+  ]
+}
\ No newline at end of file
diff --git a/dev/api/responses/chapters-update.json b/dev/api/responses/chapters-update.json
new file mode 100644 (file)
index 0000000..11dedd0
--- /dev/null
@@ -0,0 +1,39 @@
+{
+  "id": 75,
+  "book_id": 1,
+  "slug": "my-fantastic-updated-chapter",
+  "name": "My fantastic updated chapter",
+  "description": "This is an updated chapter that I've altered via the API",
+  "priority": 7,
+  "created_at": "2020-05-22 23:03:35",
+  "updated_at": "2020-05-22 23:07:20",
+  "created_by": 1,
+  "updated_by": 1,
+  "owned_by": 1,
+  "book": {
+    "id": 1,
+    "name": "BookStack User Guide",
+    "slug": "bookstack-user-guide",
+    "description": "This is a general guide on using BookStack on a day-to-day basis.",
+    "created_at": "2019-05-05 21:48:46",
+    "updated_at": "2019-12-11 20:57:31",
+    "created_by": 1,
+    "updated_by": 1
+  },
+  "tags": [
+    {
+      "name": "Category",
+      "value": "Kinda Good Content",
+      "order": 0,
+      "created_at": "2020-05-22 23:07:20",
+      "updated_at": "2020-05-22 23:07:20"
+    },
+    {
+      "name": "Rating",
+      "value": "Medium",
+      "order": 0,
+      "created_at": "2020-05-22 23:07:20",
+      "updated_at": "2020-05-22 23:07:20"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/dev/api/responses/pages-create.json b/dev/api/responses/pages-create.json
new file mode 100644 (file)
index 0000000..0b19fb4
--- /dev/null
@@ -0,0 +1,39 @@
+{
+       "id": 358,
+       "book_id": 1,
+       "chapter_id": 0,
+       "name": "My API Page",
+       "slug": "my-api-page",
+       "html": "<p id=\"bkmrk-my-new-api-page\">my new API page</p>",
+       "priority": 14,
+       "created_at": "2020-11-28 15:01:39",
+       "updated_at": "2020-11-28 15:01:39",
+       "created_by": {
+               "id": 1,
+               "name": "Admin"
+       },
+       "updated_by": {
+               "id": 1,
+               "name": "Admin"
+       },
+       "owned_by": {
+               "id": 1,
+               "name": "Admin"
+       },
+       "draft": false,
+       "markdown": "",
+       "revision_count": 1,
+       "template": false,
+       "tags": [
+               {
+                       "name": "Category",
+                       "value": "Not Bad Content",
+                       "order": 0
+               },
+               {
+                       "name": "Rating",
+                       "value": "Average",
+                       "order": 1
+               }
+       ]
+}
\ No newline at end of file
diff --git a/dev/api/responses/pages-list.json b/dev/api/responses/pages-list.json
new file mode 100644 (file)
index 0000000..9c162c6
--- /dev/null
@@ -0,0 +1,50 @@
+{
+       "data": [
+               {
+                       "id": 1,
+                       "book_id": 1,
+                       "chapter_id": 1,
+                       "name": "How to create page content",
+                       "slug": "how-to-create-page-content",
+                       "priority": 0,
+                       "draft": false,
+                       "template": false,
+                       "created_at": "2019-05-05 21:49:58",
+                       "updated_at": "2020-07-04 15:50:58",
+                       "created_by": 1,
+                       "updated_by": 1,
+                       "owned_by": 1
+               },
+               {
+                       "id": 2,
+                       "book_id": 1,
+                       "chapter_id": 1,
+                       "name": "How to use images",
+                       "slug": "how-to-use-images",
+                       "priority": 2,
+                       "draft": false,
+                       "template": false,
+                       "created_at": "2019-05-05 21:53:30",
+                       "updated_at": "2019-06-06 12:03:04",
+                       "created_by": 1,
+                       "updated_by": 1,
+                       "owned_by": 1
+               },
+               {
+                       "id": 3,
+                       "book_id": 1,
+                       "chapter_id": 1,
+                       "name": "Drawings via draw.io",
+                       "slug": "drawings-via-drawio",
+                       "priority": 3,
+                       "draft": false,
+                       "template": false,
+                       "created_at": "2019-05-05 21:53:49",
+                       "updated_at": "2019-12-18 21:56:52",
+                       "created_by": 1,
+                       "updated_by": 1,
+                       "owned_by": 1
+               }
+       ],
+       "total": 322
+}
\ No newline at end of file
diff --git a/dev/api/responses/pages-read.json b/dev/api/responses/pages-read.json
new file mode 100644 (file)
index 0000000..93f7770
--- /dev/null
@@ -0,0 +1,39 @@
+{
+       "id": 306,
+       "book_id": 1,
+       "chapter_id": 0,
+       "name": "A page written in markdown",
+       "slug": "a-page-written-in-markdown",
+       "html": "<h1 id=\"bkmrk-how-this-is-built\">How this is built</h1>\r\n<p id=\"bkmrk-this-page-is-written\">This page is written in markdown. BookStack stores the page data in HTML.</p>\r\n<p id=\"bkmrk-here%27s-a-cute-pictur\">Here's a cute picture of my cat:</p>\r\n<p id=\"bkmrk-\"><a href=\"http://example.com/uploads/images/gallery/2020-04/yXSrubes.jpg\"><img src=\"http://example.com/uploads/images/gallery/2020-04/scaled-1680-/yXSrubes.jpg\" alt=\"yXSrubes.jpg\"></a></p>",
+       "priority": 13,
+       "created_at": "2020-02-02 21:40:38",
+       "updated_at": "2020-11-28 14:43:20",
+       "created_by": {
+               "id": 1,
+               "name": "Admin"
+       },
+       "updated_by": {
+               "id": 1,
+               "name": "Admin"
+       },
+       "owned_by": {
+               "id": 1,
+               "name": "Admin"
+       },
+       "draft": false,
+       "markdown": "# How this is built\r\n\r\nThis page is written in markdown. BookStack stores the page data in HTML.\r\n\r\nHere's a cute picture of my cat:\r\n\r\n[![yXSrubes.jpg](http://example.com/uploads/images/gallery/2020-04/scaled-1680-/yXSrubes.jpg)](http://example.com/uploads/images/gallery/2020-04/yXSrubes.jpg)",
+       "revision_count": 5,
+       "template": false,
+       "tags": [
+               {
+                       "name": "Category",
+                       "value": "Top Content",
+                       "order": 0
+               },
+               {
+                       "name": "Animal",
+                       "value": "Cat",
+                       "order": 1
+               }
+       ]
+}
\ No newline at end of file
diff --git a/dev/api/responses/pages-update.json b/dev/api/responses/pages-update.json
new file mode 100644 (file)
index 0000000..ae5c0ea
--- /dev/null
@@ -0,0 +1,39 @@
+{
+       "id": 361,
+       "book_id": 1,
+       "chapter_id": 1,
+       "name": "My updated API Page",
+       "slug": "my-updated-api-page",
+       "html": "<p id=\"bkmrk-my-new-api-page---up\">my new API page - Updated</p>",
+       "priority": 16,
+       "created_at": "2020-11-28 15:10:54",
+       "updated_at": "2020-11-28 15:13:03",
+       "created_by": {
+               "id": 1,
+               "name": "Admin"
+       },
+       "updated_by": {
+               "id": 1,
+               "name": "Admin"
+       },
+       "owned_by": {
+               "id": 1,
+               "name": "Admin"
+       },
+       "draft": false,
+       "markdown": "",
+       "revision_count": 5,
+       "template": false,
+       "tags": [
+               {
+                       "name": "Category",
+                       "value": "API Examples",
+                       "order": 0
+               },
+               {
+                       "name": "Rating",
+                       "value": "Alright",
+                       "order": 0
+               }
+       ]
+}
\ No newline at end of file
diff --git a/dev/api/responses/shelves-create.json b/dev/api/responses/shelves-create.json
new file mode 100644 (file)
index 0000000..fafa4c9
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "name": "My shelf",
+  "description": "This is my shelf with some books",
+  "created_by": 1,
+  "updated_by": 1,
+  "owned_by": 1,
+  "slug": "my-shelf",
+  "updated_at": "2020-04-10 13:24:09",
+  "created_at": "2020-04-10 13:24:09",
+  "id": 14
+}
\ No newline at end of file
diff --git a/dev/api/responses/shelves-list.json b/dev/api/responses/shelves-list.json
new file mode 100644 (file)
index 0000000..f5e9d03
--- /dev/null
@@ -0,0 +1,41 @@
+{
+  "data": [
+    {
+      "id": 8,
+      "name": "Qui qui aspernatur autem molestiae libero necessitatibus molestias.",
+      "slug": "qui-qui-aspernatur-autem-molestiae-libero-necessitatibus-molestias",
+      "description": "Enim dolor ut quia error dolores est. Aut distinctio consequuntur non nisi nostrum. Labore cupiditate error labore aliquid provident impedit voluptatibus. Quaerat impedit excepturi eius qui eius voluptatem reiciendis.",
+      "created_at": "2019-05-05 22:10:16",
+      "updated_at": "2020-04-10 13:00:45",
+      "created_by": 4,
+      "updated_by": 1,
+      "owned_by": 1,
+      "image_id": 31
+    },
+    {
+      "id": 9,
+      "name": "Ipsum aut inventore fuga libero non facilis.",
+      "slug": "ipsum-aut-inventore-fuga-libero-non-facilis",
+      "description": "Labore culpa modi perspiciatis harum sit. Maxime non et nam est. Quae ut laboriosam repellendus sunt quisquam. Velit at est perspiciatis nesciunt adipisci nobis illo. Sed possimus odit optio officiis nisi voluptates officiis dolor.",
+      "created_at": "2019-05-05 22:10:16",
+      "updated_at": "2020-04-10 13:00:58",
+      "created_by": 4,
+      "updated_by": 1,
+      "owned_by": 1,
+      "image_id": 28
+    },
+    {
+      "id": 10,
+      "name": "Omnis reiciendis aut molestias sint accusantium.",
+      "slug": "omnis-reiciendis-aut-molestias-sint-accusantium",
+      "description": "Qui ea occaecati alias est dolores voluptatem doloribus. Ad reiciendis corporis vero nostrum omnis et. Non doloribus ut eaque ut quos dolores.",
+      "created_at": "2019-05-05 22:10:16",
+      "updated_at": "2020-04-10 13:00:53",
+      "created_by": 4,
+      "updated_by": 1,
+      "owned_by": 4,
+      "image_id": 30
+    }
+  ],
+  "total": 3
+}
\ No newline at end of file
diff --git a/dev/api/responses/shelves-read.json b/dev/api/responses/shelves-read.json
new file mode 100644 (file)
index 0000000..d663e82
--- /dev/null
@@ -0,0 +1,57 @@
+{
+  "id": 14,
+  "name": "My shelf",
+  "slug": "my-shelf",
+  "description": "This is my shelf with some books",
+  "created_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "updated_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "owned_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "created_at": "2020-04-10 13:24:09",
+  "updated_at": "2020-04-10 13:31:04",
+  "tags": [
+    {
+      "id": 16,
+      "name": "Category",
+      "value": "Guide",
+      "order": 0
+    }
+  ],
+  "cover": {
+    "id": 501,
+    "name": "anafrancisconi_Sp04AfFCPNM.jpg",
+    "url": "http://bookstack.local/uploads/images/cover_book/2020-04/anafrancisconi_Sp04AfFCPNM.jpg",
+    "created_at": "2020-04-10 13:31:04",
+    "updated_at": "2020-04-10 13:31:04",
+    "created_by": 1,
+    "updated_by": 1,
+    "path": "/uploads/images/cover_book/2020-04/anafrancisconi_Sp04AfFCPNM.jpg",
+    "type": "cover_book",
+    "uploaded_to": 14
+  },
+  "books": [
+    {
+      "id": 5,
+      "name": "Sint explicabo alias sunt.",
+      "slug": "jbsQrzuaXe"
+    },
+    {
+      "id": 1,
+      "name": "BookStack User Guide",
+      "slug": "bookstack-user-guide"
+    },
+    {
+      "id": 3,
+      "name": "Molestiae doloribus sint velit suscipit dolorem.",
+      "slug": "H99QxALaoG"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/dev/api/responses/shelves-update.json b/dev/api/responses/shelves-update.json
new file mode 100644 (file)
index 0000000..4bde44b
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "id": 14,
+  "name": "My updated shelf",
+  "slug": "my-updated-shelf",
+  "description": "This is my update shelf with some books",
+  "created_by": 1,
+  "updated_by": 1,
+  "owned_by": 1,
+  "image_id": 501,
+  "created_at": "2020-04-10 13:24:09",
+  "updated_at": "2020-04-10 13:48:22"
+}
\ No newline at end of file
index ff44f0c8d3f5d7b3af67a55818022805d8d113cf..e91d34a713377a7e579a6594000c01fb5c98ba57 100755 (executable)
@@ -7,8 +7,9 @@ env
 if [[ -n "$1" ]]; then
     exec "$@"
 else
+    composer install
     wait-for-it db:3306 -t 45
     php artisan migrate --database=mysql
     chown -R www-data:www-data storage
     exec apache2-foreground
-fi
\ No newline at end of file
+fi
index e59e1e8a027b55f73a7466812754692a468d9185..a8f33fd3d93c2be93d34f5c3bb4b01f7578815de 100755 (executable)
@@ -5,4 +5,4 @@ set -e
 npm install
 npm rebuild node-sass
 
-exec npm run watch
\ No newline at end of file
+SHELL=/bin/sh exec npm run watch
diff --git a/dev/docs/components.md b/dev/docs/components.md
new file mode 100644 (file)
index 0000000..832765d
--- /dev/null
@@ -0,0 +1,99 @@
+# JavaScript Components
+
+This document details the format for JavaScript components in BookStack. This is a really simple class-based setup with a few helpers provided.
+
+#### Defining a Component in JS
+
+```js
+class Dropdown {
+    setup() {
+        this.toggle = this.$refs.toggle;
+        this.menu = this.$refs.menu;
+    
+        this.speed = parseInt(this.$opts.speed);
+    }
+}
+```
+
+All usage of $refs, $manyRefs and $opts should be done at the top of the `setup` function so any requirements can be easily seen.
+
+#### Using a Component in HTML
+
+A component is used like so:
+
+```html
+<div component="dropdown"></div>
+
+<!-- or, for multiple -->
+
+<div components="dropdown image-picker"></div>
+```
+
+The names will be parsed and new component instance will be created if a matching name is found in the `components/index.js` componentMapping. 
+
+#### Element References
+
+Within a component you'll often need to refer to other element instances. This can be done like so:
+
+```html
+<div component="dropdown">
+    <span refs="dropdown@toggle othercomponent@handle">View more</span>
+</div>
+```
+
+You can then access the span element as `this.$refs.toggle` in your component.
+
+#### Component Options
+
+```html
+<div component="dropdown"
+    option:dropdown:delay="500"
+    option:dropdown:show>
+</div>
+```
+
+Will result with `this.$opts` being:
+
+```json
+{
+    "delay": "500",
+    "show": ""  
+}
+```
+
+#### Global Helpers
+
+There are various global helper libraries which can be used in components:
+
+```js
+// HTTP service
+window.$http.get(url, params);
+window.$http.post(url, data);
+window.$http.put(url, data);
+window.$http.delete(url, data);
+window.$http.patch(url, data);
+
+// Global event system
+// Emit a global event
+window.$events.emit(eventName, eventData);
+// Listen to a global event
+window.$events.listen(eventName, callback);
+// Show a success message
+window.$events.success(message);
+// Show an error message
+window.$events.error(message);
+// Show validation errors, if existing, as an error notification
+window.$events.showValidationErrors(error);
+
+// Translator
+// Take the given plural text and count to decide on what plural option
+// to use, Similar to laravel's trans_choice function but instead
+// takes the direction directly instead of a translation key.
+window.trans_plural(translationString, count, replacements);
+
+// Component System
+// Parse and initialise any components from the given root el down.
+window.components.init(rootEl);
+// Get the first active component of the given name
+window.components.first(name);
+```
\ No newline at end of file
index 47afc27a15d7109a2bdec012a5735641f71ef44a..06f13e8d296d5b89c7d5c5e0daf688f561ae8359 100644 (file)
   "requires": true,
   "lockfileVersion": 1,
   "dependencies": {
-    "@webassemblyjs/ast": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
-      "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==",
+    "ansi-regex": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+      "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/helper-module-context": "1.8.5",
-        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
-        "@webassemblyjs/wast-parser": "1.8.5"
+        "color-convert": "^1.9.0"
       }
     },
-    "@webassemblyjs/floating-point-hex-parser": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz",
-      "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-api-error": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz",
-      "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-buffer": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz",
-      "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-code-frame": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz",
-      "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==",
+    "anymatch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+      "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/wast-printer": "1.8.5"
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
       }
     },
-    "@webassemblyjs/helper-fsm": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz",
-      "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-module-context": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz",
-      "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==",
-      "dev": true,
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
       "requires": {
-        "@webassemblyjs/ast": "1.8.5",
-        "mamacro": "^0.0.3"
+        "sprintf-js": "~1.0.2"
       }
     },
-    "@webassemblyjs/helper-wasm-bytecode": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz",
-      "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==",
+    "async-limiter": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
       "dev": true
     },
-    "@webassemblyjs/helper-wasm-section": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz",
-      "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.8.5",
-        "@webassemblyjs/helper-buffer": "1.8.5",
-        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
-        "@webassemblyjs/wasm-gen": "1.8.5"
-      }
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "binary-extensions": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+      "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+      "dev": true
     },
-    "@webassemblyjs/ieee754": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz",
-      "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==",
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
       "requires": {
-        "@xtuc/ieee754": "^1.2.0"
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
-    "@webassemblyjs/leb128": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz",
-      "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==",
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
       "dev": true,
       "requires": {
-        "@xtuc/long": "4.2.2"
+        "fill-range": "^7.0.1"
       }
     },
-    "@webassemblyjs/utf8": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz",
-      "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==",
+    "camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
       "dev": true
     },
-    "@webassemblyjs/wasm-edit": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz",
-      "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==",
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.8.5",
-        "@webassemblyjs/helper-buffer": "1.8.5",
-        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
-        "@webassemblyjs/helper-wasm-section": "1.8.5",
-        "@webassemblyjs/wasm-gen": "1.8.5",
-        "@webassemblyjs/wasm-opt": "1.8.5",
-        "@webassemblyjs/wasm-parser": "1.8.5",
-        "@webassemblyjs/wast-printer": "1.8.5"
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
       }
     },
-    "@webassemblyjs/wasm-gen": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz",
-      "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==",
+    "chokidar": {
+      "version": "3.4.2",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+      "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.8.5",
-        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
-        "@webassemblyjs/ieee754": "1.8.5",
-        "@webassemblyjs/leb128": "1.8.5",
-        "@webassemblyjs/utf8": "1.8.5"
+        "anymatch": "~3.1.1",
+        "braces": "~3.0.2",
+        "fsevents": "~2.1.2",
+        "glob-parent": "~5.1.0",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.4.0"
       }
     },
-    "@webassemblyjs/wasm-opt": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz",
-      "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==",
+    "chokidar-cli": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-2.1.0.tgz",
+      "integrity": "sha512-6n21AVpW6ywuEPoxJcLXMA2p4T+SLjWsXKny/9yTWFz0kKxESI3eUylpeV97LylING/27T/RVTY0f2/0QaWq9Q==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.8.5",
-        "@webassemblyjs/helper-buffer": "1.8.5",
-        "@webassemblyjs/wasm-gen": "1.8.5",
-        "@webassemblyjs/wasm-parser": "1.8.5"
+        "chokidar": "^3.2.3",
+        "lodash.debounce": "^4.0.8",
+        "lodash.throttle": "^4.1.1",
+        "yargs": "^13.3.0"
       }
     },
-    "@webassemblyjs/wasm-parser": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz",
-      "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==",
-      "dev": true,
+    "clipboard": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
+      "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
       "requires": {
-        "@webassemblyjs/ast": "1.8.5",
-        "@webassemblyjs/helper-api-error": "1.8.5",
-        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
-        "@webassemblyjs/ieee754": "1.8.5",
-        "@webassemblyjs/leb128": "1.8.5",
-        "@webassemblyjs/utf8": "1.8.5"
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
       }
     },
-    "@webassemblyjs/wast-parser": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz",
-      "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==",
+    "cliui": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.8.5",
-        "@webassemblyjs/floating-point-hex-parser": "1.8.5",
-        "@webassemblyjs/helper-api-error": "1.8.5",
-        "@webassemblyjs/helper-code-frame": "1.8.5",
-        "@webassemblyjs/helper-fsm": "1.8.5",
-        "@xtuc/long": "4.2.2"
+        "string-width": "^3.1.0",
+        "strip-ansi": "^5.2.0",
+        "wrap-ansi": "^5.1.0"
       }
     },
-    "@webassemblyjs/wast-printer": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz",
-      "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==",
+    "codemirror": {
+      "version": "5.58.1",
+      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.58.1.tgz",
+      "integrity": "sha512-UGb/ueu20U4xqWk8hZB3xIfV2/SFqnSLYONiM3wTMDqko0bsYrsAkGGhqUzbRkYm89aBKPyHtuNEbVWF9FTFzw=="
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.8.5",
-        "@webassemblyjs/wast-parser": "1.8.5",
-        "@xtuc/long": "4.2.2"
+        "color-name": "1.1.3"
       }
     },
-    "@xtuc/ieee754": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
-      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
-      "dev": true
-    },
-    "@xtuc/long": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
-      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
       "dev": true
     },
-    "abbrev": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
       "dev": true
     },
-    "acorn": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
-      "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
-      "dev": true
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+      "dev": true,
+      "requires": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      }
     },
-    "acorn-dynamic-import": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
-      "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
       "dev": true
     },
-    "ajv": {
-      "version": "6.10.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
-      "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
       "dev": true,
       "requires": {
-        "fast-deep-equal": "^2.0.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
+        "object-keys": "^1.0.12"
       }
     },
-    "ajv-errors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz",
-      "integrity": "sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=",
-      "dev": true
+    "delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
     },
-    "ajv-keywords": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz",
-      "integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=",
-      "dev": true
+    "dropzone": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.7.2.tgz",
+      "integrity": "sha512-m217bJHtf0J1IiKn4Tv6mnu1h5QvQNBnKZ39gma7hzGQhIZMxYq1vYEHs4AVd4ThFwmALys+52NAOD4zdLTG4w=="
     },
-    "amdefine": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
-      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+    "emoji-regex": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+      "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
       "dev": true
     },
-    "ansi-regex": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-      "dev": true
+    "entities": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
+      "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
     },
-    "ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
       "dev": true,
       "requires": {
-        "color-convert": "^1.9.0"
+        "is-arrayish": "^0.2.1"
       }
     },
-    "anymatch": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-      "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+    "es-abstract": {
+      "version": "1.17.6",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+      "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
       "dev": true,
       "requires": {
-        "micromatch": "^3.1.4",
-        "normalize-path": "^2.1.1"
-      },
-      "dependencies": {
-        "normalize-path": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-          "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
-          "dev": true,
-          "requires": {
-            "remove-trailing-separator": "^1.0.1"
-          }
-        }
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1",
+        "is-callable": "^1.2.0",
+        "is-regex": "^1.1.0",
+        "object-inspect": "^1.7.0",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.0",
+        "string.prototype.trimend": "^1.0.1",
+        "string.prototype.trimstart": "^1.0.1"
       }
     },
-    "aproba": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-      "dev": true
-    },
-    "are-we-there-yet": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
-      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+    "es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
       "dev": true,
       "requires": {
-        "delegates": "^1.0.0",
-        "readable-stream": "^2.0.6"
-      }
-    },
-    "argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "requires": {
-        "sprintf-js": "~1.0.2"
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
       }
     },
-    "arr-diff": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-      "dev": true
-    },
-    "arr-flatten": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
-      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+    "esbuild": {
+      "version": "0.7.8",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.7.8.tgz",
+      "integrity": "sha512-6UT1nZB+8ja5avctUC6d3kGOUAhy6/ZYHljL4nk3++1ipadghBhUCAcwsTHsmUvdu04CcGKzo13mE+ZQ2O3zrA==",
       "dev": true
     },
-    "arr-union": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
       "dev": true
     },
-    "array-filter": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
-      "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
-      "dev": true
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
     },
-    "array-find-index": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
-      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
-      "dev": true
+    "find-up": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+      "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+      "dev": true,
+      "requires": {
+        "locate-path": "^3.0.0"
+      }
     },
-    "array-map": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
-      "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
-      "dev": true
+    "fsevents": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "dev": true,
+      "optional": true
     },
-    "array-reduce": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
-      "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
       "dev": true
     },
-    "array-unique": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
       "dev": true
     },
-    "asn1": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+    "glob-parent": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+      "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
       "dev": true,
       "requires": {
-        "safer-buffer": "~2.1.0"
+        "is-glob": "^4.0.1"
       }
     },
-    "asn1.js": {
-      "version": "4.10.1",
-      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
-      "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
-      "dev": true,
+    "good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
       "requires": {
-        "bn.js": "^4.0.0",
-        "inherits": "^2.0.1",
-        "minimalistic-assert": "^1.0.0"
+        "delegate": "^3.1.2"
       }
     },
-    "assert": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
-      "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+    "graceful-fs": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+      "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+      "dev": true
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
       "dev": true,
       "requires": {
-        "object-assign": "^4.1.1",
-        "util": "0.10.3"
-      },
-      "dependencies": {
-        "inherits": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
-          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
-          "dev": true
-        },
-        "util": {
-          "version": "0.10.3",
-          "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
-          "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
-          "dev": true,
-          "requires": {
-            "inherits": "2.0.1"
-          }
-        }
+        "function-bind": "^1.1.1"
       }
     },
-    "assert-plus": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-      "dev": true
-    },
-    "assign-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
       "dev": true
     },
-    "async-each": {
+    "has-symbols": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
-      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
-      "dev": true
-    },
-    "async-foreach": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
-      "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
-      "dev": true
-    },
-    "asynckit": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
-    },
-    "atob": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
-      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
-      "dev": true
-    },
-    "aws-sign2": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+      "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
       "dev": true
     },
-    "aws4": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+    "hosted-git-info": {
+      "version": "2.8.8",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+      "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
       "dev": true
     },
-    "balanced-match": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
       "dev": true
     },
-    "base": {
-      "version": "0.11.2",
-      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
-      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
       "dev": true,
       "requires": {
-        "cache-base": "^1.0.1",
-        "class-utils": "^0.3.5",
-        "component-emitter": "^1.2.1",
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.1",
-        "mixin-deep": "^1.2.0",
-        "pascalcase": "^0.1.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
+        "binary-extensions": "^2.0.0"
       }
     },
-    "base64-js": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
-      "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
+    "is-callable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
+      "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==",
       "dev": true
     },
-    "bcrypt-pbkdf": {
+    "is-date-object": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
-      "dev": true,
-      "requires": {
-        "tweetnacl": "^0.14.3"
-      }
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+      "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+      "dev": true
     },
-    "big.js": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
-      "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
       "dev": true
     },
-    "binary-extensions": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz",
-      "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=",
+    "is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
       "dev": true
     },
-    "block-stream": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
-      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
       "dev": true,
       "requires": {
-        "inherits": "~2.0.0"
+        "is-extglob": "^2.1.1"
       }
     },
-    "bluebird": {
-      "version": "3.5.5",
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
-      "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==",
-      "dev": true
-    },
-    "bn.js": {
-      "version": "4.11.8",
-      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
-      "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
       "dev": true
     },
-    "brace-expansion": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
-      "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
+    "is-regex": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+      "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
       "dev": true,
       "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "has-symbols": "^1.0.1"
       }
     },
-    "braces": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+    "is-symbol": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+      "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
       "dev": true,
       "requires": {
-        "arr-flatten": "^1.1.0",
-        "array-unique": "^0.3.2",
-        "extend-shallow": "^2.0.1",
-        "fill-range": "^4.0.0",
-        "isobject": "^3.0.1",
-        "repeat-element": "^1.1.2",
-        "snapdragon": "^0.8.1",
-        "snapdragon-node": "^2.0.1",
-        "split-string": "^3.0.2",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
+        "has-symbols": "^1.0.1"
       }
     },
-    "brorand": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
-      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
       "dev": true
     },
-    "browserify-aes": {
-      "version": "1.2.0",
-      "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
-      "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
-      "dev": true,
-      "requires": {
-        "buffer-xor": "^1.0.3",
-        "cipher-base": "^1.0.0",
-        "create-hash": "^1.1.0",
-        "evp_bytestokey": "^1.0.3",
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "browserify-cipher": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
-      "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
-      "dev": true,
-      "requires": {
-        "browserify-aes": "^1.0.4",
-        "browserify-des": "^1.0.0",
-        "evp_bytestokey": "^1.0.0"
-      }
-    },
-    "browserify-des": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
-      "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
-      "dev": true,
-      "requires": {
-        "cipher-base": "^1.0.1",
-        "des.js": "^1.0.0",
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
-      }
-    },
-    "browserify-rsa": {
-      "version": "4.0.1",
-      "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
-      "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.0",
-        "randombytes": "^2.0.1"
-      }
-    },
-    "browserify-sign": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
-      "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.1",
-        "browserify-rsa": "^4.0.0",
-        "create-hash": "^1.1.0",
-        "create-hmac": "^1.1.2",
-        "elliptic": "^6.0.0",
-        "inherits": "^2.0.1",
-        "parse-asn1": "^5.0.0"
-      }
-    },
-    "browserify-zlib": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
-      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
-      "dev": true,
-      "requires": {
-        "pako": "~1.0.5"
-      }
-    },
-    "buffer": {
-      "version": "4.9.1",
-      "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
-      "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
-      "dev": true,
-      "requires": {
-        "base64-js": "^1.0.2",
-        "ieee754": "^1.1.4",
-        "isarray": "^1.0.0"
-      }
-    },
-    "buffer-from": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
-      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
-      "dev": true
-    },
-    "buffer-xor": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
-      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
-      "dev": true
-    },
-    "builtin-modules": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
-      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
-      "dev": true
-    },
-    "builtin-status-codes": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
-      "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
-      "dev": true
-    },
-    "cacache": {
-      "version": "11.3.2",
-      "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz",
-      "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==",
-      "dev": true,
-      "requires": {
-        "bluebird": "^3.5.3",
-        "chownr": "^1.1.1",
-        "figgy-pudding": "^3.5.1",
-        "glob": "^7.1.3",
-        "graceful-fs": "^4.1.15",
-        "lru-cache": "^5.1.1",
-        "mississippi": "^3.0.0",
-        "mkdirp": "^0.5.1",
-        "move-concurrently": "^1.0.1",
-        "promise-inflight": "^1.0.1",
-        "rimraf": "^2.6.2",
-        "ssri": "^6.0.1",
-        "unique-filename": "^1.1.1",
-        "y18n": "^4.0.0"
-      },
-      "dependencies": {
-        "graceful-fs": {
-          "version": "4.1.15",
-          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
-          "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
-          "dev": true
-        },
-        "lru-cache": {
-          "version": "5.1.1",
-          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-          "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
-          "dev": true,
-          "requires": {
-            "yallist": "^3.0.2"
-          }
-        },
-        "y18n": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
-          "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
-          "dev": true
-        },
-        "yallist": {
-          "version": "3.0.3",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
-          "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
-          "dev": true
-        }
-      }
-    },
-    "cache-base": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
-      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
-      "dev": true,
-      "requires": {
-        "collection-visit": "^1.0.0",
-        "component-emitter": "^1.2.1",
-        "get-value": "^2.0.6",
-        "has-value": "^1.0.0",
-        "isobject": "^3.0.1",
-        "set-value": "^2.0.0",
-        "to-object-path": "^0.3.0",
-        "union-value": "^1.0.0",
-        "unset-value": "^1.0.0"
-      }
-    },
-    "camelcase": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-      "dev": true
-    },
-    "camelcase-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
-      "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
-      "dev": true,
-      "requires": {
-        "camelcase": "^2.0.0",
-        "map-obj": "^1.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
-          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
-          "dev": true
-        }
-      }
-    },
-    "caseless": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
-      "dev": true
-    },
-    "chalk": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-      "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      }
-    },
-    "chokidar": {
-      "version": "2.1.6",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz",
-      "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==",
-      "dev": true,
-      "requires": {
-        "anymatch": "^2.0.0",
-        "async-each": "^1.0.1",
-        "braces": "^2.3.2",
-        "fsevents": "^1.2.7",
-        "glob-parent": "^3.1.0",
-        "inherits": "^2.0.3",
-        "is-binary-path": "^1.0.0",
-        "is-glob": "^4.0.0",
-        "normalize-path": "^3.0.0",
-        "path-is-absolute": "^1.0.0",
-        "readdirp": "^2.2.1",
-        "upath": "^1.1.1"
-      }
-    },
-    "chownr": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
-      "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
-      "dev": true
-    },
-    "chrome-trace-event": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
-      "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==",
-      "dev": true,
-      "requires": {
-        "tslib": "^1.9.0"
-      }
-    },
-    "cipher-base": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
-      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "class-utils": {
-      "version": "0.3.6",
-      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
-      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
-      "dev": true,
-      "requires": {
-        "arr-union": "^3.1.0",
-        "define-property": "^0.2.5",
-        "isobject": "^3.0.0",
-        "static-extend": "^0.1.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        }
-      }
-    },
-    "clipboard": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz",
-      "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==",
-      "requires": {
-        "good-listener": "^1.2.2",
-        "select": "^1.1.2",
-        "tiny-emitter": "^2.0.0"
-      }
-    },
-    "cliui": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
-      "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
-      "dev": true,
-      "requires": {
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wrap-ansi": "^2.0.0"
-      }
-    },
-    "clone-deep": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz",
-      "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==",
-      "dev": true,
-      "requires": {
-        "for-own": "^1.0.0",
-        "is-plain-object": "^2.0.4",
-        "kind-of": "^6.0.0",
-        "shallow-clone": "^1.0.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
-      }
-    },
-    "code-point-at": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-      "dev": true
-    },
-    "codemirror": {
-      "version": "5.47.0",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.47.0.tgz",
-      "integrity": "sha512-kV49Fr+NGFHFc/Imsx6g180hSlkGhuHxTSDDmDHOuyln0MQYFLixDY4+bFkBVeCEiepYfDimAF/e++9jPJk4QA=="
-    },
-    "collection-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
-      "dev": true,
-      "requires": {
-        "map-visit": "^1.0.0",
-        "object-visit": "^1.0.0"
-      }
-    },
-    "color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "requires": {
-        "color-name": "1.1.3"
-      }
-    },
-    "color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-      "dev": true
-    },
-    "combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
-      "requires": {
-        "delayed-stream": "~1.0.0"
-      }
-    },
-    "commander": {
-      "version": "2.20.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
-      "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
-      "dev": true
-    },
-    "commondir": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-      "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
-      "dev": true
-    },
-    "component-emitter": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
-      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
-      "dev": true
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
-    },
-    "concat-stream": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
-      "dev": true,
-      "requires": {
-        "buffer-from": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.2.2",
-        "typedarray": "^0.0.6"
-      }
-    },
-    "console-browserify": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
-      "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
-      "dev": true,
-      "requires": {
-        "date-now": "^0.1.4"
-      }
-    },
-    "console-control-strings": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-      "dev": true
-    },
-    "constants-browserify": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
-      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
-      "dev": true
-    },
-    "copy-concurrently": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
-      "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
-      "dev": true,
-      "requires": {
-        "aproba": "^1.1.1",
-        "fs-write-stream-atomic": "^1.0.8",
-        "iferr": "^0.1.5",
-        "mkdirp": "^0.5.1",
-        "rimraf": "^2.5.4",
-        "run-queue": "^1.0.0"
-      }
-    },
-    "copy-descriptor": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
-      "dev": true
-    },
-    "core-util-is": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-      "dev": true
-    },
-    "create-ecdh": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
-      "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.0",
-        "elliptic": "^6.0.0"
-      }
-    },
-    "create-hash": {
-      "version": "1.2.0",
-      "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
-      "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
-      "dev": true,
-      "requires": {
-        "cipher-base": "^1.0.1",
-        "inherits": "^2.0.1",
-        "md5.js": "^1.3.4",
-        "ripemd160": "^2.0.1",
-        "sha.js": "^2.4.0"
-      }
-    },
-    "create-hmac": {
-      "version": "1.1.7",
-      "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
-      "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
-      "dev": true,
-      "requires": {
-        "cipher-base": "^1.0.3",
-        "create-hash": "^1.1.0",
-        "inherits": "^2.0.1",
-        "ripemd160": "^2.0.0",
-        "safe-buffer": "^5.0.1",
-        "sha.js": "^2.4.8"
-      }
-    },
-    "cross-spawn": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
-      "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
-      "dev": true,
-      "requires": {
-        "lru-cache": "^4.0.1",
-        "which": "^1.2.9"
-      }
-    },
-    "crypto-browserify": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
-      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
-      "dev": true,
-      "requires": {
-        "browserify-cipher": "^1.0.0",
-        "browserify-sign": "^4.0.0",
-        "create-ecdh": "^4.0.0",
-        "create-hash": "^1.1.0",
-        "create-hmac": "^1.1.0",
-        "diffie-hellman": "^5.0.0",
-        "inherits": "^2.0.1",
-        "pbkdf2": "^3.0.3",
-        "public-encrypt": "^4.0.0",
-        "randombytes": "^2.0.0",
-        "randomfill": "^1.0.3"
-      }
-    },
-    "css-loader": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz",
-      "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==",
-      "dev": true,
-      "requires": {
-        "camelcase": "^5.2.0",
-        "icss-utils": "^4.1.0",
-        "loader-utils": "^1.2.3",
-        "normalize-path": "^3.0.0",
-        "postcss": "^7.0.14",
-        "postcss-modules-extract-imports": "^2.0.0",
-        "postcss-modules-local-by-default": "^2.0.6",
-        "postcss-modules-scope": "^2.1.0",
-        "postcss-modules-values": "^2.0.0",
-        "postcss-value-parser": "^3.3.0",
-        "schema-utils": "^1.0.0"
-      },
-      "dependencies": {
-        "big.js": {
-          "version": "5.2.2",
-          "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
-          "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
-          "dev": true
-        },
-        "json5": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
-          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.0"
-          }
-        },
-        "loader-utils": {
-          "version": "1.2.3",
-          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
-          "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
-          "dev": true,
-          "requires": {
-            "big.js": "^5.2.2",
-            "emojis-list": "^2.0.0",
-            "json5": "^1.0.1"
-          }
-        },
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
-        }
-      }
-    },
-    "cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "dev": true
-    },
-    "currently-unhandled": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
-      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
-      "dev": true,
-      "requires": {
-        "array-find-index": "^1.0.1"
-      }
-    },
-    "cyclist": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
-      "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
-      "dev": true
-    },
-    "dashdash": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "date-now": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
-      "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
-      "dev": true
-    },
-    "debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "requires": {
-        "ms": "2.0.0"
-      }
-    },
-    "decamelize": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
-      "dev": true
-    },
-    "decode-uri-component": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
-      "dev": true
-    },
-    "define-properties": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
-      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
-      "dev": true,
-      "requires": {
-        "object-keys": "^1.0.12"
-      }
-    },
-    "define-property": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
-      "dev": true,
-      "requires": {
-        "is-descriptor": "^1.0.2",
-        "isobject": "^3.0.1"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
-      }
-    },
-    "delayed-stream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-      "dev": true
-    },
-    "delegate": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
-      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
-    },
-    "delegates": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
-      "dev": true
-    },
-    "des.js": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
-      "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "minimalistic-assert": "^1.0.0"
-      }
-    },
-    "detect-file": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
-      "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
-      "dev": true
-    },
-    "diffie-hellman": {
-      "version": "5.0.3",
-      "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
-      "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.0",
-        "miller-rabin": "^4.0.0",
-        "randombytes": "^2.0.0"
-      }
-    },
-    "domain-browser": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
-      "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
-      "dev": true
-    },
-    "dropzone": {
-      "version": "5.5.1",
-      "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.5.1.tgz",
-      "integrity": "sha512-3VduRWLxx9hbVr42QieQN25mx/I61/mRdUSuxAmDGdDqZIN8qtP7tcKMa3KfpJjuGjOJGYYUzzeq6eGDnkzesA=="
-    },
-    "duplexify": {
-      "version": "3.7.1",
-      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
-      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
-      "dev": true,
-      "requires": {
-        "end-of-stream": "^1.0.0",
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.0",
-        "stream-shift": "^1.0.0"
-      }
-    },
-    "ecc-jsbn": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
-      "dev": true,
-      "requires": {
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.1.0"
-      }
-    },
-    "elliptic": {
-      "version": "6.4.1",
-      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
-      "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.4.0",
-        "brorand": "^1.0.1",
-        "hash.js": "^1.0.0",
-        "hmac-drbg": "^1.0.0",
-        "inherits": "^2.0.1",
-        "minimalistic-assert": "^1.0.0",
-        "minimalistic-crypto-utils": "^1.0.0"
-      }
-    },
-    "emojis-list": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
-      "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
-      "dev": true
-    },
-    "end-of-stream": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
-      "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
-      "dev": true,
-      "requires": {
-        "once": "^1.4.0"
-      }
-    },
-    "enhanced-resolve": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz",
-      "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "memory-fs": "^0.4.0",
-        "tapable": "^1.0.0"
-      }
-    },
-    "entities": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
-      "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
-    },
-    "errno": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
-      "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
-      "dev": true,
-      "requires": {
-        "prr": "~1.0.1"
-      }
-    },
-    "error-ex": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
-      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
-      "requires": {
-        "is-arrayish": "^0.2.1"
-      }
-    },
-    "es-abstract": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
-      "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
-      "dev": true,
-      "requires": {
-        "es-to-primitive": "^1.1.1",
-        "function-bind": "^1.1.1",
-        "has": "^1.0.1",
-        "is-callable": "^1.1.3",
-        "is-regex": "^1.0.4"
-      }
-    },
-    "es-to-primitive": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
-      "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
-      "dev": true,
-      "requires": {
-        "is-callable": "^1.1.4",
-        "is-date-object": "^1.0.1",
-        "is-symbol": "^1.0.2"
-      }
-    },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-      "dev": true
-    },
-    "eslint-scope": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
-      "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
-      "dev": true,
-      "requires": {
-        "esrecurse": "^4.1.0",
-        "estraverse": "^4.1.1"
-      }
-    },
-    "esrecurse": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
-      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
-      "dev": true,
-      "requires": {
-        "estraverse": "^4.1.0"
-      }
-    },
-    "estraverse": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
-      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
-      "dev": true
-    },
-    "events": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
-      "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==",
-      "dev": true
-    },
-    "evp_bytestokey": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
-      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
-      "dev": true,
-      "requires": {
-        "md5.js": "^1.3.4",
-        "safe-buffer": "^5.1.1"
-      }
-    },
-    "execa": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
-      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
-      "dev": true,
-      "requires": {
-        "cross-spawn": "^6.0.0",
-        "get-stream": "^4.0.0",
-        "is-stream": "^1.1.0",
-        "npm-run-path": "^2.0.0",
-        "p-finally": "^1.0.0",
-        "signal-exit": "^3.0.0",
-        "strip-eof": "^1.0.0"
-      },
-      "dependencies": {
-        "cross-spawn": {
-          "version": "6.0.5",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-          "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-          "dev": true,
-          "requires": {
-            "nice-try": "^1.0.4",
-            "path-key": "^2.0.1",
-            "semver": "^5.5.0",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        }
-      }
-    },
-    "expand-brackets": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-      "dev": true,
-      "requires": {
-        "debug": "^2.3.3",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "posix-character-classes": "^0.1.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "expand-tilde": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
-      "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
-      "dev": true,
-      "requires": {
-        "homedir-polyfill": "^1.0.1"
-      }
-    },
-    "extend": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
-      "dev": true
-    },
-    "extend-shallow": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
-      "dev": true,
-      "requires": {
-        "assign-symbols": "^1.0.0",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
-      }
-    },
-    "extglob": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-      "dev": true,
-      "requires": {
-        "array-unique": "^0.3.2",
-        "define-property": "^1.0.0",
-        "expand-brackets": "^2.1.4",
-        "extend-shallow": "^2.0.1",
-        "fragment-cache": "^0.2.1",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
-      }
-    },
-    "extsprintf": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
-      "dev": true
-    },
-    "fast-deep-equal": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-      "dev": true
-    },
-    "fast-json-stable-stringify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
-      "dev": true
-    },
-    "figgy-pudding": {
-      "version": "3.5.1",
-      "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
-      "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
-      "dev": true
-    },
-    "fill-range": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-number": "^3.0.0",
-        "repeat-string": "^1.6.1",
-        "to-regex-range": "^2.1.0"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "find-cache-dir": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
-      "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
-      "dev": true,
-      "requires": {
-        "commondir": "^1.0.1",
-        "make-dir": "^2.0.0",
-        "pkg-dir": "^3.0.0"
-      }
-    },
-    "find-up": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-      "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
-      "dev": true,
-      "requires": {
-        "locate-path": "^3.0.0"
-      }
-    },
-    "findup-sync": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
-      "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
-      "dev": true,
-      "requires": {
-        "detect-file": "^1.0.0",
-        "is-glob": "^3.1.0",
-        "micromatch": "^3.0.4",
-        "resolve-dir": "^1.0.1"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "is-glob": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^2.1.0"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
-        }
-      }
-    },
-    "flush-write-stream": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
-      "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.3.6"
-      },
-      "dependencies": {
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
-      }
-    },
-    "for-in": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
-      "dev": true
-    },
-    "for-own": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
-      "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
-      "dev": true,
-      "requires": {
-        "for-in": "^1.0.1"
-      }
-    },
-    "forever-agent": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
-      "dev": true
-    },
-    "form-data": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
-      "dev": true,
-      "requires": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.6",
-        "mime-types": "^2.1.12"
-      }
-    },
-    "fragment-cache": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
-      "dev": true,
-      "requires": {
-        "map-cache": "^0.2.2"
-      }
-    },
-    "from2": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
-      "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.0"
-      }
-    },
-    "fs-write-stream-atomic": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
-      "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "iferr": "^0.1.5",
-        "imurmurhash": "^0.1.4",
-        "readable-stream": "1 || 2"
-      }
-    },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
-    },
-    "fsevents": {
-      "version": "1.2.9",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
-      "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "nan": "^2.12.1",
-        "node-pre-gyp": "^0.12.0"
-      },
-      "dependencies": {
-        "abbrev": {
-          "version": "1.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "ansi-regex": {
-          "version": "2.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "aproba": {
-          "version": "1.2.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "are-we-there-yet": {
-          "version": "1.1.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "delegates": "^1.0.0",
-            "readable-stream": "^2.0.6"
-          }
-        },
-        "balanced-match": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "brace-expansion": {
-          "version": "1.1.11",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "balanced-match": "^1.0.0",
-            "concat-map": "0.0.1"
-          }
-        },
-        "chownr": {
-          "version": "1.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "code-point-at": {
-          "version": "1.1.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "concat-map": {
-          "version": "0.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "console-control-strings": {
-          "version": "1.1.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "core-util-is": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "debug": {
-          "version": "4.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        },
-        "deep-extend": {
-          "version": "0.6.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "delegates": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "detect-libc": {
-          "version": "1.0.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "fs-minipass": {
-          "version": "1.2.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minipass": "^2.2.1"
-          }
-        },
-        "fs.realpath": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "gauge": {
-          "version": "2.7.4",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "aproba": "^1.0.3",
-            "console-control-strings": "^1.0.0",
-            "has-unicode": "^2.0.0",
-            "object-assign": "^4.1.0",
-            "signal-exit": "^3.0.0",
-            "string-width": "^1.0.1",
-            "strip-ansi": "^3.0.1",
-            "wide-align": "^1.1.0"
-          }
-        },
-        "glob": {
-          "version": "7.1.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "has-unicode": {
-          "version": "2.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "iconv-lite": {
-          "version": "0.4.24",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "safer-buffer": ">= 2.1.2 < 3"
-          }
-        },
-        "ignore-walk": {
-          "version": "3.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minimatch": "^3.0.4"
-          }
-        },
-        "inflight": {
-          "version": "1.0.6",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "once": "^1.3.0",
-            "wrappy": "1"
-          }
-        },
-        "inherits": {
-          "version": "2.0.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "ini": {
-          "version": "1.3.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "number-is-nan": "^1.0.0"
-          }
-        },
-        "isarray": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "minimatch": {
-          "version": "3.0.4",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "brace-expansion": "^1.1.7"
-          }
-        },
-        "minimist": {
-          "version": "0.0.8",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "minipass": {
-          "version": "2.3.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "safe-buffer": "^5.1.2",
-            "yallist": "^3.0.0"
-          }
-        },
-        "minizlib": {
-          "version": "1.2.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minipass": "^2.2.1"
-          }
-        },
-        "mkdirp": {
-          "version": "0.5.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minimist": "0.0.8"
-          }
-        },
-        "ms": {
-          "version": "2.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "needle": {
-          "version": "2.3.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "debug": "^4.1.0",
-            "iconv-lite": "^0.4.4",
-            "sax": "^1.2.4"
-          }
-        },
-        "node-pre-gyp": {
-          "version": "0.12.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "detect-libc": "^1.0.2",
-            "mkdirp": "^0.5.1",
-            "needle": "^2.2.1",
-            "nopt": "^4.0.1",
-            "npm-packlist": "^1.1.6",
-            "npmlog": "^4.0.2",
-            "rc": "^1.2.7",
-            "rimraf": "^2.6.1",
-            "semver": "^5.3.0",
-            "tar": "^4"
-          }
-        },
-        "nopt": {
-          "version": "4.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "abbrev": "1",
-            "osenv": "^0.1.4"
-          }
-        },
-        "npm-bundled": {
-          "version": "1.0.6",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "npm-packlist": {
-          "version": "1.4.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ignore-walk": "^3.0.1",
-            "npm-bundled": "^1.0.1"
-          }
-        },
-        "npmlog": {
-          "version": "4.1.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "are-we-there-yet": "~1.1.2",
-            "console-control-strings": "~1.1.0",
-            "gauge": "~2.7.3",
-            "set-blocking": "~2.0.0"
-          }
-        },
-        "number-is-nan": {
-          "version": "1.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "object-assign": {
-          "version": "4.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "once": {
-          "version": "1.4.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "wrappy": "1"
-          }
-        },
-        "os-homedir": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "os-tmpdir": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "osenv": {
-          "version": "0.1.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "os-homedir": "^1.0.0",
-            "os-tmpdir": "^1.0.0"
-          }
-        },
-        "path-is-absolute": {
-          "version": "1.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "rc": {
-          "version": "1.2.8",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "deep-extend": "^0.6.0",
-            "ini": "~1.3.0",
-            "minimist": "^1.2.0",
-            "strip-json-comments": "~2.0.1"
-          },
-          "dependencies": {
-            "minimist": {
-              "version": "1.2.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            }
-          }
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "rimraf": {
-          "version": "2.6.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "glob": "^7.1.3"
-          }
-        },
-        "safe-buffer": {
-          "version": "5.1.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "safer-buffer": {
-          "version": "2.1.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "sax": {
-          "version": "1.2.4",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "semver": {
-          "version": "5.7.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "set-blocking": {
-          "version": "2.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "signal-exit": {
-          "version": "3.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "string-width": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "code-point-at": "^1.0.0",
-            "is-fullwidth-code-point": "^1.0.0",
-            "strip-ansi": "^3.0.0"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "3.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ansi-regex": "^2.0.0"
-          }
-        },
-        "strip-json-comments": {
-          "version": "2.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "tar": {
-          "version": "4.4.8",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "chownr": "^1.1.1",
-            "fs-minipass": "^1.2.5",
-            "minipass": "^2.3.4",
-            "minizlib": "^1.1.1",
-            "mkdirp": "^0.5.0",
-            "safe-buffer": "^5.1.2",
-            "yallist": "^3.0.2"
-          }
-        },
-        "util-deprecate": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "wide-align": {
-          "version": "1.1.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "string-width": "^1.0.2 || 2"
-          }
-        },
-        "wrappy": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "yallist": {
-          "version": "3.0.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        }
-      }
-    },
-    "fstream": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
-      "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "inherits": "~2.0.0",
-        "mkdirp": ">=0.5 0",
-        "rimraf": "2"
-      }
-    },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
-    },
-    "gauge": {
-      "version": "2.7.4",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
-      "dev": true,
-      "requires": {
-        "aproba": "^1.0.3",
-        "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.0",
-        "object-assign": "^4.1.0",
-        "signal-exit": "^3.0.0",
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wide-align": "^1.1.0"
-      }
-    },
-    "gaze": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
-      "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
-      "dev": true,
-      "requires": {
-        "globule": "^1.0.0"
-      }
-    },
-    "get-caller-file": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
-      "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
-      "dev": true
-    },
-    "get-stdin": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
-      "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
-      "dev": true
-    },
-    "get-stream": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
-      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
-      "dev": true,
-      "requires": {
-        "pump": "^3.0.0"
-      }
-    },
-    "get-value": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
-      "dev": true
-    },
-    "getpass": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "glob": {
-      "version": "7.1.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
-      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
-      "dev": true,
-      "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      }
-    },
-    "glob-parent": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-      "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
-      "dev": true,
-      "requires": {
-        "is-glob": "^3.1.0",
-        "path-dirname": "^1.0.0"
-      },
-      "dependencies": {
-        "is-glob": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^2.1.0"
-          }
-        }
-      }
-    },
-    "global-modules": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
-      "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
-      "dev": true,
-      "requires": {
-        "global-prefix": "^1.0.1",
-        "is-windows": "^1.0.1",
-        "resolve-dir": "^1.0.0"
-      }
-    },
-    "global-prefix": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
-      "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
-      "dev": true,
-      "requires": {
-        "expand-tilde": "^2.0.2",
-        "homedir-polyfill": "^1.0.1",
-        "ini": "^1.3.4",
-        "is-windows": "^1.0.1",
-        "which": "^1.2.14"
-      }
-    },
-    "globule": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
-      "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
-      "dev": true,
-      "requires": {
-        "glob": "~7.1.1",
-        "lodash": "~4.17.10",
-        "minimatch": "~3.0.2"
-      }
-    },
-    "good-listener": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
-      "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
-      "requires": {
-        "delegate": "^3.1.2"
-      }
-    },
-    "graceful-fs": {
-      "version": "4.1.11",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
-      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
-      "dev": true
-    },
-    "har-schema": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
-      "dev": true
-    },
-    "har-validator": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
-      "dev": true,
-      "requires": {
-        "ajv": "^6.5.5",
-        "har-schema": "^2.0.0"
-      }
-    },
-    "has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
-      "requires": {
-        "function-bind": "^1.1.1"
-      }
-    },
-    "has-ansi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^2.0.0"
-      }
-    },
-    "has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-      "dev": true
-    },
-    "has-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
-      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
-      "dev": true
-    },
-    "has-unicode": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
-      "dev": true
-    },
-    "has-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
-      "dev": true,
-      "requires": {
-        "get-value": "^2.0.6",
-        "has-values": "^1.0.0",
-        "isobject": "^3.0.0"
-      }
-    },
-    "has-values": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
-      "dev": true,
-      "requires": {
-        "is-number": "^3.0.0",
-        "kind-of": "^4.0.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "hash-base": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
-      "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "hash.js": {
-      "version": "1.1.7",
-      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
-      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.3",
-        "minimalistic-assert": "^1.0.1"
-      }
-    },
-    "hmac-drbg": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
-      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
-      "dev": true,
-      "requires": {
-        "hash.js": "^1.0.3",
-        "minimalistic-assert": "^1.0.0",
-        "minimalistic-crypto-utils": "^1.0.1"
-      }
-    },
-    "homedir-polyfill": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
-      "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
-      "dev": true,
-      "requires": {
-        "parse-passwd": "^1.0.0"
-      }
-    },
-    "hosted-git-info": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
-      "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
-      "dev": true
-    },
-    "http-signature": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0",
-        "jsprim": "^1.2.2",
-        "sshpk": "^1.7.0"
-      }
-    },
-    "https-browserify": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
-      "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
-      "dev": true
-    },
-    "icss-replace-symbols": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
-      "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
-      "dev": true
-    },
-    "icss-utils": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz",
-      "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==",
-      "dev": true,
-      "requires": {
-        "postcss": "^7.0.14"
-      }
-    },
-    "ieee754": {
-      "version": "1.1.13",
-      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
-      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
-      "dev": true
-    },
-    "iferr": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-      "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
-      "dev": true
-    },
-    "import-local": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
-      "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
-      "dev": true,
-      "requires": {
-        "pkg-dir": "^3.0.0",
-        "resolve-cwd": "^2.0.0"
-      }
-    },
-    "imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
-      "dev": true
-    },
-    "in-publish": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
-      "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
-      "dev": true
-    },
-    "indent-string": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
-      "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
-      "dev": true,
-      "requires": {
-        "repeating": "^2.0.0"
-      }
-    },
-    "indexes-of": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
-      "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
-      "dev": true
-    },
-    "indexof": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-      "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
-      "dev": true
-    },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "inherits": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-      "dev": true
-    },
-    "ini": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
-      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
-      "dev": true
-    },
-    "interpret": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
-      "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
-      "dev": true
-    },
-    "invert-kv": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
-      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
-      "dev": true
-    },
-    "is-accessor-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      }
-    },
-    "is-arrayish": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-      "dev": true
-    },
-    "is-binary-path": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
-      "dev": true,
-      "requires": {
-        "binary-extensions": "^1.0.0"
-      }
-    },
-    "is-buffer": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
-      "dev": true
-    },
-    "is-builtin-module": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
-      "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
-      "dev": true,
-      "requires": {
-        "builtin-modules": "^1.0.0"
-      }
-    },
-    "is-callable": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
-      "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
-      "dev": true
-    },
-    "is-data-descriptor": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      }
-    },
-    "is-date-object": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
-      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
-      "dev": true
-    },
-    "is-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-      "dev": true,
-      "requires": {
-        "is-accessor-descriptor": "^0.1.6",
-        "is-data-descriptor": "^0.1.4",
-        "kind-of": "^5.0.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
-      }
-    },
-    "is-extendable": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
-      "dev": true
-    },
-    "is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true
-    },
-    "is-finite": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
-      "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
-      "dev": true,
-      "requires": {
-        "number-is-nan": "^1.0.0"
-      }
-    },
-    "is-fullwidth-code-point": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
-      "dev": true,
-      "requires": {
-        "number-is-nan": "^1.0.0"
-      }
-    },
-    "is-glob": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
-      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
-      "dev": true,
-      "requires": {
-        "is-extglob": "^2.1.1"
-      }
-    },
-    "is-number": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      }
-    },
-    "is-plain-obj": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
-      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
-      "dev": true
-    },
-    "is-plain-object": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
-      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.1"
-      }
-    },
-    "is-regex": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
-      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
-      "dev": true,
-      "requires": {
-        "has": "^1.0.1"
-      }
-    },
-    "is-stream": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
-      "dev": true
-    },
-    "is-symbol": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
-      "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
-      "dev": true,
-      "requires": {
-        "has-symbols": "^1.0.0"
-      }
-    },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
-      "dev": true
-    },
-    "is-utf8": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
-      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
-      "dev": true
-    },
-    "is-windows": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
-      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
-      "dev": true
-    },
-    "is-wsl": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
-      "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
-      "dev": true
-    },
-    "isarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-      "dev": true
-    },
-    "isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
-      "dev": true
-    },
-    "isobject": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-      "dev": true
-    },
-    "isstream": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
-      "dev": true
-    },
-    "js-base64": {
-      "version": "2.5.1",
-      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
-      "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
-      "dev": true
-    },
-    "jsbn": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
-      "dev": true
-    },
-    "json-parse-better-errors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
-      "dev": true
-    },
-    "json-schema": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
-      "dev": true
-    },
-    "json-schema-traverse": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-      "dev": true
-    },
-    "json-stringify-safe": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
-      "dev": true
-    },
-    "json5": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
-      "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
-      "dev": true
-    },
-    "jsonify": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
-      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
-      "dev": true
-    },
-    "jsprim": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
-      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "1.0.0",
-        "extsprintf": "1.3.0",
-        "json-schema": "0.2.3",
-        "verror": "1.10.0"
-      }
-    },
-    "kind-of": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-      "dev": true,
-      "requires": {
-        "is-buffer": "^1.1.5"
-      }
-    },
-    "lcid": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
-      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
-      "dev": true,
-      "requires": {
-        "invert-kv": "^1.0.0"
-      }
-    },
-    "linkify-it": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz",
-      "integrity": "sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=",
-      "requires": {
-        "uc.micro": "^1.0.1"
-      }
-    },
-    "livereload": {
-      "version": "0.8.0",
-      "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.8.0.tgz",
-      "integrity": "sha512-Hi5Na6VIK3e8zlgOS50fu+iOTKWj5hM0BE7NKpZkwnfWTnktTjA38ZUXa2NlJww8/GrdVhpnxdqlLad5fkO27g==",
-      "dev": true,
-      "requires": {
-        "chokidar": "^2.1.5",
-        "opts": ">= 1.2.0",
-        "ws": "^1.1.5"
-      }
-    },
-    "load-json-file": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
-      "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "parse-json": "^2.2.0",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0",
-        "strip-bom": "^2.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        }
-      }
-    },
-    "loader-runner": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
-      "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
-      "dev": true
-    },
-    "loader-utils": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
-      "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
-      "dev": true,
-      "requires": {
-        "big.js": "^3.1.3",
-        "emojis-list": "^2.0.0",
-        "json5": "^0.5.0"
-      }
-    },
-    "locate-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-      "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
-      "dev": true,
-      "requires": {
-        "p-locate": "^3.0.0",
-        "path-exists": "^3.0.0"
-      }
-    },
-    "lodash": {
-      "version": "4.17.15",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
-      "dev": true
-    },
-    "lodash.tail": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz",
-      "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=",
-      "dev": true
-    },
-    "loud-rejection": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
-      "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
-      "dev": true,
-      "requires": {
-        "currently-unhandled": "^0.4.1",
-        "signal-exit": "^3.0.0"
-      }
-    },
-    "lru-cache": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
-      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
-      "dev": true,
-      "requires": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
-      }
-    },
-    "make-dir": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
-      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
-      "dev": true,
-      "requires": {
-        "pify": "^4.0.1",
-        "semver": "^5.6.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "4.0.1",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
-          "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
-          "dev": true
-        }
-      }
-    },
-    "mamacro": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz",
-      "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==",
-      "dev": true
-    },
-    "map-age-cleaner": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
-      "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
-      "dev": true,
-      "requires": {
-        "p-defer": "^1.0.0"
-      }
-    },
-    "map-cache": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
-      "dev": true
-    },
-    "map-obj": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
-      "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
-      "dev": true
-    },
-    "map-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
-      "dev": true,
-      "requires": {
-        "object-visit": "^1.0.0"
-      }
-    },
-    "markdown-it": {
-      "version": "8.4.2",
-      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz",
-      "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==",
-      "requires": {
-        "argparse": "^1.0.7",
-        "entities": "~1.1.1",
-        "linkify-it": "^2.0.0",
-        "mdurl": "^1.0.1",
-        "uc.micro": "^1.0.5"
-      }
-    },
-    "markdown-it-task-lists": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
-      "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="
-    },
-    "md5.js": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
-      "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
-      "dev": true,
-      "requires": {
-        "hash-base": "^3.0.0",
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
-      }
-    },
-    "mdurl": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
-      "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
-    },
-    "mem": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
-      "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
-      "dev": true,
-      "requires": {
-        "map-age-cleaner": "^0.1.1",
-        "mimic-fn": "^2.0.0",
-        "p-is-promise": "^2.0.0"
-      }
-    },
-    "memory-fs": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
-      "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
-      "dev": true,
-      "requires": {
-        "errno": "^0.1.3",
-        "readable-stream": "^2.0.1"
-      }
-    },
-    "memorystream": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
-      "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
-      "dev": true
-    },
-    "meow": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
-      "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
-      "dev": true,
-      "requires": {
-        "camelcase-keys": "^2.0.0",
-        "decamelize": "^1.1.2",
-        "loud-rejection": "^1.0.0",
-        "map-obj": "^1.0.1",
-        "minimist": "^1.1.3",
-        "normalize-package-data": "^2.3.4",
-        "object-assign": "^4.0.1",
-        "read-pkg-up": "^1.0.1",
-        "redent": "^1.0.0",
-        "trim-newlines": "^1.0.0"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
-        }
-      }
-    },
-    "micromatch": {
-      "version": "3.1.10",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-      "dev": true,
-      "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "braces": "^2.3.1",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "extglob": "^2.0.4",
-        "fragment-cache": "^0.2.1",
-        "kind-of": "^6.0.2",
-        "nanomatch": "^1.2.9",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
-      }
-    },
-    "miller-rabin": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
-      "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.0.0",
-        "brorand": "^1.0.1"
-      }
-    },
-    "mime-db": {
-      "version": "1.40.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
-      "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
-      "dev": true
-    },
-    "mime-types": {
-      "version": "2.1.24",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
-      "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
-      "dev": true,
-      "requires": {
-        "mime-db": "1.40.0"
-      }
-    },
-    "mimic-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
-      "dev": true
-    },
-    "mini-css-extract-plugin": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz",
-      "integrity": "sha512-RQIw6+7utTYn8DBGsf/LpRgZCJMpZt+kuawJ/fju0KiOL6nAaTBNmCJwS7HtwSCXfS47gCkmtBFS7HdsquhdxQ==",
-      "dev": true,
-      "requires": {
-        "loader-utils": "^1.1.0",
-        "normalize-url": "1.9.1",
-        "schema-utils": "^1.0.0",
-        "webpack-sources": "^1.1.0"
-      }
-    },
-    "minimalistic-assert": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
-      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
-      "dev": true
-    },
-    "minimalistic-crypto-utils": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
-      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
-      "dev": true
-    },
-    "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
-      "requires": {
-        "brace-expansion": "^1.1.7"
-      }
-    },
-    "minimist": {
-      "version": "0.0.8",
-      "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-      "dev": true
-    },
-    "mississippi": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
-      "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
-      "dev": true,
-      "requires": {
-        "concat-stream": "^1.5.0",
-        "duplexify": "^3.4.2",
-        "end-of-stream": "^1.1.0",
-        "flush-write-stream": "^1.0.0",
-        "from2": "^2.1.0",
-        "parallel-transform": "^1.1.0",
-        "pump": "^3.0.0",
-        "pumpify": "^1.3.3",
-        "stream-each": "^1.1.0",
-        "through2": "^2.0.0"
-      }
-    },
-    "mixin-deep": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
-      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
-      "dev": true,
-      "requires": {
-        "for-in": "^1.0.2",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
-      }
-    },
-    "mixin-object": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz",
-      "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=",
-      "dev": true,
-      "requires": {
-        "for-in": "^0.1.3",
-        "is-extendable": "^0.1.1"
-      },
-      "dependencies": {
-        "for-in": {
-          "version": "0.1.8",
-          "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz",
-          "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=",
-          "dev": true
-        }
-      }
-    },
-    "mkdirp": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
-      "dev": true,
-      "requires": {
-        "minimist": "0.0.8"
-      }
-    },
-    "move-concurrently": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
-      "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
-      "dev": true,
-      "requires": {
-        "aproba": "^1.1.1",
-        "copy-concurrently": "^1.0.0",
-        "fs-write-stream-atomic": "^1.0.8",
-        "mkdirp": "^0.5.1",
-        "rimraf": "^2.5.4",
-        "run-queue": "^1.0.3"
-      }
-    },
-    "ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-      "dev": true
-    },
-    "nan": {
-      "version": "2.14.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
-      "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
-      "dev": true
-    },
-    "nanomatch": {
-      "version": "1.2.13",
-      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
-      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
-      "dev": true,
-      "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "fragment-cache": "^0.2.1",
-        "is-windows": "^1.0.2",
-        "kind-of": "^6.0.2",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
-      }
-    },
-    "neo-async": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
-      "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
-      "dev": true
-    },
-    "nice-try": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
-      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
-      "dev": true
-    },
-    "node-gyp": {
-      "version": "3.8.0",
-      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
-      "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
-      "dev": true,
-      "requires": {
-        "fstream": "^1.0.0",
-        "glob": "^7.0.3",
-        "graceful-fs": "^4.1.2",
-        "mkdirp": "^0.5.0",
-        "nopt": "2 || 3",
-        "npmlog": "0 || 1 || 2 || 3 || 4",
-        "osenv": "0",
-        "request": "^2.87.0",
-        "rimraf": "2",
-        "semver": "~5.3.0",
-        "tar": "^2.0.0",
-        "which": "1"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
-          "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
-          "dev": true
-        }
-      }
-    },
-    "node-libs-browser": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz",
-      "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==",
-      "dev": true,
-      "requires": {
-        "assert": "^1.1.1",
-        "browserify-zlib": "^0.2.0",
-        "buffer": "^4.3.0",
-        "console-browserify": "^1.1.0",
-        "constants-browserify": "^1.0.0",
-        "crypto-browserify": "^3.11.0",
-        "domain-browser": "^1.1.1",
-        "events": "^3.0.0",
-        "https-browserify": "^1.0.0",
-        "os-browserify": "^0.3.0",
-        "path-browserify": "0.0.0",
-        "process": "^0.11.10",
-        "punycode": "^1.2.4",
-        "querystring-es3": "^0.2.0",
-        "readable-stream": "^2.3.3",
-        "stream-browserify": "^2.0.1",
-        "stream-http": "^2.7.2",
-        "string_decoder": "^1.0.0",
-        "timers-browserify": "^2.0.4",
-        "tty-browserify": "0.0.0",
-        "url": "^0.11.0",
-        "util": "^0.11.0",
-        "vm-browserify": "0.0.4"
-      }
-    },
-    "node-sass": {
-      "version": "4.12.0",
-      "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
-      "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==",
-      "dev": true,
-      "requires": {
-        "async-foreach": "^0.1.3",
-        "chalk": "^1.1.1",
-        "cross-spawn": "^3.0.0",
-        "gaze": "^1.0.0",
-        "get-stdin": "^4.0.1",
-        "glob": "^7.0.3",
-        "in-publish": "^2.0.0",
-        "lodash": "^4.17.11",
-        "meow": "^3.7.0",
-        "mkdirp": "^0.5.1",
-        "nan": "^2.13.2",
-        "node-gyp": "^3.8.0",
-        "npmlog": "^4.0.0",
-        "request": "^2.88.0",
-        "sass-graph": "^2.2.4",
-        "stdout-stream": "^1.4.0",
-        "true-case-path": "^1.0.2"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          }
-        },
-        "supports-color": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true
-        }
-      }
-    },
-    "nopt": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
-      "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
-      "dev": true,
-      "requires": {
-        "abbrev": "1"
-      }
-    },
-    "normalize-package-data": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
-      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
-      "dev": true,
-      "requires": {
-        "hosted-git-info": "^2.1.4",
-        "is-builtin-module": "^1.0.0",
-        "semver": "2 || 3 || 4 || 5",
-        "validate-npm-package-license": "^3.0.1"
-      }
-    },
-    "normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true
-    },
-    "normalize-url": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
-      "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
-      "dev": true,
-      "requires": {
-        "object-assign": "^4.0.1",
-        "prepend-http": "^1.0.0",
-        "query-string": "^4.1.0",
-        "sort-keys": "^1.0.0"
-      }
-    },
-    "npm-run-all": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
-      "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^3.2.1",
-        "chalk": "^2.4.1",
-        "cross-spawn": "^6.0.5",
-        "memorystream": "^0.3.1",
-        "minimatch": "^3.0.4",
-        "pidtree": "^0.3.0",
-        "read-pkg": "^3.0.0",
-        "shell-quote": "^1.6.1",
-        "string.prototype.padend": "^3.0.0"
-      },
-      "dependencies": {
-        "cross-spawn": {
-          "version": "6.0.5",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-          "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-          "dev": true,
-          "requires": {
-            "nice-try": "^1.0.4",
-            "path-key": "^2.0.1",
-            "semver": "^5.5.0",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        },
-        "load-json-file": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
-          "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.1.2",
-            "parse-json": "^4.0.0",
-            "pify": "^3.0.0",
-            "strip-bom": "^3.0.0"
-          }
-        },
-        "parse-json": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
-          "dev": true,
-          "requires": {
-            "error-ex": "^1.3.1",
-            "json-parse-better-errors": "^1.0.1"
-          }
-        },
-        "path-type": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
-          "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
-          "dev": true,
-          "requires": {
-            "pify": "^3.0.0"
-          }
-        },
-        "read-pkg": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
-          "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
-          "dev": true,
-          "requires": {
-            "load-json-file": "^4.0.0",
-            "normalize-package-data": "^2.3.2",
-            "path-type": "^3.0.0"
-          }
-        },
-        "strip-bom": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-          "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
-          "dev": true
-        }
-      }
-    },
-    "npm-run-path": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
-      "dev": true,
-      "requires": {
-        "path-key": "^2.0.0"
-      }
-    },
-    "npmlog": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
-      "dev": true,
-      "requires": {
-        "are-we-there-yet": "~1.1.2",
-        "console-control-strings": "~1.1.0",
-        "gauge": "~2.7.3",
-        "set-blocking": "~2.0.0"
-      }
-    },
-    "number-is-nan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-      "dev": true
-    },
-    "oauth-sign": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
-      "dev": true
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
-      "dev": true
-    },
-    "object-copy": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
-      "dev": true,
-      "requires": {
-        "copy-descriptor": "^0.1.0",
-        "define-property": "^0.2.5",
-        "kind-of": "^3.0.3"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        }
-      }
-    },
-    "object-keys": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
-      "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
-      "dev": true
-    },
-    "object-visit": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.0"
-      }
-    },
-    "object.pick": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.1"
-      }
-    },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "options": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
-      "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=",
-      "dev": true
-    },
-    "opts": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/opts/-/opts-1.2.6.tgz",
-      "integrity": "sha1-0YXAQlz9652h0YKQi2W1wCOP67M=",
-      "dev": true
-    },
-    "os-browserify": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
-      "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
-      "dev": true
-    },
-    "os-homedir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
-      "dev": true
-    },
-    "os-locale": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
-      "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
-      "dev": true,
-      "requires": {
-        "lcid": "^1.0.0"
-      }
-    },
-    "os-tmpdir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
-      "dev": true
-    },
-    "osenv": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
-      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
-      "dev": true,
-      "requires": {
-        "os-homedir": "^1.0.0",
-        "os-tmpdir": "^1.0.0"
-      }
-    },
-    "p-defer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
-      "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
-      "dev": true
-    },
-    "p-finally": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
-      "dev": true
-    },
-    "p-is-promise": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
-      "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
-      "dev": true
-    },
-    "p-limit": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
-      "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
-      "dev": true,
-      "requires": {
-        "p-try": "^2.0.0"
-      }
-    },
-    "p-locate": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-      "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
-      "dev": true,
-      "requires": {
-        "p-limit": "^2.0.0"
-      }
-    },
-    "p-try": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true
-    },
-    "pako": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz",
-      "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==",
-      "dev": true
-    },
-    "parallel-transform": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
-      "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
-      "dev": true,
-      "requires": {
-        "cyclist": "~0.2.2",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.1.5"
-      }
-    },
-    "parse-asn1": {
-      "version": "5.1.4",
-      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
-      "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
-      "dev": true,
-      "requires": {
-        "asn1.js": "^4.0.0",
-        "browserify-aes": "^1.0.0",
-        "create-hash": "^1.1.0",
-        "evp_bytestokey": "^1.0.0",
-        "pbkdf2": "^3.0.3",
-        "safe-buffer": "^5.1.1"
-      }
-    },
-    "parse-json": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
-      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
-      "dev": true,
-      "requires": {
-        "error-ex": "^1.2.0"
-      }
-    },
-    "parse-passwd": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
-      "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
-      "dev": true
-    },
-    "pascalcase": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
-      "dev": true
-    },
-    "path-browserify": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
-      "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
-      "dev": true
-    },
-    "path-dirname": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
-      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
-      "dev": true
-    },
-    "path-exists": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-      "dev": true
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
-    },
-    "path-key": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
-      "dev": true
-    },
-    "path-type": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
-      "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        }
-      }
-    },
-    "pbkdf2": {
-      "version": "3.0.17",
-      "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
-      "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
-      "dev": true,
-      "requires": {
-        "create-hash": "^1.1.2",
-        "create-hmac": "^1.1.4",
-        "ripemd160": "^2.0.1",
-        "safe-buffer": "^5.0.1",
-        "sha.js": "^2.4.8"
-      }
-    },
-    "performance-now": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
-      "dev": true
-    },
-    "pidtree": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz",
-      "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==",
-      "dev": true
-    },
-    "pify": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-      "dev": true
-    },
-    "pinkie": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
-      "dev": true
-    },
-    "pinkie-promise": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
-      "dev": true,
-      "requires": {
-        "pinkie": "^2.0.0"
-      }
-    },
-    "pkg-dir": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
-      "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
-      "dev": true,
-      "requires": {
-        "find-up": "^3.0.0"
-      }
-    },
-    "posix-character-classes": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
-      "dev": true
-    },
-    "postcss": {
-      "version": "7.0.16",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz",
-      "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.4.2",
-        "source-map": "^0.6.1",
-        "supports-color": "^6.1.0"
-      },
-      "dependencies": {
-        "chalk": {
-          "version": "2.4.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "5.5.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-              "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-              "dev": true,
-              "requires": {
-                "has-flag": "^3.0.0"
-              }
-            }
-          }
-        },
-        "supports-color": {
-          "version": "6.1.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
-          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
-      }
-    },
-    "postcss-modules-extract-imports": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz",
-      "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==",
-      "dev": true,
-      "requires": {
-        "postcss": "^7.0.5"
-      }
-    },
-    "postcss-modules-local-by-default": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz",
-      "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==",
-      "dev": true,
-      "requires": {
-        "postcss": "^7.0.6",
-        "postcss-selector-parser": "^6.0.0",
-        "postcss-value-parser": "^3.3.1"
-      }
-    },
-    "postcss-modules-scope": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz",
-      "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==",
-      "dev": true,
-      "requires": {
-        "postcss": "^7.0.6",
-        "postcss-selector-parser": "^6.0.0"
-      }
-    },
-    "postcss-modules-values": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz",
-      "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==",
-      "dev": true,
-      "requires": {
-        "icss-replace-symbols": "^1.1.0",
-        "postcss": "^7.0.6"
-      }
-    },
-    "postcss-selector-parser": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
-      "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
-      "dev": true,
-      "requires": {
-        "cssesc": "^3.0.0",
-        "indexes-of": "^1.0.1",
-        "uniq": "^1.0.1"
-      }
-    },
-    "postcss-value-parser": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
-      "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
-      "dev": true
-    },
-    "prepend-http": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
-      "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
-      "dev": true
-    },
-    "process": {
-      "version": "0.11.10",
-      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
-      "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
-      "dev": true
-    },
-    "process-nextick-args": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
-      "dev": true
-    },
-    "promise-inflight": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
-      "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
-      "dev": true
-    },
-    "prr": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
-      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
-      "dev": true
-    },
-    "pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
-      "dev": true
-    },
-    "psl": {
-      "version": "1.1.32",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz",
-      "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==",
-      "dev": true
-    },
-    "public-encrypt": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
-      "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.0",
-        "browserify-rsa": "^4.0.0",
-        "create-hash": "^1.1.0",
-        "parse-asn1": "^5.0.0",
-        "randombytes": "^2.0.1",
-        "safe-buffer": "^5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
-      }
-    },
-    "pump": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
-      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
-      "dev": true,
-      "requires": {
-        "end-of-stream": "^1.1.0",
-        "once": "^1.3.1"
-      }
-    },
-    "pumpify": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
-      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
-      "dev": true,
-      "requires": {
-        "duplexify": "^3.6.0",
-        "inherits": "^2.0.3",
-        "pump": "^2.0.0"
-      },
-      "dependencies": {
-        "pump": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
-          "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
-          "dev": true,
-          "requires": {
-            "end-of-stream": "^1.1.0",
-            "once": "^1.3.1"
-          }
-        }
-      }
-    },
-    "punycode": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-      "dev": true
-    },
-    "qs": {
-      "version": "6.5.2",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
-      "dev": true
-    },
-    "query-string": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
-      "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
-      "dev": true,
-      "requires": {
-        "object-assign": "^4.1.0",
-        "strict-uri-encode": "^1.0.0"
-      }
-    },
-    "querystring": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
-      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
-      "dev": true
-    },
-    "querystring-es3": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
-      "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
-      "dev": true
-    },
-    "randombytes": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
-      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "^5.1.0"
-      }
-    },
-    "randomfill": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
-      "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
-      "dev": true,
-      "requires": {
-        "randombytes": "^2.0.5",
-        "safe-buffer": "^5.1.0"
-      }
-    },
-    "read-pkg": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
-      "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
-      "dev": true,
-      "requires": {
-        "load-json-file": "^1.0.0",
-        "normalize-package-data": "^2.3.2",
-        "path-type": "^1.0.0"
-      }
-    },
-    "read-pkg-up": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
-      "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
-      "dev": true,
-      "requires": {
-        "find-up": "^1.0.0",
-        "read-pkg": "^1.0.0"
-      },
-      "dependencies": {
-        "find-up": {
-          "version": "1.1.2",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
-          "dev": true,
-          "requires": {
-            "path-exists": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "path-exists": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
-          "dev": true,
-          "requires": {
-            "pinkie-promise": "^2.0.0"
-          }
-        }
-      }
-    },
-    "readable-stream": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
-      "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
-      "dev": true,
-      "requires": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~1.0.6",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.0.3",
-        "util-deprecate": "~1.0.1"
-      }
-    },
-    "readdirp": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
-      "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.11",
-        "micromatch": "^3.1.10",
-        "readable-stream": "^2.0.2"
-      }
-    },
-    "redent": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
-      "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
-      "dev": true,
-      "requires": {
-        "indent-string": "^2.1.0",
-        "strip-indent": "^1.0.1"
-      }
-    },
-    "regex-not": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
-      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^3.0.2",
-        "safe-regex": "^1.1.0"
-      }
-    },
-    "remove-trailing-separator": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
-      "dev": true
-    },
-    "repeat-element": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
-      "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
-      "dev": true
-    },
-    "repeat-string": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
-      "dev": true
-    },
-    "repeating": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
-      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
-      "dev": true,
-      "requires": {
-        "is-finite": "^1.0.0"
-      }
-    },
-    "request": {
-      "version": "2.88.0",
-      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
-      "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
-      "dev": true,
-      "requires": {
-        "aws-sign2": "~0.7.0",
-        "aws4": "^1.8.0",
-        "caseless": "~0.12.0",
-        "combined-stream": "~1.0.6",
-        "extend": "~3.0.2",
-        "forever-agent": "~0.6.1",
-        "form-data": "~2.3.2",
-        "har-validator": "~5.1.0",
-        "http-signature": "~1.2.0",
-        "is-typedarray": "~1.0.0",
-        "isstream": "~0.1.2",
-        "json-stringify-safe": "~5.0.1",
-        "mime-types": "~2.1.19",
-        "oauth-sign": "~0.9.0",
-        "performance-now": "^2.1.0",
-        "qs": "~6.5.2",
-        "safe-buffer": "^5.1.2",
-        "tough-cookie": "~2.4.3",
-        "tunnel-agent": "^0.6.0",
-        "uuid": "^3.3.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
-      }
-    },
-    "require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
-      "dev": true
-    },
-    "require-main-filename": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
-      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
-      "dev": true
-    },
-    "resolve-cwd": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
-      "dev": true,
-      "requires": {
-        "resolve-from": "^3.0.0"
-      }
-    },
-    "resolve-dir": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
-      "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
-      "dev": true,
-      "requires": {
-        "expand-tilde": "^2.0.0",
-        "global-modules": "^1.0.0"
-      }
-    },
-    "resolve-from": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-      "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
-      "dev": true
-    },
-    "resolve-url": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
-      "dev": true
-    },
-    "ret": {
-      "version": "0.1.15",
-      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
-      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
-      "dev": true
-    },
-    "rimraf": {
-      "version": "2.6.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
-      "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
-      "dev": true,
-      "requires": {
-        "glob": "^7.0.5"
-      }
-    },
-    "ripemd160": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
-      "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
-      "dev": true,
-      "requires": {
-        "hash-base": "^3.0.0",
-        "inherits": "^2.0.1"
-      }
-    },
-    "run-queue": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
-      "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
-      "dev": true,
-      "requires": {
-        "aproba": "^1.1.1"
-      }
-    },
-    "safe-buffer": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
-      "dev": true
-    },
-    "safe-regex": {
-      "version": "1.1.0",
-      "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
-      "dev": true,
-      "requires": {
-        "ret": "~0.1.10"
-      }
-    },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
-      "dev": true
-    },
-    "sass-graph": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
-      "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
-      "dev": true,
-      "requires": {
-        "glob": "^7.0.0",
-        "lodash": "^4.0.0",
-        "scss-tokenizer": "^0.2.3",
-        "yargs": "^7.0.0"
-      }
-    },
-    "sass-loader": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz",
-      "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==",
-      "dev": true,
-      "requires": {
-        "clone-deep": "^2.0.1",
-        "loader-utils": "^1.0.1",
-        "lodash.tail": "^4.1.1",
-        "neo-async": "^2.5.0",
-        "pify": "^3.0.0",
-        "semver": "^5.5.0"
-      }
-    },
-    "schema-utils": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
-      "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
-      "dev": true,
-      "requires": {
-        "ajv": "^6.1.0",
-        "ajv-errors": "^1.0.0",
-        "ajv-keywords": "^3.1.0"
-      }
-    },
-    "scss-tokenizer": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
-      "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
-      "dev": true,
-      "requires": {
-        "js-base64": "^2.1.8",
-        "source-map": "^0.4.2"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
-          "dev": true,
-          "requires": {
-            "amdefine": ">=0.0.4"
-          }
-        }
-      }
-    },
-    "select": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
-      "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
-    },
-    "semver": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-      "dev": true
-    },
-    "serialize-javascript": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.7.0.tgz",
-      "integrity": "sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==",
-      "dev": true
-    },
-    "set-blocking": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
-      "dev": true
-    },
-    "set-value": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
-      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-extendable": "^0.1.1",
-        "is-plain-object": "^2.0.3",
-        "split-string": "^3.0.1"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "setimmediate": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
-      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
-      "dev": true
-    },
-    "sha.js": {
-      "version": "2.4.11",
-      "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
-      "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "shallow-clone": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz",
-      "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==",
-      "dev": true,
-      "requires": {
-        "is-extendable": "^0.1.1",
-        "kind-of": "^5.0.0",
-        "mixin-object": "^2.0.1"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
-      }
-    },
-    "shebang-command": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
-      "dev": true,
-      "requires": {
-        "shebang-regex": "^1.0.0"
-      }
-    },
-    "shebang-regex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
       "dev": true
     },
-    "shell-quote": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
-      "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
-      "dev": true,
-      "requires": {
-        "array-filter": "~0.0.0",
-        "array-map": "~0.0.0",
-        "array-reduce": "~0.0.0",
-        "jsonify": "~0.0.0"
-      }
-    },
-    "signal-exit": {
+    "linkify-it": {
       "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
-      "dev": true
-    },
-    "snapdragon": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
-      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
-      "dev": true,
-      "requires": {
-        "base": "^0.11.1",
-        "debug": "^2.2.0",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "map-cache": "^0.2.2",
-        "source-map": "^0.5.6",
-        "source-map-resolve": "^0.5.0",
-        "use": "^3.1.0"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        }
-      }
-    },
-    "snapdragon-node": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
-      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
-      "dev": true,
-      "requires": {
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.0",
-        "snapdragon-util": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
-      }
-    },
-    "snapdragon-util": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
-      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.2.0"
-      }
-    },
-    "sort-keys": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
-      "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
-      "dev": true,
-      "requires": {
-        "is-plain-obj": "^1.0.0"
-      }
-    },
-    "sortablejs": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.9.0.tgz",
-      "integrity": "sha512-Ot6bYJ6PoqPmpsqQYXjn1+RKrY2NWQvQt/o4jfd/UYwVWndyO5EPO8YHbnm5HIykf8ENsm4JUrdAvolPT86yYA=="
-    },
-    "source-list-map": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz",
-      "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==",
-      "dev": true
-    },
-    "source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true
-    },
-    "source-map-resolve": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
-      "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
-      "dev": true,
+      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz",
+      "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==",
       "requires": {
-        "atob": "^2.1.1",
-        "decode-uri-component": "^0.2.0",
-        "resolve-url": "^0.2.1",
-        "source-map-url": "^0.4.0",
-        "urix": "^0.1.0"
+        "uc.micro": "^1.0.1"
       }
     },
-    "source-map-support": {
-      "version": "0.5.12",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
-      "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
+    "livereload": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.1.tgz",
+      "integrity": "sha512-9g7sua11kkyZNo2hLRCG3LuZZwqexoyEyecSlV8cAsfAVVCZqLzVir6XDqmH0r+Vzgnd5LrdHDMyjtFnJQLAYw==",
       "dev": true,
       "requires": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
+        "chokidar": "^3.3.0",
+        "livereload-js": "^3.1.0",
+        "opts": ">= 1.2.0",
+        "ws": "^6.2.1"
       }
     },
-    "source-map-url": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
-      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+    "livereload-js": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.3.1.tgz",
+      "integrity": "sha512-CBu1gTEfzVhlOK1WASKAAJ9Qx1fHECTq0SUB67sfxwQssopTyvzqTlgl+c0h9pZ6V+Fzd2rc510ppuNusg9teQ==",
       "dev": true
     },
-    "spdx-correct": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
-      "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==",
+    "load-json-file": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+      "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
       "dev": true,
       "requires": {
-        "spdx-expression-parse": "^3.0.0",
-        "spdx-license-ids": "^3.0.0"
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^4.0.0",
+        "pify": "^3.0.0",
+        "strip-bom": "^3.0.0"
       }
     },
-    "spdx-exceptions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
-      "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
-      "dev": true
-    },
-    "spdx-expression-parse": {
+    "locate-path": {
       "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
-      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
-      "dev": true,
-      "requires": {
-        "spdx-exceptions": "^2.1.0",
-        "spdx-license-ids": "^3.0.0"
-      }
-    },
-    "spdx-license-ids": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz",
-      "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==",
-      "dev": true
-    },
-    "split-string": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
-      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^3.0.0"
-      }
-    },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
-    },
-    "sshpk": {
-      "version": "1.16.1",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
-      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
-      "dev": true,
-      "requires": {
-        "asn1": "~0.2.3",
-        "assert-plus": "^1.0.0",
-        "bcrypt-pbkdf": "^1.0.0",
-        "dashdash": "^1.12.0",
-        "ecc-jsbn": "~0.1.1",
-        "getpass": "^0.1.1",
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.0.2",
-        "tweetnacl": "~0.14.0"
-      }
-    },
-    "ssri": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
-      "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
-      "dev": true,
-      "requires": {
-        "figgy-pudding": "^3.5.1"
-      }
-    },
-    "static-extend": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
-      "dev": true,
-      "requires": {
-        "define-property": "^0.2.5",
-        "object-copy": "^0.1.0"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        }
-      }
-    },
-    "stdout-stream": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
-      "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
-      "dev": true,
-      "requires": {
-        "readable-stream": "^2.0.1"
-      }
-    },
-    "stream-browserify": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
-      "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
-      "dev": true,
-      "requires": {
-        "inherits": "~2.0.1",
-        "readable-stream": "^2.0.2"
-      }
-    },
-    "stream-each": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
-      "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
-      "dev": true,
-      "requires": {
-        "end-of-stream": "^1.1.0",
-        "stream-shift": "^1.0.0"
-      }
-    },
-    "stream-http": {
-      "version": "2.8.3",
-      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
-      "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+      "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
       "dev": true,
       "requires": {
-        "builtin-status-codes": "^3.0.0",
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.3.6",
-        "to-arraybuffer": "^1.0.0",
-        "xtend": "^4.0.0"
-      },
-      "dependencies": {
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
+        "p-locate": "^3.0.0",
+        "path-exists": "^3.0.0"
       }
     },
-    "stream-shift": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
-      "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
+    "lodash.debounce": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+      "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
       "dev": true
     },
-    "strict-uri-encode": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
-      "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
+    "lodash.throttle": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+      "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=",
       "dev": true
     },
-    "string-width": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-      "dev": true,
-      "requires": {
-        "code-point-at": "^1.0.0",
-        "is-fullwidth-code-point": "^1.0.0",
-        "strip-ansi": "^3.0.0"
-      }
-    },
-    "string.prototype.padend": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz",
-      "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "es-abstract": "^1.4.3",
-        "function-bind": "^1.0.2"
-      }
-    },
-    "string_decoder": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
-      "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
-      "dev": true,
+    "markdown-it": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.1.tgz",
+      "integrity": "sha512-aU1TzmBKcWNNYvH9pjq6u92BML+Hz3h5S/QpfTFwiQF852pLT+9qHsrhM9JYipkOXZxGn+sGH8oyJE9FD9WezQ==",
       "requires": {
-        "safe-buffer": "~5.1.0"
+        "argparse": "^1.0.7",
+        "entities": "~2.0.0",
+        "linkify-it": "^3.0.1",
+        "mdurl": "^1.0.1",
+        "uc.micro": "^1.0.5"
       }
     },
-    "strip-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^2.0.0"
-      }
+    "markdown-it-task-lists": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
+      "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="
     },
-    "strip-bom": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
-      "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
-      "dev": true,
-      "requires": {
-        "is-utf8": "^0.2.0"
-      }
+    "mdurl": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+      "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
     },
-    "strip-eof": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+    "memorystream": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+      "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
       "dev": true
     },
-    "strip-indent": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
-      "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
       "dev": true,
       "requires": {
-        "get-stdin": "^4.0.1"
+        "brace-expansion": "^1.1.7"
       }
     },
-    "style-loader": {
-      "version": "0.23.1",
-      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
-      "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==",
-      "dev": true,
-      "requires": {
-        "loader-utils": "^1.1.0",
-        "schema-utils": "^1.0.0"
-      }
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
     },
-    "supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
       "dev": true,
       "requires": {
-        "has-flag": "^3.0.0"
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
       }
     },
-    "tapable": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
-      "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
       "dev": true
     },
-    "tar": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
-      "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
-      "dev": true,
-      "requires": {
-        "block-stream": "*",
-        "fstream": "^1.0.12",
-        "inherits": "2"
-      }
-    },
-    "terser": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-4.0.0.tgz",
-      "integrity": "sha512-dOapGTU0hETFl1tCo4t56FN+2jffoKyER9qBGoUFyZ6y7WLoKT0bF+lAYi6B6YsILcGF3q1C2FBh8QcKSCgkgA==",
+    "npm-run-all": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
+      "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
       "dev": true,
       "requires": {
-        "commander": "^2.19.0",
-        "source-map": "~0.6.1",
-        "source-map-support": "~0.5.10"
+        "ansi-styles": "^3.2.1",
+        "chalk": "^2.4.1",
+        "cross-spawn": "^6.0.5",
+        "memorystream": "^0.3.1",
+        "minimatch": "^3.0.4",
+        "pidtree": "^0.3.0",
+        "read-pkg": "^3.0.0",
+        "shell-quote": "^1.6.1",
+        "string.prototype.padend": "^3.0.0"
       }
     },
-    "terser-webpack-plugin": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.3.0.tgz",
-      "integrity": "sha512-W2YWmxPjjkUcOWa4pBEv4OP4er1aeQJlSo2UhtCFQCuRXEHjOFscO8VyWHj9JLlA0RzQb8Y2/Ta78XZvT54uGg==",
-      "dev": true,
-      "requires": {
-        "cacache": "^11.3.2",
-        "find-cache-dir": "^2.0.0",
-        "is-wsl": "^1.1.0",
-        "loader-utils": "^1.2.3",
-        "schema-utils": "^1.0.0",
-        "serialize-javascript": "^1.7.0",
-        "source-map": "^0.6.1",
-        "terser": "^4.0.0",
-        "webpack-sources": "^1.3.0",
-        "worker-farm": "^1.7.0"
-      },
-      "dependencies": {
-        "big.js": {
-          "version": "5.2.2",
-          "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
-          "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
-          "dev": true
-        },
-        "json5": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
-          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.0"
-          }
-        },
-        "loader-utils": {
-          "version": "1.2.3",
-          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
-          "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
-          "dev": true,
-          "requires": {
-            "big.js": "^5.2.2",
-            "emojis-list": "^2.0.0",
-            "json5": "^1.0.1"
-          }
-        },
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
-        }
-      }
+    "object-inspect": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+      "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+      "dev": true
     },
-    "through2": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
-      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
-      "dev": true,
-      "requires": {
-        "readable-stream": "~2.3.6",
-        "xtend": "~4.0.1"
-      },
-      "dependencies": {
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
-      }
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true
     },
-    "timers-browserify": {
-      "version": "2.0.10",
-      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz",
-      "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==",
+    "object.assign": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
       "dev": true,
       "requires": {
-        "setimmediate": "^1.0.4"
+        "define-properties": "^1.1.2",
+        "function-bind": "^1.1.1",
+        "has-symbols": "^1.0.0",
+        "object-keys": "^1.0.11"
       }
     },
-    "tiny-emitter": {
+    "opts": {
       "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz",
-      "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow=="
-    },
-    "to-arraybuffer": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
-      "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+      "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz",
+      "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==",
       "dev": true
     },
-    "to-object-path": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+    "p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "p-try": "^2.0.0"
       }
     },
-    "to-regex": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
-      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+    "p-locate": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+      "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
       "dev": true,
       "requires": {
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "regex-not": "^1.0.2",
-        "safe-regex": "^1.1.0"
+        "p-limit": "^2.0.0"
       }
     },
-    "to-regex-range": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
-      "dev": true,
-      "requires": {
-        "is-number": "^3.0.0",
-        "repeat-string": "^1.6.1"
-      }
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true
     },
-    "tough-cookie": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
-      "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+    "parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
       "dev": true,
       "requires": {
-        "psl": "^1.1.24",
-        "punycode": "^1.4.1"
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1"
       }
     },
-    "trim-newlines": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
-      "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+    "path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
       "dev": true
     },
-    "true-case-path": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
-      "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
-      "dev": true,
-      "requires": {
-        "glob": "^7.1.2"
-      }
-    },
-    "tslib": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
-      "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
       "dev": true
     },
-    "tty-browserify": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
-      "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
       "dev": true
     },
-    "tunnel-agent": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+    "path-type": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
       "dev": true,
       "requires": {
-        "safe-buffer": "^5.0.1"
+        "pify": "^3.0.0"
       }
     },
-    "tweetnacl": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+    "picomatch": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+      "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
       "dev": true
     },
-    "typedarray": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+    "pidtree": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
+      "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
       "dev": true
     },
-    "uc.micro": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz",
-      "integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg=="
+    "pify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+      "dev": true
     },
-    "ultron": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
-      "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=",
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
       "dev": true
     },
-    "union-value": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
-      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+    "read-pkg": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+      "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
       "dev": true,
       "requires": {
-        "arr-union": "^3.1.0",
-        "get-value": "^2.0.6",
-        "is-extendable": "^0.1.1",
-        "set-value": "^2.0.1"
+        "load-json-file": "^4.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^3.0.0"
       }
     },
-    "uniq": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
-      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
-      "dev": true
-    },
-    "unique-filename": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
-      "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+    "readdirp": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+      "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
       "dev": true,
       "requires": {
-        "unique-slug": "^2.0.0"
+        "picomatch": "^2.2.1"
       }
     },
-    "unique-slug": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz",
-      "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==",
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+      "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
       "dev": true,
       "requires": {
-        "imurmurhash": "^0.1.4"
+        "path-parse": "^1.0.6"
       }
     },
-    "unset-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+    "sass": {
+      "version": "1.26.11",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.11.tgz",
+      "integrity": "sha512-W1l/+vjGjIamsJ6OnTe0K37U2DBO/dgsv2Z4c89XQ8ZOO6l/VwkqwLSqoYzJeJs6CLuGSTRWc91GbQFL3lvrvw==",
       "dev": true,
       "requires": {
-        "has-value": "^0.3.1",
-        "isobject": "^3.0.0"
-      },
-      "dependencies": {
-        "has-value": {
-          "version": "0.3.1",
-          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
-          "dev": true,
-          "requires": {
-            "get-value": "^2.0.3",
-            "has-values": "^0.1.4",
-            "isobject": "^2.0.0"
-          },
-          "dependencies": {
-            "isobject": {
-              "version": "2.1.0",
-              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
-              "dev": true,
-              "requires": {
-                "isarray": "1.0.0"
-              }
-            }
-          }
-        },
-        "has-values": {
-          "version": "0.1.4",
-          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
-          "dev": true
-        }
+        "chokidar": ">=2.0.0 <4.0.0"
       }
     },
-    "upath": {
+    "select": {
       "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz",
-      "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==",
+      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+      "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+    },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
       "dev": true
     },
-    "uri-js": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
-      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
       "dev": true,
       "requires": {
-        "punycode": "^2.1.0"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-          "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-          "dev": true
-        }
+        "shebang-regex": "^1.0.0"
       }
     },
-    "urix": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true
+    },
+    "shell-quote": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
+      "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
       "dev": true
     },
-    "url": {
-      "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
-      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+    "sortablejs": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.12.0.tgz",
+      "integrity": "sha512-bPn57rCjBRlt2sC24RBsu40wZsmLkSo2XeqG8k6DC1zru5eObQUIPPZAQG7W2SJ8FZQYq+BEJmvuw1Zxb3chqg=="
+    },
+    "spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
       "dev": true,
       "requires": {
-        "punycode": "1.3.2",
-        "querystring": "0.2.0"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "1.3.2",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
-          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
-          "dev": true
-        }
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
       }
     },
-    "use": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
-      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
       "dev": true
     },
-    "util": {
-      "version": "0.11.1",
-      "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
-      "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+    "spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
       "dev": true,
       "requires": {
-        "inherits": "2.0.3"
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
       }
     },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-      "dev": true
-    },
-    "uuid": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
-      "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+    "spdx-license-ids": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
       "dev": true
     },
-    "v8-compile-cache": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz",
-      "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==",
-      "dev": true
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
     },
-    "validate-npm-package-license": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
-      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+    "string-width": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+      "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
       "dev": true,
       "requires": {
-        "spdx-correct": "^3.0.0",
-        "spdx-expression-parse": "^3.0.0"
+        "emoji-regex": "^7.0.1",
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^5.1.0"
       }
     },
-    "verror": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+    "string.prototype.padend": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz",
+      "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0",
-        "core-util-is": "1.0.2",
-        "extsprintf": "^1.2.0"
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1"
       }
     },
-    "vm-browserify": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
-      "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
+    "string.prototype.trimend": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+      "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
       "dev": true,
       "requires": {
-        "indexof": "0.0.1"
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
       }
     },
-    "vue": {
-      "version": "2.6.10",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz",
-      "integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ=="
-    },
-    "vuedraggable": {
-      "version": "2.21.0",
-      "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.21.0.tgz",
-      "integrity": "sha512-UDp0epjaZikuInoJA9rlEIJaSTQThabq0R9x7TqBdl0qGVFKKzo6glP6ubfzWBmV4iRIfbSOs2DV06s3B5h5tA==",
+    "string.prototype.trimstart": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+      "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+      "dev": true,
       "requires": {
-        "sortablejs": "^1.9.0"
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
       }
     },
-    "watchpack": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
-      "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==",
+    "strip-ansi": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+      "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
       "dev": true,
       "requires": {
-        "chokidar": "^2.0.2",
-        "graceful-fs": "^4.1.2",
-        "neo-async": "^2.5.0"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
-        }
+        "ansi-regex": "^4.1.0"
       }
     },
-    "webpack": {
-      "version": "4.32.2",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.32.2.tgz",
-      "integrity": "sha512-F+H2Aa1TprTQrpodRAWUMJn7A8MgDx82yQiNvYMaj3d1nv3HetKU0oqEulL9huj8enirKi8KvEXQ3QtuHF89Zg==",
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.8.5",
-        "@webassemblyjs/helper-module-context": "1.8.5",
-        "@webassemblyjs/wasm-edit": "1.8.5",
-        "@webassemblyjs/wasm-parser": "1.8.5",
-        "acorn": "^6.0.5",
-        "acorn-dynamic-import": "^4.0.0",
-        "ajv": "^6.1.0",
-        "ajv-keywords": "^3.1.0",
-        "chrome-trace-event": "^1.0.0",
-        "enhanced-resolve": "^4.1.0",
-        "eslint-scope": "^4.0.0",
-        "json-parse-better-errors": "^1.0.2",
-        "loader-runner": "^2.3.0",
-        "loader-utils": "^1.1.0",
-        "memory-fs": "~0.4.1",
-        "micromatch": "^3.1.8",
-        "mkdirp": "~0.5.0",
-        "neo-async": "^2.5.0",
-        "node-libs-browser": "^2.0.0",
-        "schema-utils": "^1.0.0",
-        "tapable": "^1.1.0",
-        "terser-webpack-plugin": "^1.1.0",
-        "watchpack": "^1.5.0",
-        "webpack-sources": "^1.3.0"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
-        }
+        "has-flag": "^3.0.0"
       }
     },
-    "webpack-cli": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.2.tgz",
-      "integrity": "sha512-FLkobnaJJ+03j5eplxlI0TUxhGCOdfewspIGuvDVtpOlrAuKMFC57K42Ukxqs1tn8947/PM6tP95gQc0DCzRYA==",
+    "tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
       "dev": true,
       "requires": {
-        "chalk": "^2.4.1",
-        "cross-spawn": "^6.0.5",
-        "enhanced-resolve": "^4.1.0",
-        "findup-sync": "^2.0.0",
-        "global-modules": "^1.0.0",
-        "import-local": "^2.0.0",
-        "interpret": "^1.1.0",
-        "loader-utils": "^1.1.0",
-        "supports-color": "^5.5.0",
-        "v8-compile-cache": "^2.0.2",
-        "yargs": "^12.0.5"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "cliui": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
-          "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
-          "dev": true,
-          "requires": {
-            "string-width": "^2.1.1",
-            "strip-ansi": "^4.0.0",
-            "wrap-ansi": "^2.0.0"
-          }
-        },
-        "cross-spawn": {
-          "version": "6.0.5",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-          "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-          "dev": true,
-          "requires": {
-            "nice-try": "^1.0.4",
-            "path-key": "^2.0.1",
-            "semver": "^5.5.0",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        },
-        "invert-kv": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
-          "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-          "dev": true
-        },
-        "lcid": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
-          "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
-          "dev": true,
-          "requires": {
-            "invert-kv": "^2.0.0"
-          }
-        },
-        "os-locale": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
-          "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
-          "dev": true,
-          "requires": {
-            "execa": "^1.0.0",
-            "lcid": "^2.0.0",
-            "mem": "^4.0.0"
-          }
-        },
-        "string-width": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
-          "dev": true,
-          "requires": {
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^4.0.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        },
-        "which-module": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-          "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
-          "dev": true
-        },
-        "yargs": {
-          "version": "12.0.5",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
-          "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
-          "dev": true,
-          "requires": {
-            "cliui": "^4.0.0",
-            "decamelize": "^1.2.0",
-            "find-up": "^3.0.0",
-            "get-caller-file": "^1.0.1",
-            "os-locale": "^3.0.0",
-            "require-directory": "^2.1.1",
-            "require-main-filename": "^1.0.1",
-            "set-blocking": "^2.0.0",
-            "string-width": "^2.0.0",
-            "which-module": "^2.0.0",
-            "y18n": "^3.2.1 || ^4.0.0",
-            "yargs-parser": "^11.1.1"
-          }
-        },
-        "yargs-parser": {
-          "version": "11.1.1",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
-          "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
-          "dev": true,
-          "requires": {
-            "camelcase": "^5.0.0",
-            "decamelize": "^1.2.0"
-          }
-        }
+        "is-number": "^7.0.0"
       }
     },
-    "webpack-sources": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz",
-      "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==",
+    "uc.micro": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+      "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
       "dev": true,
       "requires": {
-        "source-list-map": "^2.0.0",
-        "source-map": "~0.6.1"
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
       }
     },
     "which": {
       }
     },
     "which-module": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
-      "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
       "dev": true
     },
-    "wide-align": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
-      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
-      "dev": true,
-      "requires": {
-        "string-width": "^1.0.2 || 2"
-      }
-    },
-    "worker-farm": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
-      "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
-      "dev": true,
-      "requires": {
-        "errno": "~0.1.7"
-      }
-    },
     "wrap-ansi": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
-      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+      "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
       "dev": true,
       "requires": {
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1"
+        "ansi-styles": "^3.2.0",
+        "string-width": "^3.0.0",
+        "strip-ansi": "^5.0.0"
       }
     },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
-    },
     "ws": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
-      "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
+      "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
       "dev": true,
       "requires": {
-        "options": ">=0.0.5",
-        "ultron": "1.0.x"
+        "async-limiter": "~1.0.0"
       }
     },
-    "xtend": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
-      "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
-      "dev": true
-    },
     "y18n": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
-      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
-      "dev": true
-    },
-    "yallist": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
       "dev": true
     },
     "yargs": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
-      "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
+      "version": "13.3.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+      "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
       "dev": true,
       "requires": {
-        "camelcase": "^3.0.0",
-        "cliui": "^3.2.0",
-        "decamelize": "^1.1.1",
-        "get-caller-file": "^1.0.1",
-        "os-locale": "^1.4.0",
-        "read-pkg-up": "^1.0.1",
+        "cliui": "^5.0.0",
+        "find-up": "^3.0.0",
+        "get-caller-file": "^2.0.1",
         "require-directory": "^2.1.1",
-        "require-main-filename": "^1.0.1",
+        "require-main-filename": "^2.0.0",
         "set-blocking": "^2.0.0",
-        "string-width": "^1.0.2",
-        "which-module": "^1.0.0",
-        "y18n": "^3.2.1",
-        "yargs-parser": "^5.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
-          "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
-          "dev": true
-        }
+        "string-width": "^3.0.0",
+        "which-module": "^2.0.0",
+        "y18n": "^4.0.0",
+        "yargs-parser": "^13.1.2"
       }
     },
     "yargs-parser": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
-      "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
+      "version": "13.1.2",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+      "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
       "dev": true,
       "requires": {
-        "camelcase": "^3.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
-          "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
-          "dev": true
-        }
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
       }
     }
   }
index 0d1afbb0c05031a44f702399e00f0cfb6f8f5c63..d5e93a31e3dc4aaca1e4835196656da0c1f28d4f 100644 (file)
@@ -1,36 +1,33 @@
 {
   "private": true,
   "scripts": {
-    "build": "webpack",
-    "production": "NODE_ENV=production webpack && rm -f ./public/dist/*styles.js",
-    "build-profile": "NODE_ENV=production webpack --profile --json > webpack-stats.json && rm -f ./public/dist/*styles.js",
+    "build:css:dev": "sass ./resources/sass:./public/dist",
+    "build:css:watch": "sass ./resources/sass:./public/dist --watch",
+    "build:css:production": "sass ./resources/sass:./public/dist -s compressed",
+    "build:js:dev": "esbuild --bundle ./resources/js/index.js --outfile=public/dist/app.js --sourcemap --target=es2019 --main-fields=module,main",
+    "build:js:watch": "chokidar --initial \"./resources/**/*.js\" -c \"npm run build:js:dev\"",
+    "build:js:production": "NODE_ENV=production esbuild --bundle ./resources/js/index.js --outfile=public/dist/app.js --sourcemap --target=es2019 --main-fields=module,main --minify",
+    "build": "npm-run-all --parallel build:*:dev",
+    "production": "npm-run-all --parallel build:*:production",
     "dev": "npm-run-all --parallel watch livereload",
-    "watch": "webpack --watch",
+    "watch": "npm-run-all --parallel build:*:watch",
     "livereload": "livereload ./public/dist/",
     "permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads"
   },
   "devDependencies": {
-    "css-loader": "^2.1.1",
-    "livereload": "^0.8.0",
-    "mini-css-extract-plugin": "^0.7.0",
-    "node-sass": "^4.12.0",
+    "chokidar-cli": "^2.1.0",
+    "esbuild": "0.7.8",
+    "livereload": "^0.9.1",
     "npm-run-all": "^4.1.5",
-    "sass-loader": "^7.1.0",
-    "style-loader": "^0.23.1",
-    "webpack": "^4.32.2",
-    "webpack-cli": "^3.3.2"
+    "punycode": "^2.1.1",
+    "sass": "^1.26.11"
   },
   "dependencies": {
-    "clipboard": "^2.0.4",
-    "codemirror": "^5.47.0",
-    "dropzone": "^5.5.1",
-    "markdown-it": "^8.4.2",
+    "clipboard": "^2.0.6",
+    "codemirror": "^5.58.1",
+    "dropzone": "^5.7.2",
+    "markdown-it": "^11.0.1",
     "markdown-it-task-lists": "^2.1.1",
-    "sortablejs": "^1.9.0",
-    "vue": "^2.6.10",
-    "vuedraggable": "^2.21.0"
-  },
-  "browser": {
-    "vue": "vue/dist/vue.common.js"
+    "sortablejs": "^1.12.0"
   }
 }
index 009791fc9d4cb33ddc88001dec5fef60172e7dc1..ccde28033ab9a3675dbf0203c073e09c01108925 100644 (file)
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -3,6 +3,7 @@
     <description>The coding standard for BookStack.</description>
     <file>app</file>
     <exclude-pattern>*/migrations/*</exclude-pattern>
+    <exclude-pattern>*/tests/*</exclude-pattern>
     <arg value="np"/>
     <rule ref="PSR2"/>
 </ruleset>
\ No newline at end of file
index 9f83e95ff238ea30a9672077b9fef6add7a885ab..0332182883ceb77a5b477842593053aa5181ee11 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <phpunit backupGlobals="false"
          backupStaticAttributes="false"
-         bootstrap="bootstrap/init.php"
+         bootstrap="vendor/autoload.php"
          colors="true"
          convertErrorsToExceptions="true"
          convertNoticesToExceptions="true"
         <server name="APP_ENV" value="testing"/>
         <server name="APP_DEBUG" value="false"/>
         <server name="APP_LANG" value="en"/>
+        <server name="APP_THEME" value="none"/>
         <server name="APP_AUTO_LANG_PUBLIC" value="true"/>
+        <server name="APP_URL" value="http://bookstack.dev"/>
+        <server name="ALLOWED_IFRAME_HOSTS" value=""/>
         <server name="CACHE_DRIVER" value="array"/>
         <server name="SESSION_DRIVER" value="array"/>
         <server name="QUEUE_CONNECTION" value="sync"/>
@@ -34,6 +37,7 @@
         <server name="DISABLE_EXTERNAL_SERVICES" value="true"/>
         <server name="AVATAR_URL" value=""/>
         <server name="LDAP_VERSION" value="3"/>
+        <server name="SESSION_SECURE_COOKIE" value="null"/>
         <server name="STORAGE_TYPE" value="local"/>
         <server name="STORAGE_ATTACHMENT_TYPE" value="local"/>
         <server name="STORAGE_IMAGE_TYPE" value="local"/>
         <server name="GOOGLE_AUTO_REGISTER" value=""/>
         <server name="GOOGLE_AUTO_CONFIRM_EMAIL" value=""/>
         <server name="GOOGLE_SELECT_ACCOUNT" value=""/>
-        <server name="APP_URL" value="http://bookstack.dev"/>
         <server name="DEBUGBAR_ENABLED" value="false"/>
+        <server name="SAML2_ENABLED" value="false"/>
+        <server name="API_REQUESTS_PER_MIN" value="180"/>
+        <server name="LOG_FAILED_LOGIN_MESSAGE" value=""/>
+        <server name="LOG_FAILED_LOGIN_CHANNEL" value="testing"/>
+        <server name="WKHTMLTOPDF" value="false"/>
     </php>
 </phpunit>
index 0d55354ec6fb756086be2bcace7be11078064f29..3aec5e27e5db801fa9e321c0a97acbb49e10908f 100644 (file)
@@ -5,11 +5,16 @@
 
     RewriteEngine On
 
+    # Handle Authorization Header
+    RewriteCond %{HTTP:Authorization} .
+    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+
     # Redirect Trailing Slashes If Not A Folder...
     RewriteCond %{REQUEST_FILENAME} !-d
-    RewriteRule ^(.*)/$ /$1 [L,R=301]
+    RewriteCond %{REQUEST_URI} (.+)/$
+    RewriteRule ^ %1 [L,R=301]
 
-    # Handle Front Controller...
+    # Send Requests To Front Controller...
     RewriteCond %{REQUEST_FILENAME} !-d
     RewriteCond %{REQUEST_FILENAME} !-f
     RewriteRule ^ index.php [L]
index 8205764728cdb1dc6bd8bdfb78f20ebec5525ac3..9d890e90a4ef4cd9ade7b25b434248c63766d3d0 100644 (file)
@@ -11,15 +11,17 @@ define('LARAVEL_START', microtime(true));
 
 /*
 |--------------------------------------------------------------------------
-| Initialize The App
+| Register The Auto Loader
 |--------------------------------------------------------------------------
 |
-| We need to get things going before we start up the app.
-| The init file loads everything in, in the correct order.
+| Composer provides a convenient, automatically generated class loader for
+| our application. We just need to utilize it! We'll simply require it
+| into the script here so that we don't have to worry about manual
+| loading any of our classes later on. It feels great to relax.
 |
 */
 
-require __DIR__.'/../bootstrap/init.php';
+require __DIR__.'/../vendor/autoload.php';
 
 /*
 |--------------------------------------------------------------------------
index a598b6c4afd82b0766265858db70f95b3dfa5ee1..67cde482ab9bf5502daa3bd769a7c0a72321cd75 100644 (file)
@@ -1 +1 @@
-!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=function(e){return function(){return e}};function c(r){for(var o=[],e=1;e<arguments.length;e++)o[e-1]=arguments[e];return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=o.concat(e);return r.apply(null,n)}}var n,r,o,a,i,l,u=t(!1),s=t(!0),m=u,f=s,d=function(){return p},p=(a={fold:function(e,t){return e()},is:m,isSome:m,isNone:f,getOr:o=function(e){return e},getOrThunk:r=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:function(){return null},getOrUndefined:function(){return undefined},or:o,orThunk:r,map:d,ap:d,each:function(){},bind:d,flatten:d,exists:m,forall:f,filter:d,equals:n=function(e){return e.isNone()},equals_:n,toArray:function(){return[]},toString:t("none()")},Object.freeze&&Object.freeze(a),a),y=function(n){var e=function(){return n},t=function(){return o},r=function(e){return e(n)},o={fold:function(e,t){return t(n)},is:function(e){return n===e},isSome:f,isNone:m,getOr:e,getOrThunk:e,getOrDie:e,getOrNull:e,getOrUndefined:e,or:t,orThunk:t,map:function(e){return y(e(n))},ap:function(e){return e.fold(d,function(e){return y(e(n))})},each:function(e){e(n)},bind:r,flatten:e,exists:r,forall:r,filter:function(e){return e(n)?o:p},equals:function(e){return e.is(n)},equals_:function(e,t){return e.fold(m,function(e){return t(n,e)})},toArray:function(){return[n]},toString:function(){return"some("+n+")"}};return o},h={some:y,none:d,from:function(e){return null===e||e===undefined?p:y(e)}},g=(i="function",function(e){return function(e){if(null===e)return"null";var t=typeof e;return"object"===t&&Array.prototype.isPrototypeOf(e)?"array":"object"===t&&String.prototype.isPrototypeOf(e)?"string":t}(e)===i}),k=(l=Array.prototype.indexOf)===undefined?function(e,t){return x(e,t)}:function(e,t){return l.call(e,t)},v=function(e,t){return-1<k(e,t)},b=function(e,t){for(var n=e.length,r=new Array(n),o=0;o<n;o++){var a=e[o];r[o]=t(a,o,e)}return r},x=function(e,t){for(var n=0,r=e.length;n<r;++n)if(e[n]===t)return n;return-1},C=(Array.prototype.slice,g(Array.from)&&Array.from,tinymce.util.Tools.resolve("tinymce.util.I18n")),w=tinymce.util.Tools.resolve("tinymce.Env"),A=w.mac?"\u2318":"Ctrl",S=w.mac?"Ctrl + Alt":"Shift + Alt",O={shortcuts:[{shortcut:A+" + B",action:"Bold"},{shortcut:A+" + I",action:"Italic"},{shortcut:A+" + U",action:"Underline"},{shortcut:A+" + A",action:"Select all"},{shortcut:A+" + Y or "+A+" + Shift + Z",action:"Redo"},{shortcut:A+" + Z",action:"Undo"},{shortcut:S+" + 1",action:"Header 1"},{shortcut:S+" + 2",action:"Header 2"},{shortcut:S+" + 3",action:"Header 3"},{shortcut:S+" + 4",action:"Header 4"},{shortcut:S+" + 5",action:"Header 5"},{shortcut:S+" + 6",action:"Header 6"},{shortcut:S+" + 7",action:"Paragraph"},{shortcut:S+" + 8",action:"Div"},{shortcut:S+" + 9",action:"Address"},{shortcut:"Alt + F9",action:"Focus to menubar"},{shortcut:"Alt + F10",action:"Focus to toolbar"},{shortcut:"Alt + F11",action:"Focus to element path"},{shortcut:"Ctrl + F9",action:"Focus to contextual toolbar"},{shortcut:A+" + K",action:"Insert link (if link plugin activated)"},{shortcut:A+" + S",action:"Save (if save plugin activated)"},{shortcut:A+" + F",action:"Find (if searchreplace plugin activated)"}]},T=function(){var e=b(O.shortcuts,function(e){return'<tr data-mce-tabstop="1" tabindex="-1" aria-label="Action: '+(t=e).action+", Shortcut: "+t.shortcut.replace(/Ctrl/g,"Control")+'"><td>'+C.translate(e.action)+"</td><td>"+e.shortcut+"</td></tr>";var t}).join("");return{title:"Handy Shortcuts",type:"container",style:"overflow-y: auto; overflow-x: hidden; max-height: 250px",items:[{type:"container",html:'<div><table class="mce-table-striped"><thead><th>'+C.translate("Action")+"</th><th>"+C.translate("Shortcut")+"</th></thead>"+e+"</table></div>"}]}},P=Object.keys,_=[{key:"advlist",name:"Advanced List"},{key:"anchor",name:"Anchor"},{key:"autolink",name:"Autolink"},{key:"autoresize",name:"Autoresize"},{key:"autosave",name:"Autosave"},{key:"bbcode",name:"BBCode"},{key:"charmap",name:"Character Map"},{key:"code",name:"Code"},{key:"codesample",name:"Code Sample"},{key:"colorpicker",name:"Color Picker"},{key:"compat3x",name:"3.x Compatibility"},{key:"contextmenu",name:"Context Menu"},{key:"directionality",name:"Directionality"},{key:"emoticons",name:"Emoticons"},{key:"fullpage",name:"Full Page"},{key:"fullscreen",name:"Full Screen"},{key:"help",name:"Help"},{key:"hr",name:"Horizontal Rule"},{key:"image",name:"Image"},{key:"imagetools",name:"Image Tools"},{key:"importcss",name:"Import CSS"},{key:"insertdatetime",name:"Insert Date/Time"},{key:"legacyoutput",name:"Legacy Output"},{key:"link",name:"Link"},{key:"lists",name:"Lists"},{key:"media",name:"Media"},{key:"nonbreaking",name:"Nonbreaking"},{key:"noneditable",name:"Noneditable"},{key:"pagebreak",name:"Page Break"},{key:"paste",name:"Paste"},{key:"preview",name:"Preview"},{key:"print",name:"Print"},{key:"save",name:"Save"},{key:"searchreplace",name:"Search and Replace"},{key:"spellchecker",name:"Spell Checker"},{key:"tabfocus",name:"Tab Focus"},{key:"table",name:"Table"},{key:"template",name:"Template"},{key:"textcolor",name:"Text Color"},{key:"textpattern",name:"Text Pattern"},{key:"toc",name:"Table of Contents"},{key:"visualblocks",name:"Visual Blocks"},{key:"visualchars",name:"Visual Characters"},{key:"wordcount",name:"Word Count"}],H=c(function(e,o){return e.replace(/\$\{([^{}]*)\}/g,function(e,t){var n,r=o[t];return"string"==(n=typeof r)||"number"===n?r.toString():e})},'<a href="${url}" target="_blank" rel="noopener">${name}</a>'),F=function(t,n){return function(e,t){for(var n=0,r=e.length;n<r;n++){var o=e[n];if(t(o,n,e))return h.some(o)}return h.none()}(_,function(e){return e.key===n}).fold(function(){var e=t.plugins[n].getMetadata;return"function"==typeof e?H(e()):n},function(e){return H({name:e.name,url:"https://www.tinymce.com/docs/plugins/"+e.key})})},M=function(t){var e,n,r,o=(r=P((e=t).plugins),e.settings.forced_plugins===undefined?r:function(e,t){for(var n=[],r=0,o=e.length;r<o;r++){var a=e[r];t(a,r,e)&&n.push(a)}return n}(r,(n=c(v,e.settings.forced_plugins),function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return!n.apply(null,e)}))),a=b(o,function(e){return"<li>"+F(t,e)+"</li>"}),i=a.length,l=a.join("");return"<p><b>"+C.translate(["Plugins installed ({0}):",i])+"</b></p><ul>"+l+"</ul>"},E=function(e){return{title:"Plugins",type:"container",style:"overflow-y: auto; overflow-x: hidden;",layout:"flex",padding:10,spacing:10,items:[(t=e,{type:"container",html:'<div style="overflow-y: auto; overflow-x: hidden; max-height: 230px; height: 230px;" data-mce-tabstop="1" tabindex="-1">'+M(t)+"</div>",flex:1}),{type:"container",html:'<div style="padding: 10px; background: #e3e7f4; height: 100%;" data-mce-tabstop="1" tabindex="-1"><p><b>'+C.translate("Premium plugins:")+'</b></p><ul><li>PowerPaste</li><li>Spell Checker Pro</li><li>Accessibility Checker</li><li>Advanced Code Editor</li><li>Enhanced Media Embed</li><li>Link Checker</li></ul><br /><p style="float: right;"><a href="https://www.tinymce.com/pricing/?utm_campaign=editor_referral&utm_medium=help_dialog&utm_source=tinymce" target="_blank">'+C.translate("Learn more...")+"</a></p></div>",flex:1}]};var t},I=tinymce.util.Tools.resolve("tinymce.EditorManager"),j=function(){var e,t,n='<a href="https://www.tinymce.com/docs/changelog/?utm_campaign=editor_referral&utm_medium=help_dialog&utm_source=tinymce" target="_blank">TinyMCE '+(e=I.majorVersion,t=I.minorVersion,0===e.indexOf("@")?"X.X.X":e+"."+t)+"</a>";return[{type:"label",html:C.translate(["You are using {0}",n])},{type:"spacer",flex:1},{text:"Close",onclick:function(){this.parent().parent().close()}}]},L=function(e,t){return function(){e.windowManager.open({title:"Help",bodyType:"tabpanel",layout:"flex",body:[T(),E(e)],buttons:j(),onPostRender:function(){this.getEl("title").innerHTML='<img src="'+t+'/img/logo.png" alt="TinyMCE Logo" style="display: inline-block; width: 200px; height: 50px">'}})}},B=function(e,t){e.addCommand("mceHelp",L(e,t))},N=function(e,t){e.addButton("help",{icon:"help",onclick:L(e,t)}),e.addMenuItem("help",{text:"Help",icon:"help",context:"help",onclick:L(e,t)})};e.add("help",function(e,t){N(e,t),B(e,t),e.shortcuts.add("Alt+0","Open help dialog","mceHelp")})}();
\ No newline at end of file
+!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=function(){},a=function(e){return function(){return e}};function l(r){for(var o=[],e=1;e<arguments.length;e++)o[e-1]=arguments[e];return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=o.concat(e);return r.apply(null,n)}}var n,r,o,i,c,u=a(!1),s=a(!0),m=function(){return d},d=(n=function(e){return e.isNone()},i={fold:function(e,t){return e()},is:u,isSome:u,isNone:s,getOr:o=function(e){return e},getOrThunk:r=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:a(null),getOrUndefined:a(undefined),or:o,orThunk:r,map:m,each:t,bind:m,exists:u,forall:s,filter:m,equals:n,equals_:n,toArray:function(){return[]},toString:a("none()")},Object.freeze&&Object.freeze(i),i),f=function(n){var e=a(n),t=function(){return o},r=function(e){return e(n)},o={fold:function(e,t){return t(n)},is:function(e){return n===e},isSome:s,isNone:u,getOr:e,getOrThunk:e,getOrDie:e,getOrNull:e,getOrUndefined:e,or:t,orThunk:t,map:function(e){return f(e(n))},each:function(e){e(n)},bind:r,exists:r,forall:r,filter:function(e){return e(n)?o:d},toArray:function(){return[n]},toString:function(){return"some("+n+")"},equals:function(e){return e.is(n)},equals_:function(e,t){return e.fold(u,function(e){return t(n,e)})}};return o},p={some:f,none:m,from:function(e){return null===e||e===undefined?d:f(e)}},y=(c="function",function(e){return function(e){if(null===e)return"null";var t=typeof e;return"object"===t&&(Array.prototype.isPrototypeOf(e)||e.constructor&&"Array"===e.constructor.name)?"array":"object"===t&&(String.prototype.isPrototypeOf(e)||e.constructor&&"String"===e.constructor.name)?"string":t}(e)===c}),h=(Array.prototype.slice,Array.prototype.indexOf),g=function(e,t){return n=e,r=t,-1<h.call(n,r);var n,r},k=function(e,t){for(var n=e.length,r=new Array(n),o=0;o<n;o++){var a=e[o];r[o]=t(a,o)}return r},v=(y(Array.from)&&Array.from,tinymce.util.Tools.resolve("tinymce.util.I18n")),b=tinymce.util.Tools.resolve("tinymce.Env"),x=b.mac?"\u2318":"Ctrl",A=b.mac?"Ctrl + Alt":"Shift + Alt",C={shortcuts:[{shortcut:x+" + B",action:"Bold"},{shortcut:x+" + I",action:"Italic"},{shortcut:x+" + U",action:"Underline"},{shortcut:x+" + A",action:"Select all"},{shortcut:x+" + Y or "+x+" + Shift + Z",action:"Redo"},{shortcut:x+" + Z",action:"Undo"},{shortcut:A+" + 1",action:"Header 1"},{shortcut:A+" + 2",action:"Header 2"},{shortcut:A+" + 3",action:"Header 3"},{shortcut:A+" + 4",action:"Header 4"},{shortcut:A+" + 5",action:"Header 5"},{shortcut:A+" + 6",action:"Header 6"},{shortcut:A+" + 7",action:"Paragraph"},{shortcut:A+" + 8",action:"Div"},{shortcut:A+" + 9",action:"Address"},{shortcut:"Alt + F9",action:"Focus to menubar"},{shortcut:"Alt + F10",action:"Focus to toolbar"},{shortcut:"Alt + F11",action:"Focus to element path"},{shortcut:"Ctrl + F9",action:"Focus to contextual toolbar"},{shortcut:x+" + K",action:"Insert link (if link plugin activated)"},{shortcut:x+" + S",action:"Save (if save plugin activated)"},{shortcut:x+" + F",action:"Find (if searchreplace plugin activated)"}]},w=function(){var e=k(C.shortcuts,function(e){return'<tr data-mce-tabstop="1" tabindex="-1" aria-label="Action: '+(t=e).action+", Shortcut: "+t.shortcut.replace(/Ctrl/g,"Control")+'"><td>'+v.translate(e.action)+"</td><td>"+e.shortcut+"</td></tr>";var t}).join("");return{title:"Handy Shortcuts",type:"container",style:"overflow-y: auto; overflow-x: hidden; max-height: 250px",items:[{type:"container",html:'<div><table class="mce-table-striped"><thead><th>'+v.translate("Action")+"</th><th>"+v.translate("Shortcut")+"</th></thead>"+e+"</table></div>"}]}},S=Object.keys,O=[{key:"advlist",name:"Advanced List"},{key:"anchor",name:"Anchor"},{key:"autolink",name:"Autolink"},{key:"autoresize",name:"Autoresize"},{key:"autosave",name:"Autosave"},{key:"bbcode",name:"BBCode"},{key:"charmap",name:"Character Map"},{key:"code",name:"Code"},{key:"codesample",name:"Code Sample"},{key:"colorpicker",name:"Color Picker"},{key:"compat3x",name:"3.x Compatibility"},{key:"contextmenu",name:"Context Menu"},{key:"directionality",name:"Directionality"},{key:"emoticons",name:"Emoticons"},{key:"fullpage",name:"Full Page"},{key:"fullscreen",name:"Full Screen"},{key:"help",name:"Help"},{key:"hr",name:"Horizontal Rule"},{key:"image",name:"Image"},{key:"imagetools",name:"Image Tools"},{key:"importcss",name:"Import CSS"},{key:"insertdatetime",name:"Insert Date/Time"},{key:"legacyoutput",name:"Legacy Output"},{key:"link",name:"Link"},{key:"lists",name:"Lists"},{key:"media",name:"Media"},{key:"nonbreaking",name:"Nonbreaking"},{key:"noneditable",name:"Noneditable"},{key:"pagebreak",name:"Page Break"},{key:"paste",name:"Paste"},{key:"preview",name:"Preview"},{key:"print",name:"Print"},{key:"save",name:"Save"},{key:"searchreplace",name:"Search and Replace"},{key:"spellchecker",name:"Spell Checker"},{key:"tabfocus",name:"Tab Focus"},{key:"table",name:"Table"},{key:"template",name:"Template"},{key:"textcolor",name:"Text Color"},{key:"textpattern",name:"Text Pattern"},{key:"toc",name:"Table of Contents"},{key:"visualblocks",name:"Visual Blocks"},{key:"visualchars",name:"Visual Characters"},{key:"wordcount",name:"Word Count"}],T=l(function(e,o){return e.replace(/\$\{([^{}]*)\}/g,function(e,t){var n,r=o[t];return"string"==(n=typeof r)||"number"===n?r.toString():e})},'<a href="${url}" target="_blank" rel="noopener">${name}</a>'),P=function(t,n){return function(e,t){for(var n=0,r=e.length;n<r;n++){var o=e[n];if(t(o,n))return p.some(o)}return p.none()}(O,function(e){return e.key===n}).fold(function(){var e=t.plugins[n].getMetadata;return"function"==typeof e?T(e()):n},function(e){return T({name:e.name,url:"https://www.tinymce.com/docs/plugins/"+e.key})})},_=function(t){var e,n,r,o=(r=S((e=t).plugins),e.settings.forced_plugins===undefined?r:function(e,t){for(var n=[],r=0,o=e.length;r<o;r++){var a=e[r];t(a,r)&&n.push(a)}return n}(r,(n=l(g,e.settings.forced_plugins),function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return!n.apply(null,e)}))),a=k(o,function(e){return"<li>"+P(t,e)+"</li>"}),i=a.length,c=a.join("");return"<p><b>"+v.translate(["Plugins installed ({0}):",i])+"</b></p><ul>"+c+"</ul>"},H=function(e){return{title:"Plugins",type:"container",style:"overflow-y: auto; overflow-x: hidden;",layout:"flex",padding:10,spacing:10,items:[(t=e,{type:"container",html:'<div style="overflow-y: auto; overflow-x: hidden; max-height: 230px; height: 230px;" data-mce-tabstop="1" tabindex="-1">'+_(t)+"</div>",flex:1}),{type:"container",html:'<div style="padding: 10px; background: #e3e7f4; height: 100%;" data-mce-tabstop="1" tabindex="-1"><p><b>'+v.translate("Premium plugins:")+'</b></p><ul><li>PowerPaste</li><li>Spell Checker Pro</li><li>Accessibility Checker</li><li>Advanced Code Editor</li><li>Enhanced Media Embed</li><li>Link Checker</li></ul><br /><p style="float: right;"><a href="https://www.tinymce.com/pricing/?utm_campaign=editor_referral&utm_medium=help_dialog&utm_source=tinymce" target="_blank">'+v.translate("Learn more...")+"</a></p></div>",flex:1}]};var t},F=tinymce.util.Tools.resolve("tinymce.EditorManager"),M=function(){var e,t,n='<a href="https://www.tinymce.com/docs/changelog/?utm_campaign=editor_referral&utm_medium=help_dialog&utm_source=tinymce" target="_blank">TinyMCE '+(e=F.majorVersion,t=F.minorVersion,0===e.indexOf("@")?"X.X.X":e+"."+t)+"</a>";return[{type:"label",html:v.translate(["You are using {0}",n])},{type:"spacer",flex:1},{text:"Close",onclick:function(){this.parent().parent().close()}}]},E=function(e,t){return function(){e.windowManager.open({title:"Help",bodyType:"tabpanel",layout:"flex",body:[w(),H(e)],buttons:M(),onPostRender:function(){this.getEl("title").innerHTML='<img src="'+t+'/img/logo.png" alt="TinyMCE Logo" style="display: inline-block; width: 200px; height: 50px">'}})}},I=function(e,t){e.addCommand("mceHelp",E(e,t))},j=function(e,t){e.addButton("help",{icon:"help",onclick:E(e,t)}),e.addMenuItem("help",{text:"Help",icon:"help",context:"help",onclick:E(e,t)})};e.add("help",function(e,t){j(e,t),I(e,t),e.shortcuts.add("Alt+0","Open help dialog","mceHelp")})}();
\ No newline at end of file
index d4764ad6251f5a7e303f8117c716381f976ccd92..23473aa76db46cb19fc958f4b490eabe208ce60b 100644 (file)
@@ -1 +1 @@
-!function(l){"use strict";var i,e=tinymce.util.Tools.resolve("tinymce.PluginManager"),d=function(e){return!1!==e.settings.image_dimensions},u=function(e){return!0===e.settings.image_advtab},m=function(e){return e.getParam("image_prepend_url","")},n=function(e){return e.getParam("image_class_list")},r=function(e){return!1!==e.settings.image_description},a=function(e){return!0===e.settings.image_title},o=function(e){return!0===e.settings.image_caption},c=function(e){return e.getParam("image_list",!1)},s=function(e){return e.getParam("images_upload_url",!1)},g=function(e){return e.getParam("images_upload_handler",!1)},f=function(e){return e.getParam("images_upload_url")},p=function(e){return e.getParam("images_upload_handler")},h=function(e){return e.getParam("images_upload_base_path")},v=function(e){return e.getParam("images_upload_credentials")},b="undefined"!=typeof l.window?l.window:Function("return this;")(),y=function(e,t){return function(e,t){for(var n=t!==undefined&&null!==t?t:b,r=0;r<e.length&&n!==undefined&&null!==n;++r)n=n[e[r]];return n}(e.split("."),t)},x={getOrDie:function(e,t){var n=y(e,t);if(n===undefined||null===n)throw e+" not available on this browser";return n}},w=tinymce.util.Tools.resolve("tinymce.util.Promise"),C=tinymce.util.Tools.resolve("tinymce.util.Tools"),S=tinymce.util.Tools.resolve("tinymce.util.XHR"),N=function(e,t){return Math.max(parseInt(e,10),parseInt(t,10))},_=function(e,n){var r=l.document.createElement("img");function t(e,t){r.parentNode&&r.parentNode.removeChild(r),n({width:e,height:t})}r.onload=function(){t(N(r.width,r.clientWidth),N(r.height,r.clientHeight))},r.onerror=function(){t(0,0)};var a=r.style;a.visibility="hidden",a.position="fixed",a.bottom=a.left="0px",a.width=a.height="auto",l.document.body.appendChild(r),r.src=e},T=function(e,a,t){return function n(e,r){return r=r||[],C.each(e,function(e){var t={text:e.text||e.title};e.menu?t.menu=n(e.menu):(t.value=e.value,a(t)),r.push(t)}),r}(e,t||[])},A=function(e){return e&&(e=e.replace(/px$/,"")),e},R=function(e){return 0<e.length&&/^[0-9]+$/.test(e)&&(e+="px"),e},I=function(e){if(e.margin){var t=e.margin.split(" ");switch(t.length){case 1:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[0],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[0];break;case 2:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[1];break;case 3:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[1];break;case 4:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[3]}delete e.margin}return e},t=function(e,t){var n=c(e);"string"==typeof n?S.send({url:n,success:function(e){t(JSON.parse(e))}}):"function"==typeof n?n(t):t(n)},O=function(e,t,n){function r(){n.onload=n.onerror=null,e.selection&&(e.selection.select(n),e.nodeChanged())}n.onload=function(){t.width||t.height||!d(e)||e.dom.setAttribs(n,{width:n.clientWidth,height:n.clientHeight}),r()},n.onerror=r},L=function(r){return new w(function(e,t){var n=new(x.getOrDie("FileReader"));n.onload=function(){e(n.result)},n.onerror=function(){t(n.error.message)},n.readAsDataURL(r)})},P=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),U=Object.prototype.hasOwnProperty,E=(i=function(e,t){return t},function(){for(var e=new Array(arguments.length),t=0;t<e.length;t++)e[t]=arguments[t];if(0===e.length)throw new Error("Can't merge zero objects");for(var n={},r=0;r<e.length;r++){var a=e[r];for(var o in a)U.call(a,o)&&(n[o]=i(n[o],a[o]))}return n}),k=P.DOM,M=function(e){return e.style.marginLeft&&e.style.marginRight&&e.style.marginLeft===e.style.marginRight?A(e.style.marginLeft):""},D=function(e){return e.style.marginTop&&e.style.marginBottom&&e.style.marginTop===e.style.marginBottom?A(e.style.marginTop):""},z=function(e){return e.style.borderWidth?A(e.style.borderWidth):""},B=function(e,t){return e.hasAttribute(t)?e.getAttribute(t):""},H=function(e,t){return e.style[t]?e.style[t]:""},j=function(e){return null!==e.parentNode&&"FIGURE"===e.parentNode.nodeName},F=function(e,t,n){e.setAttribute(t,n)},W=function(e){var t,n,r,a;j(e)?(a=(r=e).parentNode,k.insertAfter(r,a),k.remove(a)):(t=e,n=k.create("figure",{"class":"image"}),k.insertAfter(n,t),n.appendChild(t),n.appendChild(k.create("figcaption",{contentEditable:!0},"Caption")),n.contentEditable="false")},J=function(e,t){var n=e.getAttribute("style"),r=t(null!==n?n:"");0<r.length?(e.setAttribute("style",r),e.setAttribute("data-mce-style",r)):e.removeAttribute("style")},V=function(e,r){return function(e,t,n){e.style[t]?(e.style[t]=R(n),J(e,r)):F(e,t,n)}},G=function(e,t){return e.style[t]?A(e.style[t]):B(e,t)},$=function(e,t){var n=R(t);e.style.marginLeft=n,e.style.marginRight=n},X=function(e,t){var n=R(t);e.style.marginTop=n,e.style.marginBottom=n},q=function(e,t){var n=R(t);e.style.borderWidth=n},K=function(e,t){e.style.borderStyle=t},Q=function(e){return"FIGURE"===e.nodeName},Y=function(e,t){var n=l.document.createElement("img");return F(n,"style",t.style),(M(n)||""!==t.hspace)&&$(n,t.hspace),(D(n)||""!==t.vspace)&&X(n,t.vspace),(z(n)||""!==t.border)&&q(n,t.border),(H(n,"borderStyle")||""!==t.borderStyle)&&K(n,t.borderStyle),e(n.getAttribute("style"))},Z=function(e,t){return{src:B(t,"src"),alt:B(t,"alt"),title:B(t,"title"),width:G(t,"width"),height:G(t,"height"),"class":B(t,"class"),style:e(B(t,"style")),caption:j(t),hspace:M(t),vspace:D(t),border:z(t),borderStyle:H(t,"borderStyle")}},ee=function(e,t,n,r,a){n[r]!==t[r]&&a(e,r,n[r])},te=function(r,a){return function(e,t,n){r(e,n),J(e,a)}},ne=function(e,t,n){var r=Z(e,n);ee(n,r,t,"caption",function(e,t,n){return W(e)}),ee(n,r,t,"src",F),ee(n,r,t,"alt",F),ee(n,r,t,"title",F),ee(n,r,t,"width",V(0,e)),ee(n,r,t,"height",V(0,e)),ee(n,r,t,"class",F),ee(n,r,t,"style",te(function(e,t){return F(e,"style",t)},e)),ee(n,r,t,"hspace",te($,e)),ee(n,r,t,"vspace",te(X,e)),ee(n,r,t,"border",te(q,e)),ee(n,r,t,"borderStyle",te(K,e))},re=function(e,t){var n=e.dom.styles.parse(t),r=I(n),a=e.dom.styles.parse(e.dom.styles.serialize(r));return e.dom.styles.serialize(a)},ae=function(e){var t=e.selection.getNode(),n=e.dom.getParent(t,"figure.image");return n?e.dom.select("img",n)[0]:t&&("IMG"!==t.nodeName||t.getAttribute("data-mce-object")||t.getAttribute("data-mce-placeholder"))?null:t},oe=function(t,e){var n=t.dom,r=n.getParent(e.parentNode,function(e){return t.schema.getTextBlockElements()[e.nodeName]},t.getBody());return r?n.split(r,e):e},ie=function(t){var e=ae(t);return e?Z(function(e){return re(t,e)},e):{src:"",alt:"",title:"",width:"",height:"","class":"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:""}},le=function(t,e){var n=function(e,t){var n=l.document.createElement("img");if(ne(e,E(t,{caption:!1}),n),F(n,"alt",t.alt),t.caption){var r=k.create("figure",{"class":"image"});return r.appendChild(n),r.appendChild(k.create("figcaption",{contentEditable:!0},"Caption")),r.contentEditable="false",r}return n}(function(e){return re(t,e)},e);t.dom.setAttrib(n,"data-mce-id","__mcenew"),t.focus(),t.selection.setContent(n.outerHTML);var r=t.dom.select('*[data-mce-id="__mcenew"]')[0];if(t.dom.setAttrib(r,"data-mce-id",null),Q(r)){var a=oe(t,r);t.selection.select(a)}else t.selection.select(r)},ue=function(e,t){var n=ae(e);n?t.src?function(t,e){var n,r=ae(t);if(ne(function(e){return re(t,e)},e,r),n=r,t.dom.setAttrib(n,"src",n.getAttribute("src")),Q(r.parentNode)){var a=r.parentNode;oe(t,a),t.selection.select(r.parentNode)}else t.selection.select(r),O(t,e,r)}(e,t):function(e,t){if(t){var n=e.dom.is(t.parentNode,"figure.image")?t.parentNode:t;e.dom.remove(n),e.focus(),e.nodeChanged(),e.dom.isEmpty(e.getBody())&&(e.setContent(""),e.selection.setCursorLocation())}}(e,n):t.src&&le(e,t)},ce=function(n,r){r.find("#style").each(function(e){var t=Y(function(e){return re(n,e)},E({src:"",alt:"",title:"",width:"",height:"","class":"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:""},r.toJSON()));e.value(t)})},se=function(t){return{title:"Advanced",type:"form",pack:"start",items:[{label:"Style",name:"style",type:"textbox",onchange:(o=t,function(e){var t=o.dom,n=e.control.rootControl;if(u(o)){var r=n.toJSON(),a=t.parseStyle(r.style);n.find("#vspace").value(""),n.find("#hspace").value(""),((a=I(a))["margin-top"]&&a["margin-bottom"]||a["margin-right"]&&a["margin-left"])&&(a["margin-top"]===a["margin-bottom"]?n.find("#vspace").value(A(a["margin-top"])):n.find("#vspace").value(""),a["margin-right"]===a["margin-left"]?n.find("#hspace").value(A(a["margin-right"])):n.find("#hspace").value("")),a["border-width"]?n.find("#border").value(A(a["border-width"])):n.find("#border").value(""),a["border-style"]?n.find("#borderStyle").value(a["border-style"]):n.find("#borderStyle").value(""),n.find("#style").value(t.serializeStyle(t.parseStyle(t.serializeStyle(a))))}})},{type:"form",layout:"grid",packV:"start",columns:2,padding:0,defaults:{type:"textbox",maxWidth:50,onchange:function(e){ce(t,e.control.rootControl)}},items:[{label:"Vertical space",name:"vspace"},{label:"Border width",name:"border"},{label:"Horizontal space",name:"hspace"},{label:"Border style",type:"listbox",name:"borderStyle",width:90,maxWidth:90,onselect:function(e){ce(t,e.control.rootControl)},values:[{text:"Select...",value:""},{text:"Solid",value:"solid"},{text:"Dotted",value:"dotted"},{text:"Dashed",value:"dashed"},{text:"Double",value:"double"},{text:"Groove",value:"groove"},{text:"Ridge",value:"ridge"},{text:"Inset",value:"inset"},{text:"Outset",value:"outset"},{text:"None",value:"none"},{text:"Hidden",value:"hidden"}]}]}]};var o},de=function(e,t){e.state.set("oldVal",e.value()),t.state.set("oldVal",t.value())},me=function(e,t){var n=e.find("#width")[0],r=e.find("#height")[0],a=e.find("#constrain")[0];n&&r&&a&&t(n,r,a.checked())},ge=function(e,t,n){var r=e.state.get("oldVal"),a=t.state.get("oldVal"),o=e.value(),i=t.value();n&&r&&a&&o&&i&&(o!==r?(i=Math.round(o/r*i),isNaN(i)||t.value(i)):(o=Math.round(i/a*o),isNaN(o)||e.value(o))),de(e,t)},fe=function(e){me(e,ge)},pe=function(){var e=function(e){fe(e.control.rootControl)};return{type:"container",label:"Dimensions",layout:"flex",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:5,size:5,onchange:e,ariaLabel:"Width"},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:5,size:5,onchange:e,ariaLabel:"Height"},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}},he=function(e){me(e,de)},ve=fe,be=function(e){e.meta=e.control.rootControl.toJSON()},ye=function(s,e){var t=[{name:"src",type:"filepicker",filetype:"image",label:"Source",autofocus:!0,onchange:function(e){var t,n,r,a,o,i,l,u,c;n=s,i=(t=e).meta||{},l=t.control,u=l.rootControl,(c=u.find("#image-list")[0])&&c.value(n.convertURL(l.value(),"src")),C.each(i,function(e,t){u.find("#"+t).value(e)}),i.width||i.height||(r=n.convertURL(l.value(),"src"),a=m(n),o=new RegExp("^(?:[a-z]+:)?//","i"),a&&!o.test(r)&&r.substring(0,a.length)!==a&&(r=a+r),l.value(r),_(n.documentBaseURI.toAbsolute(l.value()),function(e){e.width&&e.height&&d(n)&&(u.find("#width").value(e.width),u.find("#height").value(e.height),he(u))}))},onbeforecall:be},e];return r(s)&&t.push({name:"alt",type:"textbox",label:"Image description"}),a(s)&&t.push({name:"title",type:"textbox",label:"Image Title"}),d(s)&&t.push(pe()),n(s)&&t.push({name:"class",type:"listbox",label:"Class",values:T(n(s),function(e){e.value&&(e.textStyle=function(){return s.formatter.getCssText({inline:"img",classes:[e.value]})})})}),o(s)&&t.push({name:"caption",type:"checkbox",label:"Caption"}),t},xe=function(e,t){return{title:"General",type:"form",items:ye(e,t)}},we=ye,Ce=function(){return x.getOrDie("URL")},Se=function(e){return Ce().createObjectURL(e)},Ne=function(e){Ce().revokeObjectURL(e)},_e=tinymce.util.Tools.resolve("tinymce.ui.Factory"),Te=function(){};function Ae(i){var t=function(e,r,a,t){var o,n;(o=new(x.getOrDie("XMLHttpRequest"))).open("POST",i.url),o.withCredentials=i.credentials,o.upload.onprogress=function(e){t(e.loaded/e.total*100)},o.onerror=function(){a("Image upload failed due to a XHR Transport error. Code: "+o.status)},o.onload=function(){var e,t,n;o.status<200||300<=o.status?a("HTTP Error: "+o.status):(e=JSON.parse(o.responseText))&&"string"==typeof e.location?r((t=i.basePath,n=e.location,t?t.replace(/\/$/,"")+"/"+n.replace(/^\//,""):n)):a("Invalid JSON: "+o.responseText)},(n=new l.FormData).append("file",e.blob(),e.filename()),o.send(n)};return i=C.extend({credentials:!1,handler:t},i),{upload:function(e){return i.url||i.handler!==t?(r=e,a=i.handler,new w(function(e,t){try{a(r,e,t,Te)}catch(n){t(n.message)}})):w.reject("Upload url missing from the settings.");var r,a}}}var Re=function(u){return function(e){var t=_e.get("Throbber"),n=e.control.rootControl,r=new t(n.getEl()),a=e.control.value(),o=Se(a),i=Ae({url:f(u),basePath:h(u),credentials:v(u),handler:p(u)}),l=function(){r.hide(),Ne(o)};return r.show(),L(a).then(function(e){var t=u.editorUpload.blobCache.create({blob:a,blobUri:o,name:a.name?a.name.replace(/\.[^\.]+$/,""):null,base64:e.split(",")[1]});return i.upload(t).then(function(e){var t=n.find("#src");return t.value(e),n.find("tabpanel")[0].activateTab(0),t.fire("change"),l(),e})})["catch"](function(e){u.windowManager.alert(e),l()})}},Ie=".jpg,.jpeg,.png,.gif",Oe=function(e){return{title:"Upload",type:"form",layout:"flex",direction:"column",align:"stretch",padding:"20 20 20 20",items:[{type:"container",layout:"flex",direction:"column",align:"center",spacing:10,items:[{text:"Browse for an image",type:"browsebutton",accept:Ie,onchange:Re(e)},{text:"OR",type:"label"}]},{text:"Drop an image here",type:"dropzone",accept:Ie,height:100,onchange:Re(e)}]}};function Le(r){for(var a=[],e=1;e<arguments.length;e++)a[e-1]=arguments[e];return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=a.concat(e);return r.apply(null,n)}}var Pe=function(t,e){var n=e.control.getRoot();ve(n),t.undoManager.transact(function(){var e=E(ie(t),n.toJSON());ue(t,e)}),t.editorUpload.uploadImagesAuto()};function Ue(o){function e(e){var n,t,r=ie(o);if(e&&(t={type:"listbox",label:"Image list",name:"image-list",values:T(e,function(e){e.value=o.convertURL(e.value||e.url,"src")},[{text:"None",value:""}]),value:r.src&&o.convertURL(r.src,"src"),onselect:function(e){var t=n.find("#alt");(!t.value()||e.lastControl&&t.value()===e.lastControl.text())&&t.value(e.control.text()),n.find("#src").value(e.control.value()).fire("change")},onPostRender:function(){t=this}}),u(o)||s(o)||g(o)){var a=[xe(o,t)];u(o)&&a.push(se(o)),(s(o)||g(o))&&a.push(Oe(o)),n=o.windowManager.open({title:"Insert/edit image",data:r,bodyType:"tabpanel",body:a,onSubmit:Le(Pe,o)})}else n=o.windowManager.open({title:"Insert/edit image",data:r,body:we(o,t),onSubmit:Le(Pe,o)});he(n)}return{open:function(){t(o,e)}}}var Ee=function(e){e.addCommand("mceImage",Ue(e).open)},ke=function(o){return function(e){for(var t,n,r=e.length,a=function(e){e.attr("contenteditable",o?"true":null)};r--;)t=e[r],(n=t.attr("class"))&&/\bimage\b/.test(n)&&(t.attr("contenteditable",o?"false":null),C.each(t.getAll("figcaption"),a))}},Me=function(e){e.on("preInit",function(){e.parser.addNodeFilter("figure",ke(!0)),e.serializer.addNodeFilter("figure",ke(!1))})},De=function(e){e.addButton("image",{icon:"image",tooltip:"Insert/edit image",onclick:Ue(e).open,stateSelector:"img:not([data-mce-object],[data-mce-placeholder]),figure.image"}),e.addMenuItem("image",{icon:"image",text:"Image",onclick:Ue(e).open,context:"insert",prependToContext:!0})};e.add("image",function(e){Me(e),De(e),Ee(e)})}(window);
\ No newline at end of file
+!function(l){"use strict";var i,e=tinymce.util.Tools.resolve("tinymce.PluginManager"),d=function(e){return!1!==e.settings.image_dimensions},u=function(e){return!0===e.settings.image_advtab},m=function(e){return e.getParam("image_prepend_url","")},n=function(e){return e.getParam("image_class_list")},r=function(e){return!1!==e.settings.image_description},a=function(e){return!0===e.settings.image_title},o=function(e){return!0===e.settings.image_caption},c=function(e){return e.getParam("image_list",!1)},s=function(e){return e.getParam("images_upload_url",!1)},g=function(e){return e.getParam("images_upload_handler",!1)},f=function(e){return e.getParam("images_upload_url")},p=function(e){return e.getParam("images_upload_handler")},h=function(e){return e.getParam("images_upload_base_path")},v=function(e){return e.getParam("images_upload_credentials")},b="undefined"!=typeof l.window?l.window:Function("return this;")(),y=function(e,t){return function(e,t){for(var n=t!==undefined&&null!==t?t:b,r=0;r<e.length&&n!==undefined&&null!==n;++r)n=n[e[r]];return n}(e.split("."),t)},x={getOrDie:function(e,t){var n=y(e,t);if(n===undefined||null===n)throw new Error(e+" not available on this browser");return n}},w=tinymce.util.Tools.resolve("tinymce.util.Promise"),C=tinymce.util.Tools.resolve("tinymce.util.Tools"),S=tinymce.util.Tools.resolve("tinymce.util.XHR"),N=function(e,t){return Math.max(parseInt(e,10),parseInt(t,10))},_=function(e,n){var r=l.document.createElement("img");function t(e,t){r.parentNode&&r.parentNode.removeChild(r),n({width:e,height:t})}r.onload=function(){t(N(r.width,r.clientWidth),N(r.height,r.clientHeight))},r.onerror=function(){t(0,0)};var a=r.style;a.visibility="hidden",a.position="fixed",a.bottom=a.left="0px",a.width=a.height="auto",l.document.body.appendChild(r),r.src=e},T=function(e,a,t){return function n(e,r){return r=r||[],C.each(e,function(e){var t={text:e.text||e.title};e.menu?t.menu=n(e.menu):(t.value=e.value,a(t)),r.push(t)}),r}(e,t||[])},A=function(e){return e&&(e=e.replace(/px$/,"")),e},R=function(e){return 0<e.length&&/^[0-9]+$/.test(e)&&(e+="px"),e},I=function(e){if(e.margin){var t=e.margin.split(" ");switch(t.length){case 1:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[0],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[0];break;case 2:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[1];break;case 3:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[1];break;case 4:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[3]}delete e.margin}return e},t=function(e,t){var n=c(e);"string"==typeof n?S.send({url:n,success:function(e){t(JSON.parse(e))}}):"function"==typeof n?n(t):t(n)},O=function(e,t,n){function r(){n.onload=n.onerror=null,e.selection&&(e.selection.select(n),e.nodeChanged())}n.onload=function(){t.width||t.height||!d(e)||e.dom.setAttribs(n,{width:n.clientWidth,height:n.clientHeight}),r()},n.onerror=r},L=function(r){return new w(function(e,t){var n=new(x.getOrDie("FileReader"));n.onload=function(){e(n.result)},n.onerror=function(){t(n.error.message)},n.readAsDataURL(r)})},P=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),U=Object.prototype.hasOwnProperty,E=(i=function(e,t){return t},function(){for(var e=new Array(arguments.length),t=0;t<e.length;t++)e[t]=arguments[t];if(0===e.length)throw new Error("Can't merge zero objects");for(var n={},r=0;r<e.length;r++){var a=e[r];for(var o in a)U.call(a,o)&&(n[o]=i(n[o],a[o]))}return n}),k=P.DOM,M=function(e){return e.style.marginLeft&&e.style.marginRight&&e.style.marginLeft===e.style.marginRight?A(e.style.marginLeft):""},D=function(e){return e.style.marginTop&&e.style.marginBottom&&e.style.marginTop===e.style.marginBottom?A(e.style.marginTop):""},z=function(e){return e.style.borderWidth?A(e.style.borderWidth):""},B=function(e,t){return e.hasAttribute(t)?e.getAttribute(t):""},H=function(e,t){return e.style[t]?e.style[t]:""},j=function(e){return null!==e.parentNode&&"FIGURE"===e.parentNode.nodeName},F=function(e,t,n){e.setAttribute(t,n)},W=function(e){var t,n,r,a;j(e)?(a=(r=e).parentNode,k.insertAfter(r,a),k.remove(a)):(t=e,n=k.create("figure",{"class":"image"}),k.insertAfter(n,t),n.appendChild(t),n.appendChild(k.create("figcaption",{contentEditable:!0},"Caption")),n.contentEditable="false")},J=function(e,t){var n=e.getAttribute("style"),r=t(null!==n?n:"");0<r.length?(e.setAttribute("style",r),e.setAttribute("data-mce-style",r)):e.removeAttribute("style")},V=function(e,r){return function(e,t,n){e.style[t]?(e.style[t]=R(n),J(e,r)):F(e,t,n)}},G=function(e,t){return e.style[t]?A(e.style[t]):B(e,t)},$=function(e,t){var n=R(t);e.style.marginLeft=n,e.style.marginRight=n},X=function(e,t){var n=R(t);e.style.marginTop=n,e.style.marginBottom=n},q=function(e,t){var n=R(t);e.style.borderWidth=n},K=function(e,t){e.style.borderStyle=t},Q=function(e){return"FIGURE"===e.nodeName},Y=function(e,t){var n=l.document.createElement("img");return F(n,"style",t.style),(M(n)||""!==t.hspace)&&$(n,t.hspace),(D(n)||""!==t.vspace)&&X(n,t.vspace),(z(n)||""!==t.border)&&q(n,t.border),(H(n,"borderStyle")||""!==t.borderStyle)&&K(n,t.borderStyle),e(n.getAttribute("style"))},Z=function(e,t){return{src:B(t,"src"),alt:B(t,"alt"),title:B(t,"title"),width:G(t,"width"),height:G(t,"height"),"class":B(t,"class"),style:e(B(t,"style")),caption:j(t),hspace:M(t),vspace:D(t),border:z(t),borderStyle:H(t,"borderStyle")}},ee=function(e,t,n,r,a){n[r]!==t[r]&&a(e,r,n[r])},te=function(r,a){return function(e,t,n){r(e,n),J(e,a)}},ne=function(e,t,n){var r=Z(e,n);ee(n,r,t,"caption",function(e,t,n){return W(e)}),ee(n,r,t,"src",F),ee(n,r,t,"alt",F),ee(n,r,t,"title",F),ee(n,r,t,"width",V(0,e)),ee(n,r,t,"height",V(0,e)),ee(n,r,t,"class",F),ee(n,r,t,"style",te(function(e,t){return F(e,"style",t)},e)),ee(n,r,t,"hspace",te($,e)),ee(n,r,t,"vspace",te(X,e)),ee(n,r,t,"border",te(q,e)),ee(n,r,t,"borderStyle",te(K,e))},re=function(e,t){var n=e.dom.styles.parse(t),r=I(n),a=e.dom.styles.parse(e.dom.styles.serialize(r));return e.dom.styles.serialize(a)},ae=function(e){var t=e.selection.getNode(),n=e.dom.getParent(t,"figure.image");return n?e.dom.select("img",n)[0]:t&&("IMG"!==t.nodeName||t.getAttribute("data-mce-object")||t.getAttribute("data-mce-placeholder"))?null:t},oe=function(t,e){var n=t.dom,r=n.getParent(e.parentNode,function(e){return t.schema.getTextBlockElements()[e.nodeName]},t.getBody());return r?n.split(r,e):e},ie=function(t){var e=ae(t);return e?Z(function(e){return re(t,e)},e):{src:"",alt:"",title:"",width:"",height:"","class":"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:""}},le=function(t,e){var n=function(e,t){var n=l.document.createElement("img");if(ne(e,E(t,{caption:!1}),n),F(n,"alt",t.alt),t.caption){var r=k.create("figure",{"class":"image"});return r.appendChild(n),r.appendChild(k.create("figcaption",{contentEditable:!0},"Caption")),r.contentEditable="false",r}return n}(function(e){return re(t,e)},e);t.dom.setAttrib(n,"data-mce-id","__mcenew"),t.focus(),t.selection.setContent(n.outerHTML);var r=t.dom.select('*[data-mce-id="__mcenew"]')[0];if(t.dom.setAttrib(r,"data-mce-id",null),Q(r)){var a=oe(t,r);t.selection.select(a)}else t.selection.select(r)},ue=function(e,t){var n=ae(e);n?t.src?function(t,e){var n,r=ae(t);if(ne(function(e){return re(t,e)},e,r),n=r,t.dom.setAttrib(n,"src",n.getAttribute("src")),Q(r.parentNode)){var a=r.parentNode;oe(t,a),t.selection.select(r.parentNode)}else t.selection.select(r),O(t,e,r)}(e,t):function(e,t){if(t){var n=e.dom.is(t.parentNode,"figure.image")?t.parentNode:t;e.dom.remove(n),e.focus(),e.nodeChanged(),e.dom.isEmpty(e.getBody())&&(e.setContent(""),e.selection.setCursorLocation())}}(e,n):t.src&&le(e,t)},ce=function(n,r){r.find("#style").each(function(e){var t=Y(function(e){return re(n,e)},E({src:"",alt:"",title:"",width:"",height:"","class":"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:""},r.toJSON()));e.value(t)})},se=function(t){return{title:"Advanced",type:"form",pack:"start",items:[{label:"Style",name:"style",type:"textbox",onchange:(o=t,function(e){var t=o.dom,n=e.control.rootControl;if(u(o)){var r=n.toJSON(),a=t.parseStyle(r.style);n.find("#vspace").value(""),n.find("#hspace").value(""),((a=I(a))["margin-top"]&&a["margin-bottom"]||a["margin-right"]&&a["margin-left"])&&(a["margin-top"]===a["margin-bottom"]?n.find("#vspace").value(A(a["margin-top"])):n.find("#vspace").value(""),a["margin-right"]===a["margin-left"]?n.find("#hspace").value(A(a["margin-right"])):n.find("#hspace").value("")),a["border-width"]?n.find("#border").value(A(a["border-width"])):n.find("#border").value(""),a["border-style"]?n.find("#borderStyle").value(a["border-style"]):n.find("#borderStyle").value(""),n.find("#style").value(t.serializeStyle(t.parseStyle(t.serializeStyle(a))))}})},{type:"form",layout:"grid",packV:"start",columns:2,padding:0,defaults:{type:"textbox",maxWidth:50,onchange:function(e){ce(t,e.control.rootControl)}},items:[{label:"Vertical space",name:"vspace"},{label:"Border width",name:"border"},{label:"Horizontal space",name:"hspace"},{label:"Border style",type:"listbox",name:"borderStyle",width:90,maxWidth:90,onselect:function(e){ce(t,e.control.rootControl)},values:[{text:"Select...",value:""},{text:"Solid",value:"solid"},{text:"Dotted",value:"dotted"},{text:"Dashed",value:"dashed"},{text:"Double",value:"double"},{text:"Groove",value:"groove"},{text:"Ridge",value:"ridge"},{text:"Inset",value:"inset"},{text:"Outset",value:"outset"},{text:"None",value:"none"},{text:"Hidden",value:"hidden"}]}]}]};var o},de=function(e,t){e.state.set("oldVal",e.value()),t.state.set("oldVal",t.value())},me=function(e,t){var n=e.find("#width")[0],r=e.find("#height")[0],a=e.find("#constrain")[0];n&&r&&a&&t(n,r,a.checked())},ge=function(e,t,n){var r=e.state.get("oldVal"),a=t.state.get("oldVal"),o=e.value(),i=t.value();n&&r&&a&&o&&i&&(o!==r?(i=Math.round(o/r*i),isNaN(i)||t.value(i)):(o=Math.round(i/a*o),isNaN(o)||e.value(o))),de(e,t)},fe=function(e){me(e,ge)},pe=function(){var e=function(e){fe(e.control.rootControl)};return{type:"container",label:"Dimensions",layout:"flex",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:5,size:5,onchange:e,ariaLabel:"Width"},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:5,size:5,onchange:e,ariaLabel:"Height"},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}},he=function(e){me(e,de)},ve=fe,be=function(e){e.meta=e.control.rootControl.toJSON()},ye=function(s,e){var t=[{name:"src",type:"filepicker",filetype:"image",label:"Source",autofocus:!0,onchange:function(e){var t,n,r,a,o,i,l,u,c;n=s,i=(t=e).meta||{},l=t.control,u=l.rootControl,(c=u.find("#image-list")[0])&&c.value(n.convertURL(l.value(),"src")),C.each(i,function(e,t){u.find("#"+t).value(e)}),i.width||i.height||(r=n.convertURL(l.value(),"src"),a=m(n),o=new RegExp("^(?:[a-z]+:)?//","i"),a&&!o.test(r)&&r.substring(0,a.length)!==a&&(r=a+r),l.value(r),_(n.documentBaseURI.toAbsolute(l.value()),function(e){e.width&&e.height&&d(n)&&(u.find("#width").value(e.width),u.find("#height").value(e.height),he(u))}))},onbeforecall:be},e];return r(s)&&t.push({name:"alt",type:"textbox",label:"Image description"}),a(s)&&t.push({name:"title",type:"textbox",label:"Image Title"}),d(s)&&t.push(pe()),n(s)&&t.push({name:"class",type:"listbox",label:"Class",values:T(n(s),function(e){e.value&&(e.textStyle=function(){return s.formatter.getCssText({inline:"img",classes:[e.value]})})})}),o(s)&&t.push({name:"caption",type:"checkbox",label:"Caption"}),t},xe=function(e,t){return{title:"General",type:"form",items:ye(e,t)}},we=ye,Ce=function(){return x.getOrDie("URL")},Se=function(e){return Ce().createObjectURL(e)},Ne=function(e){Ce().revokeObjectURL(e)},_e=tinymce.util.Tools.resolve("tinymce.ui.Factory"),Te=function(){};function Ae(i){var t=function(e,r,a,t){var o,n;(o=new(x.getOrDie("XMLHttpRequest"))).open("POST",i.url),o.withCredentials=i.credentials,o.upload.onprogress=function(e){t(e.loaded/e.total*100)},o.onerror=function(){a("Image upload failed due to a XHR Transport error. Code: "+o.status)},o.onload=function(){var e,t,n;o.status<200||300<=o.status?a("HTTP Error: "+o.status):(e=JSON.parse(o.responseText))&&"string"==typeof e.location?r((t=i.basePath,n=e.location,t?t.replace(/\/$/,"")+"/"+n.replace(/^\//,""):n)):a("Invalid JSON: "+o.responseText)},(n=new l.FormData).append("file",e.blob(),e.filename()),o.send(n)};return i=C.extend({credentials:!1,handler:t},i),{upload:function(e){return i.url||i.handler!==t?(r=e,a=i.handler,new w(function(e,t){try{a(r,e,t,Te)}catch(n){t(n.message)}})):w.reject("Upload url missing from the settings.");var r,a}}}var Re=function(u){return function(e){var t=_e.get("Throbber"),n=e.control.rootControl,r=new t(n.getEl()),a=e.control.value(),o=Se(a),i=Ae({url:f(u),basePath:h(u),credentials:v(u),handler:p(u)}),l=function(){r.hide(),Ne(o)};return r.show(),L(a).then(function(e){var t=u.editorUpload.blobCache.create({blob:a,blobUri:o,name:a.name?a.name.replace(/\.[^\.]+$/,""):null,base64:e.split(",")[1]});return i.upload(t).then(function(e){var t=n.find("#src");return t.value(e),n.find("tabpanel")[0].activateTab(0),t.fire("change"),l(),e})})["catch"](function(e){u.windowManager.alert(e),l()})}},Ie=".jpg,.jpeg,.png,.gif",Oe=function(e){return{title:"Upload",type:"form",layout:"flex",direction:"column",align:"stretch",padding:"20 20 20 20",items:[{type:"container",layout:"flex",direction:"column",align:"center",spacing:10,items:[{text:"Browse for an image",type:"browsebutton",accept:Ie,onchange:Re(e)},{text:"OR",type:"label"}]},{text:"Drop an image here",type:"dropzone",accept:Ie,height:100,onchange:Re(e)}]}};function Le(r){for(var a=[],e=1;e<arguments.length;e++)a[e-1]=arguments[e];return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=a.concat(e);return r.apply(null,n)}}var Pe=function(t,e){var n=e.control.getRoot();ve(n),t.undoManager.transact(function(){var e=E(ie(t),n.toJSON());ue(t,e)}),t.editorUpload.uploadImagesAuto()};function Ue(o){function e(e){var n,t,r=ie(o);if(e&&(t={type:"listbox",label:"Image list",name:"image-list",values:T(e,function(e){e.value=o.convertURL(e.value||e.url,"src")},[{text:"None",value:""}]),value:r.src&&o.convertURL(r.src,"src"),onselect:function(e){var t=n.find("#alt");(!t.value()||e.lastControl&&t.value()===e.lastControl.text())&&t.value(e.control.text()),n.find("#src").value(e.control.value()).fire("change")},onPostRender:function(){t=this}}),u(o)||s(o)||g(o)){var a=[xe(o,t)];u(o)&&a.push(se(o)),(s(o)||g(o))&&a.push(Oe(o)),n=o.windowManager.open({title:"Insert/edit image",data:r,bodyType:"tabpanel",body:a,onSubmit:Le(Pe,o)})}else n=o.windowManager.open({title:"Insert/edit image",data:r,body:we(o,t),onSubmit:Le(Pe,o)});he(n)}return{open:function(){t(o,e)}}}var Ee=function(e){e.addCommand("mceImage",Ue(e).open)},ke=function(o){return function(e){for(var t,n,r=e.length,a=function(e){e.attr("contenteditable",o?"true":null)};r--;)t=e[r],(n=t.attr("class"))&&/\bimage\b/.test(n)&&(t.attr("contenteditable",o?"false":null),C.each(t.getAll("figcaption"),a))}},Me=function(e){e.on("preInit",function(){e.parser.addNodeFilter("figure",ke(!0)),e.serializer.addNodeFilter("figure",ke(!1))})},De=function(e){e.addButton("image",{icon:"image",tooltip:"Insert/edit image",onclick:Ue(e).open,stateSelector:"img:not([data-mce-object],[data-mce-placeholder]),figure.image"}),e.addMenuItem("image",{icon:"image",text:"Image",onclick:Ue(e).open,context:"insert",prependToContext:!0})};e.add("image",function(e){Me(e),De(e),Ee(e)})}(window);
\ No newline at end of file
index b912673eb2a1cb8493a1b8854230f69278f7fb1a..f1b6a11104b2b6f691284fa586127cb19395f3c5 100644 (file)
@@ -1 +1 @@
-!function(s){"use strict";var r=function(t){var e=t,n=function(){return e};return{get:n,set:function(t){e=t},clone:function(){return r(n())}}},t=tinymce.util.Tools.resolve("tinymce.PluginManager"),$=tinymce.util.Tools.resolve("tinymce.util.Tools");function n(t,e){return i(s.document.createElement("canvas"),t,e)}function o(t){return t.getContext("2d")}function i(t,e,n){return t.width=e,t.height=n,t}var h={create:n,clone:function(t){var e;return o(e=n(t.width,t.height)).drawImage(t,0,0),e},resize:i,get2dContext:o,get3dContext:function(t){var e=null;try{e=t.getContext("webgl")||t.getContext("experimental-webgl")}catch(n){}return e||(e=null),e}},p={getWidth:function(t){return t.naturalWidth||t.width},getHeight:function(t){return t.naturalHeight||t.height}},g=window.Promise?window.Promise:function(){var t=function(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],l(t,r(o,this),r(a,this))},e=t.immediateFn||"function"==typeof window.setImmediate&&window.setImmediate||function(t){s.setTimeout(t,1)};function r(t,e){return function(){t.apply(e,arguments)}}var n=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)};function i(r){var o=this;null!==this._state?e(function(){var t=o._state?r.onFulfilled:r.onRejected;if(null!==t){var e;try{e=t(o._value)}catch(n){return void r.reject(n)}r.resolve(e)}else(o._state?r.resolve:r.reject)(o._value)}):this._deferreds.push(r)}function o(t){try{if(t===this)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var e=t.then;if("function"==typeof e)return void l(r(e,t),r(o,this),r(a,this))}this._state=!0,this._value=t,u.call(this)}catch(n){a.call(this,n)}}function a(t){this._state=!1,this._value=t,u.call(this)}function u(){for(var t=0,e=this._deferreds.length;t<e;t++)i.call(this,this._deferreds[t]);this._deferreds=null}function c(t,e,n,r){this.onFulfilled="function"==typeof t?t:null,this.onRejected="function"==typeof e?e:null,this.resolve=n,this.reject=r}function l(t,e,n){var r=!1;try{t(function(t){r||(r=!0,e(t))},function(t){r||(r=!0,n(t))})}catch(o){if(r)return;r=!0,n(o)}}return t.prototype["catch"]=function(t){return this.then(null,t)},t.prototype.then=function(n,r){var o=this;return new t(function(t,e){i.call(o,new c(n,r,t,e))})},t.all=function(){var c=Array.prototype.slice.call(1===arguments.length&&n(arguments[0])?arguments[0]:arguments);return new t(function(o,i){if(0===c.length)return o([]);var a=c.length;function u(e,t){try{if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if("function"==typeof n)return void n.call(t,function(t){u(e,t)},i)}c[e]=t,0==--a&&o(c)}catch(r){i(r)}}for(var t=0;t<c.length;t++)u(t,c[t])})},t.resolve=function(e){return e&&"object"==typeof e&&e.constructor===t?e:new t(function(t){t(e)})},t.reject=function(n){return new t(function(t,e){e(n)})},t.race=function(o){return new t(function(t,e){for(var n=0,r=o.length;n<r;n++)o[n].then(t,e)})},t}(),a=function(t){return function(){return t}};function u(r){for(var o=[],t=1;t<arguments.length;t++)o[t-1]=arguments[t];return function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];var n=o.concat(t);return r.apply(null,n)}}var c,l,f,d,m=a(!1),y=a(!0),v=m,b=y,w=function(){return x},x=(d={fold:function(t,e){return t()},is:v,isSome:v,isNone:b,getOr:f=function(t){return t},getOrThunk:l=function(t){return t()},getOrDie:function(t){throw new Error(t||"error: getOrDie called on none.")},getOrNull:function(){return null},getOrUndefined:function(){return undefined},or:f,orThunk:l,map:w,ap:w,each:function(){},bind:w,flatten:w,exists:v,forall:b,filter:w,equals:c=function(t){return t.isNone()},equals_:c,toArray:function(){return[]},toString:a("none()")},Object.freeze&&Object.freeze(d),d),I=function(n){var t=function(){return n},e=function(){return o},r=function(t){return t(n)},o={fold:function(t,e){return e(n)},is:function(t){return n===t},isSome:b,isNone:v,getOr:t,getOrThunk:t,getOrDie:t,getOrNull:t,getOrUndefined:t,or:e,orThunk:e,map:function(t){return I(t(n))},ap:function(t){return t.fold(w,function(t){return I(t(n))})},each:function(t){t(n)},bind:r,flatten:t,exists:r,forall:r,filter:function(t){return t(n)?o:x},equals:function(t){return t.is(n)},equals_:function(t,e){return t.fold(v,function(t){return e(n,t)})},toArray:function(){return[n]},toString:function(){return"some("+n+")"}};return o},T={some:I,none:w,from:function(t){return null===t||t===undefined?x:I(t)}},R="undefined"!=typeof s.window?s.window:Function("return this;")(),S=function(t,e){return function(t,e){for(var n=e!==undefined&&null!==e?e:R,r=0;r<t.length&&n!==undefined&&null!==n;++r)n=n[t[r]];return n}(t.split("."),e)},O={getOrDie:function(t,e){var n=S(t,e);if(n===undefined||null===n)throw t+" not available on this browser";return n}};function F(){return new(O.getOrDie("FileReader"))}var C={atob:function(t){return O.getOrDie("atob")(t)},requestAnimationFrame:function(t){O.getOrDie("requestAnimationFrame")(t)}};function E(u){return new g(function(t,e){var n=s.URL.createObjectURL(u),r=new s.Image,o=function(){r.removeEventListener("load",i),r.removeEventListener("error",a)};function i(){o(),t(r)}function a(){o(),e("Unable to load data of type "+u.type+": "+n)}r.addEventListener("load",i),r.addEventListener("error",a),r.src=n,r.complete&&i()})}function D(r){return new g(function(t,n){var e=new s.XMLHttpRequest;e.open("GET",r,!0),e.responseType="blob",e.onload=function(){200==this.status&&t(this.response)},e.onerror=function(){var t,e=this;n(0===this.status?((t=new Error("No access to download image")).code=18,t.name="SecurityError",t):new Error("Error "+e.status+" downloading image"))},e.send()})}function A(t){var e=t.split(","),n=/data:([^;]+)/.exec(e[0]);if(!n)return T.none();for(var r,o,i,a=n[1],u=e[1],c=C.atob(u),l=c.length,s=Math.ceil(l/1024),f=new Array(s),d=0;d<s;++d){for(var h=1024*d,p=Math.min(h+1024,l),g=new Array(p-h),m=h,y=0;m<p;++y,++m)g[y]=c[m].charCodeAt(0);f[d]=(r=g,new(O.getOrDie("Uint8Array"))(r))}return T.some((o=f,i={type:a},new(O.getOrDie("Blob"))(o,i)))}function _(n){return new g(function(t,e){A(n).fold(function(){e("uri is not base64: "+n)},t)})}function k(n){return new g(function(t){var e=F();e.onloadend=function(){t(e.result)},e.readAsDataURL(n)})}var L={blobToImage:E,imageToBlob:function(t){var e=t.src;return 0===e.indexOf("data:")?_(e):D(e)},blobToArrayBuffer:function(n){return new g(function(t){var e=F();e.onloadend=function(){t(e.result)},e.readAsArrayBuffer(n)})},blobToDataUri:k,blobToBase64:function(t){return k(t).then(function(t){return t.split(",")[1]})},dataUriToBlobSync:A,canvasToBlob:function(t,n,r){return n=n||"image/png",s.HTMLCanvasElement.prototype.toBlob?new g(function(e){t.toBlob(function(t){e(t)},n,r)}):_(t.toDataURL(n,r))},canvasToDataURL:function(t,e,n){return e=e||"image/png",t.then(function(t){return t.toDataURL(e,n)})},blobToCanvas:function(t){return E(t).then(function(t){var e,n;return e=t,s.URL.revokeObjectURL(e.src),n=h.create(p.getWidth(t),p.getHeight(t)),h.get2dContext(n).drawImage(t,0,0),n})},uriToBlob:function(t){return 0===t.indexOf("blob:")?D(t):0===t.indexOf("data:")?_(t):null}},P=function(t){return L.blobToImage(t)},B=function(t){return L.imageToBlob(t)};function H(t,e,n){var r=e.type;function o(e,n){return t.then(function(t){return L.canvasToDataURL(t,e,n)})}return{getType:a(r),toBlob:function(){return g.resolve(e)},toDataURL:function(){return n},toBase64:function(){return n.split(",")[1]},toAdjustedBlob:function(e,n){return t.then(function(t){return L.canvasToBlob(t,e,n)})},toAdjustedDataURL:o,toAdjustedBase64:function(t,e){return o(t,e).then(function(t){return t.split(",")[1]})},toCanvas:function(){return t.then(h.clone)}}}function M(e){return L.blobToDataUri(e).then(function(t){return H(L.blobToCanvas(e),e,t)})}var N={fromBlob:M,fromCanvas:function(e,t){return L.canvasToBlob(e,t).then(function(t){return H(g.resolve(e),t,e.toDataURL())})},fromImage:function(t){return L.imageToBlob(t).then(function(t){return M(t)})},fromBlobAndUrlSync:function(t,e){return H(L.blobToCanvas(t),t,e)}};function U(t,e,n){return n<(t=parseFloat(t))?t=n:t<e&&(t=e),t}var j=[0,.01,.02,.04,.05,.06,.07,.08,.1,.11,.12,.14,.15,.16,.17,.18,.2,.21,.22,.24,.25,.27,.28,.3,.32,.34,.36,.38,.4,.42,.44,.46,.48,.5,.53,.56,.59,.62,.65,.68,.71,.74,.77,.8,.83,.86,.89,.92,.95,.98,1,1.06,1.12,1.18,1.24,1.3,1.36,1.42,1.48,1.54,1.6,1.66,1.72,1.78,1.84,1.9,1.96,2,2.12,2.25,2.37,2.5,2.62,2.75,2.87,3,3.2,3.4,3.6,3.8,4,4.3,4.7,4.9,5,5.5,6,6.5,6.8,7,7.3,7.5,7.8,8,8.4,8.7,9,9.4,9.6,9.8,10];function G(t,e){var n,r,o,i,a=[],u=new Array(10);for(n=0;n<5;n++){for(r=0;r<5;r++)a[r]=e[r+5*n];for(r=0;r<5;r++){for(o=i=0;o<5;o++)i+=t[r+5*o]*a[o];u[r+5*n]=i}}return u}function z(t,n){return n=U(n,0,1),t.map(function(t,e){return e%6==0?t=1-(1-t)*n:t*=n,U(t,0,1)})}var V={identity:function(){return[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1]},adjust:z,multiply:G,adjustContrast:function(t,e){var n;return e=U(e,-1,1),G(t,[(n=(e*=100)<0?127+e/100*127:127*(n=0==(n=e%1)?j[e]:j[Math.floor(e)]*(1-n)+j[Math.floor(e)+1]*n)+127)/127,0,0,0,.5*(127-n),0,n/127,0,0,.5*(127-n),0,0,n/127,0,.5*(127-n),0,0,0,1,0,0,0,0,0,1])},adjustBrightness:function(t,e){return G(t,[1,0,0,0,e=U(255*e,-255,255),0,1,0,0,e,0,0,1,0,e,0,0,0,1,0,0,0,0,0,1])},adjustSaturation:function(t,e){var n;return G(t,[.3086*(1-(n=1+(0<(e=U(e,-1,1))?3*e:e)))+n,.6094*(1-n),.082*(1-n),0,0,.3086*(1-n),.6094*(1-n)+n,.082*(1-n),0,0,.3086*(1-n),.6094*(1-n),.082*(1-n)+n,0,0,0,0,0,1,0,0,0,0,0,1])},adjustHue:function(t,e){var n,r,o,i,a;return e=U(e,-180,180)/180*Math.PI,G(t,[(o=.213)+.787*(n=Math.cos(e))+(r=Math.sin(e))*-o,(i=.715)+n*-i+r*-i,(a=.072)+n*-a+.928*r,0,0,o+n*-o+.143*r,i+n*(1-i)+.14*r,a+n*-a+-.283*r,0,0,o+n*-o+-.787*r,i+n*-i+r*i,a+.928*n+r*a,0,0,0,0,0,1,0,0,0,0,0,1])},adjustColors:function(t,e,n,r){return G(t,[e=U(e,0,2),0,0,0,0,0,n=U(n,0,2),0,0,0,0,0,r=U(r,0,2),0,0,0,0,0,1,0,0,0,0,0,1])},adjustSepia:function(t,e){return G(t,z([.393,.769,.189,0,0,.349,.686,.168,0,0,.272,.534,.131,0,0,0,0,0,1,0,0,0,0,0,1],e=U(e,0,1)))},adjustGrayscale:function(t,e){return G(t,z([.33,.34,.33,0,0,.33,.34,.33,0,0,.33,.34,.33,0,0,0,0,0,1,0,0,0,0,0,1],e=U(e,0,1)))}};function W(a,u){return a.toCanvas().then(function(t){return e=t,n=a.getType(),r=u,i=h.get2dContext(e),o=function(t,e){var n,r,o,i,a,u=t.data,c=e[0],l=e[1],s=e[2],f=e[3],d=e[4],h=e[5],p=e[6],g=e[7],m=e[8],y=e[9],v=e[10],b=e[11],w=e[12],x=e[13],I=e[14],T=e[15],R=e[16],S=e[17],O=e[18],F=e[19];for(a=0;a<u.length;a+=4)n=u[a],r=u[a+1],o=u[a+2],i=u[a+3],u[a]=n*c+r*l+o*s+i*f+d,u[a+1]=n*h+r*p+o*g+i*m+y,u[a+2]=n*v+r*b+o*w+i*x+I,u[a+3]=n*T+r*R+o*S+i*O+F;return t}(i.getImageData(0,0,e.width,e.height),r),i.putImageData(o,0,0),N.fromCanvas(e,n);var e,n,r,o,i})}function q(u,c){return u.toCanvas().then(function(t){return e=t,n=u.getType(),r=c,a=h.get2dContext(e),o=a.getImageData(0,0,e.width,e.height),i=a.getImageData(0,0,e.width,e.height),i=function(t,e,n){var r,o,i,a,u,c,l,s,f,d,h,p,g,m,y,v,b;function w(t,e,n){return n<t?t=n:t<e&&(t=e),t}for(i=Math.round(Math.sqrt(n.length)),a=Math.floor(i/2),r=t.data,o=e.data,v=t.width,b=t.height,c=0;c<b;c++)for(u=0;u<v;u++){for(l=s=f=0,h=0;h<i;h++)for(d=0;d<i;d++)p=w(u+d-a,0,v-1),g=w(c+h-a,0,b-1),m=4*(g*v+p),y=n[h*i+d],l+=r[m]*y,s+=r[m+1]*y,f+=r[m+2]*y;o[m=4*(c*v+u)]=w(l,0,255),o[m+1]=w(s,0,255),o[m+2]=w(f,0,255)}return e}(o,i,r),a.putImageData(i,0,0),N.fromCanvas(e,n);var e,n,r,o,i,a})}function Y(u){return function(e,n){return e.toCanvas().then(function(t){return function(t,e,n){var r,o,i=h.get2dContext(t),a=new Array(256);for(o=0;o<a.length;o++)a[o]=u(o,n);return r=function(t,e){var n,r=t.data;for(n=0;n<r.length;n+=4)r[n]=e[r[n]],r[n+1]=e[r[n+1]],r[n+2]=e[r[n+2]];return t}(i.getImageData(0,0,t.width,t.height),a),i.putImageData(r,0,0),N.fromCanvas(t,e)}(t,e.getType(),n)})}}function X(n){return function(t,e){return W(t,n(V.identity(),e))}}function J(e){return function(t){return q(t,e)}}var K,Z={invert:(K=[-1,0,0,0,255,0,-1,0,0,255,0,0,-1,0,255,0,0,0,1,0],function(t){return W(t,K)}),brightness:X(V.adjustBrightness),hue:X(V.adjustHue),saturate:X(V.adjustSaturation),contrast:X(V.adjustContrast),grayscale:X(V.adjustGrayscale),sepia:X(V.adjustSepia),colorize:function(t,e,n,r){return W(t,V.adjustColors(V.identity(),e,n,r))},sharpen:J([0,-1,0,-1,5,-1,0,-1,0]),emboss:J([-2,-1,0,-1,1,1,0,1,2]),gamma:Y(function(t,e){return 255*Math.pow(t/255,1-e)}),exposure:Y(function(t,e){return 255*(1-Math.exp(-t/255*e))}),colorFilter:W,convoluteFilter:q},Q={scale:function e(t,n,r){var o=p.getWidth(t),i=p.getHeight(t),a=n/o,u=r/i,c=!1;(a<.5||2<a)&&(a=a<.5?.5:2,c=!0),(u<.5||2<u)&&(u=u<.5?.5:2,c=!0);var l,s,f,d=(l=t,s=a,f=u,new g(function(t){var e=p.getWidth(l),n=p.getHeight(l),r=Math.floor(e*s),o=Math.floor(n*f),i=h.create(r,o),a=h.get2dContext(i);a.drawImage(l,0,0,e,n,0,0,r,o),t(i)}));return c?d.then(function(t){return e(t,n,r)}):d}},tt={rotate:function(c,l){return c.toCanvas().then(function(t){return e=t,n=c.getType(),r=l,o=h.create(e.width,e.height),i=h.get2dContext(o),90!=(r=r<(u=a=0)?360+r:r)&&270!=r||h.resize(o,o.height,o.width),90!=r&&180!=r||(a=o.width),270!=r&&180!=r||(u=o.height),i.translate(a,u),i.rotate(r*Math.PI/180),i.drawImage(e,0,0),N.fromCanvas(o,n);var e,n,r,o,i,a,u})},flip:function(a,u){return a.toCanvas().then(function(t){return e=t,n=a.getType(),r=u,o=h.create(e.width,e.height),i=h.get2dContext(o),"v"==r?(i.scale(1,-1),i.drawImage(e,0,-o.height)):(i.scale(-1,1),i.drawImage(e,-o.width,0)),N.fromCanvas(o,n);var e,n,r,o,i})},crop:function(c,l,s,f,d){return c.toCanvas().then(function(t){return e=t,n=c.getType(),r=l,o=s,i=f,a=d,u=h.create(i,a),h.get2dContext(u).drawImage(e,-r,-o),N.fromCanvas(u,n);var e,n,r,o,i,a,u})},resize:function(e,n,r){return e.toCanvas().then(function(t){return Q.scale(t,n,r).then(function(t){return N.fromCanvas(t,e.getType())})})}},et=function(){function t(t){this.littleEndian=!1,this._dv=new DataView(t)}return t.prototype.readByteAt=function(t){return this._dv.getUint8(t)},t.prototype.read=function(t,e){if(t+e>this.length())return null;for(var n=this.littleEndian?0:-8*(e-1),r=0,o=0;r<e;r++)o|=this.readByteAt(t+r)<<Math.abs(n+8*r);return o},t.prototype.BYTE=function(t){return this.read(t,1)},t.prototype.SHORT=function(t){return this.read(t,2)},t.prototype.LONG=function(t){return this.read(t,4)},t.prototype.SLONG=function(t){var e=this.read(t,4);return 2147483647<e?e-4294967296:e},t.prototype.CHAR=function(t){return String.fromCharCode(this.read(t,1))},t.prototype.STRING=function(t,e){return this.asArray("CHAR",t,e).join("")},t.prototype.SEGMENT=function(t,e){var n=this._dv.buffer;switch(arguments.length){case 2:return n.slice(t,t+e);case 1:return n.slice(t);default:return n}},t.prototype.asArray=function(t,e,n){for(var r=[],o=0;o<n;o++)r[o]=this[t](e+o);return r},t.prototype.length=function(){return this._dv?this._dv.byteLength:0},t}(),nt={274:"Orientation",270:"ImageDescription",271:"Make",272:"Model",305:"Software",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer"},rt={36864:"ExifVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",36867:"DateTimeOriginal",33434:"ExposureTime",33437:"FNumber",34855:"ISOSpeedRatings",37377:"ShutterSpeedValue",37378:"ApertureValue",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37386:"FocalLength",41986:"ExposureMode",41987:"WhiteBalance",41990:"SceneCaptureType",41988:"DigitalZoomRatio",41992:"Contrast",41993:"Saturation",41994:"Sharpness"},ot={0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude"},it={513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength"},at={ColorSpace:{1:"sRGB",0:"Uncalibrated"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{1:"Daylight",2:"Fliorescent",3:"Tungsten",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 -5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},ExposureMode:{0:"Auto exposure",1:"Manual exposure",2:"Auto bracket"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},GPSLatitudeRef:{N:"North latitude",S:"South latitude"},GPSLongitudeRef:{E:"East longitude",W:"West longitude"}},ut=function(){function t(t){this._offsets={tiffHeader:10,IFD0:null,IFD1:null,exifIFD:null,gpsIFD:null},this._tiffTags={};var e=this;if(e._reader=new et(t),e._idx=e._offsets.tiffHeader,65505!==e.SHORT(0)||"EXIF\0"!==e.STRING(4,5).toUpperCase())throw new Error("Exif data cannot be read or not available.");if(e._reader.littleEndian=18761==e.SHORT(e._idx),42!==e.SHORT(e._idx+=2))throw new Error("Invalid Exif data.");e._offsets.IFD0=e._offsets.tiffHeader+e.LONG(e._idx+=2),e._tiffTags=e.extractTags(e._offsets.IFD0,nt),"ExifIFDPointer"in e._tiffTags&&(e._offsets.exifIFD=e._offsets.tiffHeader+e._tiffTags.ExifIFDPointer,delete e._tiffTags.ExifIFDPointer),"GPSInfoIFDPointer"in e._tiffTags&&(e._offsets.gpsIFD=e._offsets.tiffHeader+e._tiffTags.GPSInfoIFDPointer,delete e._tiffTags.GPSInfoIFDPointer);var n=e.LONG(e._offsets.IFD0+12*e.SHORT(e._offsets.IFD0)+2);n&&(e._offsets.IFD1=e._offsets.tiffHeader+n)}return t.prototype.BYTE=function(t){return this._reader.BYTE(t)},t.prototype.SHORT=function(t){return this._reader.SHORT(t)},t.prototype.LONG=function(t){return this._reader.LONG(t)},t.prototype.SLONG=function(t){return this._reader.SLONG(t)},t.prototype.CHAR=function(t){return this._reader.CHAR(t)},t.prototype.STRING=function(t,e){return this._reader.STRING(t,e)},t.prototype.SEGMENT=function(t,e){return this._reader.SEGMENT(t,e)},t.prototype.asArray=function(t,e,n){for(var r=[],o=0;o<n;o++)r[o]=this[t](e+o);return r},t.prototype.length=function(){return this._reader.length()},t.prototype.UNDEFINED=function(){return this.BYTE.apply(this,arguments)},t.prototype.RATIONAL=function(t){return this.LONG(t)/this.LONG(t+4)},t.prototype.SRATIONAL=function(t){return this.SLONG(t)/this.SLONG(t+4)},t.prototype.ASCII=function(t){return this.CHAR(t)},t.prototype.TIFF=function(){return this._tiffTags},t.prototype.EXIF=function(){var t=null;if(this._offsets.exifIFD){try{t=this.extractTags(this._offsets.exifIFD,rt)}catch(r){return null}if(t.ExifVersion&&Array.isArray(t.ExifVersion)){for(var e=0,n="";e<t.ExifVersion.length;e++)n+=String.fromCharCode(t.ExifVersion[e]);t.ExifVersion=n}}return t},t.prototype.GPS=function(){var t=null;if(this._offsets.gpsIFD){try{t=this.extractTags(this._offsets.gpsIFD,ot)}catch(e){return null}t.GPSVersionID&&Array.isArray(t.GPSVersionID)&&(t.GPSVersionID=t.GPSVersionID.join("."))}return t},t.prototype.thumb=function(){var t=this;if(t._offsets.IFD1)try{var e=t.extractTags(t._offsets.IFD1,it);if("JPEGInterchangeFormat"in e)return t.SEGMENT(t._offsets.tiffHeader+e.JPEGInterchangeFormat,e.JPEGInterchangeFormatLength)}catch(n){}return null},t.prototype.extractTags=function(t,e){var n,r,o,i,a,u,c,l,s=this,f=[],d={},h={1:"BYTE",7:"UNDEFINED",2:"ASCII",3:"SHORT",4:"LONG",5:"RATIONAL",9:"SLONG",10:"SRATIONAL"},p={BYTE:1,UNDEFINED:1,ASCII:1,SHORT:2,LONG:4,RATIONAL:8,SLONG:4,SRATIONAL:8};for(n=s.SHORT(t),r=0;r<n;r++)if(f=[],c=t+2+12*r,(o=e[s.SHORT(c)])!==undefined){if(i=h[s.SHORT(c+=2)],a=s.LONG(c+=2),!(u=p[i]))throw new Error("Invalid Exif data.");if(c+=4,4<u*a&&(c=s.LONG(c)+s._offsets.tiffHeader),c+u*a>=s.length())throw new Error("Invalid Exif data.");"ASCII"!==i?(f=s.asArray(i,c,a),l=1==a?f[0]:f,at.hasOwnProperty(o)&&"object"!=typeof l?d[o]=at[o][l]:d[o]=l):d[o]=s.STRING(c,a).replace(/\0$/,"").trim()}return d},t}(),ct=function(t){var e,n,r=[],o=0;for(e=2;e<=t.length();)if(65488<=(n=t.SHORT(e))&&n<=65495)e+=2;else{if(65498===n||65497===n)break;o=t.SHORT(e+2)+2,65505<=n&&n<=65519&&r.push({hex:n,name:"APP"+(15&n),start:e,length:o,segment:t.SEGMENT(e,o)}),e+=o}return r},lt=function(u){return L.blobToArrayBuffer(u).then(function(t){try{var e=new et(t);if(65496===e.SHORT(0)){var n=ct(e),r=n.filter(function(t){return"APP1"===t.name}),o={};if(!r.length)return g.reject("Headers did not include required information");var i=new ut(r[0].segment);return(o={tiff:i.TIFF(),exif:i.EXIF(),gps:i.GPS(),thumb:i.thumb()}).rawHeaders=n,o}return g.reject("Image was not a jpeg")}catch(a){return g.reject("Unsupported format or not an image: "+u.type+" (Exception: "+a.message+")")}})},st=function(t,e){return tt.rotate(t,e)},ft={invert:function(t){return Z.invert(t)},sharpen:function(t){return Z.sharpen(t)},emboss:function(t){return Z.emboss(t)},brightness:function(t,e){return Z.brightness(t,e)},hue:function(t,e){return Z.hue(t,e)},saturate:function(t,e){return Z.saturate(t,e)},contrast:function(t,e){return Z.contrast(t,e)},grayscale:function(t,e){return Z.grayscale(t,e)},sepia:function(t,e){return Z.sepia(t,e)},colorize:function(t,e,n,r){return Z.colorize(t,e,n,r)},gamma:function(t,e){return Z.gamma(t,e)},exposure:function(t,e){return Z.exposure(t,e)},flip:function(t,e){return tt.flip(t,e)},crop:function(t,e,n,r,o){return tt.crop(t,e,n,r,o)},resize:function(t,e,n){return tt.resize(t,e,n)},rotate:st,exifRotate:function(e){return e.toBlob().then(lt).then(function(t){switch(t.tiff.Orientation){case 6:return st(e,90);case 3:return st(e,180);case 8:return st(e,270);default:return e}},function(){return e})}},dt=function(t){return t.toBlob()},ht={blobToImageResult:function(t){return N.fromBlob(t)},fromBlobAndUrlSync:function(t,e){return N.fromBlobAndUrlSync(t,e)},imageToImageResult:function(t){return N.fromImage(t)},imageResultToBlob:function(t,e,n){return e===undefined&&n===undefined?dt(t):t.toAdjustedBlob(e,n)},imageResultToOriginalBlob:dt,imageResultToDataURL:function(t){return t.toDataURL()}},pt=function(){return O.getOrDie("URL")},gt={createObjectURL:function(t){return pt().createObjectURL(t)},revokeObjectURL:function(t){pt().revokeObjectURL(t)}},mt=tinymce.util.Tools.resolve("tinymce.util.Delay"),yt=tinymce.util.Tools.resolve("tinymce.util.Promise"),vt=tinymce.util.Tools.resolve("tinymce.util.URI"),bt=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),wt=tinymce.util.Tools.resolve("tinymce.ui.Factory"),xt=tinymce.util.Tools.resolve("tinymce.geom.Rect"),It=function(n){return new yt(function(t){var e=function(){n.removeEventListener("load",e),t(n)};n.complete?t(n):n.addEventListener("load",e)})},Tt=tinymce.util.Tools.resolve("tinymce.dom.DomQuery"),Rt=tinymce.util.Tools.resolve("tinymce.util.Observable"),St=tinymce.util.Tools.resolve("tinymce.util.VK"),Ot=0,Ft={create:function(t){return new(wt.get("Control").extend({Defaults:{classes:"imagepanel"},selection:function(t){return arguments.length?(this.state.set("rect",t),this):this.state.get("rect")},imageSize:function(){var t=this.state.get("viewRect");return{w:t.w,h:t.h}},toggleCropRect:function(t){this.state.set("cropEnabled",t)},imageSrc:function(t){var o=this,i=new s.Image;i.src=t,It(i).then(function(){var t,e,n=o.state.get("viewRect");if((e=o.$el.find("img"))[0])e.replaceWith(i);else{var r=s.document.createElement("div");r.className="mce-imagepanel-bg",o.getEl().appendChild(r),o.getEl().appendChild(i)}t={x:0,y:0,w:i.naturalWidth,h:i.naturalHeight},o.state.set("viewRect",t),o.state.set("rect",xt.inflate(t,-20,-20)),n&&n.w===t.w&&n.h===t.h||o.zoomFit(),o.repaintImage(),o.fire("load")})},zoom:function(t){return arguments.length?(this.state.set("zoom",t),this):this.state.get("zoom")},postRender:function(){return this.imageSrc(this.settings.imageSrc),this._super()},zoomFit:function(){var t,e,n,r,o,i;t=this.$el.find("img"),e=this.getEl().clientWidth,n=this.getEl().clientHeight,r=t[0].naturalWidth,o=t[0].naturalHeight,1<=(i=Math.min((e-10)/r,(n-10)/o))&&(i=1),this.zoom(i)},repaintImage:function(){var t,e,n,r,o,i,a,u,c,l,s;s=this.getEl(),c=this.zoom(),l=this.state.get("rect"),a=this.$el.find("img"),u=this.$el.find(".mce-imagepanel-bg"),o=s.offsetWidth,i=s.offsetHeight,n=a[0].naturalWidth*c,r=a[0].naturalHeight*c,t=Math.max(0,o/2-n/2),e=Math.max(0,i/2-r/2),a.css({left:t,top:e,width:n,height:r}),u.css({left:t,top:e,width:n,height:r}),this.cropRect&&(this.cropRect.setRect({x:l.x*c+t,y:l.y*c+e,w:l.w*c,h:l.h*c}),this.cropRect.setClampRect({x:t,y:e,w:n,h:r}),this.cropRect.setViewPortRect({x:0,y:0,w:o,h:i}))},bindStates:function(){var r=this;function n(t){r.cropRect=function(l,n,s,r,o){var f,a,t,i,e="mce-",u=e+"crid-"+Ot++;function d(t,e){return{x:e.x-t.x,y:e.y-t.y,w:e.w,h:e.h}}function c(t,e,n,r){var o,i,a,u,c;o=e.x,i=e.y,a=e.w,u=e.h,o+=n*t.deltaX,i+=r*t.deltaY,(a+=n*t.deltaW)<20&&(a=20),(u+=r*t.deltaH)<20&&(u=20),c=l=xt.clamp({x:o,y:i,w:a,h:u},s,"move"===t.name),c=d(s,c),f.fire("updateRect",{rect:c}),g(c)}function h(e){function t(t,e){e.h<0&&(e.h=0),e.w<0&&(e.w=0),Tt("#"+u+"-"+t,r).css({left:e.x,top:e.y,width:e.w,height:e.h})}$.each(a,function(t){Tt("#"+u+"-"+t.name,r).css({left:e.w*t.xMul+e.x,top:e.h*t.yMul+e.y})}),t("top",{x:n.x,y:n.y,w:n.w,h:e.y-n.y}),t("right",{x:e.x+e.w,y:e.y,w:n.w-e.x-e.w+n.x,h:e.h}),t("bottom",{x:n.x,y:e.y+e.h,w:n.w,h:n.h-e.y-e.h+n.y}),t("left",{x:n.x,y:e.y,w:e.x-n.x,h:e.h}),t("move",e)}function p(t){h(l=t)}function g(t){var e,n;p((e=s,{x:(n=t).x+e.x,y:n.y+e.y,w:n.w,h:n.h}))}return a=[{name:"move",xMul:0,yMul:0,deltaX:1,deltaY:1,deltaW:0,deltaH:0,label:"Crop Mask"},{name:"nw",xMul:0,yMul:0,deltaX:1,deltaY:1,deltaW:-1,deltaH:-1,label:"Top Left Crop Handle"},{name:"ne",xMul:1,yMul:0,deltaX:0,deltaY:1,deltaW:1,deltaH:-1,label:"Top Right Crop Handle"},{name:"sw",xMul:0,yMul:1,deltaX:1,deltaY:0,deltaW:-1,deltaH:1,label:"Bottom Left Crop Handle"},{name:"se",xMul:1,yMul:1,deltaX:0,deltaY:0,deltaW:1,deltaH:1,label:"Bottom Right Crop Handle"}],i=["top","right","bottom","left"],Tt('<div id="'+u+'" class="'+e+'croprect-container" role="grid" aria-dropeffect="execute">').appendTo(r),$.each(i,function(t){Tt("#"+u,r).append('<div id="'+u+"-"+t+'"class="'+e+'croprect-block" style="display: none" data-mce-bogus="all">')}),$.each(a,function(t){Tt("#"+u,r).append('<div id="'+u+"-"+t.name+'" class="'+e+"croprect-handle "+e+"croprect-handle-"+t.name+'"style="display: none" data-mce-bogus="all" role="gridcell" tabindex="-1" aria-label="'+t.label+'" aria-grabbed="false">')}),t=$.map(a,function(e){var n;return new(wt.get("DragHelper"))(u,{document:r.ownerDocument,handle:u+"-"+e.name,start:function(){n=l},drag:function(t){c(e,n,t.deltaX,t.deltaY)}})}),h(l),Tt(r).on("focusin focusout",function(t){Tt(t.target).attr("aria-grabbed","focus"===t.type)}),Tt(r).on("keydown",function(e){var i;function t(t,e,n,r,o){t.stopPropagation(),t.preventDefault(),c(i,n,r,o)}switch($.each(a,function(t){if(e.target.id===u+"-"+t.name)return i=t,!1}),e.keyCode){case St.LEFT:t(e,0,l,-10,0);break;case St.RIGHT:t(e,0,l,10,0);break;case St.UP:t(e,0,l,0,-10);break;case St.DOWN:t(e,0,l,0,10);break;case St.ENTER:case St.SPACEBAR:e.preventDefault(),o()}}),f=$.extend({toggleVisibility:function(t){var e;e=$.map(a,function(t){return"#"+u+"-"+t.name}).concat($.map(i,function(t){return"#"+u+"-"+t})).join(","),t?Tt(e,r).show():Tt(e,r).hide()},setClampRect:function(t){s=t,h(l)},setRect:p,getInnerRect:function(){return d(s,l)},setInnerRect:g,setViewPortRect:function(t){n=t,h(l)},destroy:function(){$.each(t,function(t){t.destroy()}),t=[]}},Rt)}(t,r.state.get("viewRect"),r.state.get("viewRect"),r.getEl(),function(){r.fire("crop")}),r.cropRect.on("updateRect",function(t){var e=t.rect,n=r.zoom();e={x:Math.round(e.x/n),y:Math.round(e.y/n),w:Math.round(e.w/n),h:Math.round(e.h/n)},r.state.set("rect",e)}),r.on("remove",r.cropRect.destroy)}r.state.on("change:cropEnabled",function(t){r.cropRect.toggleVisibility(t.value),r.repaintImage()}),r.state.on("change:zoom",function(){r.repaintImage()}),r.state.on("change:rect",function(t){var e=t.value;r.cropRect||n(e),r.cropRect.setRect(e)})}}))(t)}};function Ct(t){return{blob:t,url:gt.createObjectURL(t)}}function Et(t){t&&gt.revokeObjectURL(t.url)}function Dt(t){$.each(t,Et)}function At(i,a,t,e){var u,n,r,c,o,l,s,f,d,h,p,g,m,y,v,b,w,x,I,T,R,S,O,F,C,E,D,A=function(){var n=[],r=-1;function t(){return 0<r}function e(){return-1!==r&&r<n.length-1}return{data:n,add:function(t){var e;return e=n.splice(++r),n.push(t),{state:t,removed:e}},undo:function(){if(t())return n[--r]},redo:function(){if(e())return n[++r]},canUndo:t,canRedo:e}}(),_=function(t){return i.rtl?t.reverse():t};function k(t){var e,n,r,o;e=u.find("#w")[0],n=u.find("#h")[0],r=parseInt(e.value(),10),o=parseInt(n.value(),10),u.find("#constrain")[0].checked()&&F&&C&&r&&o&&("w"===t.control.settings.name?(o=Math.round(r*E),n.value(o)):(r=Math.round(o*D),e.value(r))),F=r,C=o}function L(t){return Math.round(100*t)+"%"}function P(){u.find("#undo").disabled(!A.canUndo()),u.find("#redo").disabled(!A.canRedo()),u.statusbar.find("#save").disabled(!A.canUndo())}function B(){u.find("#undo").disabled(!0),u.find("#redo").disabled(!0)}function H(t){t&&f.imageSrc(t.url)}function M(e){return function(){var t=$.grep(O,function(t){return t.settings.name!==e});$.each(t,function(t){t.hide()}),e.show(),e.focus()}}function N(t){H(c=Ct(t))}function U(t){H(a=Ct(t)),Dt(A.add(a).removed),P()}function j(){var e=f.selection();ht.blobToImageResult(a.blob).then(function(t){ft.crop(t,e.x,e.y,e.w,e.h).then(q).then(function(t){U(t),z()})})}var G=function(e){var n=[].slice.call(arguments,1);return function(){var t=c||a;ht.blobToImageResult(t.blob).then(function(t){e.apply(this,[t].concat(n)).then(q).then(N)})}};function z(){H(a),Et(c),M(n)(),P()}function V(){c?(U(c.blob),z()):function t(e,n){c?n():setTimeout(function(){0<e--?t(e,n):i.windowManager.alert("Error: failed to apply image operation.")},10)}(100,V)}function W(t){return wt.create("Form",{layout:"flex",direction:"row",labelGap:5,border:"0 0 1 0",align:"center",pack:"center",padding:"0 10 0 10",spacing:5,flex:0,minHeight:60,defaults:{classes:"imagetool",type:"button"},items:t})}var q=function(t){return t.toBlob()};function Y(t,e){return W(_([{text:"Back",onclick:z},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:V}])).hide().on("show",function(){B(),ht.blobToImageResult(a.blob).then(function(t){return e(t)}).then(q).then(function(t){var e=Ct(t);H(e),Et(c),c=e})})}function X(t,n,e,r,o){return W(_([{text:"Back",onclick:z},{type:"spacer",flex:1},{type:"slider",flex:1,ondragend:function(t){var e;e=t.value,ht.blobToImageResult(a.blob).then(function(t){return n(t,e)}).then(q).then(function(t){var e=Ct(t);H(e),Et(c),c=e})},minValue:i.rtl?o:r,maxValue:i.rtl?r:o,value:e,previewFilter:L},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:V}])).hide().on("show",function(){this.find("slider").value(e),B()})}o=W(_([{text:"Back",onclick:z},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:j}])).hide().on("show hide",function(t){f.toggleCropRect("show"===t.type)}).on("show",B),l=W(_([{text:"Back",onclick:z},{type:"spacer",flex:1},{type:"textbox",name:"w",label:"Width",size:4,onkeyup:k},{type:"textbox",name:"h",label:"Height",size:4,onkeyup:k},{type:"checkbox",name:"constrain",text:"Constrain proportions",checked:!0,onchange:function(t){!0===t.control.value()&&(E=C/F,D=F/C)}},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:"submit"}])).hide().on("submit",function(t){var e=parseInt(u.find("#w").value(),10),n=parseInt(u.find("#h").value(),10);t.preventDefault(),function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r=[].slice.call(arguments,1);return function(){ht.blobToImageResult(a.blob).then(function(t){e.apply(this,[t].concat(r)).then(q).then(U)})}}(ft.resize,e,n)(),z()}).on("show",B),s=W(_([{text:"Back",onclick:z},{type:"spacer",flex:1},{icon:"fliph",tooltip:"Flip horizontally",onclick:G(ft.flip,"h")},{icon:"flipv",tooltip:"Flip vertically",onclick:G(ft.flip,"v")},{icon:"rotateleft",tooltip:"Rotate counterclockwise",onclick:G(ft.rotate,-90)},{icon:"rotateright",tooltip:"Rotate clockwise",onclick:G(ft.rotate,90)},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:V}])).hide().on("show",B),p=Y(0,ft.invert),I=Y(0,ft.sharpen),T=Y(0,ft.emboss),g=X(0,ft.brightness,0,-1,1),m=X(0,ft.hue,180,0,360),y=X(0,ft.saturate,0,-1,1),v=X(0,ft.contrast,0,-1,1),b=X(0,ft.grayscale,0,0,1),w=X(0,ft.sepia,0,0,1),x=function(t,o){function e(){var e,n,r;e=u.find("#r")[0].value(),n=u.find("#g")[0].value(),r=u.find("#b")[0].value(),ht.blobToImageResult(a.blob).then(function(t){return o(t,e,n,r)}).then(q).then(function(t){var e=Ct(t);H(e),Et(c),c=e})}var n=i.rtl?2:0,r=i.rtl?0:2;return W(_([{text:"Back",onclick:z},{type:"spacer",flex:1},{type:"slider",label:"R",name:"r",minValue:n,value:1,maxValue:r,ondragend:e,previewFilter:L},{type:"slider",label:"G",name:"g",minValue:n,value:1,maxValue:r,ondragend:e,previewFilter:L},{type:"slider",label:"B",name:"b",minValue:n,value:1,maxValue:r,ondragend:e,previewFilter:L},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:V}])).hide().on("show",function(){u.find("#r,#g,#b").value(1),B()})}(0,ft.colorize),R=X(0,ft.gamma,0,-1,1),S=X(0,ft.exposure,1,0,2),r=W(_([{text:"Back",onclick:z},{type:"spacer",flex:1},{text:"hue",icon:"hue",onclick:M(m)},{text:"saturate",icon:"saturate",onclick:M(y)},{text:"sepia",icon:"sepia",onclick:M(w)},{text:"emboss",icon:"emboss",onclick:M(T)},{text:"exposure",icon:"exposure",onclick:M(S)},{type:"spacer",flex:1}])).hide(),n=W(_([{tooltip:"Crop",icon:"crop",onclick:M(o)},{tooltip:"Resize",icon:"resize2",onclick:M(l)},{tooltip:"Orientation",icon:"orientation",onclick:M(s)},{tooltip:"Brightness",icon:"sun",onclick:M(g)},{tooltip:"Sharpen",icon:"sharpen",onclick:M(I)},{tooltip:"Contrast",icon:"contrast",onclick:M(v)},{tooltip:"Color levels",icon:"drop",onclick:M(x)},{tooltip:"Gamma",icon:"gamma",onclick:M(R)},{tooltip:"Invert",icon:"invert",onclick:M(p)}])),f=Ft.create({flex:1,imageSrc:a.url}),d=wt.create("Container",{layout:"flex",direction:"column",pack:"start",border:"0 1 0 0",padding:5,spacing:5,items:[{type:"button",icon:"undo",tooltip:"Undo",name:"undo",onclick:function(){H(a=A.undo()),P()}},{type:"button",icon:"redo",tooltip:"Redo",name:"redo",onclick:function(){H(a=A.redo()),P()}},{type:"button",icon:"zoomin",tooltip:"Zoom in",onclick:function(){var t=f.zoom();t<2&&(t+=.1),f.zoom(t)}},{type:"button",icon:"zoomout",tooltip:"Zoom out",onclick:function(){var t=f.zoom();.1<t&&(t-=.1),f.zoom(t)}}]}),h=wt.create("Container",{type:"container",layout:"flex",direction:"row",align:"stretch",flex:1,items:_([d,f])}),O=[n,o,l,s,r,p,g,m,y,v,b,w,x,I,T,R,S],(u=i.windowManager.open({layout:"flex",direction:"column",align:"stretch",minWidth:Math.min(bt.DOM.getViewPort().w,800),minHeight:Math.min(bt.DOM.getViewPort().h,650),title:"Edit image",items:O.concat([h]),buttons:_([{text:"Save",name:"save",subtype:"primary",onclick:function(){t(a.blob),u.close()}},{text:"Cancel",onclick:"close"}])})).on("close",function(){e(),Dt(A.data),c=A=null}),A.add(a),P(),f.on("load",function(){F=f.imageSize().w,C=f.imageSize().h,E=C/F,D=F/C,u.find("#w").value(F),u.find("#h").value(C)}),f.on("crop",j)}var _t,kt={edit:function(r,t){return new yt(function(e,n){return t.toBlob().then(function(t){At(r,Ct(t),e,n)})})}},Lt={getImageSize:function(t){var e,n;function r(t){return/^[0-9\.]+px$/.test(t)}return e=t.style.width,n=t.style.height,e||n?r(e)&&r(n)?{w:parseInt(e,10),h:parseInt(n,10)}:null:(e=t.width,n=t.height,e&&n?{w:parseInt(e,10),h:parseInt(n,10)}:null)},setImageSize:function(t,e){var n,r;e&&(n=t.style.width,r=t.style.height,(n||r)&&(t.style.width=e.w+"px",t.style.height=e.h+"px",t.removeAttribute("data-mce-style")),n=t.width,r=t.height,(n||r)&&(t.setAttribute("width",e.w),t.setAttribute("height",e.h)))},getNaturalImageSize:function(t){return{w:t.naturalWidth,h:t.naturalHeight}}},Pt=(_t="function",function(t){return function(t){if(null===t)return"null";var e=typeof t;return"object"===e&&Array.prototype.isPrototypeOf(t)?"array":"object"===e&&String.prototype.isPrototypeOf(t)?"string":e}(t)===_t}),Bt=function(t,e){for(var n=0,r=t.length;n<r;n++){var o=t[n];if(e(o,n,t))return T.some(o)}return T.none()};Array.prototype.slice;Pt(Array.from)&&Array.from;var Ht=function(t){return null!==t&&t!==undefined},Mt=function(t,e){var n;return n=e.reduce(function(t,e){return Ht(t)?t[e]:undefined},t),Ht(n)?n:null},Nt=function(e){return new yt(function(n){var t=F();t.onload=function(t){var e=t.target;n(e.result)},t.readAsText(e)})},Ut=function(e,r,o){return new yt(function(t){var n;(n=new(O.getOrDie("XMLHttpRequest"))).onreadystatechange=function(){4===n.readyState&&t({status:n.status,blob:this.response})},n.open("GET",e,!0),n.withCredentials=o,$.each(r,function(t,e){n.setRequestHeader(e,t)}),n.responseType="blob",n.send()})},jt=function(t){var e;try{e=JSON.parse(t)}catch(n){}return e},Gt=[{code:404,message:"Could not find Image Proxy"},{code:403,message:"Rejected request"},{code:0,message:"Incorrect Image Proxy URL"}],zt=[{type:"key_missing",message:"The request did not include an api key."},{type:"key_not_found",message:"The provided api key could not be found."},{type:"domain_not_trusted",message:"The api key is not valid for the request origins."}],Vt=function(e){return"ImageProxy HTTP error: "+Bt(Gt,function(t){return e===t.code}).fold(a("Unknown ImageProxy error"),function(t){return t.message})},Wt=function(t){var e=Vt(t);return yt.reject(e)},qt=function(e){return Bt(zt,function(t){return t.type===e}).fold(a("Unknown service error"),function(t){return t.message})},Yt=function(t,e){return Nt(e).then(function(t){var e,n,r=(e=jt(t),"ImageProxy Service error: "+((n=Mt(e,["error","type"]))?qt(n):"Invalid JSON in service error message"));return yt.reject(r)})},Xt=function(t,e){return 400===(n=t)||403===n||500===n?Yt(0,e):Wt(t);var n},$t=Wt,Jt=function(t,e){var n,r,o,i={"Content-Type":"application/json;charset=UTF-8","tiny-api-key":e};return Ut((n=t,r=e,o=-1===n.indexOf("?")?"?":"&",/[?&]apiKey=/.test(n)||!r?n:n+o+"apiKey="+encodeURIComponent(r)),i,!1).then(function(t){return t.status<200||300<=t.status?Xt(t.status,t.blob):yt.resolve(t.blob)})},Kt=function(t,e,n){return e?Jt(t,e):Ut(t,{},n).then(function(t){return t.status<200||300<=t.status?$t(t.status):yt.resolve(t.blob)})},Zt=0,Qt=function(t,e){t.notificationManager.open({text:e,type:"error"})},te=function(t){return t.selection.getNode()},ee=function(t,e){var n=e.src;return 0===n.indexOf("data:")||0===n.indexOf("blob:")||new vt(n).host===t.documentBaseURI.host},ne=function(t,e){return-1!==$.inArray(t.getParam("imagetools_cors_hosts",[],"string[]"),new vt(e.src).host)},re=function(t,e){var n,r,o,i,a=e.src;return ne(t,e)?Kt(e.src,null,(r=t,o=e,-1!==$.inArray(r.getParam("imagetools_credentials_hosts",[],"string[]"),new vt(o.src).host))):ee(t,e)?B(e):(a=t.getParam("imagetools_proxy"),a+=(-1===a.indexOf("?")?"?":"&")+"url="+encodeURIComponent(e.src),n=(i=t).getParam("api_key",i.getParam("imagetools_api_key","","string"),"string"),Kt(a,n,!1))},oe=function(t){var e;return(e=t.editorUpload.blobCache.getByUri(te(t).src))?yt.resolve(e.blob()):re(t,te(t))},ie=function(t){clearTimeout(t.get())},ae=function(c,l,s,f,d){return l.toBlob().then(function(t){var e,n,r,o,i,a,u;return r=c.editorUpload.blobCache,e=(i=te(c)).src,c.getParam("images_reuse_filename",!1,"boolean")&&((o=r.getByUri(e))?(e=o.uri(),n=o.name()):(a=c,n=(u=e.match(/\/([^\/\?]+)?\.(?:jpeg|jpg|png|gif)(?:\?|$)/i))?a.dom.encode(u[1]):null)),o=r.create({id:"imagetools"+Zt++,blob:t,base64:l.toBase64(),uri:e,name:n}),r.add(o),c.undoManager.transact(function(){c.$(i).on("load",function t(){var e,n,r;c.$(i).off("load",t),c.nodeChanged(),s?c.editorUpload.uploadImagesAuto():(ie(f),e=c,n=f,r=mt.setEditorTimeout(e,function(){e.editorUpload.uploadImagesAuto()},e.getParam("images_upload_timeout",3e4,"number")),n.set(r))}),d&&c.$(i).attr({width:d.w,height:d.h}),c.$(i).attr({src:o.blobUri()}).removeAttr("data-mce-src")}),o})},ue=function(e,n,t,r){return function(){return e._scanForImages().then(u(oe,e)).then(ht.blobToImageResult).then(t).then(function(t){return ae(e,t,!1,n,r)},function(t){Qt(e,t)})}},ce=function(n,r,o){return function(){var t=Lt.getImageSize(te(n)),e=t?{w:t.h,h:t.w}:null;return ue(n,r,function(t){return ft.rotate(t,o)},e)()}},le=function(t,e,n){return function(){return ue(t,e,function(t){return ft.flip(t,n)})()}},se=function(e,r){return function(){var o=te(e),i=Lt.getNaturalImageSize(o),n=function(r){return new yt(function(n){P(r).then(function(t){var e=Lt.getNaturalImageSize(t);i.w===e.w&&i.h===e.h||Lt.getImageSize(o)&&Lt.setImageSize(o,e),gt.revokeObjectURL(t.src),n(r)})})};oe(e).then(ht.blobToImageResult).then(u(function(e,t){return kt.edit(e,t).then(n).then(ht.blobToImageResult).then(function(t){return ae(e,t,!0,r)},function(){})},e),function(t){Qt(e,t)})}},fe=function(t,e){return t.dom.is(e,"img:not([data-mce-object],[data-mce-placeholder])")&&(ee(t,e)||ne(t,e)||t.settings.imagetools_proxy)},de=ie,he=function(n,t){$.each({mceImageRotateLeft:ce(n,t,-90),mceImageRotateRight:ce(n,t,90),mceImageFlipVertical:le(n,t,"v"),mceImageFlipHorizontal:le(n,t,"h"),mceEditImage:se(n,t)},function(t,e){n.addCommand(e,t)})},pe=function(n,r,o){n.on("NodeChange",function(t){var e=o.get();e&&e.src!==t.element.src&&(de(r),n.editorUpload.uploadImagesAuto(),o.set(null)),fe(n,t.element)&&o.set(t.element)})},ge=function(t){t.addButton("rotateleft",{title:"Rotate counterclockwise",cmd:"mceImageRotateLeft"}),t.addButton("rotateright",{title:"Rotate clockwise",cmd:"mceImageRotateRight"}),t.addButton("flipv",{title:"Flip vertically",cmd:"mceImageFlipVertical"}),t.addButton("fliph",{title:"Flip horizontally",cmd:"mceImageFlipHorizontal"}),t.addButton("editimage",{title:"Edit image",cmd:"mceEditImage"}),t.addButton("imageoptions",{title:"Image options",icon:"options",cmd:"mceImage"})},me=function(t){t.addContextToolbar(u(fe,t),t.getParam("imagetools_toolbar","rotateleft rotateright | flipv fliph | crop editimage imageoptions"))};t.add("imagetools",function(t){var e=r(0),n=r(null);he(t,e),ge(t),me(t),pe(t,e,n)})}(window);
\ No newline at end of file
+!function(m){"use strict";var r=function(t){var e=t,n=function(){return e};return{get:n,set:function(t){e=t},clone:function(){return r(n())}}},t=tinymce.util.Tools.resolve("tinymce.PluginManager"),G=tinymce.util.Tools.resolve("tinymce.util.Tools"),e=function(){},i=function(t){return function(){return t}};function a(r){for(var o=[],t=1;t<arguments.length;t++)o[t-1]=arguments[t];return function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];var n=o.concat(t);return r.apply(null,n)}}var n,o,c,u,l=i(!1),f=i(!0),s=function(){return d},d=(n=function(t){return t.isNone()},u={fold:function(t,e){return t()},is:l,isSome:l,isNone:f,getOr:c=function(t){return t},getOrThunk:o=function(t){return t()},getOrDie:function(t){throw new Error(t||"error: getOrDie called on none.")},getOrNull:i(null),getOrUndefined:i(undefined),or:c,orThunk:o,map:s,each:e,bind:s,exists:l,forall:f,filter:s,equals:n,equals_:n,toArray:function(){return[]},toString:i("none()")},Object.freeze&&Object.freeze(u),u),h=function(n){var t=i(n),e=function(){return o},r=function(t){return t(n)},o={fold:function(t,e){return e(n)},is:function(t){return n===t},isSome:f,isNone:l,getOr:t,getOrThunk:t,getOrDie:t,getOrNull:t,getOrUndefined:t,or:e,orThunk:e,map:function(t){return h(t(n))},each:function(t){t(n)},bind:r,exists:r,forall:r,filter:function(t){return t(n)?o:d},toArray:function(){return[n]},toString:function(){return"some("+n+")"},equals:function(t){return t.is(n)},equals_:function(t,e){return t.fold(l,function(t){return e(n,t)})}};return o},g={some:h,none:s,from:function(t){return null===t||t===undefined?d:h(t)}};function p(t,e){return w(m.document.createElement("canvas"),t,e)}function v(t){var e=p(t.width,t.height);return y(e).drawImage(t,0,0),e}function y(t){return t.getContext("2d")}function w(t,e,n){return t.width=e,t.height=n,t}function b(t){return t.naturalWidth||t.width}function x(t){return t.naturalHeight||t.height}var k=window.Promise?window.Promise:function(){var i=function(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],f(t,r(o,this),r(c,this))},t=i.immediateFn||"function"==typeof window.setImmediate&&window.setImmediate||function(t){m.setTimeout(t,1)};function r(t,e){return function(){return t.apply(e,arguments)}}var n=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)};function a(r){var o=this;null!==this._state?t(function(){var t=o._state?r.onFulfilled:r.onRejected;if(null!==t){var e;try{e=t(o._value)}catch(n){return void r.reject(n)}r.resolve(e)}else(o._state?r.resolve:r.reject)(o._value)}):this._deferreds.push(r)}function o(t){try{if(t===this)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var e=t.then;if("function"==typeof e)return void f(r(e,t),r(o,this),r(c,this))}this._state=!0,this._value=t,u.call(this)}catch(n){c.call(this,n)}}function c(t){this._state=!1,this._value=t,u.call(this)}function u(){for(var t=0,e=this._deferreds;t<e.length;t++){var n=e[t];a.call(this,n)}this._deferreds=[]}function l(t,e,n,r){this.onFulfilled="function"==typeof t?t:null,this.onRejected="function"==typeof e?e:null,this.resolve=n,this.reject=r}function f(t,e,n){var r=!1;try{t(function(t){r||(r=!0,e(t))},function(t){r||(r=!0,n(t))})}catch(o){if(r)return;r=!0,n(o)}}return i.prototype["catch"]=function(t){return this.then(null,t)},i.prototype.then=function(n,r){var o=this;return new i(function(t,e){a.call(o,new l(n,r,t,e))})},i.all=function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];var u=Array.prototype.slice.call(1===t.length&&n(t[0])?t[0]:t);return new i(function(o,i){if(0===u.length)return o([]);var a=u.length;function c(e,t){try{if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if("function"==typeof n)return void n.call(t,function(t){c(e,t)},i)}u[e]=t,0==--a&&o(u)}catch(r){i(r)}}for(var t=0;t<u.length;t++)c(t,u[t])})},i.resolve=function(e){return e&&"object"==typeof e&&e.constructor===i?e:new i(function(t){t(e)})},i.reject=function(n){return new i(function(t,e){e(n)})},i.race=function(o){return new i(function(t,e){for(var n=0,r=o;n<r.length;n++)r[n].then(t,e)})},i}();function R(t){var r,e=t.src;return 0===e.indexOf("data:")?M(e):(r=e,new k(function(t,n){var e=new m.XMLHttpRequest;e.open("GET",r,!0),e.responseType="blob",e.onload=function(){200===this.status&&t(this.response)},e.onerror=function(){var t,e=this;n(0===this.status?((t=new Error("No access to download image")).code=18,t.name="SecurityError",t):new Error("Error "+e.status+" downloading image"))},e.send()}))}function I(c){return new k(function(t,e){var n=m.URL.createObjectURL(c),r=new m.Image,o=function(){r.removeEventListener("load",i),r.removeEventListener("error",a)};function i(){o(),t(r)}function a(){o(),e("Unable to load data of type "+c.type+": "+n)}r.addEventListener("load",i),r.addEventListener("error",a),r.src=n,r.complete&&i()})}function M(n){return new k(function(t,e){(function(t){var e=t.split(","),n=/data:([^;]+)/.exec(e[0]);if(!n)return g.none();for(var r=n[1],o=e[1],i=m.atob(o),a=i.length,c=Math.ceil(a/1024),u=new Array(c),l=0;l<c;++l){for(var f=1024*l,s=Math.min(f+1024,a),d=new Array(s-f),h=f,p=0;h<s;++p,++h)d[p]=i[h].charCodeAt(0);u[l]=new Uint8Array(d)}return g.some(new m.Blob(u,{type:r}))})(n).fold(function(){e("uri is not base64: "+n)},t)})}function T(t,r,o){return r=r||"image/png",m.HTMLCanvasElement.prototype.toBlob?new k(function(e,n){t.toBlob(function(t){t?e(t):n()},r,o)}):M(t.toDataURL(r,o))}function U(t){return I(t).then(function(t){var e;e=t,m.URL.revokeObjectURL(e.src);var n=p(b(t),x(t));return y(n).drawImage(t,0,0),n})}function C(t,e,n){var r=e.type;function o(r,o){return t.then(function(t){return n=o,e=(e=r)||"image/png",t.toDataURL(e,n);var e,n})}return{getType:i(r),toBlob:function(){return k.resolve(e)},toDataURL:function(){return n},toBase64:function(){return n.split(",")[1]},toAdjustedBlob:function(e,n){return t.then(function(t){return T(t,e,n)})},toAdjustedDataURL:o,toAdjustedBase64:function(t,e){return o(t,e).then(function(t){return t.split(",")[1]})},toCanvas:function(){return t.then(v)}}}function A(e){return(n=e,new k(function(t){var e=new m.FileReader;e.onloadend=function(){t(e.result)},e.readAsDataURL(n)})).then(function(t){return C(U(e),e,t)});var n}function E(e,t){return T(e,t).then(function(t){return C(k.resolve(e),t,e.toDataURL())})}function O(t,e,n){var r="string"==typeof t?parseFloat(t):t;return n<r?r=n:r<e&&(r=e),r}var _=[0,.01,.02,.04,.05,.06,.07,.08,.1,.11,.12,.14,.15,.16,.17,.18,.2,.21,.22,.24,.25,.27,.28,.3,.32,.34,.36,.38,.4,.42,.44,.46,.48,.5,.53,.56,.59,.62,.65,.68,.71,.74,.77,.8,.83,.86,.89,.92,.95,.98,1,1.06,1.12,1.18,1.24,1.3,1.36,1.42,1.48,1.54,1.6,1.66,1.72,1.78,1.84,1.9,1.96,2,2.12,2.25,2.37,2.5,2.62,2.75,2.87,3,3.2,3.4,3.6,3.8,4,4.3,4.7,4.9,5,5.5,6,6.5,6.8,7,7.3,7.5,7.8,8,8.4,8.7,9,9.4,9.6,9.8,10];function j(t,e){for(var n,r=[],o=new Array(25),i=0;i<5;i++){for(var a=0;a<5;a++)r[a]=e[a+5*i];for(a=0;a<5;a++){for(var c=n=0;c<5;c++)n+=t[a+5*c]*r[c];o[a+5*i]=n}}return o}function z(t,n){return n=O(n,0,1),t.map(function(t,e){return e%6==0?t=1-(1-t)*n:t*=n,O(t,0,1)})}function L(a,c){return a.toCanvas().then(function(t){return e=t,n=a.getType(),r=c,o=y(e),i=function(t,e){for(var n,r,o,i,a=t.data,c=e[0],u=e[1],l=e[2],f=e[3],s=e[4],d=e[5],h=e[6],p=e[7],m=e[8],g=e[9],v=e[10],y=e[11],w=e[12],b=e[13],x=e[14],k=e[15],R=e[16],I=e[17],M=e[18],T=e[19],U=0;U<a.length;U+=4)n=a[U],r=a[U+1],o=a[U+2],i=a[U+3],a[U]=n*c+r*u+o*l+i*f+s,a[U+1]=n*d+r*h+o*p+i*m+g,a[U+2]=n*v+r*y+o*w+i*b+x,a[U+3]=n*k+r*R+o*I+i*M+T;return t}(o.getImageData(0,0,e.width,e.height),r),o.putImageData(i,0,0),E(e,n);var e,n,r,o,i})}function B(c,u){return c.toCanvas().then(function(t){return e=t,n=c.getType(),r=u,o=y(e),i=o.getImageData(0,0,e.width,e.height),a=o.getImageData(0,0,e.width,e.height),a=function(t,e,n){function r(t,e,n){return n<t?t=n:t<e&&(t=e),t}for(var o=Math.round(Math.sqrt(n.length)),i=Math.floor(o/2),a=t.data,c=e.data,u=t.width,l=t.height,f=0;f<l;f++)for(var s=0;s<u;s++){for(var d=0,h=0,p=0,m=0;m<o;m++)for(var g=0;g<o;g++){var v=r(s+g-i,0,u-1),y=r(f+m-i,0,l-1),w=4*(y*u+v),b=n[m*o+g];d+=a[w]*b,h+=a[w+1]*b,p+=a[w+2]*b}var x=4*(f*u+s);c[x]=r(d,0,255),c[x+1]=r(h,0,255),c[x+2]=r(p,0,255)}return e}(i,a,r),o.putImageData(a,0,0),E(e,n);var e,n,r,o,i,a})}function S(c){return function(e,n){return e.toCanvas().then(function(t){return function(t,e,n){for(var r=y(t),o=new Array(256),i=0;i<o.length;i++)o[i]=c(i,n);var a=function(t,e){for(var n=t.data,r=0;r<n.length;r+=4)n[r]=e[n[r]],n[r+1]=e[n[r+1]],n[r+2]=e[n[r+2]];return t}(r.getImageData(0,0,t.width,t.height),o);return r.putImageData(a,0,0),E(t,e)}(t,e.getType(),n)})}}function P(n){return function(t,e){return L(t,n([1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1],e))}}function H(e){return function(t){return B(t,e)}}var D,F=(D=[-1,0,0,0,255,0,-1,0,0,255,0,0,-1,0,255,0,0,0,1,0,0,0,0,0,1],function(t){return L(t,D)}),V=P(function(t,e){return j(t,[1,0,0,0,e=O(255*e,-255,255),0,1,0,0,e,0,0,1,0,e,0,0,0,1,0,0,0,0,0,1])}),W=P(function(t,e){e=O(e,-180,180)/180*Math.PI;var n=Math.cos(e),r=Math.sin(e),o=.213,i=.715,a=.072;return j(t,[o+.787*n+r*-o,i+n*-i+r*-i,a+n*-a+.928*r,0,0,o+n*-o+.143*r,i+n*(1-i)+.14*r,a+n*-a+-.283*r,0,0,o+n*-o+-.787*r,i+n*-i+r*i,a+.928*n+r*a,0,0,0,0,0,1,0,0,0,0,0,1])}),N=P(function(t,e){var n=1+(0<(e=O(e,-1,1))?3*e:e);return j(t,[.3086*(1-n)+n,.6094*(1-n),.082*(1-n),0,0,.3086*(1-n),.6094*(1-n)+n,.082*(1-n),0,0,.3086*(1-n),.6094*(1-n),.082*(1-n)+n,0,0,0,0,0,1,0,0,0,0,0,1])}),q=P(function(t,e){var n;return e=O(e,-1,1),j(t,[(n=(e*=100)<0?127+e/100*127:127*(n=0==(n=e%1)?_[e]:_[Math.floor(e)]*(1-n)+_[Math.floor(e)+1]*n)+127)/127,0,0,0,.5*(127-n),0,n/127,0,0,.5*(127-n),0,0,n/127,0,.5*(127-n),0,0,0,1,0,0,0,0,0,1])}),$=P(function(t,e){return j(t,z([.33,.34,.33,0,0,.33,.34,.33,0,0,.33,.34,.33,0,0,0,0,0,1,0,0,0,0,0,1],e=O(e,0,1)))}),X=P(function(t,e){return j(t,z([.393,.769,.189,0,0,.349,.686,.168,0,0,.272,.534,.131,0,0,0,0,0,1,0,0,0,0,0,1],e=O(e,0,1)))}),Y=function(t,e,n,r){return L(t,(o=n,i=r,j([1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1],[O(e,0,2),0,0,0,0,0,o=O(o,0,2),0,0,0,0,0,i=O(i,0,2),0,0,0,0,0,1,0,0,0,0,0,1])));var o,i},K=H([0,-1,0,-1,5,-1,0,-1,0]),J=H([-2,-1,0,-1,1,1,0,1,2]),Z=S(function(t,e){return 255*Math.pow(t/255,1-e)}),Q=S(function(t,e){return 255*(1-Math.exp(-t/255*e))});function tt(t,e,n){var r=b(t),o=x(t),i=e/r,a=n/o,c=!1;(i<.5||2<i)&&(i=i<.5?.5:2,c=!0),(a<.5||2<a)&&(a=a<.5?.5:2,c=!0);var u,l,f,s=(u=t,l=i,f=a,new k(function(t){var e=b(u),n=x(u),r=Math.floor(e*l),o=Math.floor(n*f),i=p(r,o),a=y(i);a.drawImage(u,0,0,e,n,0,0,r,o),t(i)}));return c?s.then(function(t){return tt(t,e,n)}):s}function et(u,l){return u.toCanvas().then(function(t){return e=t,n=u.getType(),r=l,o=p(e.width,e.height),i=y(o),90!==(r=r<(c=a=0)?360+r:r)&&270!==r||w(o,o.height,o.width),90!==r&&180!==r||(a=o.width),270!==r&&180!==r||(c=o.height),i.translate(a,c),i.rotate(r*Math.PI/180),i.drawImage(e,0,0),E(o,n);var e,n,r,o,i,a,c})}function nt(a,c){return a.toCanvas().then(function(t){return e=t,n=a.getType(),r=c,o=p(e.width,e.height),i=y(o),"v"===r?(i.scale(1,-1),i.drawImage(e,0,-o.height)):(i.scale(-1,1),i.drawImage(e,-o.width,0)),E(o,n);var e,n,r,o,i})}function rt(a,c,u,l,f){return a.toCanvas().then(function(t){return e=t,n=a.getType(),r=c,o=u,y(i=p(l,f)).drawImage(e,-r,-o),E(i,n);var e,n,r,o,i})}var ot=function(t){return F(t)},it=function(t){return K(t)},at=function(t){return J(t)},ct=function(t,e){return Z(t,e)},ut=function(t,e){return Q(t,e)},lt=function(t,e,n,r){return Y(t,e,n,r)},ft=function(t,e){return V(t,e)},st=function(t,e){return W(t,e)},dt=function(t,e){return N(t,e)},ht=function(t,e){return q(t,e)},pt=function(t,e){return $(t,e)},mt=function(t,e){return X(t,e)},gt=function(t,e){return nt(t,e)},vt=function(t,e,n,r,o){return rt(t,e,n,r,o)},yt=function(t,e,n){return o=e,i=n,(r=t).toCanvas().then(function(t){return tt(t,o,i).then(function(t){return E(t,r.getType())})});var r,o,i},wt=function(t,e){return et(t,e)},bt=function(t){return A(t)},xt="undefined"!=typeof m.window?m.window:Function("return this;")(),kt=function(t,e){return function(t,e){for(var n=e!==undefined&&null!==e?e:xt,r=0;r<t.length&&n!==undefined&&null!==n;++r)n=n[t[r]];return n}(t.split("."),e)},Rt=function(t,e){var n=kt(t,e);if(n===undefined||null===n)throw new Error(t+" not available on this browser");return n},It=function(){return Rt("URL")},Mt={createObjectURL:function(t){return It().createObjectURL(t)},revokeObjectURL:function(t){It().revokeObjectURL(t)}},Tt=tinymce.util.Tools.resolve("tinymce.util.Delay"),Ut=tinymce.util.Tools.resolve("tinymce.util.Promise"),Ct=tinymce.util.Tools.resolve("tinymce.util.URI"),At=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),Et=tinymce.util.Tools.resolve("tinymce.ui.Factory"),Ot=tinymce.util.Tools.resolve("tinymce.geom.Rect"),_t=function(n){return new Ut(function(t){var e=function(){n.removeEventListener("load",e),t(n)};n.complete?t(n):n.addEventListener("load",e)})},jt=tinymce.util.Tools.resolve("tinymce.dom.DomQuery"),zt=tinymce.util.Tools.resolve("tinymce.util.Observable"),Lt=tinymce.util.Tools.resolve("tinymce.util.VK"),Bt=0,St={create:function(t){return new(Et.get("Control").extend({Defaults:{classes:"imagepanel"},selection:function(t){return arguments.length?(this.state.set("rect",t),this):this.state.get("rect")},imageSize:function(){var t=this.state.get("viewRect");return{w:t.w,h:t.h}},toggleCropRect:function(t){this.state.set("cropEnabled",t)},imageSrc:function(t){var o=this,i=new m.Image;i.src=t,_t(i).then(function(){var t,e,n=o.state.get("viewRect");if((e=o.$el.find("img"))[0])e.replaceWith(i);else{var r=m.document.createElement("div");r.className="mce-imagepanel-bg",o.getEl().appendChild(r),o.getEl().appendChild(i)}t={x:0,y:0,w:i.naturalWidth,h:i.naturalHeight},o.state.set("viewRect",t),o.state.set("rect",Ot.inflate(t,-20,-20)),n&&n.w===t.w&&n.h===t.h||o.zoomFit(),o.repaintImage(),o.fire("load")})},zoom:function(t){return arguments.length?(this.state.set("zoom",t),this):this.state.get("zoom")},postRender:function(){return this.imageSrc(this.settings.imageSrc),this._super()},zoomFit:function(){var t,e,n,r,o,i;t=this.$el.find("img"),e=this.getEl().clientWidth,n=this.getEl().clientHeight,r=t[0].naturalWidth,o=t[0].naturalHeight,1<=(i=Math.min((e-10)/r,(n-10)/o))&&(i=1),this.zoom(i)},repaintImage:function(){var t,e,n,r,o,i,a,c,u,l,f;f=this.getEl(),u=this.zoom(),l=this.state.get("rect"),a=this.$el.find("img"),c=this.$el.find(".mce-imagepanel-bg"),o=f.offsetWidth,i=f.offsetHeight,n=a[0].naturalWidth*u,r=a[0].naturalHeight*u,t=Math.max(0,o/2-n/2),e=Math.max(0,i/2-r/2),a.css({left:t,top:e,width:n,height:r}),c.css({left:t,top:e,width:n,height:r}),this.cropRect&&(this.cropRect.setRect({x:l.x*u+t,y:l.y*u+e,w:l.w*u,h:l.h*u}),this.cropRect.setClampRect({x:t,y:e,w:n,h:r}),this.cropRect.setViewPortRect({x:0,y:0,w:o,h:i}))},bindStates:function(){var r=this;function n(t){r.cropRect=function(l,n,f,r,o){var s,a,t,i,e="mce-",c=e+"crid-"+Bt++;function d(t,e){return{x:e.x-t.x,y:e.y-t.y,w:e.w,h:e.h}}function u(t,e,n,r){var o,i,a,c,u;o=e.x,i=e.y,a=e.w,c=e.h,o+=n*t.deltaX,i+=r*t.deltaY,(a+=n*t.deltaW)<20&&(a=20),(c+=r*t.deltaH)<20&&(c=20),u=l=Ot.clamp({x:o,y:i,w:a,h:c},f,"move"===t.name),u=d(f,u),s.fire("updateRect",{rect:u}),m(u)}function h(e){function t(t,e){e.h<0&&(e.h=0),e.w<0&&(e.w=0),jt("#"+c+"-"+t,r).css({left:e.x,top:e.y,width:e.w,height:e.h})}G.each(a,function(t){jt("#"+c+"-"+t.name,r).css({left:e.w*t.xMul+e.x,top:e.h*t.yMul+e.y})}),t("top",{x:n.x,y:n.y,w:n.w,h:e.y-n.y}),t("right",{x:e.x+e.w,y:e.y,w:n.w-e.x-e.w+n.x,h:e.h}),t("bottom",{x:n.x,y:e.y+e.h,w:n.w,h:n.h-e.y-e.h+n.y}),t("left",{x:n.x,y:e.y,w:e.x-n.x,h:e.h}),t("move",e)}function p(t){h(l=t)}function m(t){var e,n;p((e=f,{x:(n=t).x+e.x,y:n.y+e.y,w:n.w,h:n.h}))}return a=[{name:"move",xMul:0,yMul:0,deltaX:1,deltaY:1,deltaW:0,deltaH:0,label:"Crop Mask"},{name:"nw",xMul:0,yMul:0,deltaX:1,deltaY:1,deltaW:-1,deltaH:-1,label:"Top Left Crop Handle"},{name:"ne",xMul:1,yMul:0,deltaX:0,deltaY:1,deltaW:1,deltaH:-1,label:"Top Right Crop Handle"},{name:"sw",xMul:0,yMul:1,deltaX:1,deltaY:0,deltaW:-1,deltaH:1,label:"Bottom Left Crop Handle"},{name:"se",xMul:1,yMul:1,deltaX:0,deltaY:0,deltaW:1,deltaH:1,label:"Bottom Right Crop Handle"}],i=["top","right","bottom","left"],jt('<div id="'+c+'" class="'+e+'croprect-container" role="grid" aria-dropeffect="execute">').appendTo(r),G.each(i,function(t){jt("#"+c,r).append('<div id="'+c+"-"+t+'"class="'+e+'croprect-block" style="display: none" data-mce-bogus="all">')}),G.each(a,function(t){jt("#"+c,r).append('<div id="'+c+"-"+t.name+'" class="'+e+"croprect-handle "+e+"croprect-handle-"+t.name+'"style="display: none" data-mce-bogus="all" role="gridcell" tabindex="-1" aria-label="'+t.label+'" aria-grabbed="false">')}),t=G.map(a,function(e){var n;return new(Et.get("DragHelper"))(c,{document:r.ownerDocument,handle:c+"-"+e.name,start:function(){n=l},drag:function(t){u(e,n,t.deltaX,t.deltaY)}})}),h(l),jt(r).on("focusin focusout",function(t){jt(t.target).attr("aria-grabbed","focus"===t.type)}),jt(r).on("keydown",function(e){var i;function t(t,e,n,r,o){t.stopPropagation(),t.preventDefault(),u(i,n,r,o)}switch(G.each(a,function(t){if(e.target.id===c+"-"+t.name)return i=t,!1}),e.keyCode){case Lt.LEFT:t(e,0,l,-10,0);break;case Lt.RIGHT:t(e,0,l,10,0);break;case Lt.UP:t(e,0,l,0,-10);break;case Lt.DOWN:t(e,0,l,0,10);break;case Lt.ENTER:case Lt.SPACEBAR:e.preventDefault(),o()}}),s=G.extend({toggleVisibility:function(t){var e;e=G.map(a,function(t){return"#"+c+"-"+t.name}).concat(G.map(i,function(t){return"#"+c+"-"+t})).join(","),t?jt(e,r).show():jt(e,r).hide()},setClampRect:function(t){f=t,h(l)},setRect:p,getInnerRect:function(){return d(f,l)},setInnerRect:m,setViewPortRect:function(t){n=t,h(l)},destroy:function(){G.each(t,function(t){t.destroy()}),t=[]}},zt)}(t,r.state.get("viewRect"),r.state.get("viewRect"),r.getEl(),function(){r.fire("crop")}),r.cropRect.on("updateRect",function(t){var e=t.rect,n=r.zoom();e={x:Math.round(e.x/n),y:Math.round(e.y/n),w:Math.round(e.w/n),h:Math.round(e.h/n)},r.state.set("rect",e)}),r.on("remove",r.cropRect.destroy)}r.state.on("change:cropEnabled",function(t){r.cropRect.toggleVisibility(t.value),r.repaintImage()}),r.state.on("change:zoom",function(){r.repaintImage()}),r.state.on("change:rect",function(t){var e=t.value;r.cropRect||n(e),r.cropRect.setRect(e)})}}))(t)}};function Pt(t){return{blob:t,url:Mt.createObjectURL(t)}}function Ht(t){t&&Mt.revokeObjectURL(t.url)}function Dt(t){G.each(t,Ht)}function Ft(i,a,t,e){var c,n,r,u,o,l,f,s,d,h,p,m,g,v,y,w,b,x,k,R,I,M,T,U,C,A,E,O=function(){var n=[],r=-1;function t(){return 0<r}function e(){return-1!==r&&r<n.length-1}return{data:n,add:function(t){var e;return e=n.splice(++r),n.push(t),{state:t,removed:e}},undo:function(){if(t())return n[--r]},redo:function(){if(e())return n[++r]},canUndo:t,canRedo:e}}(),_=function(t){return i.rtl?t.reverse():t};function j(t){var e,n,r,o;e=c.find("#w")[0],n=c.find("#h")[0],r=parseInt(e.value(),10),o=parseInt(n.value(),10),c.find("#constrain")[0].checked()&&U&&C&&r&&o&&("w"===t.control.settings.name?(o=Math.round(r*A),n.value(o)):(r=Math.round(o*E),e.value(r))),U=r,C=o}function z(t){return Math.round(100*t)+"%"}function L(){c.find("#undo").disabled(!O.canUndo()),c.find("#redo").disabled(!O.canRedo()),c.statusbar.find("#save").disabled(!O.canUndo())}function B(){c.find("#undo").disabled(!0),c.find("#redo").disabled(!0)}function S(t){t&&s.imageSrc(t.url)}function P(e){return function(){var t=G.grep(T,function(t){return t.settings.name!==e});G.each(t,function(t){t.hide()}),e.show(),e.focus()}}function H(t){S(u=Pt(t))}function D(t){S(a=Pt(t)),Dt(O.add(a).removed),L()}function F(){var e=s.selection();bt(a.blob).then(function(t){vt(t,e.x,e.y,e.w,e.h).then($).then(function(t){D(t),W()})})}var V=function(e){var n=[].slice.call(arguments,1);return function(){bt((u||a).blob).then(function(t){e.apply(this,[t].concat(n)).then($).then(H)})}};function W(){S(a),Ht(u),P(n)(),L()}function N(){u?(D(u.blob),W()):function t(e,n){u?n():setTimeout(function(){0<e--?t(e,n):i.windowManager.alert("Error: failed to apply image operation.")},10)}(100,N)}function q(t){return Et.create("Form",{layout:"flex",direction:"row",labelGap:5,border:"0 0 1 0",align:"center",pack:"center",padding:"0 10 0 10",spacing:5,flex:0,minHeight:60,defaults:{classes:"imagetool",type:"button"},items:t})}var $=function(t){return t.toBlob()};function X(t,e){return q(_([{text:"Back",onclick:W},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:N}])).hide().on("show",function(){B(),bt(a.blob).then(function(t){return e(t)}).then($).then(function(t){var e=Pt(t);S(e),Ht(u),u=e})})}function Y(t,n,e,r,o){return q(_([{text:"Back",onclick:W},{type:"spacer",flex:1},{type:"slider",flex:1,ondragend:function(t){var e;e=t.value,bt(a.blob).then(function(t){return n(t,e)}).then($).then(function(t){var e=Pt(t);S(e),Ht(u),u=e})},minValue:i.rtl?o:r,maxValue:i.rtl?r:o,value:e,previewFilter:z},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:N}])).hide().on("show",function(){this.find("slider").value(e),B()})}o=q(_([{text:"Back",onclick:W},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:F}])).hide().on("show hide",function(t){s.toggleCropRect("show"===t.type)}).on("show",B),l=q(_([{text:"Back",onclick:W},{type:"spacer",flex:1},{type:"textbox",name:"w",label:"Width",size:4,onkeyup:j},{type:"textbox",name:"h",label:"Height",size:4,onkeyup:j},{type:"checkbox",name:"constrain",text:"Constrain proportions",checked:!0,onchange:function(t){!0===t.control.value()&&(A=C/U,E=U/C)}},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:"submit"}])).hide().on("submit",function(t){var e=parseInt(c.find("#w").value(),10),n=parseInt(c.find("#h").value(),10);t.preventDefault(),function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r=[].slice.call(arguments,1);return function(){bt(a.blob).then(function(t){e.apply(this,[t].concat(r)).then($).then(D)})}}(yt,e,n)(),W()}).on("show",B),f=q(_([{text:"Back",onclick:W},{type:"spacer",flex:1},{icon:"fliph",tooltip:"Flip horizontally",onclick:V(gt,"h")},{icon:"flipv",tooltip:"Flip vertically",onclick:V(gt,"v")},{icon:"rotateleft",tooltip:"Rotate counterclockwise",onclick:V(wt,-90)},{icon:"rotateright",tooltip:"Rotate clockwise",onclick:V(wt,90)},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:N}])).hide().on("show",B),p=X(0,ot),k=X(0,it),R=X(0,at),m=Y(0,ft,0,-1,1),g=Y(0,st,180,0,360),v=Y(0,dt,0,-1,1),y=Y(0,ht,0,-1,1),w=Y(0,pt,0,0,1),b=Y(0,mt,0,0,1),x=function(t,o){function e(){var e,n,r;e=c.find("#r")[0].value(),n=c.find("#g")[0].value(),r=c.find("#b")[0].value(),bt(a.blob).then(function(t){return o(t,e,n,r)}).then($).then(function(t){var e=Pt(t);S(e),Ht(u),u=e})}var n=i.rtl?2:0,r=i.rtl?0:2;return q(_([{text:"Back",onclick:W},{type:"spacer",flex:1},{type:"slider",label:"R",name:"r",minValue:n,value:1,maxValue:r,ondragend:e,previewFilter:z},{type:"slider",label:"G",name:"g",minValue:n,value:1,maxValue:r,ondragend:e,previewFilter:z},{type:"slider",label:"B",name:"b",minValue:n,value:1,maxValue:r,ondragend:e,previewFilter:z},{type:"spacer",flex:1},{text:"Apply",subtype:"primary",onclick:N}])).hide().on("show",function(){c.find("#r,#g,#b").value(1),B()})}(0,lt),I=Y(0,ct,0,-1,1),M=Y(0,ut,1,0,2),r=q(_([{text:"Back",onclick:W},{type:"spacer",flex:1},{text:"hue",icon:"hue",onclick:P(g)},{text:"saturate",icon:"saturate",onclick:P(v)},{text:"sepia",icon:"sepia",onclick:P(b)},{text:"emboss",icon:"emboss",onclick:P(R)},{text:"exposure",icon:"exposure",onclick:P(M)},{type:"spacer",flex:1}])).hide(),n=q(_([{tooltip:"Crop",icon:"crop",onclick:P(o)},{tooltip:"Resize",icon:"resize2",onclick:P(l)},{tooltip:"Orientation",icon:"orientation",onclick:P(f)},{tooltip:"Brightness",icon:"sun",onclick:P(m)},{tooltip:"Sharpen",icon:"sharpen",onclick:P(k)},{tooltip:"Contrast",icon:"contrast",onclick:P(y)},{tooltip:"Color levels",icon:"drop",onclick:P(x)},{tooltip:"Gamma",icon:"gamma",onclick:P(I)},{tooltip:"Invert",icon:"invert",onclick:P(p)}])),s=St.create({flex:1,imageSrc:a.url}),d=Et.create("Container",{layout:"flex",direction:"column",pack:"start",border:"0 1 0 0",padding:5,spacing:5,items:[{type:"button",icon:"undo",tooltip:"Undo",name:"undo",onclick:function(){S(a=O.undo()),L()}},{type:"button",icon:"redo",tooltip:"Redo",name:"redo",onclick:function(){S(a=O.redo()),L()}},{type:"button",icon:"zoomin",tooltip:"Zoom in",onclick:function(){var t=s.zoom();t<2&&(t+=.1),s.zoom(t)}},{type:"button",icon:"zoomout",tooltip:"Zoom out",onclick:function(){var t=s.zoom();.1<t&&(t-=.1),s.zoom(t)}}]}),h=Et.create("Container",{type:"container",layout:"flex",direction:"row",align:"stretch",flex:1,items:_([d,s])}),T=[n,o,l,f,r,p,m,g,v,y,w,b,x,k,R,I,M],(c=i.windowManager.open({layout:"flex",direction:"column",align:"stretch",minWidth:Math.min(At.DOM.getViewPort().w,800),minHeight:Math.min(At.DOM.getViewPort().h,650),title:"Edit image",items:T.concat([h]),buttons:_([{text:"Save",name:"save",subtype:"primary",onclick:function(){t(a.blob),c.close()}},{text:"Cancel",onclick:"close"}])})).on("close",function(){e(),Dt(O.data),u=O=null}),O.add(a),L(),s.on("load",function(){U=s.imageSize().w,C=s.imageSize().h,A=C/U,E=U/C,c.find("#w").value(U),c.find("#h").value(C)}),s.on("crop",F)}var Vt,Wt={edit:function(r,t){return new Ut(function(e,n){return t.toBlob().then(function(t){Ft(r,Pt(t),e,n)})})}},Nt={getImageSize:function(t){var e,n;function r(t){return/^[0-9\.]+px$/.test(t)}return e=t.style.width,n=t.style.height,e||n?r(e)&&r(n)?{w:parseInt(e,10),h:parseInt(n,10)}:null:(e=t.width,n=t.height,e&&n?{w:parseInt(e,10),h:parseInt(n,10)}:null)},setImageSize:function(t,e){var n,r;e&&(n=t.style.width,r=t.style.height,(n||r)&&(t.style.width=e.w+"px",t.style.height=e.h+"px",t.removeAttribute("data-mce-style")),n=t.width,r=t.height,(n||r)&&(t.setAttribute("width",e.w),t.setAttribute("height",e.h)))},getNaturalImageSize:function(t){return{w:t.naturalWidth,h:t.naturalHeight}}},qt=(Vt="function",function(t){return function(t){if(null===t)return"null";var e=typeof t;return"object"===e&&(Array.prototype.isPrototypeOf(t)||t.constructor&&"Array"===t.constructor.name)?"array":"object"===e&&(String.prototype.isPrototypeOf(t)||t.constructor&&"String"===t.constructor.name)?"string":e}(t)===Vt}),$t=(Array.prototype.slice,function(t,e){for(var n=0,r=t.length;n<r;n++){var o=t[n];if(e(o,n))return g.some(o)}return g.none()});qt(Array.from)&&Array.from;var Xt=function(t){return null!==t&&t!==undefined},Yt=function(t,e){var n;return n=e.reduce(function(t,e){return Xt(t)?t[e]:undefined},t),Xt(n)?n:null},Gt=function(e){return new Ut(function(n){var t=new(Rt("FileReader"));t.onload=function(t){var e=t.target;n(e.result)},t.readAsText(e)})},Kt=function(e,r,o){return new Ut(function(t){var n;(n=new(Rt("XMLHttpRequest"))).onreadystatechange=function(){4===n.readyState&&t({status:n.status,blob:this.response})},n.open("GET",e,!0),n.withCredentials=o,G.each(r,function(t,e){n.setRequestHeader(e,t)}),n.responseType="blob",n.send()})},Jt=function(t){var e;try{e=JSON.parse(t)}catch(n){}return e},Zt=[{code:404,message:"Could not find Image Proxy"},{code:403,message:"Rejected request"},{code:0,message:"Incorrect Image Proxy URL"}],Qt=[{type:"key_missing",message:"The request did not include an api key."},{type:"key_not_found",message:"The provided api key could not be found."},{type:"domain_not_trusted",message:"The api key is not valid for the request origins."}],te=function(e){return"ImageProxy HTTP error: "+$t(Zt,function(t){return e===t.code}).fold(i("Unknown ImageProxy error"),function(t){return t.message})},ee=function(t){var e=te(t);return Ut.reject(e)},ne=function(e){return $t(Qt,function(t){return t.type===e}).fold(i("Unknown service error"),function(t){return t.message})},re=function(t,e){return Gt(e).then(function(t){var e,n,r=(e=Jt(t),"ImageProxy Service error: "+((n=Yt(e,["error","type"]))?ne(n):"Invalid JSON in service error message"));return Ut.reject(r)})},oe=function(t,e){return 400===(n=t)||403===n||500===n?re(0,e):ee(t);var n},ie=ee,ae=function(t,e){var n,r,o,i={"Content-Type":"application/json;charset=UTF-8","tiny-api-key":e};return Kt((n=t,r=e,o=-1===n.indexOf("?")?"?":"&",/[?&]apiKey=/.test(n)||!r?n:n+o+"apiKey="+encodeURIComponent(r)),i,!1).then(function(t){return t.status<200||300<=t.status?oe(t.status,t.blob):Ut.resolve(t.blob)})},ce=function(t,e,n){return e?ae(t,e):Kt(t,{},n).then(function(t){return t.status<200||300<=t.status?ie(t.status):Ut.resolve(t.blob)})},ue=0,le=function(t,e){t.notificationManager.open({text:e,type:"error"})},fe=function(t){return t.selection.getNode()},se=function(t,e){var n=e.src;return 0===n.indexOf("data:")||0===n.indexOf("blob:")||new Ct(n).host===t.documentBaseURI.host},de=function(t,e){return-1!==G.inArray(t.getParam("imagetools_cors_hosts",[],"string[]"),new Ct(e.src).host)},he=function(t,e){var n,r,o,i,a=e.src;return de(t,e)?ce(e.src,null,(r=t,o=e,-1!==G.inArray(r.getParam("imagetools_credentials_hosts",[],"string[]"),new Ct(o.src).host))):se(t,e)?R(e):(a=t.getParam("imagetools_proxy"),a+=(-1===a.indexOf("?")?"?":"&")+"url="+encodeURIComponent(e.src),n=(i=t).getParam("api_key",i.getParam("imagetools_api_key","","string"),"string"),ce(a,n,!1))},pe=function(t){var e;return(e=t.editorUpload.blobCache.getByUri(fe(t).src))?Ut.resolve(e.blob()):he(t,fe(t))},me=function(t){clearTimeout(t.get())},ge=function(u,l,f,s,d){return l.toBlob().then(function(t){var e,n,r,o,i,a,c;return r=u.editorUpload.blobCache,e=(i=fe(u)).src,u.getParam("images_reuse_filename",!1,"boolean")&&((o=r.getByUri(e))?(e=o.uri(),n=o.name()):(a=u,n=(c=e.match(/\/([^\/\?]+)?\.(?:jpeg|jpg|png|gif)(?:\?|$)/i))?a.dom.encode(c[1]):null)),o=r.create({id:"imagetools"+ue++,blob:t,base64:l.toBase64(),uri:e,name:n}),r.add(o),u.undoManager.transact(function(){u.$(i).on("load",function t(){var e,n,r;u.$(i).off("load",t),u.nodeChanged(),f?u.editorUpload.uploadImagesAuto():(me(s),e=u,n=s,r=Tt.setEditorTimeout(e,function(){e.editorUpload.uploadImagesAuto()},e.getParam("images_upload_timeout",3e4,"number")),n.set(r))}),d&&u.$(i).attr({width:d.w,height:d.h}),u.$(i).attr({src:o.blobUri()}).removeAttr("data-mce-src")}),o})},ve=function(e,n,t,r){return function(){return e._scanForImages().then(a(pe,e)).then(bt).then(t).then(function(t){return ge(e,t,!1,n,r)},function(t){le(e,t)})}},ye=function(n,r,o){return function(){var t=Nt.getImageSize(fe(n)),e=t?{w:t.h,h:t.w}:null;return ve(n,r,function(t){return wt(t,o)},e)()}},we=function(t,e,n){return function(){return ve(t,e,function(t){return gt(t,n)})()}},be=function(e,r){return function(){var o=fe(e),i=Nt.getNaturalImageSize(o),n=function(r){return new Ut(function(n){var t;(t=r,I(t)).then(function(t){var e=Nt.getNaturalImageSize(t);i.w===e.w&&i.h===e.h||Nt.getImageSize(o)&&Nt.setImageSize(o,e),Mt.revokeObjectURL(t.src),n(r)})})};pe(e).then(bt).then(a(function(e,t){return Wt.edit(e,t).then(n).then(bt).then(function(t){return ge(e,t,!0,r)},function(){})},e),function(t){le(e,t)})}},xe=function(t,e){return t.dom.is(e,"img:not([data-mce-object],[data-mce-placeholder])")&&(se(t,e)||de(t,e)||t.settings.imagetools_proxy)},ke=me,Re=function(n,t){G.each({mceImageRotateLeft:ye(n,t,-90),mceImageRotateRight:ye(n,t,90),mceImageFlipVertical:we(n,t,"v"),mceImageFlipHorizontal:we(n,t,"h"),mceEditImage:be(n,t)},function(t,e){n.addCommand(e,t)})},Ie=function(n,r,o){n.on("NodeChange",function(t){var e=o.get();e&&e.src!==t.element.src&&(ke(r),n.editorUpload.uploadImagesAuto(),o.set(null)),xe(n,t.element)&&o.set(t.element)})},Me=function(t){t.addButton("rotateleft",{title:"Rotate counterclockwise",cmd:"mceImageRotateLeft"}),t.addButton("rotateright",{title:"Rotate clockwise",cmd:"mceImageRotateRight"}),t.addButton("flipv",{title:"Flip vertically",cmd:"mceImageFlipVertical"}),t.addButton("fliph",{title:"Flip horizontally",cmd:"mceImageFlipHorizontal"}),t.addButton("editimage",{title:"Edit image",cmd:"mceEditImage"}),t.addButton("imageoptions",{title:"Image options",icon:"options",cmd:"mceImage"})},Te=function(t){t.addContextToolbar(a(xe,t),t.getParam("imagetools_toolbar","rotateleft rotateright | flipv fliph | crop editimage imageoptions"))};t.add("imagetools",function(t){var e=r(0),n=r(null);Re(t,e),Me(t),Te(t),Ie(t,e,n)})}(window);
\ No newline at end of file
index e71752521297b1adfa7c61d514b01f6fffe6a699..d92fc6df35bdfa4d3b06ca85c1f56ccd255c845e 100644 (file)
@@ -1 +1 @@
-!function(u){"use strict";var e,n,t,r,o,i,a,s,c=tinymce.util.Tools.resolve("tinymce.PluginManager"),f=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),d=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),l=tinymce.util.Tools.resolve("tinymce.util.VK"),p=tinymce.util.Tools.resolve("tinymce.dom.BookmarkManager"),v=tinymce.util.Tools.resolve("tinymce.util.Tools"),m=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),g=function(e){return e&&"BR"===e.nodeName},h=function(e){return e&&3===e.nodeType},y=function(e){return e&&/^(OL|UL|DL)$/.test(e.nodeName)},N=function(e){return e&&/^(OL|UL)$/.test(e.nodeName)},S=function(e){return e&&/^(DT|DD)$/.test(e.nodeName)},C=function(e){return e&&/^(LI|DT|DD)$/.test(e.nodeName)},O=function(e){return e&&/^(TH|TD)$/.test(e.nodeName)},b=g,T=function(e,n){return n&&!!e.schema.getTextBlockElements()[n.nodeName]},D=function(e,n){return e&&e.nodeName in n},L=function(e,n){return!!g(n)&&!(!e.isBlock(n.nextSibling)||g(n.previousSibling))},E=function(e,n,t){var r=e.isEmpty(n);return!(t&&0<e.select("span[data-mce-type=bookmark]",n).length)&&r},w=function(e,n){return e.isChildOf(n,e.getRoot())},k=function(e,n){if(h(e))return{container:e,offset:n};var t=f.getNode(e,n);return h(t)?{container:t,offset:n>=e.childNodes.length?t.data.length:0}:t.previousSibling&&h(t.previousSibling)?{container:t.previousSibling,offset:t.previousSibling.data.length}:t.nextSibling&&h(t.nextSibling)?{container:t.nextSibling,offset:0}:{container:e,offset:n}},A=function(e){var n=e.cloneRange(),t=k(e.startContainer,e.startOffset);n.setStart(t.container,t.offset);var r=k(e.endContainer,e.endOffset);return n.setEnd(r.container,r.offset),n},x=m.DOM,R=function(o){var i={},e=function(e){var n,t,r;t=o[e?"startContainer":"endContainer"],r=o[e?"startOffset":"endOffset"],1===t.nodeType&&(n=x.create("span",{"data-mce-type":"bookmark"}),t.hasChildNodes()?(r=Math.min(r,t.childNodes.length-1),e?t.insertBefore(n,t.childNodes[r]):x.insertAfter(n,t.childNodes[r])):t.appendChild(n),t=n,r=0),i[e?"startContainer":"endContainer"]=t,i[e?"startOffset":"endOffset"]=r};return e(!0),o.collapsed||e(),i},I=function(o){function e(e){var n,t,r;n=r=o[e?"startContainer":"endContainer"],t=o[e?"startOffset":"endOffset"],n&&(1===n.nodeType&&(t=function(e){for(var n=e.parentNode.firstChild,t=0;n;){if(n===e)return t;1===n.nodeType&&"bookmark"===n.getAttribute("data-mce-type")||t++,n=n.nextSibling}return-1}(n),n=n.parentNode,x.remove(r),!n.hasChildNodes()&&x.isBlock(n)&&n.appendChild(x.create("br"))),o[e?"startContainer":"endContainer"]=n,o[e?"startOffset":"endOffset"]=t)}e(!0),e();var n=x.createRng();return n.setStart(o.startContainer,o.startOffset),o.endContainer&&n.setEnd(o.endContainer,o.endOffset),A(n)},_=function(e){return function(){return e}},B=function(t){return function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n];return!t.apply(null,e)}},P=_(!1),M=_(!0),U=P,F=M,j=function(){return H},H=(r={fold:function(e,n){return e()},is:U,isSome:U,isNone:F,getOr:t=function(e){return e},getOrThunk:n=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:function(){return null},getOrUndefined:function(){return undefined},or:t,orThunk:n,map:j,ap:j,each:function(){},bind:j,flatten:j,exists:U,forall:F,filter:j,equals:e=function(e){return e.isNone()},equals_:e,toArray:function(){return[]},toString:_("none()")},Object.freeze&&Object.freeze(r),r),$=function(t){var e=function(){return t},n=function(){return o},r=function(e){return e(t)},o={fold:function(e,n){return n(t)},is:function(e){return t===e},isSome:F,isNone:U,getOr:e,getOrThunk:e,getOrDie:e,getOrNull:e,getOrUndefined:e,or:n,orThunk:n,map:function(e){return $(e(t))},ap:function(e){return e.fold(j,function(e){return $(e(t))})},each:function(e){e(t)},bind:r,flatten:e,exists:r,forall:r,filter:function(e){return e(t)?o:H},equals:function(e){return e.is(t)},equals_:function(e,n){return e.fold(U,function(e){return n(t,e)})},toArray:function(){return[t]},toString:function(){return"some("+t+")"}};return o},q={some:$,none:j,from:function(e){return null===e||e===undefined?H:$(e)}},W=function(n){return function(e){return function(e){if(null===e)return"null";var n=typeof e;return"object"===n&&Array.prototype.isPrototypeOf(e)?"array":"object"===n&&String.prototype.isPrototypeOf(e)?"string":n}(e)===n}},z=W("string"),K=W("boolean"),V=W("function"),X=W("number"),Q=function(e,n){for(var t=e.length,r=new Array(t),o=0;o<t;o++){var i=e[o];r[o]=n(i,o,e)}return r},Y=function(e,n){for(var t=0,r=e.length;t<r;t++)n(e[t],t,e)},G=function(e,n){for(var t=[],r=0,o=e.length;r<o;r++){var i=e[r];n(i,r,e)&&t.push(i)}return t},J=function(e,n,t){return Y(e,function(e){t=n(t,e)}),t},Z=function(e,n){for(var t=0,r=e.length;t<r;t++){var o=e[t];if(n(o,t,e))return q.some(o)}return q.none()},ee=Array.prototype.push,ne=function(e,n){return function(e){for(var n=[],t=0,r=e.length;t<r;++t){if(!Array.prototype.isPrototypeOf(e[t]))throw new Error("Arr.flatten item "+t+" was not an array, input: "+e);ee.apply(n,e[t])}return n}(Q(e,n))},te=Array.prototype.slice,re=function(e){return 0===e.length?q.none():q.some(e[0])},oe=function(e){return 0===e.length?q.none():q.some(e[e.length-1])},ie=(V(Array.from)&&Array.from,"undefined"!=typeof u.window?u.window:Function("return this;")()),ue=function(e,n){return function(e,n){for(var t=n!==undefined&&null!==n?n:ie,r=0;r<e.length&&t!==undefined&&null!==t;++r)t=t[e[r]];return t}(e.split("."),n)},ae=function(e,n){var t=ue(e,n);if(t===undefined||null===t)throw e+" not available on this browser";return t},se=function(e){var n,t=ue("ownerDocument.defaultView",e);return(n=t,ae("HTMLElement",n)).prototype.isPrototypeOf(e)},ce=tinymce.util.Tools.resolve("tinymce.dom.DomQuery"),fe=function(e){var n=e.selection.getStart(!0);return e.dom.getParent(n,"OL,UL,DL",le(e,n))},de=function(e){var t,n,r,o=e.selection.getSelectedBlocks();return v.grep((t=e,n=o,r=v.map(n,function(e){var n=t.dom.getParent(e,"li,dd,dt",le(t,e));return n||e}),ce.unique(r)),function(e){return C(e)})},le=function(e,n){var t=e.dom.getParents(n,"TD,TH");return 0<t.length?t[0]:e.getBody()},me=function(e,n){var t=e.dom.getParents(n,"ol,ul",le(e,n));return oe(t)},ge=function(n,e){var t=Q(e,function(e){return me(n,e).getOr(e)});return ce.unique(t)},pe={isList:function(e){var n=fe(e);return se(n)},getParentList:fe,getSelectedSubLists:function(e){var n,t,r,o=fe(e),i=e.selection.getSelectedBlocks();return r=i,(t=o)&&1===r.length&&r[0]===t?(n=o,v.grep(n.querySelectorAll("ol,ul,dl"),function(e){return y(e)})):v.grep(i,function(e){return y(e)&&o!==e})},getSelectedListItems:de,getClosestListRootElm:le,getSelectedDlItems:function(e){return G(de(e),S)},getSelectedListRoots:function(e){var n,t,r,o=(t=me(n=e,n.selection.getStart()),r=G(n.selection.getSelectedBlocks(),N),t.toArray().concat(r));return ge(e,o)}},ve=function(e){if(null===e||e===undefined)throw new Error("Node cannot be null or undefined");return{dom:_(e)}},he={fromHtml:function(e,n){var t=(n||u.document).createElement("div");if(t.innerHTML=e,!t.hasChildNodes()||1<t.childNodes.length)throw u.console.error("HTML does not have a single root node",e),new Error("HTML must have a single root node");return ve(t.childNodes[0])},fromTag:function(e,n){var t=(n||u.document).createElement(e);return ve(t)},fromText:function(e,n){var t=(n||u.document).createTextNode(e);return ve(t)},fromDom:ve,fromPoint:function(e,n,t){var r=e.dom();return q.from(r.elementFromPoint(n,t)).map(ve)}},ye=function(e,n){for(var t=[],r=0;r<e.length;r++){var o=e[r];if(!o.isSome())return q.none();t.push(o.getOrDie())}return q.some(n.apply(null,t))},Ne=Object.keys,Se=function(){return ae("Node")},Ce=function(e,n,t){return 0!=(e.compareDocumentPosition(n)&t)},Oe=function(e,n){return Ce(e,n,Se().DOCUMENT_POSITION_CONTAINED_BY)},be=function(e,n){var t=function(e,n){for(var t=0;t<e.length;t++){var r=e[t];if(r.test(n))return r}return undefined}(e,n);if(!t)return{major:0,minor:0};var r=function(e){return Number(n.replace(t,"$"+e))};return De(r(1),r(2))},Te=function(){return De(0,0)},De=function(e,n){return{major:e,minor:n}},Le={nu:De,detect:function(e,n){var t=String(n).toLowerCase();return 0===e.length?Te():be(e,t)},unknown:Te},Ee="Firefox",we=function(e,n){return function(){return n===e}},ke=function(e){var n=e.current;return{current:n,version:e.version,isEdge:we("Edge",n),isChrome:we("Chrome",n),isIE:we("IE",n),isOpera:we("Opera",n),isFirefox:we(Ee,n),isSafari:we("Safari",n)}},Ae={unknown:function(){return ke({current:undefined,version:Le.unknown()})},nu:ke,edge:_("Edge"),chrome:_("Chrome"),ie:_("IE"),opera:_("Opera"),firefox:_(Ee),safari:_("Safari")},xe="Windows",Re="Android",Ie="Solaris",_e="FreeBSD",Be=function(e,n){return function(){return n===e}},Pe=function(e){var n=e.current;return{current:n,version:e.version,isWindows:Be(xe,n),isiOS:Be("iOS",n),isAndroid:Be(Re,n),isOSX:Be("OSX",n),isLinux:Be("Linux",n),isSolaris:Be(Ie,n),isFreeBSD:Be(_e,n)}},Me={unknown:function(){return Pe({current:undefined,version:Le.unknown()})},nu:Pe,windows:_(xe),ios:_("iOS"),android:_(Re),linux:_("Linux"),osx:_("OSX"),solaris:_(Ie),freebsd:_(_e)},Ue=function(e,n){var t=String(n).toLowerCase();return Z(e,function(e){return e.search(t)})},Fe=function(e,t){return Ue(e,t).map(function(e){var n=Le.detect(e.versionRegexes,t);return{current:e.name,version:n}})},je=function(e,t){return Ue(e,t).map(function(e){var n=Le.detect(e.versionRegexes,t);return{current:e.name,version:n}})},He=function(e,n){return-1!==e.indexOf(n)},$e=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,qe=function(n){return function(e){return He(e,n)}},We=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:function(e){return He(e,"edge/")&&He(e,"chrome")&&He(e,"safari")&&He(e,"applewebkit")}},{name:"Chrome",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,$e],search:function(e){return He(e,"chrome")&&!He(e,"chromeframe")}},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:function(e){return He(e,"msie")||He(e,"trident")}},{name:"Opera",versionRegexes:[$e,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:qe("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:qe("firefox")},{name:"Safari",versionRegexes:[$e,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:function(e){return(He(e,"safari")||He(e,"mobile/"))&&He(e,"applewebkit")}}],ze=[{name:"Windows",search:qe("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:function(e){return He(e,"iphone")||He(e,"ipad")},versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:qe("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"OSX",search:qe("os x"),versionRegexes:[/.*?os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:qe("linux"),versionRegexes:[]},{name:"Solaris",search:qe("sunos"),versionRegexes:[]},{name:"FreeBSD",search:qe("freebsd"),versionRegexes:[]}],Ke={browsers:_(We),oses:_(ze)},Ve=function(e){var n,t,r,o,i,u,a,s,c,f,d,l=Ke.browsers(),m=Ke.oses(),g=Fe(l,e).fold(Ae.unknown,Ae.nu),p=je(m,e).fold(Me.unknown,Me.nu);return{browser:g,os:p,deviceType:(t=g,r=e,o=(n=p).isiOS()&&!0===/ipad/i.test(r),i=n.isiOS()&&!o,u=n.isAndroid()&&3===n.version.major,a=n.isAndroid()&&4===n.version.major,s=o||u||a&&!0===/mobile/i.test(r),c=n.isiOS()||n.isAndroid(),f=c&&!s,d=t.isSafari()&&n.isiOS()&&!1===/safari/i.test(r),{isiPad:_(o),isiPhone:_(i),isTablet:_(s),isPhone:_(f),isTouch:_(c),isAndroid:n.isAndroid,isiOS:n.isiOS,isWebView:_(d)})}},Xe={detect:(o=function(){var e=u.navigator.userAgent;return Ve(e)},a=!1,function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n];return a||(a=!0,i=o.apply(null,e)),i})},Qe=(u.Node.ATTRIBUTE_NODE,u.Node.CDATA_SECTION_NODE,u.Node.COMMENT_NODE,u.Node.DOCUMENT_NODE,u.Node.DOCUMENT_TYPE_NODE,u.Node.DOCUMENT_FRAGMENT_NODE,u.Node.ELEMENT_NODE),Ye=(u.Node.TEXT_NODE,u.Node.PROCESSING_INSTRUCTION_NODE,u.Node.ENTITY_REFERENCE_NODE,u.Node.ENTITY_NODE,u.Node.NOTATION_NODE,Qe),Ge=function(e,n){return e.dom()===n.dom()},Je=Xe.detect().browser.isIE()?function(e,n){return Oe(e.dom(),n.dom())}:function(e,n){var t=e.dom(),r=n.dom();return t!==r&&t.contains(r)},Ze=function(e,n){var t=e.dom();if(t.nodeType!==Ye)return!1;if(t.matches!==undefined)return t.matches(n);if(t.msMatchesSelector!==undefined)return t.msMatchesSelector(n);if(t.webkitMatchesSelector!==undefined)return t.webkitMatchesSelector(n);if(t.mozMatchesSelector!==undefined)return t.mozMatchesSelector(n);throw new Error("Browser lacks native selectors")},en=function(e){var n=e.dom();return q.from(n.parentNode).map(he.fromDom)},nn=function(e){var n=e.dom();return Q(n.childNodes,he.fromDom)},tn=function(e,n){var t=e.dom().childNodes;return q.from(t[n]).map(he.fromDom)},rn=function(e){return tn(e,0)},on=function(e){return tn(e,e.dom().childNodes.length-1)},un=(function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n]}("element","offset"),function(n,t){en(n).each(function(e){e.dom().insertBefore(t.dom(),n.dom())})}),an=function(e,n){e.dom().appendChild(n.dom())},sn=function(n,e){Y(e,function(e){an(n,e)})},cn=function(e){var n=e.dom();null!==n.parentNode&&n.parentNode.removeChild(n)},fn=function(e){return e.dom().nodeName.toLowerCase()},dn=function(e,n){var t=e.dom();!function(e,n){for(var t=Ne(e),r=0,o=t.length;r<o;r++){var i=t[r];n(e[i],i,e)}}(n,function(e,n){!function(e,n,t){if(!(z(t)||K(t)||X(t)))throw u.console.error("Invalid call to Attr.set. Key ",n,":: Value ",t,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(n,t+"")}(t,n,e)})},ln=function(e){return J(e.dom().attributes,function(e,n){return e[n.name]=n.value,e},{})},mn=function(e,n,t){if(!z(t))throw u.console.error("Invalid call to CSS.set. Property ",n,":: Value ",t,":: Element ",e),new Error("CSS value must be a string: "+t);e.style!==undefined&&e.style.setProperty(n,t)},gn=function(e){return n=e,t=!0,he.fromDom(n.dom().cloneNode(t));var n,t},pn=function(e,n){var t,r,o,i,u=(t=e,r=n,o=he.fromTag(r),i=ln(t),dn(o,i),o);un(e,u);var a=nn(e);return sn(u,a),cn(e),u},vn=function(e,n){an(e.item,n.list)},hn=function(f,e,d){var n=e.slice(0,d.depth);return oe(n).each(function(e){var n,t,r,o,i,u,a,s,c=(n=f,t=d.itemAttributes,r=d.content,o=he.fromTag("li",n),dn(o,t),sn(o,r),o);u=c,an((i=e).list,u),i.item=u,s=d,fn((a=e).list)!==s.listType&&(a.list=pn(a.list,s.listType)),dn(a.list,s.listAttributes)}),n},yn=function(e,n,t){var r,o=function(e,n,t){for(var r,o,i,u=[],a=0;a<t;a++)u.push((r=e,o=n.listType,i={list:he.fromTag(o,r),item:he.fromTag("li",r)},an(i.list,i.item),i));return u}(e,t,t.depth-n.length);return function(e){for(var n=1;n<e.length;n++)vn(e[n-1],e[n])}(o),function(e,n){for(var t=0;t<e.length-1;t++)r=e[t].item,o="list-style-type",i="none",u=r.dom(),mn(u,o,i);var r,o,i,u;oe(e).each(function(e){dn(e.list,n.listAttributes),dn(e.item,n.itemAttributes),sn(e.item,n.content)})}(o,t),r=o,ye([oe(n),re(r)],vn),n.concat(o)},Nn=function(e){return Ze(e,"OL,UL")},Sn=function(e){return rn(e).map(Nn).getOr(!1)},Cn=function(e){return 0<e.depth},On=function(e){return e.isSelected},bn=function(e){var n=nn(e),t=on(e).map(Nn).getOr(!1)?n.slice(0,-1):n;return Q(t,gn)},Tn=Object.prototype.hasOwnProperty,Dn=(s=function(e,n){return n},function(){for(var e=new Array(arguments.length),n=0;n<e.length;n++)e[n]=arguments[n];if(0===e.length)throw new Error("Can't merge zero objects");for(var t={},r=0;r<e.length;r++){var o=e[r];for(var i in o)Tn.call(o,i)&&(t[i]=s(t[i],o[i]))}return t}),Ln=function(n){Y(n,function(r,e){(function(e,n){for(var t=e[n].depth,r=n-1;0<=r;r--){if(e[r].depth===t)return q.some(e[r]);if(e[r].depth<t)break}return q.none()})(n,e).each(function(e){var n,t;t=e,(n=r).listType=t.listType,n.listAttributes=Dn({},t.listAttributes)})})},En=function(e){var n=e,t=function(){return n};return{get:t,set:function(e){n=e},clone:function(){return En(t())}}},wn=function(i,u,a,s){return rn(s).filter(Nn).fold(function(){u.each(function(e){Ge(e.start,s)&&a.set(!0)});var n,t,r,e=(n=s,t=i,r=a.get(),en(n).map(function(e){return{depth:t,isSelected:r,content:bn(n),itemAttributes:ln(n),listAttributes:ln(e),listType:fn(e)}}));u.each(function(e){Ge(e.end,s)&&a.set(!1)});var o=on(s).filter(Nn).map(function(e){return kn(i,u,a,e)}).getOr([]);return e.toArray().concat(o)},function(e){return kn(i,u,a,e)})},kn=function(n,t,r,e){return ne(nn(e),function(e){return(Nn(e)?kn:wn)(n+1,t,r,e)})},An=tinymce.util.Tools.resolve("tinymce.Env"),xn=function(e,n){var t,r,o,i,u=e.dom,a=e.schema.getBlockElements(),s=u.createFragment();if(e.settings.forced_root_block&&(o=e.settings.forced_root_block),o&&((r=u.create(o)).tagName===e.settings.forced_root_block&&u.setAttribs(r,e.settings.forced_root_block_attrs),D(n.firstChild,a)||s.appendChild(r)),n)for(;t=n.firstChild;){var c=t.nodeName;i||"SPAN"===c&&"bookmark"===t.getAttribute("data-mce-type")||(i=!0),D(t,a)?(s.appendChild(t),r=null):o?(r||(r=u.create(o),s.appendChild(r)),r.appendChild(t)):s.appendChild(t)}return e.settings.forced_root_block?i||An.ie&&!(10<An.ie)||r.appendChild(u.create("br",{"data-mce-bogus":"1"})):s.appendChild(u.create("br")),s},Rn=function(i,e){return Q(e,function(e){var n,t,r,o=(n=e.content,r=(t||u.document).createDocumentFragment(),Y(n,function(e){r.appendChild(e.dom())}),he.fromDom(r));return he.fromDom(xn(i,o.dom()))})},In=function(e,n){return Ln(n),(t=e.contentDocument,r=n,o=J(r,function(e,n){return n.depth>e.length?yn(t,e,n):hn(t,e,n)},[]),re(o).map(function(e){return e.list})).toArray();var t,r,o},_n=function(e){var n,t,r=Q(pe.getSelectedListItems(e),he.fromDom);return ye([Z(r,B(Sn)),Z((n=r,t=te.call(n,0),t.reverse(),t),B(Sn))],function(e,n){return{start:e,end:n}})},Bn=function(a,e,s){var n,t,r,o=(n=e,t=_n(a),r=En(!1),Q(n,function(e){return{sourceList:e,entries:kn(0,t,r,e)}}));Y(o,function(e){var n,t,r,o,i,u;n=e.entries,t=s,Y(G(n,On),function(e){return function(e,n){switch(e){case"Indent":n.depth++;break;case"Outdent":n.depth--;break;case"Flatten":n.depth=0}}(t,e)}),r=e.sourceList,i=a,u=e.entries,o=ne(function(e,n){if(0===e.length)return[];for(var t=n(e[0]),r=[],o=[],i=0,u=e.length;i<u;i++){var a=e[i],s=n(a);s!==t&&(r.push(o),o=[]),t=s,o.push(a)}return 0!==o.length&&r.push(o),r}(u,Cn),function(e){return re(e).map(Cn).getOr(!1)?In(i,e):Rn(i,e)}),Y(o,function(e){un(r,e)}),cn(e.sourceList)})},Pn=m.DOM,Mn=function(e,n,t){var r,o,i,u,a,s;for(i=Pn.select('span[data-mce-type="bookmark"]',n),a=xn(e,t),(r=Pn.createRng()).setStartAfter(t),r.setEndAfter(n),u=(o=r.extractContents()).firstChild;u;u=u.firstChild)if("LI"===u.nodeName&&e.dom.isEmpty(u)){Pn.remove(u);break}e.dom.isEmpty(o)||Pn.insertAfter(o,n),Pn.insertAfter(a,n),E(e.dom,t.parentNode)&&(s=t.parentNode,v.each(i,function(e){s.parentNode.insertBefore(e,t.parentNode)}),Pn.remove(s)),Pn.remove(t),E(e.dom,n)&&Pn.remove(n)},Un=function(e){Ze(e,"DT")&&pn(e,"DD")},Fn=function(r,e,n){Y(n,"Indent"===e?Un:function(e){return n=r,void(Ze(t=e,"DD")?pn(t,"DT"):Ze(t,"DT")&&en(t).each(function(e){return Mn(n,e.dom(),t.dom())}));var n,t})},jn=function(e,n){var t=Q(pe.getSelectedListRoots(e),he.fromDom),r=Q(pe.getSelectedDlItems(e),he.fromDom),o=!1;if(t.length||r.length){var i=e.selection.getBookmark();Bn(e,t,n),Fn(e,n,r),e.selection.moveToBookmark(i),e.selection.setRng(A(e.selection.getRng())),e.nodeChanged(),o=!0}return o},Hn=function(e){return jn(e,"Indent")},$n=function(e){return jn(e,"Outdent")},qn=function(e){return jn(e,"Flatten")},Wn=function(t,e){v.each(e,function(e,n){t.setAttribute(n,e)})},zn=function(e,n,t){var r,o,i,u,a,s,c;r=e,o=n,u=(i=t)["list-style-type"]?i["list-style-type"]:null,r.setStyle(o,"list-style-type",u),a=e,Wn(s=n,(c=t)["list-attributes"]),v.each(a.select("li",s),function(e){Wn(e,c["list-item-attributes"])})},Kn=function(e,n,t,r){var o,i;for(o=n[t?"startContainer":"endContainer"],i=n[t?"startOffset":"endOffset"],1===o.nodeType&&(o=o.childNodes[Math.min(i,o.childNodes.length-1)]||o),!t&&b(o.nextSibling)&&(o=o.nextSibling);o.parentNode!==r;){if(T(e,o))return o;if(/^(TD|TH)$/.test(o.parentNode.nodeName))return o;o=o.parentNode}return o},Vn=function(f,d,l){void 0===l&&(l={});var e,n=f.selection.getRng(!0),m="LI",t=pe.getClosestListRootElm(f,f.selection.getStart(!0)),g=f.dom;"false"!==g.getContentEditable(f.selection.getNode())&&("DL"===(d=d.toUpperCase())&&(m="DT"),e=R(n),v.each(function(t,e,r){for(var o,i=[],u=t.dom,n=Kn(t,e,!0,r),a=Kn(t,e,!1,r),s=[],c=n;c&&(s.push(c),c!==a);c=c.nextSibling);return v.each(s,function(e){if(T(t,e))return i.push(e),void(o=null);if(u.isBlock(e)||b(e))return b(e)&&u.remove(e),void(o=null);var n=e.nextSibling;p.isBookmarkNode(e)&&(T(t,n)||!n&&e.parentNode===r)?o=null:(o||(o=u.create("p"),e.parentNode.insertBefore(o,e),i.push(o)),o.appendChild(e))}),i}(f,n,t),function(e){var n,t,r,o,i,u,a,s,c;(t=e.previousSibling)&&y(t)&&t.nodeName===d&&(r=t,o=l,i=g.getStyle(r,"list-style-type"),u=o?o["list-style-type"]:"",i===(u=null===u?"":u))?(n=t,e=g.rename(e,m),t.appendChild(e)):(n=g.create(d),e.parentNode.insertBefore(n,e),n.appendChild(e),e=g.rename(e,m)),a=g,s=e,c=["margin","margin-right","margin-bottom","margin-left","margin-top","padding","padding-right","padding-bottom","padding-left","padding-top"],v.each(c,function(e){var n;return a.setStyle(s,((n={})[e]="",n))}),zn(g,n,l),Qn(f.dom,n)}),f.selection.setRng(I(e)))},Xn=function(e,n,t){return s=t,(a=n)&&s&&y(a)&&a.nodeName===s.nodeName&&(i=n,u=t,(o=e).getStyle(i,"list-style-type",!0)===o.getStyle(u,"list-style-type",!0))&&(r=t,n.className===r.className);var r,o,i,u,a,s},Qn=function(e,n){var t,r;if(t=n.nextSibling,Xn(e,n,t)){for(;r=t.firstChild;)n.appendChild(r);e.remove(t)}if(t=n.previousSibling,Xn(e,n,t)){for(;r=t.lastChild;)n.insertBefore(r,n.firstChild);e.remove(t)}},Yn=function(n,e,t,r,o){if(e.nodeName!==r||Gn(o)){var i=R(n.selection.getRng(!0));v.each([e].concat(t),function(e){!function(e,n,t,r){if(n.nodeName!==t){var o=e.rename(n,t);zn(e,o,r)}else zn(e,n,r)}(n.dom,e,r,o)}),n.selection.setRng(I(i))}else qn(n)},Gn=function(e){return"list-style-type"in e},Jn={toggleList:function(e,n,t){var r=pe.getParentList(e),o=pe.getSelectedSubLists(e);t=t||{},r&&0<o.length?Yn(e,r,o,n,t):function(e,n,t,r){if(n!==e.getBody())if(n)if(n.nodeName!==t||Gn(r)){var o=R(e.selection.getRng(!0));zn(e.dom,n,r),Qn(e.dom,e.dom.rename(n,t)),e.selection.setRng(I(o))}else qn(e);else Vn(e,t,r)}(e,r,n,t)},mergeWithAdjacentLists:Qn},Zn=m.DOM,et=function(e,n){var t,r=n.parentNode;"LI"===r.nodeName&&r.firstChild===n&&((t=r.previousSibling)&&"LI"===t.nodeName?(t.appendChild(n),E(e,r)&&Zn.remove(r)):Zn.setStyle(r,"listStyleType","none")),y(r)&&(t=r.previousSibling)&&"LI"===t.nodeName&&t.appendChild(n)},nt=function(n,e){v.each(v.grep(n.select("ol,ul",e)),function(e){et(n,e)})},tt=function(e,n,t,r){var o,i,u=n.startContainer,a=n.startOffset;if(3===u.nodeType&&(t?a<u.data.length:0<a))return u;for(o=e.schema.getNonEmptyElements(),1===u.nodeType&&(u=f.getNode(u,a)),i=new d(u,r),t&&L(e.dom,u)&&i.next();u=i[t?"next":"prev2"]();){if("LI"===u.nodeName&&!u.hasChildNodes())return u;if(o[u.nodeName])return u;if(3===u.nodeType&&0<u.data.length)return u}},rt=function(e,n){var t=n.childNodes;return 1===t.length&&!y(t[0])&&e.isBlock(t[0])},ot=function(e,n,t){var r,o,i,u;if(o=rt(e,t)?t.firstChild:t,rt(i=e,u=n)&&i.remove(u.firstChild,!0),!E(e,n,!0))for(;r=n.firstChild;)o.appendChild(r)},it=function(n,e,t){var r,o,i=e.parentNode;if(w(n,e)&&w(n,t)){y(t.lastChild)&&(o=t.lastChild),i===t.lastChild&&b(i.previousSibling)&&n.remove(i.previousSibling),(r=t.lastChild)&&b(r)&&e.hasChildNodes()&&n.remove(r),E(n,t,!0)&&n.$(t).empty(),ot(n,e,t),o&&t.appendChild(o);var u=Je(he.fromDom(t),he.fromDom(e))?n.getParents(e,y,t):[];n.remove(e),Y(u,function(e){E(n,e)&&e!==n.getRoot()&&n.remove(e)})}},ut=function(e,n,t,r){var o,i,u,a=e.dom;if(a.isEmpty(r))i=t,u=r,(o=e).dom.$(u).empty(),it(o.dom,i,u),o.selection.setCursorLocation(u);else{var s=R(n);it(a,t,r),e.selection.setRng(I(s))}},at=function(e,n){var t,r,o,i=e.dom,u=e.selection,a=u.getStart(),s=pe.getClosestListRootElm(e,a),c=i.getParent(u.getStart(),"LI",s);if(c){if((t=c.parentNode)===e.getBody()&&E(i,t))return!0;if(r=A(u.getRng(!0)),(o=i.getParent(tt(e,r,n,s),"LI",s))&&o!==c)return n?ut(e,r,o,c):function(e,n,t,r){var o=R(n);it(e.dom,t,r);var i=I(o);e.selection.setRng(i)}(e,r,c,o),!0;if(!o&&!n)return qn(e),!0}return!1},st=function(e,n){return at(e,n)||function(o,i){var u=o.dom,e=o.selection.getStart(),a=pe.getClosestListRootElm(o,e),s=u.getParent(e,u.isBlock,a);if(s&&u.isEmpty(s)){var n=A(o.selection.getRng(!0)),c=u.getParent(tt(o,n,i,a),"LI",a);if(c)return o.undoManager.transact(function(){var e,n,t,r;n=s,t=a,r=(e=u).getParent(n.parentNode,e.isBlock,t),e.remove(n),r&&e.isEmpty(r)&&e.remove(r),Jn.mergeWithAdjacentLists(u,c.parentNode),o.selection.select(c,!0),o.selection.collapse(i)}),!0}return!1}(e,n)},ct=function(e,n){return e.selection.isCollapsed()?st(e,n):(r=(t=e).selection.getStart(),o=pe.getClosestListRootElm(t,r),!!(t.dom.getParent(r,"LI,DT,DD",o)||0<pe.getSelectedListItems(t).length)&&(t.undoManager.transact(function(){t.execCommand("Delete"),nt(t.dom,t.getBody())}),!0));var t,r,o},ft=function(n){n.on("keydown",function(e){e.keyCode===l.BACKSPACE?ct(n,!1)&&e.preventDefault():e.keyCode===l.DELETE&&ct(n,!0)&&e.preventDefault()})},dt=ct,lt=function(n){return{backspaceDelete:function(e){dt(n,e)}}},mt=function(n,t){return function(){var e=n.dom.getParent(n.selection.getStart(),"UL,OL,DL");return e&&e.nodeName===t}},gt=function(t){t.on("BeforeExecCommand",function(e){var n=e.command.toLowerCase();"indent"===n?Hn(t):"outdent"===n&&$n(t)}),t.addCommand("InsertUnorderedList",function(e,n){Jn.toggleList(t,"UL",n)}),t.addCommand("InsertOrderedList",function(e,n){Jn.toggleList(t,"OL",n)}),t.addCommand("InsertDefinitionList",function(e,n){Jn.toggleList(t,"DL",n)}),t.addCommand("RemoveList",function(){qn(t)}),t.addQueryStateHandler("InsertUnorderedList",mt(t,"UL")),t.addQueryStateHandler("InsertOrderedList",mt(t,"OL")),t.addQueryStateHandler("InsertDefinitionList",mt(t,"DL"))},pt=function(e){return e.getParam("lists_indent_on_tab",!0)},vt=function(e){var n;pt(e)&&(n=e).on("keydown",function(e){e.keyCode!==l.TAB||l.metaKeyPressed(e)||n.undoManager.transact(function(){(e.shiftKey?$n(n):Hn(n))&&e.preventDefault()})}),ft(e)},ht=function(n,i){return function(e){var o=e.control;n.on("NodeChange",function(e){var n=function(e,n){for(var t=0;t<e.length;t++)if(n(e[t]))return t;return-1}(e.parents,O),t=-1!==n?e.parents.slice(0,n):e.parents,r=v.grep(t,y);o.active(0<r.length&&r[0].nodeName===i)})}},yt=function(e){var n,t,r;t="advlist",r=(n=e).settings.plugins?n.settings.plugins:"",-1===v.inArray(r.split(/[ ,]/),t)&&(e.addButton("numlist",{active:!1,title:"Numbered list",cmd:"InsertOrderedList",onPostRender:ht(e,"OL")}),e.addButton("bullist",{active:!1,title:"Bullet list",cmd:"InsertUnorderedList",onPostRender:ht(e,"UL")})),e.addButton("indent",{icon:"indent",title:"Increase indent",cmd:"Indent"})};c.add("lists",function(e){return vt(e),yt(e),gt(e),lt(e)})}(window);
\ No newline at end of file
+!function(u){"use strict";var e,n,t,r,o,i,s,a,c,f=tinymce.util.Tools.resolve("tinymce.PluginManager"),d=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),l=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),m=tinymce.util.Tools.resolve("tinymce.util.VK"),p=tinymce.util.Tools.resolve("tinymce.dom.BookmarkManager"),v=tinymce.util.Tools.resolve("tinymce.util.Tools"),g=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),h=function(e){return e&&"BR"===e.nodeName},y=function(e){return e&&3===e.nodeType},N=function(e){return e&&/^(OL|UL|DL)$/.test(e.nodeName)},S=function(e){return e&&/^(OL|UL)$/.test(e.nodeName)},C=function(e){return e&&/^(DT|DD)$/.test(e.nodeName)},O=function(e){return e&&/^(LI|DT|DD)$/.test(e.nodeName)},b=function(e){return e&&/^(TH|TD)$/.test(e.nodeName)},T=h,E=function(e,n){return n&&!!e.schema.getTextBlockElements()[n.nodeName]},L=function(e,n){return e&&e.nodeName in n},D=function(e,n){return!!h(n)&&!(!e.isBlock(n.nextSibling)||h(n.previousSibling))},w=function(e,n,t){var r=e.isEmpty(n);return!(t&&0<e.select("span[data-mce-type=bookmark]",n).length)&&r},k=function(e,n){return e.isChildOf(n,e.getRoot())},A=function(e,n){if(y(e))return{container:e,offset:n};var t=d.getNode(e,n);return y(t)?{container:t,offset:n>=e.childNodes.length?t.data.length:0}:t.previousSibling&&y(t.previousSibling)?{container:t.previousSibling,offset:t.previousSibling.data.length}:t.nextSibling&&y(t.nextSibling)?{container:t.nextSibling,offset:0}:{container:e,offset:n}},x=function(e){var n=e.cloneRange(),t=A(e.startContainer,e.startOffset);n.setStart(t.container,t.offset);var r=A(e.endContainer,e.endOffset);return n.setEnd(r.container,r.offset),n},R=g.DOM,I=function(o){var i={},e=function(e){var n,t,r;t=o[e?"startContainer":"endContainer"],r=o[e?"startOffset":"endOffset"],1===t.nodeType&&(n=R.create("span",{"data-mce-type":"bookmark"}),t.hasChildNodes()?(r=Math.min(r,t.childNodes.length-1),e?t.insertBefore(n,t.childNodes[r]):R.insertAfter(n,t.childNodes[r])):t.appendChild(n),t=n,r=0),i[e?"startContainer":"endContainer"]=t,i[e?"startOffset":"endOffset"]=r};return e(!0),o.collapsed||e(),i},_=function(o){function e(e){var n,t,r;n=r=o[e?"startContainer":"endContainer"],t=o[e?"startOffset":"endOffset"],n&&(1===n.nodeType&&(t=function(e){for(var n=e.parentNode.firstChild,t=0;n;){if(n===e)return t;1===n.nodeType&&"bookmark"===n.getAttribute("data-mce-type")||t++,n=n.nextSibling}return-1}(n),n=n.parentNode,R.remove(r),!n.hasChildNodes()&&R.isBlock(n)&&n.appendChild(R.create("br"))),o[e?"startContainer":"endContainer"]=n,o[e?"startOffset":"endOffset"]=t)}e(!0),e();var n=R.createRng();return n.setStart(o.startContainer,o.startOffset),o.endContainer&&n.setEnd(o.endContainer,o.endOffset),x(n)},B=function(){},P=function(e){return function(){return e}},M=function(t){return function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n];return!t.apply(null,e)}},U=P(!1),F=P(!0),j=function(){return H},H=(e=function(e){return e.isNone()},r={fold:function(e,n){return e()},is:U,isSome:U,isNone:F,getOr:t=function(e){return e},getOrThunk:n=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:P(null),getOrUndefined:P(undefined),or:t,orThunk:n,map:j,each:B,bind:j,exists:U,forall:F,filter:j,equals:e,equals_:e,toArray:function(){return[]},toString:P("none()")},Object.freeze&&Object.freeze(r),r),$=function(t){var e=P(t),n=function(){return o},r=function(e){return e(t)},o={fold:function(e,n){return n(t)},is:function(e){return t===e},isSome:F,isNone:U,getOr:e,getOrThunk:e,getOrDie:e,getOrNull:e,getOrUndefined:e,or:n,orThunk:n,map:function(e){return $(e(t))},each:function(e){e(t)},bind:r,exists:r,forall:r,filter:function(e){return e(t)?o:H},toArray:function(){return[t]},toString:function(){return"some("+t+")"},equals:function(e){return e.is(t)},equals_:function(e,n){return e.fold(U,function(e){return n(t,e)})}};return o},q={some:$,none:j,from:function(e){return null===e||e===undefined?H:$(e)}},W=function(n){return function(e){return function(e){if(null===e)return"null";var n=typeof e;return"object"===n&&(Array.prototype.isPrototypeOf(e)||e.constructor&&"Array"===e.constructor.name)?"array":"object"===n&&(String.prototype.isPrototypeOf(e)||e.constructor&&"String"===e.constructor.name)?"string":n}(e)===n}},V=W("string"),z=W("array"),K=W("boolean"),X=W("function"),Q=W("number"),Y=Array.prototype.slice,G=Array.prototype.push,J=function(e,n){for(var t=e.length,r=new Array(t),o=0;o<t;o++){var i=e[o];r[o]=n(i,o)}return r},Z=function(e,n){for(var t=0,r=e.length;t<r;t++)n(e[t],t)},ee=function(e,n){for(var t=[],r=0,o=e.length;r<o;r++){var i=e[r];n(i,r)&&t.push(i)}return t},ne=function(e,n,t){return Z(e,function(e){t=n(t,e)}),t},te=function(e,n){for(var t=0,r=e.length;t<r;t++){var o=e[t];if(n(o,t))return q.some(o)}return q.none()},re=function(e,n){return function(e){for(var n=[],t=0,r=e.length;t<r;++t){if(!z(e[t]))throw new Error("Arr.flatten item "+t+" was not an array, input: "+e);G.apply(n,e[t])}return n}(J(e,n))},oe=function(e){return 0===e.length?q.none():q.some(e[0])},ie=function(e){return 0===e.length?q.none():q.some(e[e.length-1])},ue=(X(Array.from)&&Array.from,"undefined"!=typeof u.window?u.window:Function("return this;")()),se=function(e,n){return function(e,n){for(var t=n!==undefined&&null!==n?n:ue,r=0;r<e.length&&t!==undefined&&null!==t;++r)t=t[e[r]];return t}(e.split("."),n)},ae=function(e,n){var t=se(e,n);if(t===undefined||null===t)throw new Error(e+" not available on this browser");return t},ce=function(e){var n,t=se("ownerDocument.defaultView",e);return(n=t,ae("HTMLElement",n)).prototype.isPrototypeOf(e)},fe=tinymce.util.Tools.resolve("tinymce.dom.DomQuery"),de=function(e){var n=e.selection.getStart(!0);return e.dom.getParent(n,"OL,UL,DL",me(e,n))},le=function(e){var t,n,r,o=e.selection.getSelectedBlocks();return v.grep((t=e,n=o,r=v.map(n,function(e){var n=t.dom.getParent(e,"li,dd,dt",me(t,e));return n||e}),fe.unique(r)),function(e){return O(e)})},me=function(e,n){var t=e.dom.getParents(n,"TD,TH");return 0<t.length?t[0]:e.getBody()},ge=function(e,n){var t=e.dom.getParents(n,"ol,ul",me(e,n));return ie(t)},pe=function(n,e){var t=J(e,function(e){return ge(n,e).getOr(e)});return fe.unique(t)},ve={isList:function(e){var n=de(e);return ce(n)},getParentList:de,getSelectedSubLists:function(e){var n,t,r,o=de(e),i=e.selection.getSelectedBlocks();return r=i,(t=o)&&1===r.length&&r[0]===t?(n=o,v.grep(n.querySelectorAll("ol,ul,dl"),function(e){return N(e)})):v.grep(i,function(e){return N(e)&&o!==e})},getSelectedListItems:le,getClosestListRootElm:me,getSelectedDlItems:function(e){return ee(le(e),C)},getSelectedListRoots:function(e){var n,t,r,o=(t=ge(n=e,n.selection.getStart()),r=ee(n.selection.getSelectedBlocks(),S),t.toArray().concat(r));return pe(e,o)}},he=function(e){if(null===e||e===undefined)throw new Error("Node cannot be null or undefined");return{dom:P(e)}},ye={fromHtml:function(e,n){var t=(n||u.document).createElement("div");if(t.innerHTML=e,!t.hasChildNodes()||1<t.childNodes.length)throw u.console.error("HTML does not have a single root node",e),new Error("HTML must have a single root node");return he(t.childNodes[0])},fromTag:function(e,n){var t=(n||u.document).createElement(e);return he(t)},fromText:function(e,n){var t=(n||u.document).createTextNode(e);return he(t)},fromDom:he,fromPoint:function(e,n,t){var r=e.dom();return q.from(r.elementFromPoint(n,t)).map(he)}},Ne=function(e,n,t){return e.isSome()&&n.isSome()?q.some(t(e.getOrDie(),n.getOrDie())):q.none()},Se=Object.keys,Ce=function(){return ae("Node")},Oe=function(e,n,t){return 0!=(e.compareDocumentPosition(n)&t)},be=function(e,n){return Oe(e,n,Ce().DOCUMENT_POSITION_CONTAINED_BY)},Te=function(e,n){var t=function(e,n){for(var t=0;t<e.length;t++){var r=e[t];if(r.test(n))return r}return undefined}(e,n);if(!t)return{major:0,minor:0};var r=function(e){return Number(n.replace(t,"$"+e))};return Le(r(1),r(2))},Ee=function(){return Le(0,0)},Le=function(e,n){return{major:e,minor:n}},De={nu:Le,detect:function(e,n){var t=String(n).toLowerCase();return 0===e.length?Ee():Te(e,t)},unknown:Ee},we="Firefox",ke=function(e,n){return function(){return n===e}},Ae=function(e){var n=e.current;return{current:n,version:e.version,isEdge:ke("Edge",n),isChrome:ke("Chrome",n),isIE:ke("IE",n),isOpera:ke("Opera",n),isFirefox:ke(we,n),isSafari:ke("Safari",n)}},xe={unknown:function(){return Ae({current:undefined,version:De.unknown()})},nu:Ae,edge:P("Edge"),chrome:P("Chrome"),ie:P("IE"),opera:P("Opera"),firefox:P(we),safari:P("Safari")},Re="Windows",Ie="Android",_e="Solaris",Be="FreeBSD",Pe=function(e,n){return function(){return n===e}},Me=function(e){var n=e.current;return{current:n,version:e.version,isWindows:Pe(Re,n),isiOS:Pe("iOS",n),isAndroid:Pe(Ie,n),isOSX:Pe("OSX",n),isLinux:Pe("Linux",n),isSolaris:Pe(_e,n),isFreeBSD:Pe(Be,n)}},Ue={unknown:function(){return Me({current:undefined,version:De.unknown()})},nu:Me,windows:P(Re),ios:P("iOS"),android:P(Ie),linux:P("Linux"),osx:P("OSX"),solaris:P(_e),freebsd:P(Be)},Fe=function(e,n){var t=String(n).toLowerCase();return te(e,function(e){return e.search(t)})},je=function(e,t){return Fe(e,t).map(function(e){var n=De.detect(e.versionRegexes,t);return{current:e.name,version:n}})},He=function(e,t){return Fe(e,t).map(function(e){var n=De.detect(e.versionRegexes,t);return{current:e.name,version:n}})},$e=function(e,n){return-1!==e.indexOf(n)},qe=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,We=function(n){return function(e){return $e(e,n)}},Ve=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:function(e){return $e(e,"edge/")&&$e(e,"chrome")&&$e(e,"safari")&&$e(e,"applewebkit")}},{name:"Chrome",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,qe],search:function(e){return $e(e,"chrome")&&!$e(e,"chromeframe")}},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:function(e){return $e(e,"msie")||$e(e,"trident")}},{name:"Opera",versionRegexes:[qe,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:We("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:We("firefox")},{name:"Safari",versionRegexes:[qe,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:function(e){return($e(e,"safari")||$e(e,"mobile/"))&&$e(e,"applewebkit")}}],ze=[{name:"Windows",search:We("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:function(e){return $e(e,"iphone")||$e(e,"ipad")},versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:We("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"OSX",search:We("os x"),versionRegexes:[/.*?os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:We("linux"),versionRegexes:[]},{name:"Solaris",search:We("sunos"),versionRegexes:[]},{name:"FreeBSD",search:We("freebsd"),versionRegexes:[]}],Ke={browsers:P(Ve),oses:P(ze)},Xe=function(e){var n,t,r,o,i,u,s,a,c,f,d,l=Ke.browsers(),m=Ke.oses(),g=je(l,e).fold(xe.unknown,xe.nu),p=He(m,e).fold(Ue.unknown,Ue.nu);return{browser:g,os:p,deviceType:(t=g,r=e,o=(n=p).isiOS()&&!0===/ipad/i.test(r),i=n.isiOS()&&!o,u=n.isAndroid()&&3===n.version.major,s=n.isAndroid()&&4===n.version.major,a=o||u||s&&!0===/mobile/i.test(r),c=n.isiOS()||n.isAndroid(),f=c&&!a,d=t.isSafari()&&n.isiOS()&&!1===/safari/i.test(r),{isiPad:P(o),isiPhone:P(i),isTablet:P(a),isPhone:P(f),isTouch:P(c),isAndroid:n.isAndroid,isiOS:n.isiOS,isWebView:P(d)})}},Qe={detect:(o=function(){var e=u.navigator.userAgent;return Xe(e)},s=!1,function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n];return s||(s=!0,i=o.apply(null,e)),i})},Ye=(u.Node.ATTRIBUTE_NODE,u.Node.CDATA_SECTION_NODE,u.Node.COMMENT_NODE,u.Node.DOCUMENT_NODE,u.Node.DOCUMENT_TYPE_NODE,u.Node.DOCUMENT_FRAGMENT_NODE,u.Node.ELEMENT_NODE),Ge=(u.Node.TEXT_NODE,u.Node.PROCESSING_INSTRUCTION_NODE,u.Node.ENTITY_REFERENCE_NODE,u.Node.ENTITY_NODE,u.Node.NOTATION_NODE,Ye),Je=function(e,n){return e.dom()===n.dom()},Ze=Qe.detect().browser.isIE()?function(e,n){return be(e.dom(),n.dom())}:function(e,n){var t=e.dom(),r=n.dom();return t!==r&&t.contains(r)},en=function(e,n){var t=e.dom();if(t.nodeType!==Ge)return!1;var r=t;if(r.matches!==undefined)return r.matches(n);if(r.msMatchesSelector!==undefined)return r.msMatchesSelector(n);if(r.webkitMatchesSelector!==undefined)return r.webkitMatchesSelector(n);if(r.mozMatchesSelector!==undefined)return r.mozMatchesSelector(n);throw new Error("Browser lacks native selectors")},nn=function(e){return q.from(e.dom().parentNode).map(ye.fromDom)},tn=function(e){return J(e.dom().childNodes,ye.fromDom)},rn=function(e,n){var t=e.dom().childNodes;return q.from(t[n]).map(ye.fromDom)},on=function(e){return rn(e,0)},un=function(e){return rn(e,e.dom().childNodes.length-1)},sn=(function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n]}("element","offset"),function(n,t){nn(n).each(function(e){e.dom().insertBefore(t.dom(),n.dom())})}),an=function(e,n){e.dom().appendChild(n.dom())},cn=function(n,e){Z(e,function(e){an(n,e)})},fn=function(e){var n=e.dom();null!==n.parentNode&&n.parentNode.removeChild(n)},dn=function(e){return e.dom().nodeName.toLowerCase()},ln=(a=Ye,function(e){return e.dom().nodeType===a}),mn=function(e,n){var t=e.dom();!function(e,n){for(var t=Se(e),r=0,o=t.length;r<o;r++){var i=t[r];n(e[i],i)}}(n,function(e,n){!function(e,n,t){if(!(V(t)||K(t)||Q(t)))throw u.console.error("Invalid call to Attr.set. Key ",n,":: Value ",t,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(n,t+"")}(t,n,e)})},gn=function(e){return ne(e.dom().attributes,function(e,n){return e[n.name]=n.value,e},{})},pn=function(e,n,t){if(!V(t))throw u.console.error("Invalid call to CSS.set. Property ",n,":: Value ",t,":: Element ",e),new Error("CSS value must be a string: "+t);var r;(r=e).style!==undefined&&X(r.style.getPropertyValue)&&e.style.setProperty(n,t)},vn=function(e){return n=e,t=!0,ye.fromDom(n.dom().cloneNode(t));var n,t},hn=function(e,n){var t,r,o,i,u=(t=e,r=n,o=ye.fromTag(r),i=gn(t),mn(o,i),o);sn(e,u);var s=tn(e);return cn(u,s),fn(e),u},yn=function(e,n){an(e.item,n.list)},Nn=function(f,e,d){var n=e.slice(0,d.depth);return ie(n).each(function(e){var n,t,r,o,i,u,s,a,c=(n=f,t=d.itemAttributes,r=d.content,o=ye.fromTag("li",n),mn(o,t),cn(o,r),o);u=c,an((i=e).list,u),i.item=u,a=d,dn((s=e).list)!==a.listType&&(s.list=hn(s.list,a.listType)),mn(s.list,a.listAttributes)}),n},Sn=function(e,n,t){var r,o=function(e,n,t){for(var r,o,i,u=[],s=0;s<t;s++)u.push((r=e,o=n.listType,i={list:ye.fromTag(o,r),item:ye.fromTag("li",r)},an(i.list,i.item),i));return u}(e,t,t.depth-n.length);return function(e){for(var n=1;n<e.length;n++)yn(e[n-1],e[n])}(o),function(e,n){for(var t=0;t<e.length-1;t++)r=e[t].item,o="list-style-type",i="none",u=r.dom(),pn(u,o,i);var r,o,i,u;ie(e).each(function(e){mn(e.list,n.listAttributes),mn(e.item,n.itemAttributes),cn(e.item,n.content)})}(o,t),r=o,Ne(ie(n),oe(r),yn),n.concat(o)},Cn=function(e){return en(e,"OL,UL")},On=function(e){return on(e).map(Cn).getOr(!1)},bn=function(e){return 0<e.depth},Tn=function(e){return e.isSelected},En=function(e){var n=tn(e),t=un(e).map(Cn).getOr(!1)?n.slice(0,-1):n;return J(t,vn)},Ln=Object.prototype.hasOwnProperty,Dn=(c=function(e,n){return n},function(){for(var e=new Array(arguments.length),n=0;n<e.length;n++)e[n]=arguments[n];if(0===e.length)throw new Error("Can't merge zero objects");for(var t={},r=0;r<e.length;r++){var o=e[r];for(var i in o)Ln.call(o,i)&&(t[i]=c(t[i],o[i]))}return t}),wn=function(n){Z(n,function(r,e){(function(e,n){for(var t=e[n].depth,r=n-1;0<=r;r--){if(e[r].depth===t)return q.some(e[r]);if(e[r].depth<t)break}return q.none()})(n,e).each(function(e){var n,t;t=e,(n=r).listType=t.listType,n.listAttributes=Dn({},t.listAttributes)})})},kn=function(e){var n=e,t=function(){return n};return{get:t,set:function(e){n=e},clone:function(){return kn(t())}}},An=function(i,u,s,a){return on(a).filter(Cn).fold(function(){u.each(function(e){Je(e.start,a)&&s.set(!0)});var n,t,r,e=(n=a,t=i,r=s.get(),nn(n).filter(ln).map(function(e){return{depth:t,isSelected:r,content:En(n),itemAttributes:gn(n),listAttributes:gn(e),listType:dn(e)}}));u.each(function(e){Je(e.end,a)&&s.set(!1)});var o=un(a).filter(Cn).map(function(e){return xn(i,u,s,e)}).getOr([]);return e.toArray().concat(o)},function(e){return xn(i,u,s,e)})},xn=function(n,t,r,e){return re(tn(e),function(e){return(Cn(e)?xn:An)(n+1,t,r,e)})},Rn=tinymce.util.Tools.resolve("tinymce.Env"),In=function(e,n){var t,r,o,i,u=e.dom,s=e.schema.getBlockElements(),a=u.createFragment();if(e.settings.forced_root_block&&(o=e.settings.forced_root_block),o&&((r=u.create(o)).tagName===e.settings.forced_root_block&&u.setAttribs(r,e.settings.forced_root_block_attrs),L(n.firstChild,s)||a.appendChild(r)),n)for(;t=n.firstChild;){var c=t.nodeName;i||"SPAN"===c&&"bookmark"===t.getAttribute("data-mce-type")||(i=!0),L(t,s)?(a.appendChild(t),r=null):o?(r||(r=u.create(o),a.appendChild(r)),r.appendChild(t)):a.appendChild(t)}return e.settings.forced_root_block?i||Rn.ie&&!(10<Rn.ie)||r.appendChild(u.create("br",{"data-mce-bogus":"1"})):a.appendChild(u.create("br")),a},_n=function(i,e){return J(e,function(e){var n,t,r,o=(n=e.content,r=(t||u.document).createDocumentFragment(),Z(n,function(e){r.appendChild(e.dom())}),ye.fromDom(r));return ye.fromDom(In(i,o.dom()))})},Bn=function(e,n){return wn(n),(t=e.contentDocument,r=n,o=ne(r,function(e,n){return n.depth>e.length?Sn(t,e,n):Nn(t,e,n)},[]),oe(o).map(function(e){return e.list})).toArray();var t,r,o},Pn=function(e){var n,t,r=J(ve.getSelectedListItems(e),ye.fromDom);return Ne(te(r,M(On)),te((n=r,(t=Y.call(n,0)).reverse(),t),M(On)),function(e,n){return{start:e,end:n}})},Mn=function(s,e,a){var n,t,r,o=(n=e,t=Pn(s),r=kn(!1),J(n,function(e){return{sourceList:e,entries:xn(0,t,r,e)}}));Z(o,function(e){var n,t,r,o,i,u;n=e.entries,t=a,Z(ee(n,Tn),function(e){return function(e,n){switch(e){case"Indent":n.depth++;break;case"Outdent":n.depth--;break;case"Flatten":n.depth=0}}(t,e)}),r=e.sourceList,i=s,u=e.entries,o=re(function(e,n){if(0===e.length)return[];for(var t=n(e[0]),r=[],o=[],i=0,u=e.length;i<u;i++){var s=e[i],a=n(s);a!==t&&(r.push(o),o=[]),t=a,o.push(s)}return 0!==o.length&&r.push(o),r}(u,bn),function(e){return oe(e).map(bn).getOr(!1)?Bn(i,e):_n(i,e)}),Z(o,function(e){sn(r,e)}),fn(e.sourceList)})},Un=g.DOM,Fn=function(e,n,t){var r,o,i,u,s,a;for(i=Un.select('span[data-mce-type="bookmark"]',n),s=In(e,t),(r=Un.createRng()).setStartAfter(t),r.setEndAfter(n),u=(o=r.extractContents()).firstChild;u;u=u.firstChild)if("LI"===u.nodeName&&e.dom.isEmpty(u)){Un.remove(u);break}e.dom.isEmpty(o)||Un.insertAfter(o,n),Un.insertAfter(s,n),w(e.dom,t.parentNode)&&(a=t.parentNode,v.each(i,function(e){a.parentNode.insertBefore(e,t.parentNode)}),Un.remove(a)),Un.remove(t),w(e.dom,n)&&Un.remove(n)},jn=function(e){en(e,"dt")&&hn(e,"dd")},Hn=function(r,e,n){Z(n,"Indent"===e?jn:function(e){return n=r,void(en(t=e,"dd")?hn(t,"dt"):en(t,"dt")&&nn(t).each(function(e){return Fn(n,e.dom(),t.dom())}));var n,t})},$n=function(e,n){var t=J(ve.getSelectedListRoots(e),ye.fromDom),r=J(ve.getSelectedDlItems(e),ye.fromDom),o=!1;if(t.length||r.length){var i=e.selection.getBookmark();Mn(e,t,n),Hn(e,n,r),e.selection.moveToBookmark(i),e.selection.setRng(x(e.selection.getRng())),e.nodeChanged(),o=!0}return o},qn=function(e){return $n(e,"Indent")},Wn=function(e){return $n(e,"Outdent")},Vn=function(e){return $n(e,"Flatten")},zn=function(t,e){v.each(e,function(e,n){t.setAttribute(n,e)})},Kn=function(e,n,t){var r,o,i,u,s,a,c;r=e,o=n,u=(i=t)["list-style-type"]?i["list-style-type"]:null,r.setStyle(o,"list-style-type",u),s=e,zn(a=n,(c=t)["list-attributes"]),v.each(s.select("li",a),function(e){zn(e,c["list-item-attributes"])})},Xn=function(e,n,t,r){var o,i;for(o=n[t?"startContainer":"endContainer"],i=n[t?"startOffset":"endOffset"],1===o.nodeType&&(o=o.childNodes[Math.min(i,o.childNodes.length-1)]||o),!t&&T(o.nextSibling)&&(o=o.nextSibling);o.parentNode!==r;){if(E(e,o))return o;if(/^(TD|TH)$/.test(o.parentNode.nodeName))return o;o=o.parentNode}return o},Qn=function(f,d,l){void 0===l&&(l={});var e,n=f.selection.getRng(!0),m="LI",t=ve.getClosestListRootElm(f,f.selection.getStart(!0)),g=f.dom;"false"!==g.getContentEditable(f.selection.getNode())&&("DL"===(d=d.toUpperCase())&&(m="DT"),e=I(n),v.each(function(t,e,r){for(var o,i=[],u=t.dom,n=Xn(t,e,!0,r),s=Xn(t,e,!1,r),a=[],c=n;c&&(a.push(c),c!==s);c=c.nextSibling);return v.each(a,function(e){if(E(t,e))return i.push(e),void(o=null);if(u.isBlock(e)||T(e))return T(e)&&u.remove(e),void(o=null);var n=e.nextSibling;p.isBookmarkNode(e)&&(E(t,n)||!n&&e.parentNode===r)?o=null:(o||(o=u.create("p"),e.parentNode.insertBefore(o,e),i.push(o)),o.appendChild(e))}),i}(f,n,t),function(e){var n,t,r,o,i,u,s,a,c;(t=e.previousSibling)&&N(t)&&t.nodeName===d&&(r=t,o=l,i=g.getStyle(r,"list-style-type"),u=o?o["list-style-type"]:"",i===(u=null===u?"":u))?(n=t,e=g.rename(e,m),t.appendChild(e)):(n=g.create(d),e.parentNode.insertBefore(n,e),n.appendChild(e),e=g.rename(e,m)),s=g,a=e,c=["margin","margin-right","margin-bottom","margin-left","margin-top","padding","padding-right","padding-bottom","padding-left","padding-top"],v.each(c,function(e){var n;return s.setStyle(a,((n={})[e]="",n))}),Kn(g,n,l),Gn(f.dom,n)}),f.selection.setRng(_(e)))},Yn=function(e,n,t){return a=t,(s=n)&&a&&N(s)&&s.nodeName===a.nodeName&&(i=n,u=t,(o=e).getStyle(i,"list-style-type",!0)===o.getStyle(u,"list-style-type",!0))&&(r=t,n.className===r.className);var r,o,i,u,s,a},Gn=function(e,n){var t,r;if(t=n.nextSibling,Yn(e,n,t)){for(;r=t.firstChild;)n.appendChild(r);e.remove(t)}if(t=n.previousSibling,Yn(e,n,t)){for(;r=t.lastChild;)n.insertBefore(r,n.firstChild);e.remove(t)}},Jn=function(n,e,t,r,o){if(e.nodeName!==r||Zn(o)){var i=I(n.selection.getRng(!0));v.each([e].concat(t),function(e){!function(e,n,t,r){if(n.nodeName!==t){var o=e.rename(n,t);Kn(e,o,r)}else Kn(e,n,r)}(n.dom,e,r,o)}),n.selection.setRng(_(i))}else Vn(n)},Zn=function(e){return"list-style-type"in e},et={toggleList:function(e,n,t){var r=ve.getParentList(e),o=ve.getSelectedSubLists(e);t=t||{},r&&0<o.length?Jn(e,r,o,n,t):function(e,n,t,r){if(n!==e.getBody())if(n)if(n.nodeName!==t||Zn(r)){var o=I(e.selection.getRng(!0));Kn(e.dom,n,r),Gn(e.dom,e.dom.rename(n,t)),e.selection.setRng(_(o))}else Vn(e);else Qn(e,t,r)}(e,r,n,t)},mergeWithAdjacentLists:Gn},nt=g.DOM,tt=function(e,n){var t,r=n.parentNode;"LI"===r.nodeName&&r.firstChild===n&&((t=r.previousSibling)&&"LI"===t.nodeName?(t.appendChild(n),w(e,r)&&nt.remove(r)):nt.setStyle(r,"listStyleType","none")),N(r)&&(t=r.previousSibling)&&"LI"===t.nodeName&&t.appendChild(n)},rt=function(n,e){v.each(v.grep(n.select("ol,ul",e)),function(e){tt(n,e)})},ot=function(e,n,t,r){var o,i,u=n.startContainer,s=n.startOffset;if(3===u.nodeType&&(t?s<u.data.length:0<s))return u;for(o=e.schema.getNonEmptyElements(),1===u.nodeType&&(u=d.getNode(u,s)),i=new l(u,r),t&&D(e.dom,u)&&i.next();u=i[t?"next":"prev2"]();){if("LI"===u.nodeName&&!u.hasChildNodes())return u;if(o[u.nodeName])return u;if(3===u.nodeType&&0<u.data.length)return u}},it=function(e,n){var t=n.childNodes;return 1===t.length&&!N(t[0])&&e.isBlock(t[0])},ut=function(e,n,t){var r,o,i,u;if(o=it(e,t)?t.firstChild:t,it(i=e,u=n)&&i.remove(u.firstChild,!0),!w(e,n,!0))for(;r=n.firstChild;)o.appendChild(r)},st=function(n,e,t){var r,o,i=e.parentNode;if(k(n,e)&&k(n,t)){N(t.lastChild)&&(o=t.lastChild),i===t.lastChild&&T(i.previousSibling)&&n.remove(i.previousSibling),(r=t.lastChild)&&T(r)&&e.hasChildNodes()&&n.remove(r),w(n,t,!0)&&n.$(t).empty(),ut(n,e,t),o&&t.appendChild(o);var u=Ze(ye.fromDom(t),ye.fromDom(e))?n.getParents(e,N,t):[];n.remove(e),Z(u,function(e){w(n,e)&&e!==n.getRoot()&&n.remove(e)})}},at=function(e,n,t,r){var o,i,u,s=e.dom;if(s.isEmpty(r))i=t,u=r,(o=e).dom.$(u).empty(),st(o.dom,i,u),o.selection.setCursorLocation(u);else{var a=I(n);st(s,t,r),e.selection.setRng(_(a))}},ct=function(e,n){var t,r,o,i=e.dom,u=e.selection,s=u.getStart(),a=ve.getClosestListRootElm(e,s),c=i.getParent(u.getStart(),"LI",a);if(c){if((t=c.parentNode)===e.getBody()&&w(i,t))return!0;if(r=x(u.getRng(!0)),(o=i.getParent(ot(e,r,n,a),"LI",a))&&o!==c)return n?at(e,r,o,c):function(e,n,t,r){var o=I(n);st(e.dom,t,r);var i=_(o);e.selection.setRng(i)}(e,r,c,o),!0;if(!o&&!n)return Vn(e),!0}return!1},ft=function(e,n){return ct(e,n)||function(o,i){var u=o.dom,e=o.selection.getStart(),s=ve.getClosestListRootElm(o,e),a=u.getParent(e,u.isBlock,s);if(a&&u.isEmpty(a)){var n=x(o.selection.getRng(!0)),c=u.getParent(ot(o,n,i,s),"LI",s);if(c)return o.undoManager.transact(function(){var e,n,t,r;n=a,t=s,r=(e=u).getParent(n.parentNode,e.isBlock,t),e.remove(n),r&&e.isEmpty(r)&&e.remove(r),et.mergeWithAdjacentLists(u,c.parentNode),o.selection.select(c,!0),o.selection.collapse(i)}),!0}return!1}(e,n)},dt=function(e,n){return e.selection.isCollapsed()?ft(e,n):(r=(t=e).selection.getStart(),o=ve.getClosestListRootElm(t,r),!!(t.dom.getParent(r,"LI,DT,DD",o)||0<ve.getSelectedListItems(t).length)&&(t.undoManager.transact(function(){t.execCommand("Delete"),rt(t.dom,t.getBody())}),!0));var t,r,o},lt=function(n){n.on("keydown",function(e){e.keyCode===m.BACKSPACE?dt(n,!1)&&e.preventDefault():e.keyCode===m.DELETE&&dt(n,!0)&&e.preventDefault()})},mt=dt,gt=function(n){return{backspaceDelete:function(e){mt(n,e)}}},pt=function(n,t){return function(){var e=n.dom.getParent(n.selection.getStart(),"UL,OL,DL");return e&&e.nodeName===t}},vt=function(t){t.on("BeforeExecCommand",function(e){var n=e.command.toLowerCase();"indent"===n?qn(t):"outdent"===n&&Wn(t)}),t.addCommand("InsertUnorderedList",function(e,n){et.toggleList(t,"UL",n)}),t.addCommand("InsertOrderedList",function(e,n){et.toggleList(t,"OL",n)}),t.addCommand("InsertDefinitionList",function(e,n){et.toggleList(t,"DL",n)}),t.addCommand("RemoveList",function(){Vn(t)}),t.addQueryStateHandler("InsertUnorderedList",pt(t,"UL")),t.addQueryStateHandler("InsertOrderedList",pt(t,"OL")),t.addQueryStateHandler("InsertDefinitionList",pt(t,"DL"))},ht=function(e){return e.getParam("lists_indent_on_tab",!0)},yt=function(e){var n;ht(e)&&(n=e).on("keydown",function(e){e.keyCode!==m.TAB||m.metaKeyPressed(e)||n.undoManager.transact(function(){(e.shiftKey?Wn(n):qn(n))&&e.preventDefault()})}),lt(e)},Nt=function(n,i){return function(e){var o=e.control;n.on("NodeChange",function(e){var n=function(e,n){for(var t=0;t<e.length;t++)if(n(e[t]))return t;return-1}(e.parents,b),t=-1!==n?e.parents.slice(0,n):e.parents,r=v.grep(t,N);o.active(0<r.length&&r[0].nodeName===i)})}},St=function(e){var n,t,r;t="advlist",r=(n=e).settings.plugins?n.settings.plugins:"",-1===v.inArray(r.split(/[ ,]/),t)&&(e.addButton("numlist",{active:!1,title:"Numbered list",cmd:"InsertOrderedList",onPostRender:Nt(e,"OL")}),e.addButton("bullist",{active:!1,title:"Bullet list",cmd:"InsertUnorderedList",onPostRender:Nt(e,"UL")})),e.addButton("indent",{icon:"indent",title:"Increase indent",cmd:"Indent"})};f.add("lists",function(e){return yt(e),St(e),vt(e),gt(e)})}(window);
\ No newline at end of file
index a1f5d533f36e5211110e52d0b899c8985994d72c..db2393e70f0bf93e12e50b6e674b27376d5d5a6a 100644 (file)
@@ -1 +1 @@
-!function(v){"use strict";var l=function(t){var e=t,n=function(){return e};return{get:n,set:function(t){e=t},clone:function(){return l(n())}}},e=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=function(t){return!(!/(^|[ ,])powerpaste([, ]|$)/.test(t.settings.plugins)||!e.get("powerpaste")||("undefined"!=typeof v.window.console&&v.window.console.log&&v.window.console.log("PowerPaste is incompatible with Paste plugin! Remove 'paste' from the 'plugins' option."),0))},s=function(t,e){return{clipboard:t,quirks:e}},f=function(t,e,n,r){return t.fire("PastePreProcess",{content:e,internal:n,wordContent:r})},d=function(t,e,n,r){return t.fire("PastePostProcess",{node:e,internal:n,wordContent:r})},u=function(t,e){return t.fire("PastePlainTextToggle",{state:e})},n=function(t,e){return t.fire("paste",{ieFake:e})},m={shouldPlainTextInform:function(t){return t.getParam("paste_plaintext_inform",!0)},shouldBlockDrop:function(t){return t.getParam("paste_block_drop",!1)},shouldPasteDataImages:function(t){return t.getParam("paste_data_images",!1)},shouldFilterDrop:function(t){return t.getParam("paste_filter_drop",!0)},getPreProcess:function(t){return t.getParam("paste_preprocess")},getPostProcess:function(t){return t.getParam("paste_postprocess")},getWebkitStyles:function(t){return t.getParam("paste_webkit_styles")},shouldRemoveWebKitStyles:function(t){return t.getParam("paste_remove_styles_if_webkit",!0)},shouldMergeFormats:function(t){return t.getParam("paste_merge_formats",!0)},isSmartPasteEnabled:function(t){return t.getParam("smart_paste",!0)},isPasteAsTextEnabled:function(t){return t.getParam("paste_as_text",!1)},getRetainStyleProps:function(t){return t.getParam("paste_retain_style_properties")},getWordValidElements:function(t){return t.getParam("paste_word_valid_elements","-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody")},shouldConvertWordFakeLists:function(t){return t.getParam("paste_convert_word_fake_lists",!0)},shouldUseDefaultFilters:function(t){return t.getParam("paste_enable_default_filters",!0)}},r=function(t,e,n){var r,o,i;"text"===e.pasteFormat.get()?(e.pasteFormat.set("html"),u(t,!1)):(e.pasteFormat.set("text"),u(t,!0),i=t,!1===n.get()&&m.shouldPlainTextInform(i)&&(o="Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.",(r=t).notificationManager.open({text:r.translate(o),type:"info"}),n.set(!0))),t.focus()},c=function(t,n,e){t.addCommand("mceTogglePlainTextPaste",function(){r(t,n,e)}),t.addCommand("mceInsertClipboardContent",function(t,e){e.content&&n.pasteHtml(e.content,e.internal),e.text&&n.pasteText(e.text)})},h=tinymce.util.Tools.resolve("tinymce.Env"),y=tinymce.util.Tools.resolve("tinymce.util.Delay"),b=tinymce.util.Tools.resolve("tinymce.util.Tools"),o=tinymce.util.Tools.resolve("tinymce.util.VK"),t="x-tinymce/html",i="\x3c!-- "+t+" --\x3e",g=function(t){return i+t},p=function(t){return t.replace(i,"")},x=function(t){return-1!==t.indexOf(i)},P=function(){return t},w=tinymce.util.Tools.resolve("tinymce.html.Entities"),_=function(t){return t.replace(/\r?\n/g,"<br>")},D=function(t,e,n){var r=t.split(/\n\n/),o=function(t,e){var n,r=[],o="<"+t;if("object"==typeof e){for(n in e)e.hasOwnProperty(n)&&r.push(n+'="'+w.encodeAllRaw(e[n])+'"');r.length&&(o+=" "+r.join(" "))}return o+">"}(e,n),i="</"+e+">",a=b.map(r,function(t){return t.split(/\n/).join("<br />")});return 1===a.length?a[0]:b.map(a,function(t){return o+t+i}).join("")},T=function(t){return!/<(?:\/?(?!(?:div|p|br|span)>)\w+|(?:(?!(?:span style="white-space:\s?pre;?">)|br\s?\/>))\w+\s[^>]+)>/i.test(t)},C=function(t,e,n){return e?D(t,e,n):_(t)},k=tinymce.util.Tools.resolve("tinymce.html.DomParser"),S=tinymce.util.Tools.resolve("tinymce.html.Node"),O=tinymce.util.Tools.resolve("tinymce.html.Schema"),R=tinymce.util.Tools.resolve("tinymce.html.Serializer");function F(e,t){return b.each(t,function(t){e=t.constructor===RegExp?e.replace(t,""):e.replace(t[0],t[1])}),e}var E={filter:F,innerText:function(e){var n=O(),r=k({},n),o="",i=n.getShortEndedElements(),a=b.makeMap("script noscript style textarea video audio iframe object"," "),s=n.getBlockElements();return e=F(e,[/<!\[[^\]]+\]>/g]),function t(e){var n=e.name,r=e;if("br"!==n){if("wbr"!==n)if(i[n]&&(o+=" "),a[n])o+=" ";else{if(3===e.type&&(o+=e.value),!e.shortEnded&&(e=e.firstChild))for(;t(e),e=e.next;);s[n]&&r.next&&(o+="\n","p"===n&&(o+="\n"))}}else o+="\n"}(r.parse(e)),o},trimHtml:function(t){return t=F(t,[/^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/gi,/<!--StartFragment-->|<!--EndFragment-->/g,[/( ?)<span class="Apple-converted-space">\u00a0<\/span>( ?)/g,function(t,e,n){return e||n?"\xa0":" "}],/<br class="Apple-interchange-newline">/g,/<br>$/i])},createIdGenerator:function(t){var e=0;return function(){return t+e++}},isMsEdge:function(){return-1!==v.navigator.userAgent.indexOf(" Edge/")}};function A(e){var n,t;return t=[/^[IVXLMCD]{1,2}\.[ \u00a0]/,/^[ivxlmcd]{1,2}\.[ \u00a0]/,/^[a-z]{1,2}[\.\)][ \u00a0]/,/^[A-Z]{1,2}[\.\)][ \u00a0]/,/^[0-9]+\.[ \u00a0]/,/^[\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d]+\.[ \u00a0]/,/^[\u58f1\u5f10\u53c2\u56db\u4f0d\u516d\u4e03\u516b\u4e5d\u62fe]+\.[ \u00a0]/],e=e.replace(/^[\u00a0 ]+/,""),b.each(t,function(t){if(t.test(e))return!(n=!0)}),n}function I(t){var i,a,s=1;function n(t){var e="";if(3===t.type)return t.value;if(t=t.firstChild)for(;e+=n(t),t=t.next;);return e}function u(t,e){if(3===t.type&&e.test(t.value))return t.value=t.value.replace(e,""),!1;if(t=t.firstChild)do{if(!u(t,e))return!1}while(t=t.next);return!0}function e(e,n,r){var o=e._listLevel||s;o!==s&&(o<s?i&&(i=i.parent.parent):(a=i,i=null)),i&&i.name===n?i.append(e):(a=a||i,i=new S(n,1),1<r&&i.attr("start",""+r),e.wrap(i)),e.name="li",s<o&&a&&a.lastChild.append(i),s=o,function t(e){if(e._listIgnore)e.remove();else if(e=e.firstChild)for(;t(e),e=e.next;);}(e),u(e,/^\u00a0+/),u(e,/^\s*([\u2022\u00b7\u00a7\u25CF]|\w+\.)/),u(e,/^\u00a0+/)}for(var r=[],o=t.firstChild;null!=o;)if(r.push(o),null!==(o=o.walk()))for(;void 0!==o&&o.parent!==t;)o=o.walk();for(var l=0;l<r.length;l++)if("p"===(t=r[l]).name&&t.firstChild){var c=n(t);if(/^[\s\u00a0]*[\u2022\u00b7\u00a7\u25CF]\s*/.test(c)){e(t,"ul");continue}if(A(c)){var f=/([0-9]+)\./.exec(c),d=1;f&&(d=parseInt(f[1],10)),e(t,"ol",d);continue}if(t._listLevel){e(t,"ul",1);continue}i=null}else a=i,i=null}function M(n,r,o,i){var a,s={},t=n.dom.parseStyle(i);return b.each(t,function(t,e){switch(e){case"mso-list":(a=/\w+ \w+([0-9]+)/i.exec(i))&&(o._listLevel=parseInt(a[1],10)),/Ignore/i.test(t)&&o.firstChild&&(o._listIgnore=!0,o.firstChild._listIgnore=!0);break;case"horiz-align":e="text-align";break;case"vert-align":e="vertical-align";break;case"font-color":case"mso-foreground":e="color";break;case"mso-background":case"mso-highlight":e="background";break;case"font-weight":case"font-style":return void("normal"!==t&&(s[e]=t));case"mso-element":if(/^(comment|comment-list)$/i.test(t))return void o.remove()}0!==e.indexOf("mso-comment")?0!==e.indexOf("mso-")&&("all"===m.getRetainStyleProps(n)||r&&r[e])&&(s[e]=t):o.remove()}),/(bold)/i.test(s["font-weight"])&&(delete s["font-weight"],o.wrap(new S("b",1))),/(italic)/i.test(s["font-style"])&&(delete s["font-style"],o.wrap(new S("i",1))),(s=n.dom.serializeStyle(s,o.name))||null}var B,H,j,L,N,$={preProcess:function(t,e){return m.shouldUseDefaultFilters(t)?function(r,t){var e,o;(e=m.getRetainStyleProps(r))&&(o=b.makeMap(e.split(/[, ]/))),t=E.filter(t,[/<br class="?Apple-interchange-newline"?>/gi,/<b[^>]+id="?docs-internal-[^>]*>/gi,/<!--[\s\S]+?-->/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/&nbsp;/gi,"\xa0"],[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,function(t,e){return 0<e.length?e.replace(/./," ").slice(Math.floor(e.length/2)).split("").join("\xa0"):""}]]);var n=m.getWordValidElements(r),i=O({valid_elements:n,valid_children:"-li[p]"});b.each(i.elements,function(t){t.attributes["class"]||(t.attributes["class"]={},t.attributesOrder.push("class")),t.attributes.style||(t.attributes.style={},t.attributesOrder.push("style"))});var a=k({},i);a.addAttributeFilter("style",function(t){for(var e,n=t.length;n--;)(e=t[n]).attr("style",M(r,o,e,e.attr("style"))),"span"===e.name&&e.parent&&!e.attributes.length&&e.unwrap()}),a.addAttributeFilter("class",function(t){for(var e,n,r=t.length;r--;)n=(e=t[r]).attr("class"),/^(MsoCommentReference|MsoCommentText|msoDel)$/i.test(n)&&e.remove(),e.attr("class",null)}),a.addNodeFilter("del",function(t){for(var e=t.length;e--;)t[e].remove()}),a.addNodeFilter("a",function(t){for(var e,n,r,o=t.length;o--;)if(n=(e=t[o]).attr("href"),r=e.attr("name"),n&&-1!==n.indexOf("#_msocom_"))e.remove();else if(n&&0===n.indexOf("file://")&&(n=n.split("#")[1])&&(n="#"+n),n||r){if(r&&!/^_?(?:toc|edn|ftn)/i.test(r)){e.unwrap();continue}e.attr({href:n,name:r})}else e.unwrap()});var s=a.parse(t);return m.shouldConvertWordFakeLists(r)&&I(s),t=R({validate:r.settings.validate},i).serialize(s)}(t,e):e},isWordContent:function(t){return/<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i.test(t)||/class="OutlineElement/.test(t)||/id="?docs\-internal\-guid\-/.test(t)}},W=function(t,e){return{content:t,cancelled:e}},z=function(t,e,n,r){var o,i,a,s,u,l,c=f(t,e,n,r);return t.hasEventListeners("PastePostProcess")&&!c.isDefaultPrevented()?(o=t,i=c.content,a=n,s=r,u=o.dom.create("div",{style:"display:none"},i),l=d(o,u,a,s),W(l.node.innerHTML,l.isDefaultPrevented())):W(c.content,c.isDefaultPrevented())},U=function(t,e,n){var r=$.isWordContent(e),o=r?$.preProcess(t,e):e;return z(t,o,n,r)},V=function(t,e){var n,r;return t.insertContent((n=e,r=t.dom.create("body",{},n),b.each(r.querySelectorAll("meta"),function(t){return t.parentNode.removeChild(t)}),r.innerHTML),{merge:m.shouldMergeFormats(t),paste:!0}),!0},q=function(t){return/^https?:\/\/[\w\?\-\/+=.&%@~#]+$/i.test(t)},K=function(t){return q(t)&&/.(gif|jpe?g|png)$/.test(t)},G=function(t,e,n){return!(!1!==t.selection.isCollapsed()||!q(e)||(o=e,i=n,(r=t).undoManager.extra(function(){i(r,o)},function(){r.execCommand("mceInsertLink",!1,o)}),0));var r,o,i},X=function(t,e,n){return!!K(e)&&(o=e,i=n,(r=t).undoManager.extra(function(){i(r,o)},function(){r.insertContent('<img src="'+o+'">')}),!0);var r,o,i},Y=function(t,e){var n,r;!1===m.isSmartPasteEnabled(t)?V(t,e):(n=t,r=e,b.each([G,X,V],function(t){return!0!==t(n,r,V)}))},Z=function(t){return function(){return t}},J=Z(!1),Q=Z(!0),tt=J,et=Q,nt=function(){return rt},rt=(L={fold:function(t,e){return t()},is:tt,isSome:tt,isNone:et,getOr:j=function(t){return t},getOrThunk:H=function(t){return t()},getOrDie:function(t){throw new Error(t||"error: getOrDie called on none.")},getOrNull:function(){return null},getOrUndefined:function(){return undefined},or:j,orThunk:H,map:nt,ap:nt,each:function(){},bind:nt,flatten:nt,exists:tt,forall:et,filter:nt,equals:B=function(t){return t.isNone()},equals_:B,toArray:function(){return[]},toString:Z("none()")},Object.freeze&&Object.freeze(L),L),ot=function(n){var t=function(){return n},e=function(){return o},r=function(t){return t(n)},o={fold:function(t,e){return e(n)},is:function(t){return n===t},isSome:et,isNone:tt,getOr:t,getOrThunk:t,getOrDie:t,getOrNull:t,getOrUndefined:t,or:e,orThunk:e,map:function(t){return ot(t(n))},ap:function(t){return t.fold(nt,function(t){return ot(t(n))})},each:function(t){t(n)},bind:r,flatten:t,exists:r,forall:r,filter:function(t){return t(n)?o:rt},equals:function(t){return t.is(n)},equals_:function(t,e){return t.fold(tt,function(t){return e(n,t)})},toArray:function(){return[n]},toString:function(){return"some("+n+")"}};return o},it={some:ot,none:nt,from:function(t){return null===t||t===undefined?rt:ot(t)}},at=(N="function",function(t){return function(t){if(null===t)return"null";var e=typeof t;return"object"===e&&Array.prototype.isPrototypeOf(t)?"array":"object"===e&&String.prototype.isPrototypeOf(t)?"string":e}(t)===N}),st=function(t,e){for(var n=t.length,r=new Array(n),o=0;o<n;o++){var i=t[o];r[o]=e(i,o,t)}return r},ut=function(t,e){for(var n=0,r=t.length;n<r;n++)e(t[n],n,t)},lt=Array.prototype.slice,ct=at(Array.from)?Array.from:function(t){return lt.call(t)},ft=function(t){var n=it.none(),e=[],r=function(t){o()?a(t):e.push(t)},o=function(){return n.isSome()},i=function(t){ut(t,a)},a=function(e){n.each(function(t){v.setTimeout(function(){e(t)},0)})};return t(function(t){n=it.some(t),i(e),e=[]}),{get:r,map:function(n){return ft(function(e){r(function(t){e(n(t))})})},isReady:o}},dt={nu:ft,pure:function(e){return ft(function(t){t(e)})}},mt=function(e){var t=function(t){var r;e((r=t,function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];var n=this;v.setTimeout(function(){r.apply(n,t)},0)}))},n=function(){return dt.nu(t)};return{map:function(r){return mt(function(n){t(function(t){var e=r(t);n(e)})})},bind:function(n){return mt(function(e){t(function(t){n(t).get(e)})})},anonBind:function(n){return mt(function(e){t(function(t){n.get(e)})})},toLazy:n,toCached:function(){var e=null;return mt(function(t){null===e&&(e=n()),e.get(t)})},get:t}},gt={nu:mt,pure:function(e){return mt(function(t){t(e)})}},pt=function(a,t){return t(function(r){var o=[],i=0;0===a.length?r([]):ut(a,function(t,e){var n;t.get((n=e,function(t){o[n]=t,++i>=a.length&&r(o)}))})})},vt=function(t,e){var n=st(t,e);return pt(n,gt.nu)},ht=function(t,e,n){var r=n||x(e),o=U(t,p(e),r);!1===o.cancelled&&Y(t,o.content)},yt=function(t,e){e=t.dom.encode(e).replace(/\r\n/g,"\n"),e=C(e,t.settings.forced_root_block,t.settings.forced_root_block_attrs),ht(t,e,!1)},bt=function(t){var e={};if(t){if(t.getData){var n=t.getData("Text");n&&0<n.length&&-1===n.indexOf("data:text/mce-internal,")&&(e["text/plain"]=n)}if(t.types)for(var r=0;r<t.types.length;r++){var o=t.types[r];try{e[o]=t.getData(o)}catch(i){e[o]=""}}}return e},xt=function(t,e){return e in t&&0<t[e].length},Pt=function(t){return xt(t,"text/html")||xt(t,"text/plain")},wt=E.createIdGenerator("mceclip"),_t=function(e,t,n){var r,o,i,a,s="paste"===t.type?t.clipboardData:t.dataTransfer;if(e.settings.paste_data_images&&s){var u=(i=(o=s).items?st(ct(o.items),function(t){return t.getAsFile()}):[],a=o.files?ct(o.files):[],function(t,e){for(var n=[],r=0,o=t.length;r<o;r++){var i=t[r];e(i,r,t)&&n.push(i)}return n}(0<i.length?i:a,function(t){return/^image\/(jpeg|png|gif|bmp)$/.test(t.type)}));if(0<u.length)return t.preventDefault(),(r=u,vt(r,function(r){return gt.nu(function(t){var e=r.getAsFile?r.getAsFile():r,n=new window.FileReader;n.onload=function(){t({blob:e,uri:n.result})},n.readAsDataURL(e)})})).get(function(t){n&&e.selection.setRng(n),ut(t,function(t){!function(t,e){var n,r,o,i,a,s,u,l=(n=e.uri,-1!==(r=n.indexOf(","))?n.substr(r+1):null),c=wt(),f=t.settings.images_reuse_filename&&e.blob.name?(o=t,i=e.blob.name,(a=i.match(/([\s\S]+?)\.(?:jpeg|jpg|png|gif)$/i))?o.dom.encode(a[1]):null):c,d=new v.Image;if(d.src=e.uri,s=t.settings,u=d,!s.images_dataimg_filter||s.images_dataimg_filter(u)){var m,g=t.editorUpload.blobCache,p=void 0;(m=g.findFirst(function(t){return t.base64()===l}))?p=m:(p=g.create(c,e.blob,l,f),g.add(p)),ht(t,'<img src="'+p.blobUri()+'">',!1)}else ht(t,'<img src="'+e.uri+'">',!1)}(e,t)})}),!0}return!1},Dt=function(t){return o.metaKeyPressed(t)&&86===t.keyCode||t.shiftKey&&45===t.keyCode},Tt=function(c,f,d){var m,g=0;function p(t,e,n,r){var o,i;xt(t,"text/html")?o=t["text/html"]:(o=f.getHtml(),r=r||x(o),f.isDefaultContent(o)&&(n=!0)),o=E.trimHtml(o),f.remove(),i=!1===r&&T(o),o.length&&!i||(n=!0),n&&(o=xt(t,"text/plain")&&i?t["text/plain"]:E.innerText(o)),f.isDefaultContent(o)?e||c.windowManager.alert("Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents."):n?yt(c,o):ht(c,o,r)}c.on("keydown",function(t){function e(t){Dt(t)&&!t.isDefaultPrevented()&&f.remove()}if(Dt(t)&&!t.isDefaultPrevented()){if((m=t.shiftKey&&86===t.keyCode)&&h.webkit&&-1!==v.navigator.userAgent.indexOf("Version/"))return;if(t.stopImmediatePropagation(),g=(new Date).getTime(),h.ie&&m)return t.preventDefault(),void n(c,!0);f.remove(),f.create(),c.once("keyup",e),c.once("paste",function(){c.off("keyup",e)})}}),c.on("paste",function(t){var e,n,r,o=(new Date).getTime(),i=(e=c,n=bt(t.clipboardData||e.getDoc().dataTransfer),E.isMsEdge()?b.extend(n,{"text/html":""}):n),a=(new Date).getTime()-o,s=(new Date).getTime()-g-a<1e3,u="text"===d.get()||m,l=xt(i,P());m=!1,t.isDefaultPrevented()||(r=t.clipboardData,-1!==v.navigator.userAgent.indexOf("Android")&&r&&r.items&&0===r.items.length)?f.remove():Pt(i)||!_t(c,t,f.getLastRng()||c.selection.getRng())?(s||t.preventDefault(),!h.ie||s&&!t.ieFake||xt(i,"text/html")||(f.create(),c.dom.bind(f.getEl(),"paste",function(t){t.stopPropagation()}),c.getDoc().execCommand("Paste",!1,null),i["text/html"]=f.getHtml()),xt(i,"text/html")?(t.preventDefault(),l||(l=x(i["text/html"])),p(i,s,u,l)):y.setEditorTimeout(c,function(){p(i,s,u,l)},0)):f.remove()})},Ct=function(t){return h.ie&&t.inline?v.document.body:t.getBody()},kt=function(e,t,n){var r;Ct(r=e)!==r.getBody()&&e.dom.bind(t,"paste keyup",function(t){Rt(e,n)||e.fire("paste")})},St=function(t){return t.dom.get("mcepastebin")},Ot=function(t,e){return e===t},Rt=function(t,e){var n,r=St(t);return(n=r)&&"mcepastebin"===n.id&&Ot(e,r.innerHTML)},Ft=function(a){var s=l(null),u="%MCEPASTEBIN%";return{create:function(){return e=s,n=u,o=(t=a).dom,i=t.getBody(),e.set(t.selection.getRng()),r=t.dom.add(Ct(t),"div",{id:"mcepastebin","class":"mce-pastebin",contentEditable:!0,"data-mce-bogus":"all",style:"position: fixed; top: 50%; width: 10px; height: 10px; overflow: hidden; opacity: 0"},n),(h.ie||h.gecko)&&o.setStyle(r,"left","rtl"===o.getStyle(i,"direction",!0)?65535:-65535),o.bind(r,"beforedeactivate focusin focusout",function(t){t.stopPropagation()}),kt(t,r,n),r.focus(),void t.selection.select(r,!0);var t,e,n,r,o,i},remove:function(){return function(t,e){if(St(t)){for(var n=void 0,r=e.get();n=t.dom.get("mcepastebin");)t.dom.remove(n),t.dom.unbind(n);r&&t.selection.setRng(r)}e.set(null)}(a,s)},getEl:function(){return St(a)},getHtml:function(){return function(n){var e,t,r,o,i,a=function(t,e){t.appendChild(e),n.dom.remove(e,!0)};for(t=b.grep(Ct(n).childNodes,function(t){return"mcepastebin"===t.id}),e=t.shift(),b.each(t,function(t){a(e,t)}),r=(o=n.dom.select("div[id=mcepastebin]",e)).length-1;0<=r;r--)i=n.dom.create("div"),e.insertBefore(i,o[r]),a(i,o[r]);return e?e.innerHTML:""}(a)},getLastRng:function(){return s.get()},isDefault:function(){return Rt(a,u)},isDefaultContent:function(t){return Ot(u,t)}}},Et=function(n,t){var e=Ft(n);return n.on("preInit",function(){return Tt(a=n,e,t),void a.parser.addNodeFilter("img",function(t,e,n){var r,o=function(t){t.attr("data-mce-object")||s===h.transparentSrc||t.remove()};if(!a.settings.paste_data_images&&(r=n).data&&!0===r.data.paste)for(var i=t.length;i--;)(s=t[i].attributes.map.src)&&(0===s.indexOf("webkit-fake-url")?o(t[i]):a.settings.allow_html_data_urls||0!==s.indexOf("data:")||o(t[i]))});var a,s}),{pasteFormat:t,pasteHtml:function(t,e){return ht(n,t,e)},pasteText:function(t){return yt(n,t)},pasteImageData:function(t,e){return _t(n,t,e)},getDataTransferItems:bt,hasHtmlOrText:Pt,hasContentType:xt}},At=function(){},It=function(t,e,n){if(r=t,!1!==h.iOS||r===undefined||"function"!=typeof r.setData||!0===E.isMsEdge())return!1;try{return t.clearData(),t.setData("text/html",e),t.setData("text/plain",n),t.setData(P(),e),!0}catch(o){return!1}var r},Mt=function(t,e,n,r){It(t.clipboardData,e.html,e.text)?(t.preventDefault(),r()):n(e.html,r)},Bt=function(s){return function(t,e){var n=g(t),r=s.dom.create("div",{contenteditable:"false","data-mce-bogus":"all"}),o=s.dom.create("div",{contenteditable:"true"},n);s.dom.setStyles(r,{position:"fixed",top:"0",left:"-3000px",width:"1000px",overflow:"hidden"}),r.appendChild(o),s.dom.add(s.getBody(),r);var i=s.selection.getRng();o.focus();var a=s.dom.createRng();a.selectNodeContents(o),s.selection.setRng(a),setTimeout(function(){s.selection.setRng(i),r.parentNode.removeChild(r),e()},0)}},Ht=function(t){return{html:t.selection.getContent({contextual:!0}),text:t.selection.getContent({format:"text"})}},jt=function(t){return!t.selection.isCollapsed()||!!(e=t).dom.getParent(e.selection.getStart(),"td[data-mce-selected],th[data-mce-selected]",e.getBody());var e},Lt=function(t){var e,n;t.on("cut",(e=t,function(t){jt(e)&&Mt(t,Ht(e),Bt(e),function(){setTimeout(function(){e.execCommand("Delete")},0)})})),t.on("copy",(n=t,function(t){jt(n)&&Mt(t,Ht(n),Bt(n),At)}))},Nt=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),$t=function(t,e){return Nt.getCaretRangeFromPoint(e.clientX,e.clientY,t.getDoc())},Wt=function(t,e){t.focus(),t.selection.setRng(e)},zt=function(a,s,u){m.shouldBlockDrop(a)&&a.on("dragend dragover draggesture dragdrop drop drag",function(t){t.preventDefault(),t.stopPropagation()}),m.shouldPasteDataImages(a)||a.on("drop",function(t){var e=t.dataTransfer;e&&e.files&&0<e.files.length&&t.preventDefault()}),a.on("drop",function(t){var e,n;if(n=$t(a,t),!t.isDefaultPrevented()&&!u.get()){e=s.getDataTransferItems(t.dataTransfer);var r,o=s.hasContentType(e,P());if((s.hasHtmlOrText(e)&&(!(r=e["text/plain"])||0!==r.indexOf("file://"))||!s.pasteImageData(t,n))&&n&&m.shouldFilterDrop(a)){var i=e["mce-internal"]||e["text/html"]||e["text/plain"];i&&(t.preventDefault(),y.setEditorTimeout(a,function(){a.undoManager.transact(function(){e["mce-internal"]&&a.execCommand("Delete"),Wt(a,n),i=E.trimHtml(i),e["text/html"]?s.pasteHtml(i,o):s.pasteText(i)})}))}}}),a.on("dragstart",function(t){u.set(!0)}),a.on("dragover dragend",function(t){m.shouldPasteDataImages(a)&&!1===u.get()&&(t.preventDefault(),Wt(a,$t(a,t))),"dragend"===t.type&&u.set(!1)})},Ut=function(t){var e=t.plugins.paste,n=m.getPreProcess(t);n&&t.on("PastePreProcess",function(t){n.call(e,e,t)});var r=m.getPostProcess(t);r&&t.on("PastePostProcess",function(t){r.call(e,e,t)})};function Vt(e,n){e.on("PastePreProcess",function(t){t.content=n(e,t.content,t.internal,t.wordContent)})}function qt(t,e){if(!$.isWordContent(e))return e;var n=[];b.each(t.schema.getBlockElements(),function(t,e){n.push(e)});var r=new RegExp("(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*(<\\/?("+n.join("|")+")[^>]*>)(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*","g");return e=E.filter(e,[[r,"$1"]]),e=E.filter(e,[[/<br><br>/g,"<BR><BR>"],[/<br>/g," "],[/<BR><BR>/g,"<br>"]])}function Kt(t,e,n,r){if(r||n)return e;var l,o=m.getWebkitStyles(t);if(!1===m.shouldRemoveWebKitStyles(t)||"all"===o)return e;if(o&&(l=o.split(/[, ]/)),l){var c=t.dom,f=t.selection.getNode();e=e.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi,function(t,e,n,r){var o=c.parseStyle(c.decode(n)),i={};if("none"===l)return e+r;for(var a=0;a<l.length;a++){var s=o[l[a]],u=c.getStyle(f,l[a],!0);/color/.test(l[a])&&(s=c.toHex(s),u=c.toHex(u)),u!==s&&(i[l[a]]=s)}return(i=c.serializeStyle(i,"span"))?e+' style="'+i+'"'+r:e+r})}else e=e.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi,"$1$3");return e=e.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi,function(t,e,n,r){return e+' style="'+n+'"'+r})}function Gt(n,t){n.$("a",t).find("font,u").each(function(t,e){n.dom.remove(e,!0)})}var Xt=function(t){var e,n;h.webkit&&Vt(t,Kt),h.ie&&(Vt(t,qt),n=Gt,(e=t).on("PastePostProcess",function(t){n(e,t.node)}))},Yt=function(t,e,n){var r=n.control;r.active("text"===e.pasteFormat.get()),t.on("PastePlainTextToggle",function(t){r.active(t.state)})},Zt=function(t,e){var n=function(r){for(var o=[],t=1;t<arguments.length;t++)o[t-1]=arguments[t];return function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];var n=o.concat(t);return r.apply(null,n)}}(Yt,t,e);t.addButton("pastetext",{active:!1,icon:"pastetext",tooltip:"Paste as text",cmd:"mceTogglePlainTextPaste",onPostRender:n}),t.addMenuItem("pastetext",{text:"Paste as text",selectable:!0,active:e.pasteFormat,cmd:"mceTogglePlainTextPaste",onPostRender:n})};e.add("paste",function(t){if(!1===a(t)){var e=l(!1),n=l(!1),r=l(m.isPasteAsTextEnabled(t)?"text":"html"),o=Et(t,r),i=Xt(t);return Zt(t,o),c(t,o,e),Ut(t),Lt(t),zt(t,o,n),s(o,i)}})}(window);
\ No newline at end of file
+!function(v){"use strict";var p=function(t){var e=t,n=function(){return e};return{get:n,set:function(t){e=t},clone:function(){return p(n())}}},e=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=function(t){return!(!/(^|[ ,])powerpaste([, ]|$)/.test(t.settings.plugins)||!e.get("powerpaste")||("undefined"!=typeof v.window.console&&v.window.console.log&&v.window.console.log("PowerPaste is incompatible with Paste plugin! Remove 'paste' from the 'plugins' option."),0))},u=function(t,e){return{clipboard:t,quirks:e}},d=function(t,e,n,r){return t.fire("PastePreProcess",{content:e,internal:n,wordContent:r})},m=function(t,e,n,r){return t.fire("PastePostProcess",{node:e,internal:n,wordContent:r})},s=function(t,e){return t.fire("PastePlainTextToggle",{state:e})},n=function(t,e){return t.fire("paste",{ieFake:e})},g={shouldPlainTextInform:function(t){return t.getParam("paste_plaintext_inform",!0)},shouldBlockDrop:function(t){return t.getParam("paste_block_drop",!1)},shouldPasteDataImages:function(t){return t.getParam("paste_data_images",!1)},shouldFilterDrop:function(t){return t.getParam("paste_filter_drop",!0)},getPreProcess:function(t){return t.getParam("paste_preprocess")},getPostProcess:function(t){return t.getParam("paste_postprocess")},getWebkitStyles:function(t){return t.getParam("paste_webkit_styles")},shouldRemoveWebKitStyles:function(t){return t.getParam("paste_remove_styles_if_webkit",!0)},shouldMergeFormats:function(t){return t.getParam("paste_merge_formats",!0)},isSmartPasteEnabled:function(t){return t.getParam("smart_paste",!0)},isPasteAsTextEnabled:function(t){return t.getParam("paste_as_text",!1)},getRetainStyleProps:function(t){return t.getParam("paste_retain_style_properties")},getWordValidElements:function(t){return t.getParam("paste_word_valid_elements","-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody")},shouldConvertWordFakeLists:function(t){return t.getParam("paste_convert_word_fake_lists",!0)},shouldUseDefaultFilters:function(t){return t.getParam("paste_enable_default_filters",!0)}},r=function(t,e,n){var r,o,i;"text"===e.pasteFormat.get()?(e.pasteFormat.set("html"),s(t,!1)):(e.pasteFormat.set("text"),s(t,!0),i=t,!1===n.get()&&g.shouldPlainTextInform(i)&&(o="Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.",(r=t).notificationManager.open({text:r.translate(o),type:"info"}),n.set(!0))),t.focus()},c=function(t,n,e){t.addCommand("mceTogglePlainTextPaste",function(){r(t,n,e)}),t.addCommand("mceInsertClipboardContent",function(t,e){e.content&&n.pasteHtml(e.content,e.internal),e.text&&n.pasteText(e.text)})},h=tinymce.util.Tools.resolve("tinymce.Env"),y=tinymce.util.Tools.resolve("tinymce.util.Delay"),b=tinymce.util.Tools.resolve("tinymce.util.Tools"),o=tinymce.util.Tools.resolve("tinymce.util.VK"),t="x-tinymce/html",i="\x3c!-- "+t+" --\x3e",l=function(t){return i+t},f=function(t){return t.replace(i,"")},w=function(t){return-1!==t.indexOf(i)},x=function(){return t},_=tinymce.util.Tools.resolve("tinymce.html.Entities"),P=function(t){return t.replace(/\r?\n/g,"<br>")},T=function(t,e,n){var r=t.split(/\n\n/),o=function(t,e){var n,r=[],o="<"+t;if("object"==typeof e){for(n in e)e.hasOwnProperty(n)&&r.push(n+'="'+_.encodeAllRaw(e[n])+'"');r.length&&(o+=" "+r.join(" "))}return o+">"}(e,n),i="</"+e+">",a=b.map(r,function(t){return t.split(/\n/).join("<br />")});return 1===a.length?a[0]:b.map(a,function(t){return o+t+i}).join("")},D=function(t){return!/<(?:\/?(?!(?:div|p|br|span)>)\w+|(?:(?!(?:span style="white-space:\s?pre;?">)|br\s?\/>))\w+\s[^>]+)>/i.test(t)},C=function(t,e,n){return e?T(t,e,n):P(t)},k=tinymce.util.Tools.resolve("tinymce.html.DomParser"),F=tinymce.util.Tools.resolve("tinymce.html.Serializer"),E=tinymce.util.Tools.resolve("tinymce.html.Node"),R=tinymce.util.Tools.resolve("tinymce.html.Schema");function I(e,t){return b.each(t,function(t){e=t.constructor===RegExp?e.replace(t,""):e.replace(t[0],t[1])}),e}var O={filter:I,innerText:function(e){var n=R(),r=k({},n),o="",i=n.getShortEndedElements(),a=b.makeMap("script noscript style textarea video audio iframe object"," "),u=n.getBlockElements();return e=I(e,[/<!\[[^\]]+\]>/g]),function t(e){var n=e.name,r=e;if("br"!==n){if("wbr"!==n)if(i[n]&&(o+=" "),a[n])o+=" ";else{if(3===e.type&&(o+=e.value),!e.shortEnded&&(e=e.firstChild))for(;t(e),e=e.next;);u[n]&&r.next&&(o+="\n","p"===n&&(o+="\n"))}}else o+="\n"}(r.parse(e)),o},trimHtml:function(t){return t=I(t,[/^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/gi,/<!--StartFragment-->|<!--EndFragment-->/g,[/( ?)<span class="Apple-converted-space">\u00a0<\/span>( ?)/g,function(t,e,n){return e||n?"\xa0":" "}],/<br class="Apple-interchange-newline">/g,/<br>$/i])},createIdGenerator:function(t){var e=0;return function(){return t+e++}},isMsEdge:function(){return-1!==v.navigator.userAgent.indexOf(" Edge/")}};function S(e){var n,t;return t=[/^[IVXLMCD]{1,2}\.[ \u00a0]/,/^[ivxlmcd]{1,2}\.[ \u00a0]/,/^[a-z]{1,2}[\.\)][ \u00a0]/,/^[A-Z]{1,2}[\.\)][ \u00a0]/,/^[0-9]+\.[ \u00a0]/,/^[\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d]+\.[ \u00a0]/,/^[\u58f1\u5f10\u53c2\u56db\u4f0d\u516d\u4e03\u516b\u4e5d\u62fe]+\.[ \u00a0]/],e=e.replace(/^[\u00a0 ]+/,""),b.each(t,function(t){if(t.test(e))return!(n=!0)}),n}function A(t){var i,a,u=1;function n(t){var e="";if(3===t.type)return t.value;if(t=t.firstChild)for(;e+=n(t),t=t.next;);return e}function s(t,e){if(3===t.type&&e.test(t.value))return t.value=t.value.replace(e,""),!1;if(t=t.firstChild)do{if(!s(t,e))return!1}while(t=t.next);return!0}function e(e,n,r){var o=e._listLevel||u;o!==u&&(o<u?i&&(i=i.parent.parent):(a=i,i=null)),i&&i.name===n?i.append(e):(a=a||i,i=new E(n,1),1<r&&i.attr("start",""+r),e.wrap(i)),e.name="li",u<o&&a&&a.lastChild.append(i),u=o,function t(e){if(e._listIgnore)e.remove();else if(e=e.firstChild)for(;t(e),e=e.next;);}(e),s(e,/^\u00a0+/),s(e,/^\s*([\u2022\u00b7\u00a7\u25CF]|\w+\.)/),s(e,/^\u00a0+/)}for(var r=[],o=t.firstChild;null!=o;)if(r.push(o),null!==(o=o.walk()))for(;void 0!==o&&o.parent!==t;)o=o.walk();for(var c=0;c<r.length;c++)if("p"===(t=r[c]).name&&t.firstChild){var l=n(t);if(/^[\s\u00a0]*[\u2022\u00b7\u00a7\u25CF]\s*/.test(l)){e(t,"ul");continue}if(S(l)){var f=/([0-9]+)\./.exec(l),d=1;f&&(d=parseInt(f[1],10)),e(t,"ol",d);continue}if(t._listLevel){e(t,"ul",1);continue}i=null}else a=i,i=null}function j(n,r,o,i){var a,u={},t=n.dom.parseStyle(i);return b.each(t,function(t,e){switch(e){case"mso-list":(a=/\w+ \w+([0-9]+)/i.exec(i))&&(o._listLevel=parseInt(a[1],10)),/Ignore/i.test(t)&&o.firstChild&&(o._listIgnore=!0,o.firstChild._listIgnore=!0);break;case"horiz-align":e="text-align";break;case"vert-align":e="vertical-align";break;case"font-color":case"mso-foreground":e="color";break;case"mso-background":case"mso-highlight":e="background";break;case"font-weight":case"font-style":return void("normal"!==t&&(u[e]=t));case"mso-element":if(/^(comment|comment-list)$/i.test(t))return void o.remove()}0!==e.indexOf("mso-comment")?0!==e.indexOf("mso-")&&("all"===g.getRetainStyleProps(n)||r&&r[e])&&(u[e]=t):o.remove()}),/(bold)/i.test(u["font-weight"])&&(delete u["font-weight"],o.wrap(new E("b",1))),/(italic)/i.test(u["font-style"])&&(delete u["font-style"],o.wrap(new E("i",1))),(u=n.dom.serializeStyle(u,o.name))||null}var M,L,N,B,H,$,W,U,z,V={preProcess:function(t,e){return g.shouldUseDefaultFilters(t)?function(r,t){var e,o;(e=g.getRetainStyleProps(r))&&(o=b.makeMap(e.split(/[, ]/))),t=O.filter(t,[/<br class="?Apple-interchange-newline"?>/gi,/<b[^>]+id="?docs-internal-[^>]*>/gi,/<!--[\s\S]+?-->/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/&nbsp;/gi,"\xa0"],[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,function(t,e){return 0<e.length?e.replace(/./," ").slice(Math.floor(e.length/2)).split("").join("\xa0"):""}]]);var n=g.getWordValidElements(r),i=R({valid_elements:n,valid_children:"-li[p]"});b.each(i.elements,function(t){t.attributes["class"]||(t.attributes["class"]={},t.attributesOrder.push("class")),t.attributes.style||(t.attributes.style={},t.attributesOrder.push("style"))});var a=k({},i);a.addAttributeFilter("style",function(t){for(var e,n=t.length;n--;)(e=t[n]).attr("style",j(r,o,e,e.attr("style"))),"span"===e.name&&e.parent&&!e.attributes.length&&e.unwrap()}),a.addAttributeFilter("class",function(t){for(var e,n,r=t.length;r--;)n=(e=t[r]).attr("class"),/^(MsoCommentReference|MsoCommentText|msoDel)$/i.test(n)&&e.remove(),e.attr("class",null)}),a.addNodeFilter("del",function(t){for(var e=t.length;e--;)t[e].remove()}),a.addNodeFilter("a",function(t){for(var e,n,r,o=t.length;o--;)if(n=(e=t[o]).attr("href"),r=e.attr("name"),n&&-1!==n.indexOf("#_msocom_"))e.remove();else if(n&&0===n.indexOf("file://")&&(n=n.split("#")[1])&&(n="#"+n),n||r){if(r&&!/^_?(?:toc|edn|ftn)/i.test(r)){e.unwrap();continue}e.attr({href:n,name:r})}else e.unwrap()});var u=a.parse(t);return g.shouldConvertWordFakeLists(r)&&A(u),t=F({validate:r.settings.validate},i).serialize(u)}(t,e):e},isWordContent:function(t){return/<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i.test(t)||/class="OutlineElement/.test(t)||/id="?docs\-internal\-guid\-/.test(t)}},K=function(t,e){return{content:t,cancelled:e}},q=function(t,e,n,r){var o,i,a,u,s,c,l=d(t,e,n,r),f=function(t,e){var n=k({},t.schema);n.addNodeFilter("meta",function(t){b.each(t,function(t){return t.remove()})});var r=n.parse(e,{forced_root_block:!1,isRootContent:!0});return F({validate:t.settings.validate},t.schema).serialize(r)}(t,l.content);return t.hasEventListeners("PastePostProcess")&&!l.isDefaultPrevented()?(i=f,a=n,u=r,s=(o=t).dom.create("div",{style:"display:none"},i),c=m(o,s,a,u),K(c.node.innerHTML,c.isDefaultPrevented())):K(f,l.isDefaultPrevented())},G=function(t,e,n){var r=V.isWordContent(e),o=r?V.preProcess(t,e):e;return q(t,o,n,r)},X=function(t,e){return t.insertContent(e,{merge:g.shouldMergeFormats(t),paste:!0}),!0},Y=function(t){return/^https?:\/\/[\w\?\-\/+=.&%@~#]+$/i.test(t)},Z=function(t){return Y(t)&&/.(gif|jpe?g|png)$/.test(t)},J=function(t,e,n){return!(!1!==t.selection.isCollapsed()||!Y(e)||(o=e,i=n,(r=t).undoManager.extra(function(){i(r,o)},function(){r.execCommand("mceInsertLink",!1,o)}),0));var r,o,i},Q=function(t,e,n){return!!Z(e)&&(o=e,i=n,(r=t).undoManager.extra(function(){i(r,o)},function(){r.insertContent('<img src="'+o+'">')}),!0);var r,o,i},tt=function(t,e){var n,r;!1===g.isSmartPasteEnabled(t)?X(t,e):(n=t,r=e,b.each([J,Q,X],function(t){return!0!==t(n,r,X)}))},et=function(){},nt=function(t){return function(){return t}},rt=nt(!1),ot=nt(!0),it=function(){return at},at=(M=function(t){return t.isNone()},B={fold:function(t,e){return t()},is:rt,isSome:rt,isNone:ot,getOr:N=function(t){return t},getOrThunk:L=function(t){return t()},getOrDie:function(t){throw new Error(t||"error: getOrDie called on none.")},getOrNull:nt(null),getOrUndefined:nt(undefined),or:N,orThunk:L,map:it,each:et,bind:it,exists:rt,forall:ot,filter:it,equals:M,equals_:M,toArray:function(){return[]},toString:nt("none()")},Object.freeze&&Object.freeze(B),B),ut=function(n){var t=nt(n),e=function(){return o},r=function(t){return t(n)},o={fold:function(t,e){return e(n)},is:function(t){return n===t},isSome:ot,isNone:rt,getOr:t,getOrThunk:t,getOrDie:t,getOrNull:t,getOrUndefined:t,or:e,orThunk:e,map:function(t){return ut(t(n))},each:function(t){t(n)},bind:r,exists:r,forall:r,filter:function(t){return t(n)?o:at},toArray:function(){return[n]},toString:function(){return"some("+n+")"},equals:function(t){return t.is(n)},equals_:function(t,e){return t.fold(rt,function(t){return e(n,t)})}};return o},st={some:ut,none:it,from:function(t){return null===t||t===undefined?at:ut(t)}},ct=(H="function",function(t){return function(t){if(null===t)return"null";var e=typeof t;return"object"===e&&(Array.prototype.isPrototypeOf(t)||t.constructor&&"Array"===t.constructor.name)?"array":"object"===e&&(String.prototype.isPrototypeOf(t)||t.constructor&&"String"===t.constructor.name)?"string":e}(t)===H}),lt=Array.prototype.slice,ft=function(t,e){for(var n=t.length,r=new Array(n),o=0;o<n;o++){var i=t[o];r[o]=e(i,o)}return r},dt=function(t,e){for(var n=0,r=t.length;n<r;n++)e(t[n],n)},mt=ct(Array.from)?Array.from:function(t){return lt.call(t)},pt={},gt={exports:pt};$=undefined,W=pt,U=gt,z=undefined,function(t){"object"==typeof W&&void 0!==U?U.exports=t():"function"==typeof $&&$.amd?$([],t):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).EphoxContactWrapper=t()}(function(){return function i(a,u,s){function c(e,t){if(!u[e]){if(!a[e]){var n="function"==typeof z&&z;if(!t&&n)return n(e,!0);if(l)return l(e,!0);var r=new Error("Cannot find module '"+e+"'");throw r.code="MODULE_NOT_FOUND",r}var o=u[e]={exports:{}};a[e][0].call(o.exports,function(t){return c(a[e][1][t]||t)},o,o.exports,i,a,u,s)}return u[e].exports}for(var l="function"==typeof z&&z,t=0;t<s.length;t++)c(s[t]);return c}({1:[function(t,e,n){var r,o,i=e.exports={};function a(){throw new Error("setTimeout has not been defined")}function u(){throw new Error("clearTimeout has not been defined")}function s(t){if(r===setTimeout)return setTimeout(t,0);if((r===a||!r)&&setTimeout)return r=setTimeout,setTimeout(t,0);try{return r(t,0)}catch(e){try{return r.call(null,t,0)}catch(e){return r.call(this,t,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:a}catch(t){r=a}try{o="function"==typeof clearTimeout?clearTimeout:u}catch(t){o=u}}();var c,l=[],f=!1,d=-1;function m(){f&&c&&(f=!1,c.length?l=c.concat(l):d=-1,l.length&&p())}function p(){if(!f){var t=s(m);f=!0;for(var e=l.length;e;){for(c=l,l=[];++d<e;)c&&c[d].run();d=-1,e=l.length}c=null,f=!1,function(t){if(o===clearTimeout)return clearTimeout(t);if((o===u||!o)&&clearTimeout)return o=clearTimeout,clearTimeout(t);try{o(t)}catch(e){try{return o.call(null,t)}catch(e){return o.call(this,t)}}}(t)}}function g(t,e){this.fun=t,this.array=e}function v(){}i.nextTick=function(t){var e=new Array(arguments.length-1);if(1<arguments.length)for(var n=1;n<arguments.length;n++)e[n-1]=arguments[n];l.push(new g(t,e)),1!==l.length||f||s(p)},g.prototype.run=function(){this.fun.apply(null,this.array)},i.title="browser",i.browser=!0,i.env={},i.argv=[],i.version="",i.versions={},i.on=v,i.addListener=v,i.once=v,i.off=v,i.removeListener=v,i.removeAllListeners=v,i.emit=v,i.prependListener=v,i.prependOnceListener=v,i.listeners=function(t){return[]},i.binding=function(t){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(t){throw new Error("process.chdir is not supported")},i.umask=function(){return 0}},{}],2:[function(t,f,e){(function(n){!function(t){var e=setTimeout;function r(){}function a(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=undefined,this._deferreds=[],l(t,this)}function o(r,o){for(;3===r._state;)r=r._value;0!==r._state?(r._handled=!0,a._immediateFn(function(){var t=1===r._state?o.onFulfilled:o.onRejected;if(null!==t){var e;try{e=t(r._value)}catch(n){return void u(o.promise,n)}i(o.promise,e)}else(1===r._state?i:u)(o.promise,r._value)})):r._deferreds.push(o)}function i(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var n=e.then;if(e instanceof a)return t._state=3,t._value=e,void s(t);if("function"==typeof n)return void l((r=n,o=e,function(){r.apply(o,arguments)}),t)}t._state=1,t._value=e,s(t)}catch(i){u(t,i)}var r,o}function u(t,e){t._state=2,t._value=e,s(t)}function s(t){2===t._state&&0===t._deferreds.length&&a._immediateFn(function(){t._handled||a._unhandledRejectionFn(t._value)});for(var e=0,n=t._deferreds.length;e<n;e++)o(t,t._deferreds[e]);t._deferreds=null}function c(t,e,n){this.onFulfilled="function"==typeof t?t:null,this.onRejected="function"==typeof e?e:null,this.promise=n}function l(t,e){var n=!1;try{t(function(t){n||(n=!0,i(e,t))},function(t){n||(n=!0,u(e,t))})}catch(r){if(n)return;n=!0,u(e,r)}}a.prototype["catch"]=function(t){return this.then(null,t)},a.prototype.then=function(t,e){var n=new this.constructor(r);return o(this,new c(t,e,n)),n},a.all=function(t){var s=Array.prototype.slice.call(t);return new a(function(o,i){if(0===s.length)return o([]);var a=s.length;function u(e,t){try{if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if("function"==typeof n)return void n.call(t,function(t){u(e,t)},i)}s[e]=t,0==--a&&o(s)}catch(r){i(r)}}for(var t=0;t<s.length;t++)u(t,s[t])})},a.resolve=function(e){return e&&"object"==typeof e&&e.constructor===a?e:new a(function(t){t(e)})},a.reject=function(n){return new a(function(t,e){e(n)})},a.race=function(o){return new a(function(t,e){for(var n=0,r=o.length;n<r;n++)o[n].then(t,e)})},a._immediateFn="function"==typeof n?function(t){n(t)}:function(t){e(t,0)},a._unhandledRejectionFn=function(t){"undefined"!=typeof console&&console&&console.warn("Possible Unhandled Promise Rejection:",t)},a._setImmediateFn=function(t){a._immediateFn=t},a._setUnhandledRejectionFn=function(t){a._unhandledRejectionFn=t},void 0!==f&&f.exports?f.exports=a:t.Promise||(t.Promise=a)}(this)}).call(this,t("timers").setImmediate)},{timers:3}],3:[function(s,t,c){(function(t,e){var r=s("process/browser.js").nextTick,n=Function.prototype.apply,o=Array.prototype.slice,i={},a=0;function u(t,e){this._id=t,this._clearFn=e}c.setTimeout=function(){return new u(n.call(setTimeout,window,arguments),clearTimeout)},c.setInterval=function(){return new u(n.call(setInterval,window,arguments),clearInterval)},c.clearTimeout=c.clearInterval=function(t){t.close()},u.prototype.unref=u.prototype.ref=function(){},u.prototype.close=function(){this._clearFn.call(window,this._id)},c.enroll=function(t,e){clearTimeout(t._idleTimeoutId),t._idleTimeout=e},c.unenroll=function(t){clearTimeout(t._idleTimeoutId),t._idleTimeout=-1},c._unrefActive=c.active=function(t){clearTimeout(t._idleTimeoutId);var e=t._idleTimeout;0<=e&&(t._idleTimeoutId=setTimeout(function(){t._onTimeout&&t._onTimeout()},e))},c.setImmediate="function"==typeof t?t:function(t){var e=a++,n=!(arguments.length<2)&&o.call(arguments,1);return i[e]=!0,r(function(){i[e]&&(n?t.apply(null,n):t.call(null),c.clearImmediate(e))}),e},c.clearImmediate="function"==typeof e?e:function(t){delete i[t]}}).call(this,s("timers").setImmediate,s("timers").clearImmediate)},{"process/browser.js":1,timers:3}],4:[function(t,e,n){var r=t("promise-polyfill"),o="undefined"!=typeof window?window:Function("return this;")();e.exports={boltExport:o.Promise||r}},{"promise-polyfill":2}]},{},[4])(4)});var vt=gt.exports.boltExport,ht=function(t){var n=st.none(),e=[],r=function(t){o()?a(t):e.push(t)},o=function(){return n.isSome()},i=function(t){dt(t,a)},a=function(e){n.each(function(t){v.setTimeout(function(){e(t)},0)})};return t(function(t){n=st.some(t),i(e),e=[]}),{get:r,map:function(n){return ht(function(e){r(function(t){e(n(t))})})},isReady:o}},yt={nu:ht,pure:function(e){return ht(function(t){t(e)})}},bt=function(t){v.setTimeout(function(){throw t},0)},wt=function(n){var t=function(t){n().then(t,bt)};return{map:function(t){return wt(function(){return n().then(t)})},bind:function(e){return wt(function(){return n().then(function(t){return e(t).toPromise()})})},anonBind:function(t){return wt(function(){return n().then(function(){return t.toPromise()})})},toLazy:function(){return yt.nu(t)},toCached:function(){var t=null;return wt(function(){return null===t&&(t=n()),t})},toPromise:n,get:t}},xt=function(t){return wt(function(){return new vt(t)})},_t=function(a,t){return t(function(r){var o=[],i=0;0===a.length?r([]):dt(a,function(t,e){var n;t.get((n=e,function(t){o[n]=t,++i>=a.length&&r(o)}))})})},Pt=function(t,e){return n=ft(t,e),_t(n,xt);var n},Tt=function(t,e,n){var r=n||w(e),o=G(t,f(e),r);!1===o.cancelled&&tt(t,o.content)},Dt=function(t,e){e=t.dom.encode(e).replace(/\r\n/g,"\n"),e=C(e,t.settings.forced_root_block,t.settings.forced_root_block_attrs),Tt(t,e,!1)},Ct=function(t){var e={};if(t){if(t.getData){var n=t.getData("Text");n&&0<n.length&&-1===n.indexOf("data:text/mce-internal,")&&(e["text/plain"]=n)}if(t.types)for(var r=0;r<t.types.length;r++){var o=t.types[r];try{e[o]=t.getData(o)}catch(i){e[o]=""}}}return e},kt=function(t,e){return e in t&&0<t[e].length},Ft=function(t){return kt(t,"text/html")||kt(t,"text/plain")},Et=O.createIdGenerator("mceclip"),Rt=function(e,t,n){var r,o,i,a,u="paste"===t.type?t.clipboardData:t.dataTransfer;if(e.settings.paste_data_images&&u){var s=(i=(o=u).items?ft(mt(o.items),function(t){return t.getAsFile()}):[],a=o.files?mt(o.files):[],function(t,e){for(var n=[],r=0,o=t.length;r<o;r++){var i=t[r];e(i,r)&&n.push(i)}return n}(0<i.length?i:a,function(t){return/^image\/(jpeg|png|gif|bmp)$/.test(t.type)}));if(0<s.length)return t.preventDefault(),(r=s,Pt(r,function(r){return xt(function(t){var e=r.getAsFile?r.getAsFile():r,n=new window.FileReader;n.onload=function(){t({blob:e,uri:n.result})},n.readAsDataURL(e)})})).get(function(t){n&&e.selection.setRng(n),dt(t,function(t){!function(t,e){var n,r,o,i,a,u,s,c=(n=e.uri,-1!==(r=n.indexOf(","))?n.substr(r+1):null),l=Et(),f=t.settings.images_reuse_filename&&e.blob.name?(o=t,i=e.blob.name,(a=i.match(/([\s\S]+?)\.(?:jpeg|jpg|png|gif)$/i))?o.dom.encode(a[1]):null):l,d=new v.Image;if(d.src=e.uri,u=t.settings,s=d,!u.images_dataimg_filter||u.images_dataimg_filter(s)){var m,p=t.editorUpload.blobCache,g=void 0;(m=p.findFirst(function(t){return t.base64()===c}))?g=m:(g=p.create(l,e.blob,c,f),p.add(g)),Tt(t,'<img src="'+g.blobUri()+'">',!1)}else Tt(t,'<img src="'+e.uri+'">',!1)}(e,t)})}),!0}return!1},It=function(t){return o.metaKeyPressed(t)&&86===t.keyCode||t.shiftKey&&45===t.keyCode},Ot=function(s,c,l){var e,f,d=(e=p(st.none()),{clear:function(){e.set(st.none())},set:function(t){e.set(st.some(t))},isSet:function(){return e.get().isSome()},on:function(t){e.get().each(t)}});function m(t,e,n,r){var o,i;kt(t,"text/html")?o=t["text/html"]:(o=c.getHtml(),r=r||w(o),c.isDefaultContent(o)&&(n=!0)),o=O.trimHtml(o),c.remove(),i=!1===r&&D(o),o.length&&!i||(n=!0),n&&(o=kt(t,"text/plain")&&i?t["text/plain"]:O.innerText(o)),c.isDefaultContent(o)?e||s.windowManager.alert("Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents."):n?Dt(s,o):Tt(s,o,r)}s.on("keydown",function(t){function e(t){It(t)&&!t.isDefaultPrevented()&&c.remove()}if(It(t)&&!t.isDefaultPrevented()){if((f=t.shiftKey&&86===t.keyCode)&&h.webkit&&-1!==v.navigator.userAgent.indexOf("Version/"))return;if(t.stopImmediatePropagation(),d.set(t),window.setTimeout(function(){d.clear()},100),h.ie&&f)return t.preventDefault(),void n(s,!0);c.remove(),c.create(),s.once("keyup",e),s.once("paste",function(){s.off("keyup",e)})}}),s.on("paste",function(t){var e,n,r,o=d.isSet(),i=(e=s,n=Ct(t.clipboardData||e.getDoc().dataTransfer),O.isMsEdge()?b.extend(n,{"text/html":""}):n),a="text"===l.get()||f,u=kt(i,x());f=!1,t.isDefaultPrevented()||(r=t.clipboardData,-1!==v.navigator.userAgent.indexOf("Android")&&r&&r.items&&0===r.items.length)?c.remove():Ft(i)||!Rt(s,t,c.getLastRng()||s.selection.getRng())?(o||t.preventDefault(),!h.ie||o&&!t.ieFake||kt(i,"text/html")||(c.create(),s.dom.bind(c.getEl(),"paste",function(t){t.stopPropagation()}),s.getDoc().execCommand("Paste",!1,null),i["text/html"]=c.getHtml()),kt(i,"text/html")?(t.preventDefault(),u||(u=w(i["text/html"])),m(i,o,a,u)):y.setEditorTimeout(s,function(){m(i,o,a,u)},0)):c.remove()})},St=function(t){return h.ie&&t.inline?v.document.body:t.getBody()},At=function(e,t,n){var r;St(r=e)!==r.getBody()&&e.dom.bind(t,"paste keyup",function(t){Lt(e,n)||e.fire("paste")})},jt=function(t){return t.dom.get("mcepastebin")},Mt=function(t,e){return e===t},Lt=function(t,e){var n,r=jt(t);return(n=r)&&"mcepastebin"===n.id&&Mt(e,r.innerHTML)},Nt=function(a){var u=p(null),s="%MCEPASTEBIN%";return{create:function(){return e=u,n=s,o=(t=a).dom,i=t.getBody(),e.set(t.selection.getRng()),r=t.dom.add(St(t),"div",{id:"mcepastebin","class":"mce-pastebin",contentEditable:!0,"data-mce-bogus":"all",style:"position: fixed; top: 50%; width: 10px; height: 10px; overflow: hidden; opacity: 0"},n),(h.ie||h.gecko)&&o.setStyle(r,"left","rtl"===o.getStyle(i,"direction",!0)?65535:-65535),o.bind(r,"beforedeactivate focusin focusout",function(t){t.stopPropagation()}),At(t,r,n),r.focus(),void t.selection.select(r,!0);var t,e,n,r,o,i},remove:function(){return function(t,e){if(jt(t)){for(var n=void 0,r=e.get();n=t.dom.get("mcepastebin");)t.dom.remove(n),t.dom.unbind(n);r&&t.selection.setRng(r)}e.set(null)}(a,u)},getEl:function(){return jt(a)},getHtml:function(){return function(n){var e,t,r,o,i,a=function(t,e){t.appendChild(e),n.dom.remove(e,!0)};for(t=b.grep(St(n).childNodes,function(t){return"mcepastebin"===t.id}),e=t.shift(),b.each(t,function(t){a(e,t)}),r=(o=n.dom.select("div[id=mcepastebin]",e)).length-1;0<=r;r--)i=n.dom.create("div"),e.insertBefore(i,o[r]),a(i,o[r]);return e?e.innerHTML:""}(a)},getLastRng:function(){return u.get()},isDefault:function(){return Lt(a,s)},isDefaultContent:function(t){return Mt(s,t)}}},Bt=function(n,t){var e=Nt(n);return n.on("preInit",function(){return Ot(a=n,e,t),void a.parser.addNodeFilter("img",function(t,e,n){var r,o=function(t){t.attr("data-mce-object")||u===h.transparentSrc||t.remove()};if(!a.settings.paste_data_images&&(r=n).data&&!0===r.data.paste)for(var i=t.length;i--;)(u=t[i].attributes.map.src)&&(0===u.indexOf("webkit-fake-url")?o(t[i]):a.settings.allow_html_data_urls||0!==u.indexOf("data:")||o(t[i]))});var a,u}),{pasteFormat:t,pasteHtml:function(t,e){return Tt(n,t,e)},pasteText:function(t){return Dt(n,t)},pasteImageData:function(t,e){return Rt(n,t,e)},getDataTransferItems:Ct,hasHtmlOrText:Ft,hasContentType:kt}},Ht=function(){},$t=function(t,e,n){if(r=t,!1!==h.iOS||r===undefined||"function"!=typeof r.setData||!0===O.isMsEdge())return!1;try{return t.clearData(),t.setData("text/html",e),t.setData("text/plain",n),t.setData(x(),e),!0}catch(o){return!1}var r},Wt=function(t,e,n,r){$t(t.clipboardData,e.html,e.text)?(t.preventDefault(),r()):n(e.html,r)},Ut=function(u){return function(t,e){var n=l(t),r=u.dom.create("div",{contenteditable:"false","data-mce-bogus":"all"}),o=u.dom.create("div",{contenteditable:"true"},n);u.dom.setStyles(r,{position:"fixed",top:"0",left:"-3000px",width:"1000px",overflow:"hidden"}),r.appendChild(o),u.dom.add(u.getBody(),r);var i=u.selection.getRng();o.focus();var a=u.dom.createRng();a.selectNodeContents(o),u.selection.setRng(a),setTimeout(function(){u.selection.setRng(i),r.parentNode.removeChild(r),e()},0)}},zt=function(t){return{html:t.selection.getContent({contextual:!0}),text:t.selection.getContent({format:"text"})}},Vt=function(t){return!t.selection.isCollapsed()||!!(e=t).dom.getParent(e.selection.getStart(),"td[data-mce-selected],th[data-mce-selected]",e.getBody());var e},Kt=function(t){var e,n;t.on("cut",(e=t,function(t){Vt(e)&&Wt(t,zt(e),Ut(e),function(){setTimeout(function(){e.execCommand("Delete")},0)})})),t.on("copy",(n=t,function(t){Vt(n)&&Wt(t,zt(n),Ut(n),Ht)}))},qt=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),Gt=function(t,e){return qt.getCaretRangeFromPoint(e.clientX,e.clientY,t.getDoc())},Xt=function(t,e){t.focus(),t.selection.setRng(e)},Yt=function(a,u,s){g.shouldBlockDrop(a)&&a.on("dragend dragover draggesture dragdrop drop drag",function(t){t.preventDefault(),t.stopPropagation()}),g.shouldPasteDataImages(a)||a.on("drop",function(t){var e=t.dataTransfer;e&&e.files&&0<e.files.length&&t.preventDefault()}),a.on("drop",function(t){var e,n;if(n=Gt(a,t),!t.isDefaultPrevented()&&!s.get()){e=u.getDataTransferItems(t.dataTransfer);var r,o=u.hasContentType(e,x());if((u.hasHtmlOrText(e)&&(!(r=e["text/plain"])||0!==r.indexOf("file://"))||!u.pasteImageData(t,n))&&n&&g.shouldFilterDrop(a)){var i=e["mce-internal"]||e["text/html"]||e["text/plain"];i&&(t.preventDefault(),y.setEditorTimeout(a,function(){a.undoManager.transact(function(){e["mce-internal"]&&a.execCommand("Delete"),Xt(a,n),i=O.trimHtml(i),e["text/html"]?u.pasteHtml(i,o):u.pasteText(i)})}))}}}),a.on("dragstart",function(t){s.set(!0)}),a.on("dragover dragend",function(t){g.shouldPasteDataImages(a)&&!1===s.get()&&(t.preventDefault(),Xt(a,Gt(a,t))),"dragend"===t.type&&s.set(!1)})},Zt=function(t){var e=t.plugins.paste,n=g.getPreProcess(t);n&&t.on("PastePreProcess",function(t){n.call(e,e,t)});var r=g.getPostProcess(t);r&&t.on("PastePostProcess",function(t){r.call(e,e,t)})};function Jt(e,n){e.on("PastePreProcess",function(t){t.content=n(e,t.content,t.internal,t.wordContent)})}function Qt(t,e){if(!V.isWordContent(e))return e;var n=[];b.each(t.schema.getBlockElements(),function(t,e){n.push(e)});var r=new RegExp("(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*(<\\/?("+n.join("|")+")[^>]*>)(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*","g");return e=O.filter(e,[[r,"$1"]]),e=O.filter(e,[[/<br><br>/g,"<BR><BR>"],[/<br>/g," "],[/<BR><BR>/g,"<br>"]])}function te(t,e,n,r){if(r||n)return e;var c,o=g.getWebkitStyles(t);if(!1===g.shouldRemoveWebKitStyles(t)||"all"===o)return e;if(o&&(c=o.split(/[, ]/)),c){var l=t.dom,f=t.selection.getNode();e=e.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi,function(t,e,n,r){var o=l.parseStyle(l.decode(n)),i={};if("none"===c)return e+r;for(var a=0;a<c.length;a++){var u=o[c[a]],s=l.getStyle(f,c[a],!0);/color/.test(c[a])&&(u=l.toHex(u),s=l.toHex(s)),s!==u&&(i[c[a]]=u)}return(i=l.serializeStyle(i,"span"))?e+' style="'+i+'"'+r:e+r})}else e=e.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi,"$1$3");return e=e.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi,function(t,e,n,r){return e+' style="'+n+'"'+r})}function ee(n,t){n.$("a",t).find("font,u").each(function(t,e){n.dom.remove(e,!0)})}var ne=function(t){var e,n;h.webkit&&Jt(t,te),h.ie&&(Jt(t,Qt),n=ee,(e=t).on("PastePostProcess",function(t){n(e,t.node)}))},re=function(t,e,n){var r=n.control;r.active("text"===e.pasteFormat.get()),t.on("PastePlainTextToggle",function(t){r.active(t.state)})},oe=function(t,e){var n=function(r){for(var o=[],t=1;t<arguments.length;t++)o[t-1]=arguments[t];return function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];var n=o.concat(t);return r.apply(null,n)}}(re,t,e);t.addButton("pastetext",{active:!1,icon:"pastetext",tooltip:"Paste as text",cmd:"mceTogglePlainTextPaste",onPostRender:n}),t.addMenuItem("pastetext",{text:"Paste as text",selectable:!0,active:e.pasteFormat,cmd:"mceTogglePlainTextPaste",onPostRender:n})};e.add("paste",function(t){if(!1===a(t)){var e=p(!1),n=p(!1),r=p(g.isPasteAsTextEnabled(t)?"text":"html"),o=Bt(t,r),i=ne(t);return oe(t,o),c(t,o,e),Zt(t),Kt(t),Yt(t,o,n),u(o,i)}})}(window);
\ No newline at end of file
index deaafd0f62937be45434bdb8f5b59729cc1aae4e..90d7c4830ba6988cee57a7c8cc4b73577c4d960d 100644 (file)
@@ -1 +1 @@
-!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager"),n=function(t){t.addCommand("mcePrint",function(){t.getWin().print()})},i=function(t){t.addButton("print",{title:"Print",cmd:"mcePrint"}),t.addMenuItem("print",{text:"Print",cmd:"mcePrint",icon:"print"})};t.add("print",function(t){n(t),i(t),t.addShortcut("Meta+P","","mcePrint")})}();
\ No newline at end of file
+!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager"),n=tinymce.util.Tools.resolve("tinymce.Env"),i=function(t){t.addCommand("mcePrint",function(){n.ie&&n.ie<=11?t.getDoc().execCommand("print",!1,null):t.getWin().print()})},e=function(t){t.addButton("print",{title:"Print",cmd:"mcePrint"}),t.addMenuItem("print",{text:"Print",cmd:"mcePrint",icon:"print"})};t.add("print",function(t){i(t),e(t),t.addShortcut("Meta+P","","mcePrint")})}();
\ No newline at end of file
index c3def1fac10be17cd1377d4036b1f0b909b00073..80d078b4e2a6ba33a6166d711aae3ee107549d73 100644 (file)
@@ -1 +1 @@
-!function(m){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),y=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t]},x=function(n,r){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n(r.apply(null,e))}},C=function(e){return function(){return e}},o=function(e){return e};function b(r){for(var o=[],e=1;e<arguments.length;e++)o[e-1]=arguments[e];return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=o.concat(e);return r.apply(null,n)}}var t,n,r,i,u,g=function(n){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return!n.apply(null,e)}},c=function(e){return e()},a=C(!1),l=C(!0),f=a,s=l,d=function(){return h},h=(i={fold:function(e,t){return e()},is:f,isSome:f,isNone:s,getOr:r=function(e){return e},getOrThunk:n=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:function(){return null},getOrUndefined:function(){return undefined},or:r,orThunk:n,map:d,ap:d,each:function(){},bind:d,flatten:d,exists:f,forall:s,filter:d,equals:t=function(e){return e.isNone()},equals_:t,toArray:function(){return[]},toString:C("none()")},Object.freeze&&Object.freeze(i),i),p=function(n){var e=function(){return n},t=function(){return o},r=function(e){return e(n)},o={fold:function(e,t){return t(n)},is:function(e){return n===e},isSome:s,isNone:f,getOr:e,getOrThunk:e,getOrDie:e,getOrNull:e,getOrUndefined:e,or:t,orThunk:t,map:function(e){return p(e(n))},ap:function(e){return e.fold(d,function(e){return p(e(n))})},each:function(e){e(n)},bind:r,flatten:e,exists:r,forall:r,filter:function(e){return e(n)?o:h},equals:function(e){return e.is(n)},equals_:function(e,t){return e.fold(f,function(e){return t(n,e)})},toArray:function(){return[n]},toString:function(){return"some("+n+")"}};return o},S={some:p,none:d,from:function(e){return null===e||e===undefined?h:p(e)}},v=function(t){return function(e){return function(e){if(null===e)return"null";var t=typeof e;return"object"===t&&Array.prototype.isPrototypeOf(e)?"array":"object"===t&&String.prototype.isPrototypeOf(e)?"string":t}(e)===t}},w=v("string"),R=v("array"),T=v("boolean"),D=v("function"),O=v("number"),N=(u=Array.prototype.indexOf)===undefined?function(e,t){return j(e,t)}:function(e,t){return u.call(e,t)},E=function(e,t){return-1<N(e,t)},A=function(e,t){return L(e,t).isSome()},k=function(e,t){for(var n=e.length,r=new Array(n),o=0;o<n;o++){var i=e[o];r[o]=t(i,o,e)}return r},P=function(e,t){for(var n=0,r=e.length;n<r;n++)t(e[n],n,e)},I=function(e,t){for(var n=[],r=0,o=e.length;r<o;r++){var i=e[r];t(i,r,e)&&n.push(i)}return n},B=function(e,t){if(0===e.length)return[];for(var n=t(e[0]),r=[],o=[],i=0,u=e.length;i<u;i++){var a=e[i],c=t(a);c!==n&&(r.push(o),o=[]),n=c,o.push(a)}return 0!==o.length&&r.push(o),r},W=function(e,t,n){return function(e,t){for(var n=e.length-1;0<=n;n--)t(e[n],n,e)}(e,function(e){n=t(n,e)}),n},M=function(e,t,n){return P(e,function(e){n=t(n,e)}),n},_=function(e,t){for(var n=0,r=e.length;n<r;n++){var o=e[n];if(t(o,n,e))return S.some(o)}return S.none()},L=function(e,t){for(var n=0,r=e.length;n<r;n++)if(t(e[n],n,e))return S.some(n);return S.none()},j=function(e,t){for(var n=0,r=e.length;n<r;++n)if(e[n]===t)return n;return-1},F=Array.prototype.push,z=function(e){for(var t=[],n=0,r=e.length;n<r;++n){if(!Array.prototype.isPrototypeOf(e[n]))throw new Error("Arr.flatten item "+n+" was not an array, input: "+e);F.apply(t,e[n])}return t},H=function(e,t){var n=k(e,t);return z(n)},U=function(e,t){for(var n=0,r=e.length;n<r;++n)if(!0!==t(e[n],n,e))return!1;return!0},q=Array.prototype.slice,V=function(e){var t=q.call(e,0);return t.reverse(),t},G=function(e,t){var n=q.call(e,0);return n.sort(t),n},Y=(D(Array.from)&&Array.from,Object.keys),X=function(e,t){for(var n=Y(e),r=0,o=n.length;r<o;r++){var i=n[r];t(e[i],i,e)}},K=function(e,r){return J(e,function(e,t,n){return{k:t,v:r(e,t,n)}})},J=function(r,o){var i={};return X(r,function(e,t){var n=o(e,t,r);i[n.k]=n.v}),i},$=function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];return function(){for(var n=[],e=0;e<arguments.length;e++)n[e]=arguments[e];if(t.length!==n.length)throw new Error('Wrong number of arguments to struct. Expected "['+t.length+']", got '+n.length+" arguments");var r={};return P(t,function(e,t){r[e]=C(n[t])}),r}},Q=function(e){return e.slice(0).sort()},Z=function(e,t){throw new Error("All required keys ("+Q(e).join(", ")+") were not specified. Specified keys were: "+Q(t).join(", ")+".")},ee=function(e){throw new Error("Unsupported keys for object: "+Q(e).join(", "))},te=function(t,e){if(!R(e))throw new Error("The "+t+" fields must be an array. Was: "+e+".");P(e,function(e){if(!w(e))throw new Error("The value "+e+" in the "+t+" fields was not a string.")})},ne=function(e){var n=Q(e);_(n,function(e,t){return t<n.length-1&&e===n[t+1]}).each(function(e){throw new Error("The field: "+e+" occurs more than once in the combined fields: ["+n.join(", ")+"].")})},re=function(o,i){var u=o.concat(i);if(0===u.length)throw new Error("You must specify at least one required or optional field.");return te("required",o),te("optional",i),ne(u),function(t){var n=Y(t);U(o,function(e){return E(n,e)})||Z(o,n);var e=I(n,function(e){return!E(u,e)});0<e.length&&ee(e);var r={};return P(o,function(e){r[e]=C(t[e])}),P(i,function(e){r[e]=C(Object.prototype.hasOwnProperty.call(t,e)?S.some(t[e]):S.none())}),r}},oe=(m.Node.ATTRIBUTE_NODE,m.Node.CDATA_SECTION_NODE,m.Node.COMMENT_NODE),ie=m.Node.DOCUMENT_NODE,ue=(m.Node.DOCUMENT_TYPE_NODE,m.Node.DOCUMENT_FRAGMENT_NODE,m.Node.ELEMENT_NODE),ae=m.Node.TEXT_NODE,ce=(m.Node.PROCESSING_INSTRUCTION_NODE,m.Node.ENTITY_REFERENCE_NODE,m.Node.ENTITY_NODE,m.Node.NOTATION_NODE,function(e){return e.dom().nodeName.toLowerCase()}),le=function(e){return e.dom().nodeType},fe=function(t){return function(e){return le(e)===t}},se=function(e){return le(e)===oe||"#comment"===ce(e)},de=fe(ue),me=fe(ae),ge=fe(ie),he=function(e,t,n){if(!(w(n)||T(n)||O(n)))throw m.console.error("Invalid call to Attr.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")},pe=function(e,t,n){he(e.dom(),t,n)},ve=function(e,t){var n=e.dom();X(t,function(e,t){he(n,t,e)})},be=function(e,t){var n=e.dom().getAttribute(t);return null===n?undefined:n},we=function(e,t){var n=e.dom();return!(!n||!n.hasAttribute)&&n.hasAttribute(t)},ye=function(e,t){e.dom().removeAttribute(t)},xe=function(e){return M(e.dom().attributes,function(e,t){return e[t.name]=t.value,e},{})},Ce=function(e,t){return-1!==e.indexOf(t)},Se=function(e){return e.style!==undefined},Re=function(n){var r,o=!1;return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return o||(o=!0,r=n.apply(null,e)),r}},Te=function(e){if(null===e||e===undefined)throw new Error("Node cannot be null or undefined");return{dom:C(e)}},De={fromHtml:function(e,t){var n=(t||m.document).createElement("div");if(n.innerHTML=e,!n.hasChildNodes()||1<n.childNodes.length)throw m.console.error("HTML does not have a single root node",e),new Error("HTML must have a single root node");return Te(n.childNodes[0])},fromTag:function(e,t){var n=(t||m.document).createElement(e);return Te(n)},fromText:function(e,t){var n=(t||m.document).createTextNode(e);return Te(n)},fromDom:Te,fromPoint:function(e,t,n){var r=e.dom();return S.from(r.elementFromPoint(t,n)).map(Te)}},Oe=function(e){var t=me(e)?e.dom().parentNode:e.dom();return t!==undefined&&null!==t&&t.ownerDocument.body.contains(t)},Ne=Re(function(){return Ee(De.fromDom(m.document))}),Ee=function(e){var t=e.dom().body;if(null===t||t===undefined)throw new Error("Body is not available yet");return De.fromDom(t)},Ae=function(e,t,n){if(!w(n))throw m.console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);Se(e)&&e.style.setProperty(t,n)},ke=function(e,t,n){var r=e.dom();Ae(r,t,n)},Pe=function(e,t){var n=e.dom();X(t,function(e,t){Ae(n,t,e)})},Ie=function(e,t){var n=e.dom(),r=m.window.getComputedStyle(n).getPropertyValue(t),o=""!==r||Oe(e)?r:Be(n,t);return null===o?undefined:o},Be=function(e,t){return Se(e)?e.style.getPropertyValue(t):""},We=function(e,t){var n=e.dom(),r=Be(n,t);return S.from(r).filter(function(e){return 0<e.length})},Me=function(e,t){var n,r,o=e.dom();r=t,Se(n=o)&&n.style.removeProperty(r),we(e,"style")&&""===be(e,"style").replace(/^\s+|\s+$/g,"")&&ye(e,"style")},_e="undefined"!=typeof m.window?m.window:Function("return this;")(),Le=function(e,t){return function(e,t){for(var n=t!==undefined&&null!==t?t:_e,r=0;r<e.length&&n!==undefined&&null!==n;++r)n=n[e[r]];return n}(e.split("."),t)},je=function(e,t){var n=Le(e,t);if(n===undefined||null===n)throw e+" not available on this browser";return n},Fe=function(){return je("Node")},ze=function(e,t,n){return 0!=(e.compareDocumentPosition(t)&n)},He=function(e,t){return ze(e,t,Fe().DOCUMENT_POSITION_CONTAINED_BY)},Ue=function(e,t){var n=function(e,t){for(var n=0;n<e.length;n++){var r=e[n];if(r.test(t))return r}return undefined}(e,t);if(!n)return{major:0,minor:0};var r=function(e){return Number(t.replace(n,"$"+e))};return Ve(r(1),r(2))},qe=function(){return Ve(0,0)},Ve=function(e,t){return{major:e,minor:t}},Ge={nu:Ve,detect:function(e,t){var n=String(t).toLowerCase();return 0===e.length?qe():Ue(e,n)},unknown:qe},Ye="Firefox",Xe=function(e,t){return function(){return t===e}},Ke=function(e){var t=e.current;return{current:t,version:e.version,isEdge:Xe("Edge",t),isChrome:Xe("Chrome",t),isIE:Xe("IE",t),isOpera:Xe("Opera",t),isFirefox:Xe(Ye,t),isSafari:Xe("Safari",t)}},Je={unknown:function(){return Ke({current:undefined,version:Ge.unknown()})},nu:Ke,edge:C("Edge"),chrome:C("Chrome"),ie:C("IE"),opera:C("Opera"),firefox:C(Ye),safari:C("Safari")},$e="Windows",Qe="Android",Ze="Solaris",et="FreeBSD",tt=function(e,t){return function(){return t===e}},nt=function(e){var t=e.current;return{current:t,version:e.version,isWindows:tt($e,t),isiOS:tt("iOS",t),isAndroid:tt(Qe,t),isOSX:tt("OSX",t),isLinux:tt("Linux",t),isSolaris:tt(Ze,t),isFreeBSD:tt(et,t)}},rt={unknown:function(){return nt({current:undefined,version:Ge.unknown()})},nu:nt,windows:C($e),ios:C("iOS"),android:C(Qe),linux:C("Linux"),osx:C("OSX"),solaris:C(Ze),freebsd:C(et)},ot=function(e,t){var n=String(t).toLowerCase();return _(e,function(e){return e.search(n)})},it=function(e,n){return ot(e,n).map(function(e){var t=Ge.detect(e.versionRegexes,n);return{current:e.name,version:t}})},ut=function(e,n){return ot(e,n).map(function(e){var t=Ge.detect(e.versionRegexes,n);return{current:e.name,version:t}})},at=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,ct=function(t){return function(e){return Ce(e,t)}},lt=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:function(e){return Ce(e,"edge/")&&Ce(e,"chrome")&&Ce(e,"safari")&&Ce(e,"applewebkit")}},{name:"Chrome",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,at],search:function(e){return Ce(e,"chrome")&&!Ce(e,"chromeframe")}},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:function(e){return Ce(e,"msie")||Ce(e,"trident")}},{name:"Opera",versionRegexes:[at,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:ct("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:ct("firefox")},{name:"Safari",versionRegexes:[at,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:function(e){return(Ce(e,"safari")||Ce(e,"mobile/"))&&Ce(e,"applewebkit")}}],ft=[{name:"Windows",search:ct("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:function(e){return Ce(e,"iphone")||Ce(e,"ipad")},versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:ct("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"OSX",search:ct("os x"),versionRegexes:[/.*?os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:ct("linux"),versionRegexes:[]},{name:"Solaris",search:ct("sunos"),versionRegexes:[]},{name:"FreeBSD",search:ct("freebsd"),versionRegexes:[]}],st={browsers:C(lt),oses:C(ft)},dt=function(e){var t,n,r,o,i,u,a,c,l,f,s,d=st.browsers(),m=st.oses(),g=it(d,e).fold(Je.unknown,Je.nu),h=ut(m,e).fold(rt.unknown,rt.nu);return{browser:g,os:h,deviceType:(n=g,r=e,o=(t=h).isiOS()&&!0===/ipad/i.test(r),i=t.isiOS()&&!o,u=t.isAndroid()&&3===t.version.major,a=t.isAndroid()&&4===t.version.major,c=o||u||a&&!0===/mobile/i.test(r),l=t.isiOS()||t.isAndroid(),f=l&&!c,s=n.isSafari()&&t.isiOS()&&!1===/safari/i.test(r),{isiPad:C(o),isiPhone:C(i),isTablet:C(c),isPhone:C(f),isTouch:C(l),isAndroid:t.isAndroid,isiOS:t.isiOS,isWebView:C(s)})}},mt={detect:Re(function(){var e=m.navigator.userAgent;return dt(e)})},gt=ue,ht=ie,pt=function(e,t){var n=e.dom();if(n.nodeType!==gt)return!1;if(n.matches!==undefined)return n.matches(t);if(n.msMatchesSelector!==undefined)return n.msMatchesSelector(t);if(n.webkitMatchesSelector!==undefined)return n.webkitMatchesSelector(t);if(n.mozMatchesSelector!==undefined)return n.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")},vt=function(e){return e.nodeType!==gt&&e.nodeType!==ht||0===e.childElementCount},bt=function(e,t){return e.dom()===t.dom()},wt=mt.detect().browser.isIE()?function(e,t){return He(e.dom(),t.dom())}:function(e,t){var n=e.dom(),r=t.dom();return n!==r&&n.contains(r)},yt=pt,xt=function(e){return De.fromDom(e.dom().ownerDocument)},Ct=function(e){var t=e.dom();return S.from(t.parentNode).map(De.fromDom)},St=function(e,t){for(var n=D(t)?t:C(!1),r=e.dom(),o=[];null!==r.parentNode&&r.parentNode!==undefined;){var i=r.parentNode,u=De.fromDom(i);if(o.push(u),!0===n(u))break;r=i}return o},Rt=function(e){var t=e.dom();return S.from(t.previousSibling).map(De.fromDom)},Tt=function(e){var t=e.dom();return S.from(t.nextSibling).map(De.fromDom)},Dt=function(e){var t=e.dom();return k(t.childNodes,De.fromDom)},Ot=function(e,t){var n=e.dom().childNodes;return S.from(n[t]).map(De.fromDom)},Nt=($("element","offset"),function(t,n){Ct(t).each(function(e){e.dom().insertBefore(n.dom(),t.dom())})}),Et=function(e,t){Tt(e).fold(function(){Ct(e).each(function(e){kt(e,t)})},function(e){Nt(e,t)})},At=function(t,n){Ot(t,0).fold(function(){kt(t,n)},function(e){t.dom().insertBefore(n.dom(),e.dom())})},kt=function(e,t){e.dom().appendChild(t.dom())},Pt=function(e,t){Nt(e,t),kt(t,e)},It=function(r,o){P(o,function(e,t){var n=0===t?r:o[t-1];Et(n,e)})},Bt=function(t,e){P(e,function(e){kt(t,e)})},Wt=function(e){e.dom().textContent="",P(Dt(e),function(e){Mt(e)})},Mt=function(e){var t=e.dom();null!==t.parentNode&&t.parentNode.removeChild(t)},_t=function(e){var t,n=Dt(e);0<n.length&&(t=e,P(n,function(e){Nt(t,e)})),Mt(e)},Lt=$("width","height"),jt=$("rows","columns"),Ft=$("row","column"),zt=$("x","y"),Ht=$("element","rowspan","colspan"),Ut=$("element","rowspan","colspan","isNew"),qt={dimensions:Lt,grid:jt,address:Ft,coords:zt,extended:$("element","rowspan","colspan","row","column"),detail:Ht,detailnew:Ut,rowdata:$("element","cells","section"),elementnew:$("element","isNew"),rowdatanew:$("element","cells","section","isNew"),rowcells:$("cells","section"),rowdetails:$("details","section"),bounds:$("startRow","startCol","finishRow","finishCol")},Vt=function(e,t){var n=[];return P(Dt(e),function(e){t(e)&&(n=n.concat([e])),n=n.concat(Vt(e,t))}),n},Gt=function(e,t,n){return r=function(e){return pt(e,t)},I(St(e,n),r);var r},Yt=function(e,t){return n=function(e){return pt(e,t)},I(Dt(e),n);var n},Xt=function(e,t){return n=t,o=(r=e)===undefined?m.document:r.dom(),vt(o)?[]:k(o.querySelectorAll(n),De.fromDom);var n,r,o};function Kt(e,t,n,r,o){return e(n,r)?S.some(n):D(o)&&o(n)?S.none():t(n,r,o)}var Jt,$t,Qt,Zt,en,tn=function(e,t,n){for(var r=e.dom(),o=D(n)?n:C(!1);r.parentNode;){r=r.parentNode;var i=De.fromDom(r);if(t(i))return S.some(i);if(o(i))break}return S.none()},nn=function(e,t,n){return tn(e,function(e){return pt(e,t)},n)},rn=function(e,t){return n=function(e){return pt(e,t)},_(e.dom().childNodes,x(n,De.fromDom)).map(De.fromDom);var n},on=function(e,t){return n=t,o=(r=e)===undefined?m.document:r.dom(),vt(o)?S.none():S.from(o.querySelector(n)).map(De.fromDom);var n,r,o},un=function(e,t,n){return Kt(pt,nn,e,t,n)},an=function(e,t,n){return H(Dt(e),function(e){return pt(e,t)?n(e)?[e]:[]:an(e,t,n)})},cn={firstLayer:function(e,t){return an(e,t,C(!0))},filterFirstLayer:an},ln=function(e,t,n){var r=n!==undefined?n:C(!1);return r(t)?S.none():E(e,ce(t))?S.some(t):nn(t,e.join(","),function(e){return pt(e,"table")||r(e)})},fn=function(t,e){return Ct(e).map(function(e){return Yt(e,t)})},sn=b(fn,"th,td"),dn=b(fn,"tr"),mn=function(e,t){return parseInt(be(e,t),10)},gn={cell:function(e,t){return ln(["td","th"],e,t)},firstCell:function(e){return on(e,"th,td")},cells:function(e){return cn.firstLayer(e,"th,td")},neighbourCells:sn,table:function(e,t){return un(e,"table",t)},row:function(e,t){return ln(["tr"],e,t)},rows:function(e){return cn.firstLayer(e,"tr")},notCell:function(e,t){return ln(["caption","tr","tbody","tfoot","thead"],e,t)},neighbourRows:dn,attr:mn,grid:function(e,t,n){var r=mn(e,t),o=mn(e,n);return qt.grid(r,o)}},hn=function(e){var t=gn.rows(e);return k(t,function(e){var t=e,n=Ct(t).map(function(e){var t=ce(e);return"tfoot"===t||"thead"===t||"tbody"===t?t:"tbody"}).getOr("tbody"),r=k(gn.cells(e),function(e){var t=we(e,"rowspan")?parseInt(be(e,"rowspan"),10):1,n=we(e,"colspan")?parseInt(be(e,"colspan"),10):1;return qt.detail(e,t,n)});return qt.rowdata(t,r,n)})},pn=function(e,n){return k(e,function(e){var t=k(gn.cells(e),function(e){var t=we(e,"rowspan")?parseInt(be(e,"rowspan"),10):1,n=we(e,"colspan")?parseInt(be(e,"colspan"),10):1;return qt.detail(e,t,n)});return qt.rowdata(e,t,n.section())})},vn=function(e,t){return e+","+t},bn=function(e,t){var n=H(e.all(),function(e){return e.cells()});return I(n,t)},wn={generate:function(e){var f={},t=[],n=e.length,s=0;P(e,function(e,c){var l=[];P(e.cells(),function(e,t){for(var n=0;f[vn(c,n)]!==undefined;)n++;for(var r=qt.extended(e.element(),e.rowspan(),e.colspan(),c,n),o=0;o<e.colspan();o++)for(var i=0;i<e.rowspan();i++){var u=n+o,a=vn(c+i,u);f[a]=r,s=Math.max(s,u+1)}l.push(r)}),t.push(qt.rowdata(e.element(),l,e.section()))});var r=qt.grid(n,s);return{grid:C(r),access:C(f),all:C(t)}},getAt:function(e,t,n){var r=e.access()[vn(t,n)];return r!==undefined?S.some(r):S.none()},findItem:function(e,t,n){var r=bn(e,function(e){return n(t,e.element())});return 0<r.length?S.some(r[0]):S.none()},filterItems:bn,justCells:function(e){var t=k(e.all(),function(e){return e.cells()});return z(t)}},yn=$("minRow","minCol","maxRow","maxCol"),xn=function(e,t){var n,i,r,u,a,c,l,o,f,s,d=function(e){return pt(e.element(),t)},m=hn(e),g=wn.generate(m),h=(i=d,r=(n=g).grid().columns(),u=n.grid().rows(),a=r,l=c=0,X(n.access(),function(e){if(i(e)){var t=e.row(),n=t+e.rowspan()-1,r=e.column(),o=r+e.colspan()-1;t<u?u=t:c<n&&(c=n),r<a?a=r:l<o&&(l=o)}}),yn(u,a,c,l)),p="th:not("+t+"),td:not("+t+")",v=cn.filterFirstLayer(e,"th,td",function(e){return pt(e,p)});return P(v,Mt),function(e,t,n,r){for(var o,i,u,a=t.grid().columns(),c=t.grid().rows(),l=0;l<c;l++)for(var f=!1,s=0;s<a;s++)l<n.minRow()||l>n.maxRow()||s<n.minCol()||s>n.maxCol()||(wn.getAt(t,l,s).filter(r).isNone()?(o=f,i=e[l].element(),u=De.fromTag("td"),kt(u,De.fromTag("br")),(o?kt:At)(i,u)):f=!0)}(m,g,h,d),o=e,f=h,s=I(cn.firstLayer(o,"tr"),function(e){return 0===e.dom().childElementCount}),P(s,Mt),f.minCol()!==f.maxCol()&&f.minRow()!==f.maxRow()||P(cn.firstLayer(o,"th,td"),function(e){ye(e,"rowspan"),ye(e,"colspan")}),ye(o,"width"),ye(o,"height"),Me(o,"width"),Me(o,"height"),e},Cn=(Jt=me,$t="text",Qt=function(e){return Jt(e)?S.from(e.dom().nodeValue):S.none()},Zt=mt.detect().browser,{get:function(e){if(!Jt(e))throw new Error("Can only get "+$t+" value of a "+$t+" node");return en(e).getOr("")},getOption:en=Zt.isIE()&&10===Zt.version.major?function(e){try{return Qt(e)}catch(t){return S.none()}}:Qt,set:function(e,t){if(!Jt(e))throw new Error("Can only set raw "+$t+" value of a "+$t+" node");e.dom().nodeValue=t}}),Sn=function(e){return Cn.get(e)},Rn=function(e){return Cn.getOption(e)},Tn=function(e,t){Cn.set(e,t)},Dn=function(e){return"img"===ce(e)?1:Rn(e).fold(function(){return Dt(e).length},function(e){return e.length})},On=["img","br"],Nn=function(e){return Rn(e).filter(function(e){return 0!==e.trim().length||-1<e.indexOf("\xa0")}).isSome()||E(On,ce(e))},En=function(e){return r=Nn,(o=function(e){for(var t=0;t<e.childNodes.length;t++){if(r(De.fromDom(e.childNodes[t])))return S.some(De.fromDom(e.childNodes[t]));var n=o(e.childNodes[t]);if(n.isSome())return n}return S.none()})(e.dom());var r,o},An=function(e){return kn(e,Nn)},kn=function(e,i){var u=function(e){for(var t=Dt(e),n=t.length-1;0<=n;n--){var r=t[n];if(i(r))return S.some(r);var o=u(r);if(o.isSome())return o}return S.none()};return u(e)},Pn=function(e,t){return De.fromDom(e.dom().cloneNode(t))},In=function(e){return Pn(e,!1)},Bn=function(e){return Pn(e,!0)},Wn=function(e,t){var n,r,o,i,u=(n=e,r=t,o=De.fromTag(r),i=xe(n),ve(o,i),o),a=Dt(Bn(e));return Bt(u,a),u},Mn=function(){var e=De.fromTag("td");return kt(e,De.fromTag("br")),e},_n=function(e,t,n){var r=Wn(e,t);return X(n,function(e,t){null===e?ye(r,t):pe(r,t,e)}),r},Ln=function(e){return e},jn=function(e){return function(){return De.fromTag("tr",e.dom())}},Fn=function(d,e,m){return{row:jn(e),cell:function(e){var r,o,i,t,n,u,a,c=xt(e.element()),l=De.fromTag(ce(e.element()),c.dom()),f=m.getOr(["strong","em","b","i","span","font","h1","h2","h3","h4","h5","h6","p","div"]),s=0<f.length?(r=e.element(),o=l,i=f,En(r).map(function(e){var t=i.join(","),n=Gt(e,t,function(e){return bt(e,r)});return W(n,function(e,t){var n=In(t);return ye(n,"contenteditable"),kt(e,n),n},o)}).getOr(o)):l;return kt(s,De.fromTag("br")),t=e.element(),n=l,u=t.dom(),a=n.dom(),Se(u)&&Se(a)&&(a.style.cssText=u.style.cssText),Me(l,"height"),1!==e.colspan()&&Me(e.element(),"width"),d(e.element(),l),l},replace:_n,gap:Mn}},zn=function(e){return{row:jn(e),cell:Mn,replace:Ln,gap:Mn}},Hn=["body","p","div","article","aside","figcaption","figure","footer","header","nav","section","ol","ul","li","table","thead","tbody","tfoot","caption","tr","td","th","h1","h2","h3","h4","h5","h6","blockquote","pre","address"];function Un(){return{up:C({selector:nn,closest:un,predicate:tn,all:St}),down:C({selector:Xt,predicate:Vt}),styles:C({get:Ie,getRaw:We,set:ke,remove:Me}),attrs:C({get:be,set:pe,remove:ye,copyTo:function(e,t){var n=xe(e);ve(t,n)}}),insert:C({before:Nt,after:Et,afterAll:It,append:kt,appendAll:Bt,prepend:At,wrap:Pt}),remove:C({unwrap:_t,remove:Mt}),create:C({nu:De.fromTag,clone:function(e){return De.fromDom(e.dom().cloneNode(!1))},text:De.fromText}),query:C({comparePosition:function(e,t){return e.dom().compareDocumentPosition(t.dom())},prevSibling:Rt,nextSibling:Tt}),property:C({children:Dt,name:ce,parent:Ct,document:function(e){return e.dom().ownerDocument},isText:me,isComment:se,isElement:de,getText:Sn,setText:Tn,isBoundary:function(e){return!!de(e)&&("body"===ce(e)||E(Hn,ce(e)))},isEmptyTag:function(e){return!!de(e)&&E(["br","img","hr","input"],ce(e))}}),eq:bt,is:yt}}var qn=$("left","right"),Vn=function(e,t,n){var r=e.property().children(t);return L(r,b(e.eq,n)).map(function(e){return{before:C(r.slice(0,e)),after:C(r.slice(e+1))}})},Gn=function(n,r,o){return Vn(n,r,o).map(function(e){var t=n.create().clone(r);return n.insert().appendAll(t,e.before().concat([o])),n.insert().appendAll(r,e.after()),n.insert().before(r,t),qn(t,r)})},Yn=function(n,r,e){return Vn(n,r,e).map(function(e){var t=n.create().clone(r);return n.insert().appendAll(t,e.after()),n.insert().after(r,t),qn(r,t)})},Xn=function(i,e,u,a){var r=$("first","second","splits"),c=function(e,t,o){var n=r(e,S.none(),o);return u(e)?r(e,t,o):i.property().parent(e).bind(function(r){return a(i,r,e).map(function(e){var t=[{first:e.left,second:e.right}],n=u(r)?r:e.left();return c(n,S.some(e.right()),o.concat(t))}).getOr(n)})};return c(e,S.none(),[])},Kn=function(r,o,e,t){var n=o(r,e);return W(t,function(e,t){var n=o(r,t);return Jn(r,e,n)},n)},Jn=function(t,e,n){return e.bind(function(e){return n.filter(b(t.eq,e))})},$n=function(e,t,n){return 0<n.length?Kn(e,t,(r=n)[0],r.slice(1)):S.none();var r},Qn=function(e,t){return b(e.eq,t)},Zn=function(t,e,n,r){var o=r!==undefined?r:C(!1),i=[e].concat(t.up().all(e)),u=[n].concat(t.up().all(n)),a=function(t){return L(t,o).fold(function(){return t},function(e){return t.slice(0,e+1)})},c=a(i),l=a(u),f=_(c,function(e){return A(l,Qn(t,e))});return{firstpath:C(c),secondpath:C(l),shared:C(f)}},er=function(t,e,n){var r=Zn(t,e,n);return r.shared().bind(function(e){return function(o,i,e,t){var u=o.property().children(i);if(o.eq(i,e[0]))return S.some([e[0]]);if(o.eq(i,t[0]))return S.some([t[0]]);var n=function(e){var t=V(e),n=L(t,Qn(o,i)).getOr(-1),r=n<t.length-1?t[n+1]:t[n];return L(u,Qn(o,r))},r=n(e),a=n(t);return r.bind(function(r){return a.map(function(e){var t=Math.min(r,e),n=Math.max(r,e);return u.slice(t,n+1)})})}(t,e,r.firstpath(),r.secondpath())})},tr=Zn,nr=function(e,t,n){return $n(e,t,n)},rr=function(e,t,n){return er(e,t,n)},or=function(e,t,n,r){return tr(e,t,n,r)},ir=function(e,t,n){return Gn(e,t,n)},ur=function(e,t,n){return Yn(e,t,n)},ar=function(e,t,n,r){return Xn(e,t,n,r)},cr=Un(),lr={sharedOne:function(n,e){return nr(cr,function(e,t){return n(t)},e)},subset:function(e,t){return rr(cr,e,t)},ancestors:function(e,t,n){return or(cr,e,t,n)},breakToLeft:function(e,t){return ir(cr,e,t)},breakToRight:function(e,t){return ur(cr,e,t)},breakPath:function(e,t,r){return ar(cr,e,t,function(e,t,n){return r(t,n)})}},fr=function(e,t){return t.column()>=e.startCol()&&t.column()+t.colspan()-1<=e.finishCol()&&t.row()>=e.startRow()&&t.row()+t.rowspan()-1<=e.finishRow()},sr=function(e,t){var n=t.column(),r=t.column()+t.colspan()-1,o=t.row(),i=t.row()+t.rowspan()-1;return n<=e.finishCol()&&r>=e.startCol()&&o<=e.finishRow()&&i>=e.startRow()},dr=function(e,t){for(var n=!0,r=b(fr,t),o=t.startRow();o<=t.finishRow();o++)for(var i=t.startCol();i<=t.finishCol();i++)n=n&&wn.getAt(e,o,i).exists(r);return n?S.some(t):S.none()},mr=function(e,t,n){var r=wn.findItem(e,t,bt),o=wn.findItem(e,n,bt);return r.bind(function(r){return o.map(function(e){return t=r,n=e,qt.bounds(Math.min(t.row(),n.row()),Math.min(t.column(),n.column()),Math.max(t.row()+t.rowspan()-1,n.row()+n.rowspan()-1),Math.max(t.column()+t.colspan()-1,n.column()+n.colspan()-1));var t,n})})},gr=mr,hr=function(t,e,n){return mr(t,e,n).bind(function(e){return dr(t,e)})},pr=function(r,e,o,i){return wn.findItem(r,e,bt).bind(function(e){var t=0<o?e.row()+e.rowspan()-1:e.row(),n=0<i?e.column()+e.colspan()-1:e.column();return wn.getAt(r,t+o,n+i).map(function(e){return e.element()})})},vr=function(n,e,t){return gr(n,e,t).map(function(e){var t=wn.filterItems(n,b(sr,e));return k(t,function(e){return e.element()})})},br=function(e,t){return wn.findItem(e,t,function(e,t){return wt(t,e)}).bind(function(e){return e.element()})},wr=function(e){var t=hn(e);return wn.generate(t)},yr=function(n,r,o){return gn.table(n).bind(function(e){var t=wr(e);return pr(t,n,r,o)})},xr=function(e,t,n){var r=wr(e);return vr(r,t,n)},Cr=function(e,t,n,r,o){var i=wr(e),u=bt(e,n)?t:br(i,t),a=bt(e,o)?r:br(i,r);return vr(i,u,a)},Sr=function(e,t,n){var r=wr(e);return hr(r,t,n)},Rr=function(e,t){return nn(e,"table")},Tr=re(["boxes","start","finish"],[]),Dr=function(a,c,r){var l=function(t){return function(e){return r(e)||bt(e,t)}};return bt(a,c)?S.some(Tr({boxes:S.some([a]),start:a,finish:c})):Rr(a).bind(function(u){return Rr(c).bind(function(i){if(bt(u,i))return S.some(Tr({boxes:xr(u,a,c),start:a,finish:c}));if(wt(u,i)){var e=0<(t=Gt(c,"td,th",l(u))).length?t[t.length-1]:c;return S.some(Tr({boxes:Cr(u,a,u,c,i),start:a,finish:e}))}if(wt(i,u)){var t,n=0<(t=Gt(a,"td,th",l(i))).length?t[t.length-1]:a;return S.some(Tr({boxes:Cr(i,a,u,c,i),start:a,finish:n}))}return lr.ancestors(a,c).shared().bind(function(e){return un(e,"table",r).bind(function(e){var t=Gt(c,"td,th",l(e)),n=0<t.length?t[t.length-1]:c,r=Gt(a,"td,th",l(e)),o=0<r.length?r[r.length-1]:a;return S.some(Tr({boxes:Cr(e,a,u,c,i),start:o,finish:n}))})})})})},Or=Dr,Nr=function(e,t){var n=Xt(e,t);return 0<n.length?S.some(n):S.none()},Er=function(e,t,n,r,o){return(i=e,u=o,_(i,function(e){return pt(e,u)})).bind(function(e){return yr(e,t,n).bind(function(e){return n=r,nn(t=e,"table").bind(function(e){return on(e,n).bind(function(e){return Dr(e,t).bind(function(t){return t.boxes().map(function(e){return{boxes:C(e),start:C(t.start()),finish:C(t.finish())}})})})});var t,n})});var i,u},Ar=function(e,t,r){return on(e,t).bind(function(n){return on(e,r).bind(function(t){return lr.sharedOne(Rr,[n,t]).map(function(e){return{first:C(n),last:C(t),table:C(e)}})})})},kr=function(e,t){return Nr(e,t)},Pr=function(o,e,t){return Ar(o,e,t).bind(function(n){var e=function(e){return bt(o,e)},t=nn(n.first(),"thead,tfoot,tbody,table",e),r=nn(n.last(),"thead,tfoot,tbody,table",e);return t.bind(function(t){return r.bind(function(e){return bt(t,e)?Sr(n.table(),n.first(),n.last()):S.none()})})})},Ir="data-mce-selected",Br="data-mce-first-selected",Wr="data-mce-last-selected",Mr={selected:C(Ir),selectedSelector:C("td[data-mce-selected],th[data-mce-selected]"),attributeSelector:C("[data-mce-selected]"),firstSelected:C(Br),firstSelectedSelector:C("td[data-mce-first-selected],th[data-mce-first-selected]"),lastSelected:C(Wr),lastSelectedSelector:C("td[data-mce-last-selected],th[data-mce-last-selected]")},_r=function(u){if(!R(u))throw new Error("cases must be an array");if(0===u.length)throw new Error("there must be at least one case");var a=[],n={};return P(u,function(e,r){var t=Y(e);if(1!==t.length)throw new Error("one and only one name per case");var o=t[0],i=e[o];if(n[o]!==undefined)throw new Error("duplicate key detected:"+o);if("cata"===o)throw new Error("cannot have a case named cata (sorry)");if(!R(i))throw new Error("case arguments must be an array");a.push(o),n[o]=function(){var e=arguments.length;if(e!==i.length)throw new Error("Wrong number of arguments to case "+o+". Expected "+i.length+" ("+i+"), got "+e);for(var n=new Array(e),t=0;t<n.length;t++)n[t]=arguments[t];return{fold:function(){if(arguments.length!==u.length)throw new Error("Wrong number of arguments to fold. Expected "+u.length+", got "+arguments.length);return arguments[r].apply(null,n)},match:function(e){var t=Y(e);if(a.length!==t.length)throw new Error("Wrong number of arguments to match. Expected: "+a.join(",")+"\nActual: "+t.join(","));if(!U(a,function(e){return E(t,e)}))throw new Error("Not all branches were specified when using match. Specified: "+t.join(", ")+"\nRequired: "+a.join(", "));return e[o].apply(null,n)},log:function(e){m.console.log(e,{constructors:a,constructor:o,params:n})}}}}),n},Lr=_r([{none:[]},{multiple:["elements"]},{single:["selection"]}]),jr={cata:function(e,t,n,r){return e.fold(t,n,r)},none:Lr.none,multiple:Lr.multiple,single:Lr.single},Fr=function(e,t){return jr.cata(t.get(),C([]),o,C([e]))},zr=function(n,e){return jr.cata(e.get(),S.none,function(t,e){return 0===t.length?S.none():Pr(n,Mr.firstSelectedSelector(),Mr.lastSelectedSelector()).bind(function(e){return 1<t.length?S.some({bounds:C(e),cells:C(t)}):S.none()})},S.none)},Hr=function(e,t){var n=Fr(e,t);return 0<n.length&&U(n,function(e){return we(e,"rowspan")&&1<parseInt(be(e,"rowspan"),10)||we(e,"colspan")&&1<parseInt(be(e,"colspan"),10)})?S.some(n):S.none()},Ur=Fr,qr=function(e){return{element:C(e),mergable:S.none,unmergable:S.none,selection:C([e])}},Vr=$("element","clipboard","generators"),Gr={noMenu:qr,forMenu:function(e,t,n){return{element:C(n),mergable:C(zr(t,e)),unmergable:C(Hr(n,e)),selection:C(Ur(n,e))}},notCell:function(e){return qr(e)},paste:Vr,pasteRows:function(e,t,n,r,o){return{element:C(n),mergable:S.none,unmergable:S.none,selection:C(Ur(n,e)),clipboard:C(r),generators:C(o)}}},Yr=function(f,e,s,d){f.on("BeforeGetContent",function(n){!0===n.selection&&jr.cata(e.get(),y,function(e){var t;n.preventDefault(),(t=e,gn.table(t[0]).map(Bn).map(function(e){return[xn(e,Mr.attributeSelector())]})).each(function(e){var t;n.content="text"===n.format?k(e,function(e){return e.dom().innerText}).join(""):(t=f,k(e,function(e){return t.selection.serializer.serialize(e.dom(),{})}).join(""))})},y)}),f.on("BeforeSetContent",function(l){!0===l.selection&&!0===l.paste&&S.from(f.dom.getParent(f.selection.getStart(),"th,td")).each(function(e){var c=De.fromDom(e);gn.table(c).each(function(t){var e,n,r,o=I((e=l.content,(r=(n||m.document).createElement("div")).innerHTML=e,Dt(De.fromDom(r))),function(e){return"meta"!==ce(e)});if(1===o.length&&"table"===ce(o[0])){l.preventDefault();var i=De.fromDom(f.getDoc()),u=zn(i),a=Gr.paste(c,o[0],u);s.pasteCells(t,a).each(function(e){f.selection.setRng(e),f.focus(),d.clear(t)})}})})})};function Xr(r,o){var e=function(e){var t=o(e);if(t<=0||null===t){var n=Ie(e,r);return parseFloat(n)||0}return t},i=function(o,e){return M(e,function(e,t){var n=Ie(o,t),r=n===undefined?0:parseInt(n,10);return isNaN(r)?e:e+r},0)};return{set:function(e,t){if(!O(t)&&!t.match(/^[0-9]+$/))throw new Error(r+".set accepts only positive integer values. Value was "+t);var n=e.dom();Se(n)&&(n.style[r]=t+"px")},get:e,getOuter:e,aggregate:i,max:function(e,t,n){var r=i(e,n);return r<t?t-r:0}}}var Kr=Xr("height",function(e){var t=e.dom();return Oe(e)?t.getBoundingClientRect().height:t.offsetHeight}),Jr=function(e){return Kr.get(e)},$r=function(e){return Kr.getOuter(e)},Qr=Xr("width",function(e){return e.dom().offsetWidth}),Zr=function(e){return Qr.get(e)},eo=function(e){return Qr.getOuter(e)},to=mt.detect(),no=function(e,t,n){return r=Ie(e,t),o=n,i=parseFloat(r),isNaN(i)?o:i;var r,o,i},ro=function(e){return to.browser.isIE()||to.browser.isEdge()?(n=no(t=e,"padding-top",0),r=no(t,"padding-bottom",0),o=no(t,"border-top-width",0),i=no(t,"border-bottom-width",0),u=t.dom().getBoundingClientRect().height,"border-box"===Ie(t,"box-sizing")?u:u-n-r-(o+i)):no(e,"height",Jr(e));var t,n,r,o,i,u},oo=/(\d+(\.\d+)?)(\w|%)*/,io=/(\d+(\.\d+)?)%/,uo=/(\d+(\.\d+)?)px|em/,ao=function(e,t){ke(e,"height",t+"px")},co=function(e,t,n,r){var o,i,u,a,c,l,f,s,d,m=parseInt(e,10);return s=l="%",d=(f=e).length-l.length,""!==s&&(f.length<s.length||f.substr(d,d+s.length)!==s)||"table"===ce(t)?m:(o=t,i=m,u=n,a=r,c=gn.table(o).map(function(e){var t=u(e);return Math.floor(i/100*t)}).getOr(i),a(o,c),c)},lo=function(e){var t,n=We(t=e,"height").getOrThunk(function(){return ro(t)+"px"});return n?co(n,e,Jr,ao):Jr(e)},fo=function(e,t){return we(e,t)?parseInt(be(e,t),10):1},so=function(e){return We(e,"width").fold(function(){return S.from(be(e,"width"))},function(e){return S.some(e)})},mo=function(e,t){return e/t.pixelWidth()*100},go={percentageBasedSizeRegex:C(io),pixelBasedSizeRegex:C(uo),setPixelWidth:function(e,t){ke(e,"width",t+"px")},setPercentageWidth:function(e,t){ke(e,"width",t+"%")},setHeight:ao,getPixelWidth:function(t,n){return so(t).fold(function(){return Zr(t)},function(e){return function(e,t,n){if(uo.test(t)){var r=uo.exec(t);return parseInt(r[1],10)}if(io.test(t)){var o=io.exec(t),i=parseFloat(o[1]);return i/100*n.pixelWidth()}return Zr(e)}(t,e,n)})},getPercentageWidth:function(t,n){return so(t).fold(function(){var e=Zr(t);return mo(e,n)},function(e){return function(e,t,n){if(io.test(t)){var r=io.exec(t);return parseFloat(r[1])}var o=Zr(e);return mo(o,n)}(t,e,n)})},getGenericWidth:function(e){return so(e).bind(function(e){if(oo.test(e)){var t=oo.exec(e);return S.some({width:C(t[1]),unit:C(t[3])})}return S.none()})},setGenericWidth:function(e,t,n){ke(e,"width",t+n)},getHeight:function(e){return n="rowspan",lo(t=e)/fo(t,n);var t,n},getRawWidth:so},ho=function(n,r){go.getGenericWidth(n).each(function(e){var t=e.width()/2;go.setGenericWidth(n,t,e.unit()),go.setGenericWidth(r,t,e.unit())})},po=function(n,r){return{left:C(n),top:C(r),translate:function(e,t){return po(n+e,r+t)}}},vo=po,bo=function(e,t){return e!==undefined?e:t!==undefined?t:0},wo=function(e){var t,n,r=e.dom().ownerDocument,o=r.body,i=(t=De.fromDom(r),(n=t.dom())===n.window&&t instanceof m.Window?t:ge(t)?n.defaultView||n.parentWindow:null),u=r.documentElement,a=bo(i.pageYOffset,u.scrollTop),c=bo(i.pageXOffset,u.scrollLeft),l=bo(u.clientTop,o.clientTop),f=bo(u.clientLeft,o.clientLeft);return yo(e).translate(c-f,a-l)},yo=function(e){var t,n,r,o=e.dom(),i=o.ownerDocument,u=i.body,a=De.fromDom(i.documentElement);return u===o?vo(u.offsetLeft,u.offsetTop):(t=e,n=a||De.fromDom(m.document.documentElement),tn(t,b(bt,n)).isSome()?(r=o.getBoundingClientRect(),vo(r.left,r.top)):vo(0,0))},xo=$("row","y"),Co=$("col","x"),So=function(e){return wo(e).left()+eo(e)},Ro=function(e){return wo(e).left()},To=function(e,t){return Co(e,Ro(t))},Do=function(e,t){return Co(e,So(t))},Oo=function(e){return wo(e).top()},No=function(n,t,r){if(0===r.length)return[];var e=k(r.slice(1),function(e,t){return e.map(function(e){return n(t,e)})}),o=r[r.length-1].map(function(e){return t(r.length-1,e)});return e.concat([o])},Eo={delta:o,positions:b(No,function(e,t){return xo(e,Oo(t))},function(e,t){return xo(e,Oo(t)+$r(t))}),edge:Oo},Ao={delta:o,edge:Ro,positions:b(No,To,Do)},ko={height:Eo,rtl:{delta:function(e,t){return-e},edge:So,positions:b(No,Do,To)},ltr:Ao},Po={ltr:ko.ltr,rtl:ko.rtl};function Io(t){var n=function(e){return t(e).isRtl()?Po.rtl:Po.ltr};return{delta:function(e,t){return n(t).delta(e,t)},edge:function(e){return n(e).edge(e)},positions:function(e,t){return n(t).positions(e)}}}var Bo,Wo=function(e){var t=hn(e);return wn.generate(t).grid()},Mo=Object.prototype.hasOwnProperty,_o=(Bo=function(e,t){return t},function(){for(var e=new Array(arguments.length),t=0;t<e.length;t++)e[t]=arguments[t];if(0===e.length)throw new Error("Can't merge zero objects");for(var n={},r=0;r<e.length;r++){var o=e[r];for(var i in o)Mo.call(o,i)&&(n[i]=Bo(n[i],o[i]))}return n}),Lo=function(e){for(var t=[],n=function(e){t.push(e)},r=0;r<e.length;r++)e[r].each(n);return t},jo=function(e,t){for(var n=0;n<e.length;n++){var r=t(e[n],n);if(r.isSome())return r}return S.none()},Fo=function(e,t,n,r){n===r?ye(e,t):pe(e,t,n)},zo=function(o,e){var i=[],u=[],t=function(e,t){0<e.length?function(e,t){var n=rn(o,t).getOrThunk(function(){var e=De.fromTag(t,xt(o).dom());return kt(o,e),e});Wt(n);var r=k(e,function(e){e.isNew()&&i.push(e.element());var t=e.element();return Wt(t),P(e.cells(),function(e){e.isNew()&&u.push(e.element()),Fo(e.element(),"colspan",e.colspan(),1),Fo(e.element(),"rowspan",e.rowspan(),1),kt(t,e.element())}),t});Bt(n,r)}(e,t):rn(o,t).each(Mt)},n=[],r=[],a=[];return P(e,function(e){switch(e.section()){case"thead":n.push(e);break;case"tbody":r.push(e);break;case"tfoot":a.push(e)}}),t(n,"thead"),t(r,"tbody"),t(a,"tfoot"),{newRows:C(i),newCells:C(u)}},Ho=function(e){return k(e,function(e){var n=In(e.element());return P(e.cells(),function(e){var t=Bn(e.element());Fo(t,"colspan",e.colspan(),1),Fo(t,"rowspan",e.rowspan(),1),kt(n,t)}),n})},Uo=function(e,t){var n=be(e,t);return n===undefined||""===n?[]:n.split(" ")},qo=function(e){return e.dom().classList!==undefined},Vo=function(e,t){return o=t,i=Uo(n=e,r="class").concat([o]),pe(n,r,i.join(" ")),!0;var n,r,o,i},Go=function(e,t){return o=t,0<(i=I(Uo(n=e,r="class"),function(e){return e!==o})).length?pe(n,r,i.join(" ")):ye(n,r),!1;var n,r,o,i},Yo=function(e,t){qo(e)?e.dom().classList.add(t):Vo(e,t)},Xo=function(e){0===(qo(e)?e.dom().classList:Uo(e,"class")).length&&ye(e,"class")},Ko=function(e,t){return qo(e)&&e.dom().classList.contains(t)},Jo=function(e,t){for(var n=[],r=0;r<e;r++)n.push(t(r));return n},$o=function(e,t){for(var n=[],r=e;r<t;r++)n.push(r);return n},Qo=function(t,n){if(n<0||n>=t.length-1)return S.none();var e=t[n].fold(function(){var e=V(t.slice(0,n));return jo(e,function(e,t){return e.map(function(e){return{value:e,delta:t+1}})})},function(e){return S.some({value:e,delta:0})}),r=t[n+1].fold(function(){var e=t.slice(n+1);return jo(e,function(e,t){return e.map(function(e){return{value:e,delta:t+1}})})},function(e){return S.some({value:e,delta:1})});return e.bind(function(n){return r.map(function(e){var t=e.delta+n.delta;return Math.abs(e.value-n.value)/t})})},Zo=function(e,t,n){var r=e();return _(r,t).orThunk(function(){return S.from(r[0]).orThunk(n)}).map(function(e){return e.element()})},ei=function(n){var e=n.grid(),t=$o(0,e.columns()),r=$o(0,e.rows());return k(t,function(t){return Zo(function(){return H(r,function(e){return wn.getAt(n,e,t).filter(function(e){return e.column()===t}).fold(C([]),function(e){return[e]})})},function(e){return 1===e.colspan()},function(){return wn.getAt(n,0,t)})})},ti=function(n){var e=n.grid(),t=$o(0,e.rows()),r=$o(0,e.columns());return k(t,function(t){return Zo(function(){return H(r,function(e){return wn.getAt(n,t,e).filter(function(e){return e.row()===t}).fold(C([]),function(e){return[e]})})},function(e){return 1===e.rowspan()},function(){return wn.getAt(n,t,0)})})},ni=function(e){var t=e.replace(/\./g,"-");return{resolve:function(e){return t+"-"+e}}},ri={resolve:ni("ephox-snooker").resolve},oi=function(e,t,n,r,o){var i=De.fromTag("div");return Pe(i,{position:"absolute",left:t-r/2+"px",top:n+"px",height:o+"px",width:r+"px"}),ve(i,{"data-column":e,role:"presentation"}),i},ii=function(e,t,n,r,o){var i=De.fromTag("div");return Pe(i,{position:"absolute",left:t+"px",top:n-o/2+"px",height:o+"px",width:r+"px"}),ve(i,{"data-row":e,role:"presentation"}),i},ui=ri.resolve("resizer-bar"),ai=ri.resolve("resizer-rows"),ci=ri.resolve("resizer-cols"),li=function(e){var t=Xt(e.parent(),"."+ui);P(t,Mt)},fi=function(n,e,r){var o=n.origin();P(e,function(e,t){e.each(function(e){var t=r(o,e);Yo(t,ui),kt(n.parent(),t)})})},si=function(e,t,n,r,o,i){var u,a,c,l,f=wo(t),s=0<n.length?o.positions(n,t):[];u=e,a=s,c=f,l=eo(t),fi(u,a,function(e,t){var n=ii(t.row(),c.left()-e.left(),t.y()-e.top(),l,7);return Yo(n,ai),n});var d,m,g,h,p=0<r.length?i.positions(r,t):[];d=e,m=p,g=f,h=$r(t),fi(d,m,function(e,t){var n=oi(t.col(),t.x()-e.left(),g.top()-e.top(),7,h);return Yo(n,ci),n})},di=function(e,t){var n=Xt(e.parent(),"."+ui);P(n,t)},mi={refresh:function(e,t,n,r){li(e);var o=hn(t),i=wn.generate(o),u=ti(i),a=ei(i);si(e,t,u,a,n,r)},hide:function(e){di(e,function(e){ke(e,"display","none")})},show:function(e){di(e,function(e){ke(e,"display","block")})},destroy:li,isRowBar:function(e){return Ko(e,ai)},isColBar:function(e){return Ko(e,ci)}},gi=function(e,t){return qt.rowcells(t,e.section())},hi=function(e,t){return e.cells()[t]},pi={addCell:function(e,t,n){var r=e.cells(),o=r.slice(0,t),i=r.slice(t),u=o.concat([n]).concat(i);return gi(e,u)},setCells:gi,mutateCell:function(e,t,n){e.cells()[t]=n},getCell:hi,getCellElement:function(e,t){return hi(e,t).element()},mapCells:function(e,t){var n=e.cells(),r=k(n,t);return qt.rowcells(r,e.section())},cellLength:function(e){return e.cells().length}},vi=function(e,t){if(0===e.length)return 0;var n=e[0];return L(e,function(e){return!t(n.element(),e.element())}).fold(function(){return e.length},function(e){return e})},bi=function(e,t,n,r){var o,i,u,a,c=(o=e,i=t,o[i]).cells().slice(n),l=vi(c,r),f=(u=e,a=n,k(u,function(e){return pi.getCell(e,a)})).slice(t),s=vi(f,r);return{colspan:C(l),rowspan:C(s)}},wi=function(o,i){var u=k(o,function(e,t){return k(e.cells(),function(e,t){return!1})});return k(o,function(e,r){var t=H(e.cells(),function(e,t){if(!1===u[r][t]){var n=bi(o,r,t,i);return function(e,t,n,r){for(var o=e;o<e+n;o++)for(var i=t;i<t+r;i++)u[o][i]=!0}(r,t,n.rowspan(),n.colspan()),[qt.detailnew(e.element(),n.rowspan(),n.colspan(),e.isNew())]}return[]});return qt.rowdetails(t,e.section())})},yi=function(e,t,n){for(var r=[],o=0;o<e.grid().rows();o++){for(var i=[],u=0;u<e.grid().columns();u++){var a=wn.getAt(e,o,u).map(function(e){return qt.elementnew(e.element(),n)}).getOrThunk(function(){return qt.elementnew(t.gap(),!0)});i.push(a)}var c=qt.rowcells(i,e.all()[o].section());r.push(c)}return r},xi=function(e,r){return k(e,function(e){var t,n=(t=e.details(),jo(t,function(e){return Ct(e.element()).map(function(e){var t=Ct(e).isNone();return qt.elementnew(e,t)})}).getOrThunk(function(){return qt.elementnew(r.row(),!0)}));return qt.rowdatanew(n.element(),e.details(),e.section(),n.isNew())})},Ci=function(e,t){var n=wi(e,bt);return xi(n,t)},Si=function(e,t){var n=z(k(e.all(),function(e){return e.cells()}));return _(n,function(e){return bt(t,e.element())})},Ri=function(a,c,l,f,s){return function(n,r,e,o,i){var t=hn(r),u=wn.generate(t);return c(u,e).map(function(e){var t=yi(u,o,!1),n=a(t,e,bt,s(o)),r=Ci(n.grid(),o);return{grid:C(r),cursor:n.cursor}}).fold(function(){return S.none()},function(e){var t=zo(r,e.grid());return l(r,e.grid(),i),f(r),mi.refresh(n,r,ko.height,i),S.some({cursor:e.cursor,newRows:t.newRows,newCells:t.newCells})})}},Ti=Ci,Di=function(t,e){return gn.cell(e.element()).bind(function(e){return Si(t,e)})},Oi=function(t,e){var n=k(e.selection(),function(e){return gn.cell(e).bind(function(e){return Si(t,e)})}),r=Lo(n);return 0<r.length?S.some(r):S.none()},Ni=function(t,n){return gn.cell(n.element()).bind(function(e){return Si(t,e).map(function(e){return _o(e,{generators:n.generators,clipboard:n.clipboard})})})},Ei=function(t,e){var n=k(e.selection(),function(e){return gn.cell(e).bind(function(e){return Si(t,e)})}),r=Lo(n);return 0<r.length?S.some(_o({cells:r},{generators:e.generators,clipboard:e.clipboard})):S.none()},Ai=function(e,t){return t.mergable()},ki=function(e,t){return t.unmergable()},Pi=function(n){return{is:function(e){return n===e},isValue:l,isError:a,getOr:C(n),getOrThunk:C(n),getOrDie:C(n),or:function(e){return Pi(n)},orThunk:function(e){return Pi(n)},fold:function(e,t){return t(n)},map:function(e){return Pi(e(n))},mapError:function(e){return Pi(n)},each:function(e){e(n)},bind:function(e){return e(n)},exists:function(e){return e(n)},forall:function(e){return e(n)},toOption:function(){return S.some(n)}}},Ii=function(n){return{is:a,isValue:a,isError:l,getOr:o,getOrThunk:function(e){return e()},getOrDie:function(){return e=String(n),function(){throw new Error(e)}();var e},or:function(e){return e},orThunk:function(e){return e()},fold:function(e,t){return e(n)},map:function(e){return Ii(n)},mapError:function(e){return Ii(e(n))},each:y,bind:function(e){return Ii(n)},exists:a,forall:l,toOption:S.none}},Bi={value:Pi,error:Ii},Wi=function(e,t){return k(e,function(){return qt.elementnew(t.cell(),!0)})},Mi=function(t,e,n){return t.concat(Jo(e,function(e){return pi.setCells(t[t.length-1],Wi(t[t.length-1].cells(),n))}))},_i=function(e,t,n){return k(e,function(e){return pi.setCells(e,e.cells().concat(Wi($o(0,t),n)))})},Li=function(e,t,n){if(e.row()>=t.length||e.column()>pi.cellLength(t[0]))return Bi.error("invalid start address out of table bounds, row: "+e.row()+", column: "+e.column());var r=t.slice(e.row()),o=r[0].cells().slice(e.column()),i=pi.cellLength(n[0]),u=n.length;return Bi.value({rowDelta:C(r.length-u),colDelta:C(o.length-i)})},ji=function(e,t){var n=pi.cellLength(e[0]),r=pi.cellLength(t[0]);return{rowDelta:C(0),colDelta:C(n-r)}},Fi=function(e,t,n){var r=t.colDelta()<0?_i:o;return(t.rowDelta()<0?Mi:o)(r(e,Math.abs(t.colDelta()),n),Math.abs(t.rowDelta()),n)},zi=function(e,t,n,r){if(0===e.length)return e;for(var o=t.startRow();o<=t.finishRow();o++)for(var i=t.startCol();i<=t.finishCol();i++)pi.mutateCell(e[o],i,qt.elementnew(r(),!1));return e},Hi=function(e,t,n,r){for(var o=!0,i=0;i<e.length;i++)for(var u=0;u<pi.cellLength(e[0]);u++){var a=n(pi.getCellElement(e[i],u),t);!0===a&&!1===o?pi.mutateCell(e[i],u,qt.elementnew(r(),!0)):!0===a&&(o=!1)}return e},Ui=function(i,n,u,a){if(0<n&&n<i.length){var e=i[n-1].cells(),t=(r=u,M(e,function(e,t){return A(e,function(e){return r(e.element(),t.element())})?e:e.concat([t])},[]));P(t,function(r){for(var o=S.none(),e=function(n){for(var e=function(t){var e=i[n].cells()[t];u(e.element(),r.element())&&(o.isNone()&&(o=S.some(a())),o.each(function(e){pi.mutateCell(i[n],t,qt.elementnew(e,!0))}))},t=0;t<pi.cellLength(i[0]);t++)e(t)},t=n;t<i.length;t++)e(t)})}var r;return i},qi=function(n,r,o,i,u){return Li(n,r,o).map(function(e){var t=Fi(r,e,i);return function(e,t,n,r,o){for(var i,u,a,c,l,f=e.row(),s=e.column(),d=f+n.length,m=s+pi.cellLength(n[0]),g=f;g<d;g++)for(var h=s;h<m;h++){i=t,u=g,a=h,c=void 0,c=b(o,pi.getCell(i[u],a).element()),l=i[u],1<i.length&&1<pi.cellLength(l)&&(0<a&&c(pi.getCellElement(l,a-1))||a<l.length-1&&c(pi.getCellElement(l,a+1))||0<u&&c(pi.getCellElement(i[u-1],a))||u<i.length-1&&c(pi.getCellElement(i[u+1],a)))&&Hi(t,pi.getCellElement(t[g],h),o,r.cell);var p=pi.getCellElement(n[g-f],h-s),v=r.replace(p);pi.mutateCell(t[g],h,qt.elementnew(v,!0))}return t}(n,t,o,i,u)})},Vi=function(e,t,n,r,o){Ui(t,e,o,r.cell);var i=ji(n,t),u=Fi(n,i,r),a=ji(t,u),c=Fi(t,a,r);return c.slice(0,e).concat(u).concat(c.slice(e,c.length))},Gi=function(n,r,e,o,i){var t=n.slice(0,r),u=n.slice(r),a=pi.mapCells(n[e],function(e,t){return 0<r&&r<n.length&&o(pi.getCellElement(n[r-1],t),pi.getCellElement(n[r],t))?pi.getCell(n[r],t):qt.elementnew(i(e.element(),o),!0)});return t.concat([a]).concat(u)},Yi=function(e,n,r,o,i){return k(e,function(e){var t=0<n&&n<pi.cellLength(e)&&o(pi.getCellElement(e,n-1),pi.getCellElement(e,n))?pi.getCell(e,n):qt.elementnew(i(pi.getCellElement(e,r),o),!0);return pi.addCell(e,n,t)})},Xi=function(e,r,o,i,u){var a=o+1;return k(e,function(e,t){var n=t===r?qt.elementnew(u(pi.getCellElement(e,o),i),!0):pi.getCell(e,o);return pi.addCell(e,a,n)})},Ki=function(e,t,n,r,o){var i=t+1,u=e.slice(0,i),a=e.slice(i),c=pi.mapCells(e[t],function(e,t){return t===n?qt.elementnew(o(e.element(),r),!0):e});return u.concat([c]).concat(a)},Ji=function(e,t,n){return e.slice(0,t).concat(e.slice(n+1))},$i=function(e,n,r){var t=k(e,function(e){var t=e.cells().slice(0,n).concat(e.cells().slice(r+1));return qt.rowcells(t,e.section())});return I(t,function(e){return 0<e.cells().length})},Qi=function(e){return e.dom().textContent},Zi=function(e,n,r,o){return k(e,function(e){return pi.mapCells(e,function(e){return t=e,A(n,function(e){return r(t.element(),e.element())})?qt.elementnew(o(e.element(),r),!0):e;var t})})},eu=function(e,t,n,r){return pi.getCellElement(e[t],n)!==undefined&&0<t&&r(pi.getCellElement(e[t-1],n),pi.getCellElement(e[t],n))},tu=function(e,t,n){return 0<t&&n(pi.getCellElement(e,t-1),pi.getCellElement(e,t))},nu=function(n,r,o,e){var t=H(n,function(e,t){return eu(n,t,r,o)||tu(e,r,o)?[]:[pi.getCell(e,r)]});return Zi(n,t,o,e)},ru=function(n,r,o,e){var i=n[r],t=H(i.cells(),function(e,t){return eu(n,r,t,o)||tu(i,t,o)?[]:[e]});return Zi(n,t,o,e)},ou=function(e,r,o,i){var t=B(e,function(e){return ce(pi.getCell(e,r).element())});return H(t,function(e){return"th"===ce(pi.getCell(e[0],r).element())?e:(t=k(e,function(e){var t=pi.getCell(e,r);return{row:e,text:Qi(t.element())}}),n=G(t,function(e,t){var n=o(e.text,t.text);return i?n:-1*n}),k(n,function(e){return e.row}));var t,n})},iu=function(e,t,o,i){var n=e[t].cells(),r=k(n,function(e){return Qi(e.element())}),u=k(e,function(e){return{zippedCells:k(e.cells(),function(e,t){return{cell:e,reference:n[t],referenceText:r[t]}}),section:e.section()}});return k(u,function(e){var t=B(e.zippedCells,function(e){return ce(e.reference.element())}),n=H(t,function(e){return"th"===ce(e[0].reference.element())?e:G(e,function(e,t){var n=o(e.referenceText,t.referenceText);return i?n:-1*n})}),r=k(n,function(e){return e.cell});return qt.rowcells(r,e.section)})},uu=function(e){return{fold:e}},au=function(){return uu(function(e,t,n,r,o){return e()})},cu=function(i){return uu(function(e,t,n,r,o){return t(i)})},lu=function(i,u){return uu(function(e,t,n,r,o){return n(i,u)})},fu=function(i,u,a){return uu(function(e,t,n,r,o){return r(i,u,a)})},su=function(i,u){return uu(function(e,t,n,r,o){return o(i,u)})},du=function(e,t,i,u){var n,r,a=e.slice(0),o=(r=t,0===(n=e).length?au():1===n.length?cu(0):0===r?lu(0,1):r===n.length-1?su(r-1,r):0<r&&r<n.length-1?fu(r-1,r,r+1):au()),c=function(e){return k(e,C(0))},l=C(c(a)),f=function(e,t){if(0<=i){var n=Math.max(u.minCellWidth(),a[t]-i);return c(a.slice(0,e)).concat([i,n-a[t]]).concat(c(a.slice(t+1)))}var r=Math.max(u.minCellWidth(),a[e]+i),o=a[e]-r;return c(a.slice(0,e)).concat([r-a[e],o]).concat(c(a.slice(t+1)))},s=f;return o.fold(l,function(e){return u.singleColumnWidth(a[e],i)},s,function(e,t,n){return f(t,n)},function(e,t){if(0<=i)return c(a.slice(0,t)).concat([i]);var n=Math.max(u.minCellWidth(),a[t]+i);return c(a.slice(0,t)).concat([n-a[t]])})},mu=function(e,t){return we(e,t)&&1<parseInt(be(e,t),10)},gu={hasColspan:function(e){return mu(e,"colspan")},hasRowspan:function(e){return mu(e,"rowspan")},minWidth:C(10),minHeight:C(10),getInt:function(e,t){return parseInt(Ie(e,t),10)}},hu=function(e,t,n){return We(e,t).fold(function(){return n(e)+"px"},function(e){return e})},pu=function(e){return hu(e,"width",go.getPixelWidth)},vu=function(e){return hu(e,"height",go.getHeight)},bu=function(e,t,n,r,o){var i=ei(e),u=k(i,function(e){return e.map(t.edge)});return k(i,function(e,t){return e.filter(g(gu.hasColspan)).fold(function(){var e=Qo(u,t);return r(e)},function(e){return n(e,o)})})},wu=function(e){return e.map(function(e){return e+"px"}).getOr("")},yu=function(e,t,n,r){var o=ti(e),i=k(o,function(e){return e.map(t.edge)});return k(o,function(e,t){return e.filter(g(gu.hasRowspan)).fold(function(){var e=Qo(i,t);return r(e)},function(e){return n(e)})})},xu={getRawWidths:function(e,t){return bu(e,t,pu,wu)},getPixelWidths:function(e,t,n){return bu(e,t,go.getPixelWidth,function(e){return e.getOrThunk(n.minCellWidth)},n)},getPercentageWidths:function(e,t,n){return bu(e,t,go.getPercentageWidth,function(e){return e.fold(function(){return n.minCellWidth()},function(e){return e/n.pixelWidth()*100})},n)},getPixelHeights:function(e,t){return yu(e,t,go.getHeight,function(e){return e.getOrThunk(gu.minHeight)})},getRawHeights:function(e,t){return yu(e,t,vu,wu)}},Cu=function(e,t,n){for(var r=0,o=e;o<t;o++)r+=n[o]!==undefined?n[o]:0;return r},Su=function(e,n){var t=wn.justCells(e);return k(t,function(e){var t=Cu(e.column(),e.column()+e.colspan(),n);return{element:e.element,width:C(t),colspan:e.colspan}})},Ru=function(e,n){var t=wn.justCells(e);return k(t,function(e){var t=Cu(e.row(),e.row()+e.rowspan(),n);return{element:e.element,height:C(t),rowspan:e.rowspan}})},Tu=function(e,n){return k(e.all(),function(e,t){return{element:e.element,height:C(n[t])}})},Du=function(e){var t=parseInt(e,10),n=o;return{width:C(t),pixelWidth:C(t),getWidths:xu.getPixelWidths,getCellDelta:n,singleColumnWidth:function(e,t){return[Math.max(gu.minWidth(),e+t)-e]},minCellWidth:gu.minWidth,setElementWidth:go.setPixelWidth,setTableWidth:function(e,t,n){var r=W(t,function(e,t){return e+t},0);go.setPixelWidth(e,r)}}},Ou=function(e,t){if(go.percentageBasedSizeRegex().test(t)){var n=go.percentageBasedSizeRegex().exec(t);return o=n[1],i=e,u=parseFloat(o),a=Zr(i),{width:C(u),pixelWidth:C(a),getWidths:xu.getPercentageWidths,getCellDelta:function(e){return e/a*100},singleColumnWidth:function(e,t){return[100-e]},minCellWidth:function(){return gu.minWidth()/a*100},setElementWidth:go.setPercentageWidth,setTableWidth:function(e,t,n){var r=u+n;go.setPercentageWidth(e,r)}}}if(go.pixelBasedSizeRegex().test(t)){var r=go.pixelBasedSizeRegex().exec(t);return Du(r[1])}var o,i,u,a,c=Zr(e);return Du(c)},Nu=function(t){return go.getRawWidth(t).fold(function(){var e=Zr(t);return Du(e)},function(e){return Ou(t,e)})},Eu=function(e){return wn.generate(e)},Au=function(e){var t=hn(e);return Eu(t)},ku=function(e,t,n,r){var o=Nu(e),i=o.getCellDelta(t),u=Au(e),a=o.getWidths(u,r,o),c=du(a,n,i,o),l=k(c,function(e,t){return e+a[t]}),f=Su(u,l);P(f,function(e){o.setElementWidth(e.element(),e.width())}),n===u.grid().columns()-1&&o.setTableWidth(e,l,i)},Pu=function(e,n,r,t){var o=Au(e),i=xu.getPixelHeights(o,t),u=k(i,function(e,t){return r===t?Math.max(n+e,gu.minHeight()):e}),a=Ru(o,u),c=Tu(o,u);P(c,function(e){go.setHeight(e.element(),e.height())}),P(a,function(e){go.setHeight(e.element(),e.height())});var l=W(u,function(e,t){return e+t},0);go.setHeight(e,l)},Iu=function(e,t,n){var r=Nu(e),o=Eu(t),i=r.getWidths(o,n,r),u=Su(o,i);P(u,function(e){r.setElementWidth(e.element(),e.width())});var a=W(i,function(e,t){return t+e},0);0<u.length&&r.setTableWidth(e,a)},Bu=function(e){var t=e,n=function(){return t};return{get:n,set:function(e){t=e},clone:function(){return Bu(n())}}},Wu=function(r,o,i){if(0===o.length)throw new Error("You must specify at least one required field.");return te("required",o),ne(o),function(t){var n=Y(t);U(o,function(e){return E(n,e)})||Z(o,n),r(o,n);var e=I(o,function(e){return!i.validate(t[e],e)});return 0<e.length&&function(e,t){throw new Error("All values need to be of type: "+t+". Keys ("+Q(e).join(", ")+") were not.")}(e,i.label),t}},Mu=function(t,e){var n=I(e,function(e){return!E(t,e)});0<n.length&&ee(n)},_u=function(e){return Wu(Mu,e,{validate:D,label:"function"})},Lu=function(e){var t=we(e,"colspan")?parseInt(be(e,"colspan"),10):1,n=we(e,"rowspan")?parseInt(be(e,"rowspan"),10):1;return{element:C(e),colspan:C(t),rowspan:C(n)}},ju=_u(["cell","row","replace","gap"]),Fu=function(r,e){ju(r);var n=Bu(S.none()),o=e!==undefined?e:Lu,i=function(e){var t,n=o(e);return t=n,r.cell(t)},u=function(e){var t=i(e);return n.get().isNone()&&n.set(S.some(t)),a=S.some({item:e,replacement:t}),t},a=S.none();return{getOrInit:function(t,n){return a.fold(function(){return u(t)},function(e){return n(t,e.item)?e.replacement:u(t)})},cursor:n.get}},zu=function(o,a){return function(n){var r=Bu(S.none());ju(n);var i=[],u=function(e){var t=n.replace(e,a,{scope:o});return i.push({item:e,sub:t}),r.get().isNone()&&r.set(S.some(t)),t};return{replaceOrInit:function(t,n){return(r=t,o=n,_(i,function(e){return o(e.item,r)})).fold(function(){return u(t)},function(e){return n(t,e.item)?e.sub:u(t)});var r,o},cursor:r.get}}},Hu=function(n){ju(n);var e=Bu(S.none());return{combine:function(t){return e.get().isNone()&&e.set(S.some(t)),function(){var e=n.cell({element:C(t),colspan:C(1),rowspan:C(1)});return Me(e,"width"),Me(t,"width"),e}},cursor:e.get}},Uu=["body","p","div","article","aside","figcaption","figure","footer","header","nav","section","ol","ul","table","thead","tfoot","tbody","caption","tr","td","th","h1","h2","h3","h4","h5","h6","blockquote","pre","address"],qu=function(e,t){var n=e.property().name(t);return E(Uu,n)},Vu=function(e,t){return E(["br","img","hr","input"],e.property().name(t))},Gu=qu,Yu=function(e,t){var n=e.property().name(t);return E(["ol","ul"],n)},Xu=Vu,Ku=Un(),Ju=function(e){return Gu(Ku,e)},$u=function(e){return Yu(Ku,e)},Qu=function(e){return Xu(Ku,e)},Zu=function(e){var t,i=function(e){return"br"===ce(e)},n=function(o){return An(o).bind(function(n){var r=Tt(n).map(function(e){return!!Ju(e)||(Qu(e)?"img"!==ce(e):void 0)}).getOr(!1);return Ct(n).map(function(e){return!0===r||"li"===ce(t=e)||tn(t,$u).isSome()||i(n)||Ju(e)&&!bt(o,e)?[]:[De.fromTag("br")];var t})}).getOr([])},r=0===(t=H(e,function(e){var t=Dt(e);return U(t,function(e){return i(e)||me(e)&&0===Sn(e).trim().length})?[]:t.concat(n(e))})).length?[De.fromTag("br")]:t;Wt(e[0]),Bt(e[0],r)},ea=function(e){0===gn.cells(e).length&&Mt(e)},ta=$("grid","cursor"),na=function(e,t,n){return ra(e,t,n).orThunk(function(){return ra(e,0,0)})},ra=function(e,t,n){return S.from(e[t]).bind(function(e){return S.from(e.cells()[n]).bind(function(e){return S.from(e.element())})})},oa=function(e,t,n){return ta(e,ra(e,t,n))},ia=function(e){return M(e,function(e,t){return A(e,function(e){return e.row()===t.row()})?e:e.concat([t])},[]).sort(function(e,t){return e.row()-t.row()})},ua=function(e){return M(e,function(e,t){return A(e,function(e){return e.column()===t.column()})?e:e.concat([t])},[]).sort(function(e,t){return e.column()-t.column()})},aa=function(e,t,n){var r=pn(e,n),o=wn.generate(r);return yi(o,t,!0)},ca=function(a){return function(e,t,n,r,o,i,u){return Ri(function(e,t,n,r){return a(e,t,i,u)},Di,y,y,Fu)(e,t,n,r,o)}},la=Iu,fa={insertRowBefore:Ri(function(e,t,n,r){var o=t.row(),i=t.row(),u=Gi(e,i,o,n,r.getOrInit);return oa(u,i,t.column())},Di,y,y,Fu),insertRowsBefore:Ri(function(e,t,n,r){var o=t[0].row(),i=t[0].row(),u=ia(t),a=M(u,function(e,t){return Gi(e,i,o,n,r.getOrInit)},e);return oa(a,i,t[0].column())},Oi,y,y,Fu),insertRowAfter:Ri(function(e,t,n,r){var o=t.row(),i=t.row()+t.rowspan(),u=Gi(e,i,o,n,r.getOrInit);return oa(u,i,t.column())},Di,y,y,Fu),insertRowsAfter:Ri(function(e,t,n,r){var o=ia(t),i=o[o.length-1].row(),u=o[o.length-1].row()+o[o.length-1].rowspan(),a=M(o,function(e,t){return Gi(e,u,i,n,r.getOrInit)},e);return oa(a,u,t[0].column())},Oi,y,y,Fu),insertColumnBefore:Ri(function(e,t,n,r){var o=t.column(),i=t.column(),u=Yi(e,i,o,n,r.getOrInit);return oa(u,t.row(),i)},Di,la,y,Fu),insertColumnsBefore:Ri(function(e,t,n,r){var o=ua(t),i=o[0].column(),u=o[0].column(),a=M(o,function(e,t){return Yi(e,u,i,n,r.getOrInit)},e);return oa(a,t[0].row(),u)},Oi,la,y,Fu),insertColumnAfter:Ri(function(e,t,n,r){var o=t.column(),i=t.column()+t.colspan(),u=Yi(e,i,o,n,r.getOrInit);return oa(u,t.row(),i)},Di,la,y,Fu),insertColumnsAfter:Ri(function(e,t,n,r){var o=t[t.length-1].column(),i=t[t.length-1].column()+t[t.length-1].colspan(),u=ua(t),a=M(u,function(e,t){return Yi(e,i,o,n,r.getOrInit)},e);return oa(a,t[0].row(),i)},Oi,la,y,Fu),splitCellIntoColumns:Ri(function(e,t,n,r){var o=Xi(e,t.row(),t.column(),n,r.getOrInit);return oa(o,t.row(),t.column())},Di,la,y,Fu),splitCellIntoRows:Ri(function(e,t,n,r){var o=Ki(e,t.row(),t.column(),n,r.getOrInit);return oa(o,t.row(),t.column())},Di,y,y,Fu),eraseColumns:Ri(function(e,t,n,r){var o=ua(t),i=$i(e,o[0].column(),o[o.length-1].column()),u=na(i,t[0].row(),t[0].column());return ta(i,u)},Oi,la,ea,Fu),eraseRows:Ri(function(e,t,n,r){var o=ia(t),i=Ji(e,o[0].row(),o[o.length-1].row()),u=na(i,t[0].row(),t[0].column());return ta(i,u)},Oi,y,ea,Fu),makeColumnHeader:Ri(function(e,t,n,r){var o=nu(e,t.column(),n,r.replaceOrInit);return oa(o,t.row(),t.column())},Di,y,y,zu("row","th")),unmakeColumnHeader:Ri(function(e,t,n,r){var o=nu(e,t.column(),n,r.replaceOrInit);return oa(o,t.row(),t.column())},Di,y,y,zu(null,"td")),makeRowHeader:Ri(function(e,t,n,r){var o=ru(e,t.row(),n,r.replaceOrInit);return oa(o,t.row(),t.column())},Di,y,y,zu("col","th")),unmakeRowHeader:Ri(function(e,t,n,r){var o=ru(e,t.row(),n,r.replaceOrInit);return oa(o,t.row(),t.column())},Di,y,y,zu(null,"td")),mergeCells:Ri(function(e,t,n,r){var o=t.cells();Zu(o);var i=zi(e,t.bounds(),n,C(o[0]));return ta(i,S.from(o[0]))},Ai,y,y,Hu),unmergeCells:Ri(function(e,t,n,r){var o=W(t,function(e,t){return Hi(e,t,n,r.combine(t))},e);return ta(o,S.from(t[0]))},ki,la,y,Hu),pasteCells:Ri(function(e,n,t,r){var o,i,u,a,c=(o=n.clipboard(),i=n.generators(),u=hn(o),a=wn.generate(u),yi(a,i,!0)),l=qt.address(n.row(),n.column());return qi(l,e,c,n.generators(),t).fold(function(){return ta(e,S.some(n.element()))},function(e){var t=na(e,n.row(),n.column());return ta(e,t)})},Ni,la,y,Fu),pasteRowsBefore:Ri(function(e,t,n,r){var o=e[t.cells[0].row()],i=t.cells[0].row(),u=aa(t.clipboard(),t.generators(),o),a=Vi(i,e,u,t.generators(),n),c=na(a,t.cells[0].row(),t.cells[0].column());return ta(a,c)},Ei,y,y,Fu),pasteRowsAfter:Ri(function(e,t,n,r){var o=e[t.cells[0].row()],i=t.cells[t.cells.length-1].row()+t.cells[t.cells.length-1].rowspan(),u=aa(t.clipboard(),t.generators(),o),a=Vi(i,e,u,t.generators(),n),c=na(a,t.cells[0].row(),t.cells[0].column());return ta(a,c)},Ei,y,y,Fu),sortRows:ca(function(e,t,n,r){var o=ou(e,t.column(),n,r);return oa(o,t.row(),t.column())}),sortColumns:ca(function(e,t,n,r){var o=iu(e,t.row(),n,r);return oa(o,t.row(),t.column())})},sa=function(e){return De.fromDom(e.getBody())},da=function(e){return e.getBoundingClientRect().width},ma=function(e){return e.getBoundingClientRect().height},ga=function(t){return function(e){return bt(e,sa(t))}},ha=function(e){return/^[0-9]+$/.test(e)&&(e+="px"),e},pa=function(e){var t=Xt(e,"td[data-mce-style],th[data-mce-style]");ye(e,"data-mce-style"),P(t,function(e){ye(e,"data-mce-style")})},va={isRtl:C(!1)},ba={isRtl:C(!0)},wa={directionAt:function(e){return"rtl"==("rtl"===Ie(e,"direction")?"rtl":"ltr")?ba:va}},ya=["tableprops","tabledelete","|","tableinsertrowbefore","tableinsertrowafter","tabledeleterow","|","tableinsertcolbefore","tableinsertcolafter","tabledeletecol"],xa={"border-collapse":"collapse",width:"100%"},Ca={border:"1"},Sa=function(e){return e.getParam("table_cell_advtab",!0,"boolean")},Ra=function(e){return e.getParam("table_row_advtab",!0,"boolean")},Ta=function(e){return e.getParam("table_advtab",!0,"boolean")},Da=function(e){return e.getParam("table_style_by_css",!1,"boolean")},Oa=function(e){return e.getParam("table_cell_class_list",[],"array")},Na=function(e){return e.getParam("table_row_class_list",[],"array")},Ea=function(e){return e.getParam("table_class_list",[],"array")},Aa=function(e){return!1===e.getParam("table_responsive_width")},ka=function(e,t){return e.fire("newrow",{node:t})},Pa=function(e,t){return e.fire("newcell",{node:t})},Ia=function(e,t,n,r){e.fire("ObjectResizeStart",{target:t,width:n,height:r})},Ba=function(e,t,n,r){e.fire("ObjectResized",{target:t,width:n,height:r})},Wa=function(f,e){var t,n=function(e){return"table"===ce(sa(e))},s=(t=f.getParam("table_clone_elements"),w(t)?S.some(t.split(/[ ,]/)):Array.isArray(t)?S.some(t):S.none()),r=function(u,a,c,l){return function(e,t){pa(e);var n=l(),r=De.fromDom(f.getDoc()),o=Io(wa.directionAt),i=Fn(c,r,s);return a(e)?u(n,e,t,i,o).bind(function(e){return P(e.newRows(),function(e){ka(f,e.dom())}),P(e.newCells(),function(e){Pa(f,e.dom())}),e.cursor().map(function(e){var t=f.dom.createRng();return t.setStart(e.dom(),0),t.setEnd(e.dom(),0),t})}):S.none()}};return{deleteRow:r(fa.eraseRows,function(e){var t=Wo(e);return!1===n(f)||1<t.rows()},y,e),deleteColumn:r(fa.eraseColumns,function(e){var t=Wo(e);return!1===n(f)||1<t.columns()},y,e),insertRowsBefore:r(fa.insertRowsBefore,l,y,e),insertRowsAfter:r(fa.insertRowsAfter,l,y,e),insertColumnsBefore:r(fa.insertColumnsBefore,l,ho,e),insertColumnsAfter:r(fa.insertColumnsAfter,l,ho,e),mergeCells:r(fa.mergeCells,l,y,e),unmergeCells:r(fa.unmergeCells,l,y,e),pasteRowsBefore:r(fa.pasteRowsBefore,l,y,e),pasteRowsAfter:r(fa.pasteRowsAfter,l,y,e),pasteCells:r(fa.pasteCells,l,y,e)}},Ma=function(e,t,r){var n=hn(e),o=wn.generate(n);return Oi(o,t).map(function(e){var t=yi(o,r,!1).slice(e[0].row(),e[e.length-1].row()+e[e.length-1].rowspan()),n=Ti(t,r);return Ho(n)})},_a=tinymce.util.Tools.resolve("tinymce.util.Tools"),La=function(e,t,n){n&&e.formatter.apply("align"+n,{},t)},ja=function(e,t,n){n&&e.formatter.apply("valign"+n,{},t)},Fa=function(t,n){_a.each("left center right".split(" "),function(e){t.formatter.remove("align"+e,{},n)})},za=function(t,n){_a.each("top middle bottom".split(" "),function(e){t.formatter.remove("valign"+e,{},n)})},Ha=function(o,e,i){var t;return t=function(e,t){for(var n=0;n<t.length;n++){var r=o.getStyle(t[n],i);if(void 0===e&&(e=r),e!==r)return""}return e}(t,o.select("td,th",e))},Ua=function(e,t){var n=e.dom,r=t.control.rootControl,o=r.toJSON(),i=n.parseStyle(o.style);i["border-style"]=o.borderStyle,i["border-color"]=o.borderColor,i["background-color"]=o.backgroundColor,i.width=o.width?ha(o.width):"",i.height=o.height?ha(o.height):"",r.find("#style").value(n.serializeStyle(n.parseStyle(n.serializeStyle(i))))},qa=function(e,t){var n=e.dom,r=t.control.rootControl,o=r.toJSON(),i=n.parseStyle(o.style);r.find("#borderStyle").value(i["border-style"]||""),r.find("#borderColor").value(i["border-color"]||""),r.find("#backgroundColor").value(i["background-color"]||""),r.find("#width").value(i.width||""),r.find("#height").value(i.height||"")},Va={createStyleForm:function(n){var e=function(){var e=n.getParam("color_picker_callback");if(e)return function(t){return e.call(n,function(e){t.control.value(e).fire("change")},t.control.value())}};return{title:"Advanced",type:"form",defaults:{onchange:b(Ua,n)},items:[{label:"Style",name:"style",type:"textbox",onchange:b(qa,n)},{type:"form",padding:0,formItemDefaults:{layout:"grid",alignH:["start","right"]},defaults:{size:7},items:[{label:"Border style",type:"listbox",name:"borderStyle",width:90,onselect:b(Ua,n),values:[{text:"Select...",value:""},{text:"Solid",value:"solid"},{text:"Dotted",value:"dotted"},{text:"Dashed",value:"dashed"},{text:"Double",value:"double"},{text:"Groove",value:"groove"},{text:"Ridge",value:"ridge"},{text:"Inset",value:"inset"},{text:"Outset",value:"outset"},{text:"None",value:"none"},{text:"Hidden",value:"hidden"}]},{label:"Border color",type:"colorbox",name:"borderColor",onaction:e()},{label:"Background color",type:"colorbox",name:"backgroundColor",onaction:e()}]}]}},buildListItems:function(e,r,t){var o=function(e,n){return n=n||[],_a.each(e,function(e){var t={text:e.text||e.title};e.menu?t.menu=o(e.menu):(t.value=e.value,r&&r(t)),n.push(t)}),n};return o(e,t||[])},updateStyleField:Ua,extractAdvancedStyles:function(e,t){var n=e.parseStyle(e.getAttrib(t,"style")),r={};return n["border-style"]&&(r.borderStyle=n["border-style"]),n["border-color"]&&(r.borderColor=n["border-color"]),n["background-color"]&&(r.backgroundColor=n["background-color"]),r.style=e.serializeStyle(n),r},updateAdvancedFields:qa,syncAdvancedStyleFields:function(e,t){t.control.rootControl.find("#style")[0].getEl().isEqualNode(m.document.activeElement)?qa(e,t):Ua(e,t)}},Ga=function(r,o,e){var i,u=r.dom;function a(e,t,n){(1===o.length||n)&&u.setAttrib(e,t,n)}function c(e,t,n){(1===o.length||n)&&u.setStyle(e,t,n)}Sa(r)&&Va.syncAdvancedStyleFields(r,e),i=e.control.rootControl.toJSON(),r.undoManager.transact(function(){_a.each(o,function(e){var t,n;a(e,"scope",i.scope),1===o.length?a(e,"style",i.style):(t=e,n=i.style,delete t.dataset.mceStyle,t.style.cssText+=";"+n),a(e,"class",i["class"]),c(e,"width",ha(i.width)),c(e,"height",ha(i.height)),i.type&&e.nodeName.toLowerCase()!==i.type&&(e=u.rename(e,i.type)),1===o.length&&(Fa(r,e),za(r,e)),i.align&&La(r,e,i.align),i.valign&&ja(r,e,i.valign)}),r.focus()})},Ya=function(t){var e,n,r,o=[];if(o=t.dom.select("td[data-mce-selected],th[data-mce-selected]"),e=t.dom.getParent(t.selection.getStart(),"td,th"),!o.length&&e&&o.push(e),e=e||o[0]){var i,u,a,c;1<o.length?n={width:"",height:"",scope:"","class":"",align:"",valign:"",style:"",type:e.nodeName.toLowerCase()}:(u=e,a=(i=t).dom,c={width:a.getStyle(u,"width")||a.getAttrib(u,"width"),height:a.getStyle(u,"height")||a.getAttrib(u,"height"),scope:a.getAttrib(u,"scope"),"class":a.getAttrib(u,"class"),type:u.nodeName.toLowerCase(),style:"",align:"",valign:""},_a.each("left center right".split(" "),function(e){i.formatter.matchNode(u,"align"+e)&&(c.align=e)}),_a.each("top middle bottom".split(" "),function(e){i.formatter.matchNode(u,"valign"+e)&&(c.valign=e)}),Sa(i)&&_a.extend(c,Va.extractAdvancedStyles(a,u)),n=c),0<Oa(t).length&&(r={name:"class",type:"listbox",label:"Class",values:Va.buildListItems(Oa(t),function(e){e.value&&(e.textStyle=function(){return t.formatter.getCssText({block:"td",classes:[e.value]})})})});var l={type:"form",layout:"flex",direction:"column",labelGapCalc:"children",padding:0,items:[{type:"form",layout:"grid",columns:2,labelGapCalc:!1,padding:0,defaults:{type:"textbox",maxWidth:50},items:[{label:"Width",name:"width",onchange:b(Va.updateStyleField,t)},{label:"Height",name:"height",onchange:b(Va.updateStyleField,t)},{label:"Cell type",name:"type",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"Cell",value:"td"},{text:"Header cell",value:"th"}]},{label:"Scope",name:"scope",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Row",value:"row"},{text:"Column",value:"col"},{text:"Row group",value:"rowgroup"},{text:"Column group",value:"colgroup"}]},{label:"H Align",name:"align",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"V Align",name:"valign",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Top",value:"top"},{text:"Middle",value:"middle"},{text:"Bottom",value:"bottom"}]}]},r]};Sa(t)?t.windowManager.open({title:"Cell properties",bodyType:"tabpanel",data:n,body:[{title:"General",type:"form",items:l},Va.createStyleForm(t)],onsubmit:b(Ga,t,o)}):t.windowManager.open({title:"Cell properties",data:n,body:l,onsubmit:b(Ga,t,o)})}};function Xa(f,s,d,e){var m=f.dom;function g(e,t,n){(1===s.length||n)&&m.setAttrib(e,t,n)}Ra(f)&&Va.syncAdvancedStyleFields(f,e);var h=e.control.rootControl.toJSON();f.undoManager.transact(function(){_a.each(s,function(e){var t,n,r,o,i,u,a,c,l;g(e,"scope",h.scope),g(e,"style",h.style),g(e,"class",h["class"]),t=e,n="height",r=ha(h.height),(1===s.length||r)&&m.setStyle(t,n,r),h.type!==e.parentNode.nodeName.toLowerCase()&&(o=f.dom,i=e,u=h.type,a=o.getParent(i,"table"),c=i.parentNode,(l=o.select(u,a)[0])||(l=o.create(u),a.firstChild?"CAPTION"===a.firstChild.nodeName?o.insertAfter(l,a.firstChild):a.insertBefore(l,a.firstChild):a.appendChild(l)),l.appendChild(i),c.hasChildNodes()||o.remove(c)),h.align!==d.align&&(Fa(f,e),La(f,e,h.align))}),f.focus()})}var Ka=function(t){var e,n,r,o,i,u,a,c,l,f,s=t.dom,d=[];e=s.getParent(t.selection.getStart(),"table"),n=s.getParent(t.selection.getStart(),"td,th"),_a.each(e.rows,function(t){_a.each(t.cells,function(e){if(s.getAttrib(e,"data-mce-selected")||e===n)return d.push(t),!1})}),(r=d[0])&&(1<d.length?i={height:"",scope:"",style:"","class":"",align:"",type:r.parentNode.nodeName.toLowerCase()}:(c=r,l=(a=t).dom,f={height:l.getStyle(c,"height")||l.getAttrib(c,"height"),scope:l.getAttrib(c,"scope"),"class":l.getAttrib(c,"class"),align:"",style:"",type:c.parentNode.nodeName.toLowerCase()},_a.each("left center right".split(" "),function(e){a.formatter.matchNode(c,"align"+e)&&(f.align=e)}),Ra(a)&&_a.extend(f,Va.extractAdvancedStyles(l,c)),i=f),0<Na(t).length&&(o={name:"class",type:"listbox",label:"Class",values:Va.buildListItems(Na(t),function(e){e.value&&(e.textStyle=function(){return t.formatter.getCssText({block:"tr",classes:[e.value]})})})}),u={type:"form",columns:2,padding:0,defaults:{type:"textbox"},items:[{type:"listbox",name:"type",label:"Row type",text:"Header",maxWidth:null,values:[{text:"Header",value:"thead"},{text:"Body",value:"tbody"},{text:"Footer",value:"tfoot"}]},{type:"listbox",name:"align",label:"Alignment",text:"None",maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"Height",name:"height"},o]},Ra(t)?t.windowManager.open({title:"Row properties",data:i,bodyType:"tabpanel",body:[{title:"General",type:"form",items:u},Va.createStyleForm(t)],onsubmit:b(Xa,t,d,i)}):t.windowManager.open({title:"Row properties",data:i,body:u,onsubmit:b(Xa,t,d,i)}))},Ja=tinymce.util.Tools.resolve("tinymce.Env"),$a={styles:{"border-collapse":"collapse",width:"100%"},attributes:{border:"1"},percentages:!0},Qa=function(e,t,n,r,o){void 0===o&&(o=$a);var i=De.fromTag("table");Pe(i,o.styles),ve(i,o.attributes);var u=De.fromTag("tbody");kt(i,u);for(var a=[],c=0;c<e;c++){for(var l=De.fromTag("tr"),f=0;f<t;f++){var s=c<n||f<r?De.fromTag("th"):De.fromTag("td");f<r&&pe(s,"scope","row"),c<n&&pe(s,"scope","col"),kt(s,De.fromTag("br")),o.percentages&&ke(s,"width",100/t+"%"),kt(l,s)}a.push(l)}return Bt(u,a),i},Za=function(e,t){e.selection.select(t.dom(),!0),e.selection.collapse(!0)},ec=function(r,e,t){var n,o,i=r.getParam("table_default_styles",xa,"object"),u={styles:i,attributes:(o=r,o.getParam("table_default_attributes",Ca,"object")),percentages:(n=i.width,w(n)&&-1!==n.indexOf("%")&&!Aa(r))},a=Qa(t,e,0,0,u);pe(a,"data-mce-id","__mce");var c,l,f,s=(c=a,l=De.fromTag("div"),f=De.fromDom(c.dom().cloneNode(!0)),kt(l,f),l.dom().innerHTML);return r.insertContent(s),on(sa(r),'table[data-mce-id="__mce"]').map(function(e){var t,n;return Aa(r)&&ke(e,"width",Ie(e,"width")),ye(e,"data-mce-id"),t=r,P(Xt(e,"tr"),function(e){ka(t,e.dom()),P(Xt(e,"th,td"),function(e){Pa(t,e.dom())})}),n=r,on(e,"td,th").each(b(Za,n)),e.dom()}).getOr(null)};function tc(e,t,n,r){if("TD"===t.tagName||"TH"===t.tagName)e.setStyle(t,n,r);else if(t.children)for(var o=0;o<t.children.length;o++)tc(e,t.children[o],n,r)}var nc=function(e,t,n){var r,o,i=e.dom;Ta(e)&&Va.syncAdvancedStyleFields(e,n),!1===(o=n.control.rootControl.toJSON())["class"]&&delete o["class"],e.undoManager.transact(function(){t||(t=ec(e,o.cols||1,o.rows||1)),function(e,t,n){var r,o=e.dom,i={},u={};if(i["class"]=n["class"],u.height=ha(n.height),o.getAttrib(t,"width")&&!Da(e)?i.width=(r=n.width)?r.replace(/px$/,""):"":u.width=ha(n.width),Da(e)?(u["border-width"]=ha(n.border),u["border-spacing"]=ha(n.cellspacing),_a.extend(i,{"data-mce-border-color":n.borderColor,"data-mce-cell-padding":n.cellpadding,"data-mce-border":n.border})):_a.extend(i,{border:n.border,cellpadding:n.cellpadding,cellspacing:n.cellspacing}),Da(e)&&t.children)for(var a=0;a<t.children.length;a++)tc(o,t.children[a],{"border-width":ha(n.border),"border-color":n.borderColor,padding:ha(n.cellpadding)});n.style?_a.extend(u,o.parseStyle(n.style)):u=_a.extend({},o.parseStyle(o.getAttrib(t,"style")),u),i.style=o.serializeStyle(u),o.setAttribs(t,i)}(e,t,o),(r=i.select("caption",t)[0])&&!o.caption&&i.remove(r),!r&&o.caption&&((r=i.create("caption")).innerHTML=Ja.ie?"\xa0":'<br data-mce-bogus="1"/>',t.insertBefore(r,t.firstChild)),Fa(e,t),o.align&&La(e,t,o.align),e.focus(),e.addVisual()})},rc=function(t,e){var n,r,o,i,u,a,c,l,f,s,d=t.dom,m={};!0===e?(n=d.getParent(t.selection.getStart(),"table"))&&(c=n,l=(a=t).dom,f={width:l.getStyle(c,"width")||l.getAttrib(c,"width"),height:l.getStyle(c,"height")||l.getAttrib(c,"height"),cellspacing:l.getStyle(c,"border-spacing")||l.getAttrib(c,"cellspacing"),cellpadding:l.getAttrib(c,"data-mce-cell-padding")||l.getAttrib(c,"cellpadding")||Ha(a.dom,c,"padding"),border:l.getAttrib(c,"data-mce-border")||l.getAttrib(c,"border")||Ha(a.dom,c,"border"),borderColor:l.getAttrib(c,"data-mce-border-color"),caption:!!l.select("caption",c)[0],"class":l.getAttrib(c,"class")},_a.each("left center right".split(" "),function(e){a.formatter.matchNode(c,"align"+e)&&(f.align=e)}),Ta(a)&&_a.extend(f,Va.extractAdvancedStyles(l,c)),m=f):(r={label:"Cols",name:"cols"},o={label:"Rows",name:"rows"}),0<Ea(t).length&&(m["class"]&&(m["class"]=m["class"].replace(/\s*mce\-item\-table\s*/g,"")),i={name:"class",type:"listbox",label:"Class",values:Va.buildListItems(Ea(t),function(e){e.value&&(e.textStyle=function(){return t.formatter.getCssText({block:"table",classes:[e.value]})})})}),u={type:"form",layout:"flex",direction:"column",labelGapCalc:"children",padding:0,items:[{type:"form",labelGapCalc:!1,padding:0,layout:"grid",columns:2,defaults:{type:"textbox",maxWidth:50},items:(s=t,s.getParam("table_appearance_options",!0,"boolean")?[r,o,{label:"Width",name:"width",onchange:b(Va.updateStyleField,t)},{label:"Height",name:"height",onchange:b(Va.updateStyleField,t)},{label:"Cell spacing",name:"cellspacing"},{label:"Cell padding",name:"cellpadding"},{label:"Border",name:"border"},{label:"Caption",name:"caption",type:"checkbox"}]:[r,o,{label:"Width",name:"width",onchange:b(Va.updateStyleField,t)},{label:"Height",name:"height",onchange:b(Va.updateStyleField,t)}])},{label:"Alignment",name:"align",type:"listbox",text:"None",values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},i]},Ta(t)?t.windowManager.open({title:"Table properties",data:m,bodyType:"tabpanel",body:[{title:"General",type:"form",items:u},Va.createStyleForm(t)],onsubmit:b(nc,t,n)}):t.windowManager.open({title:"Table properties",data:m,body:u,onsubmit:b(nc,t,n)})},oc=_a.each,ic=function(a,t,c,l,n){var r=ga(a),e=function(e){return function(){return S.from(a.dom.getParent(a.selection.getStart(),e)).map(De.fromDom)}},o=e("caption"),f=e("th,td"),s=function(e){return gn.table(e,r)},d=function(e){return{width:da(e.dom()),height:da(e.dom())}},i=function(n){f().each(function(t){s(t).each(function(i){var e=Gr.forMenu(l,i,t),u=d(i);n(i,e).each(function(e){var t,n,r,o;t=a,n=u,o=d(r=i),n.width===o.width&&n.height===o.height||(Ia(t,r.dom(),n.width,n.height),Ba(t,r.dom(),o.width,o.height)),a.selection.setRng(e),a.focus(),c.clear(i),pa(i)})})})},u=function(e){return f().bind(function(o){return s(o).bind(function(e){var t=De.fromDom(a.getDoc()),n=Gr.forMenu(l,e,o),r=Fn(y,t,S.none());return Ma(e,n,r)})})},m=function(u){n.get().each(function(e){var i=k(e,function(e){return Bn(e)});f().each(function(o){s(o).each(function(t){var e=De.fromDom(a.getDoc()),n=zn(e),r=Gr.pasteRows(l,t,o,i,n);u(t,r).each(function(e){a.selection.setRng(e),a.focus(),c.clear(t)})})})})};oc({mceTableSplitCells:function(){i(t.unmergeCells)},mceTableMergeCells:function(){i(t.mergeCells)},mceTableInsertRowBefore:function(){i(t.insertRowsBefore)},mceTableInsertRowAfter:function(){i(t.insertRowsAfter)},mceTableInsertColBefore:function(){i(t.insertColumnsBefore)},mceTableInsertColAfter:function(){i(t.insertColumnsAfter)},mceTableDeleteCol:function(){i(t.deleteColumn)},mceTableDeleteRow:function(){i(t.deleteRow)},mceTableCutRow:function(e){n.set(u()),i(t.deleteRow)},mceTableCopyRow:function(e){n.set(u())},mceTablePasteRowBefore:function(e){m(t.pasteRowsBefore)},mceTablePasteRowAfter:function(e){m(t.pasteRowsAfter)},mceTableDelete:function(){f().orThunk(o).each(function(e){gn.table(e,r).filter(g(r)).each(function(e){var t=De.fromText("");Et(e,t),Mt(e);var n=a.dom.createRng();n.setStart(t.dom(),0),n.setEnd(t.dom(),0),a.selection.setRng(n)})})}},function(e,t){a.addCommand(t,e)}),oc({mceInsertTable:b(rc,a),mceTableProps:b(rc,a,!0),mceTableRowProps:b(Ka,a),mceTableCellProps:b(Ya,a)},function(n,e){a.addCommand(e,function(e,t){n(t)})})},uc=function(e){var t=S.from(e.dom().documentElement).map(De.fromDom).getOr(e);return{parent:C(t),view:C(e),origin:C(vo(0,0))}},ac=function(e,t){return{parent:C(t),view:C(e),origin:C(vo(0,0))}};function cc(e){var n=$.apply(null,e),r=[];return{bind:function(e){if(e===undefined)throw"Event bind error: undefined handler";r.push(e)},unbind:function(t){r=I(r,function(e){return e!==t})},trigger:function(){var t=n.apply(null,arguments);P(r,function(e){e(t)})}}}var lc={create:function(e){return{registry:K(e,function(e){return{bind:e.bind,unbind:e.unbind}}),trigger:K(e,function(e){return e.trigger})}}},fc={mode:_u(["compare","extract","mutate","sink"]),sink:_u(["element","start","stop","destroy"]),api:_u(["forceDrop","drop","move","delayDrop"])},sc={resolve:ni("ephox-dragster").resolve},dc=function(m,g){return function(e){if(m(e)){var t,n,r,o,i,u,a,c=De.fromDom(e.target),l=function(){e.stopPropagation()},f=function(){e.preventDefault()},s=x(f,l),d=(t=c,n=e.clientX,r=e.clientY,o=l,i=f,u=s,a=e,{target:C(t),x:C(n),y:C(r),stop:o,prevent:i,kill:u,raw:C(a)});g(d)}}},mc=function(e,t,n,r){return o=e,i=t,u=!1,a=dc(n,r),o.dom().addEventListener(i,a,u),{unbind:b(gc,o,i,a,u)};var o,i,u,a},gc=function(e,t,n,r){e.dom().removeEventListener(t,n,r)},hc=C(!0),pc=function(e,t,n){return mc(e,t,hc,n)},vc=fc.mode({compare:function(e,t){return vo(t.left()-e.left(),t.top()-e.top())},extract:function(e){return S.some(vo(e.x(),e.y()))},sink:function(e,t){var n,r,o,i=(n=t,r=_o({layerClass:sc.resolve("blocker")},n),o=De.fromTag("div"),pe(o,"role","presentation"),Pe(o,{position:"fixed",left:"0px",top:"0px",width:"100%",height:"100%"}),Yo(o,sc.resolve("blocker")),Yo(o,r.layerClass),{element:function(){return o},destroy:function(){Mt(o)}}),u=pc(i.element(),"mousedown",e.forceDrop),a=pc(i.element(),"mouseup",e.drop),c=pc(i.element(),"mousemove",e.move),l=pc(i.element(),"mouseout",e.delayDrop);return fc.sink({element:i.element,start:function(e){kt(e,i.element())},stop:function(){Mt(i.element())},destroy:function(){i.destroy(),a.unbind(),c.unbind(),l.unbind(),u.unbind()}})},mutate:function(e,t){e.mutate(t.left(),t.top())}});function bc(){var i=S.none(),u=lc.create({move:cc(["info"])});return{onEvent:function(e,o){o.extract(e).each(function(e){var t,n,r;(t=o,n=e,r=i.map(function(e){return t.compare(e,n)}),i=S.some(n),r).each(function(e){u.trigger.move(e)})})},reset:function(){i=S.none()},events:u.registry}}function wc(){var e={onEvent:function(e,t){},reset:y},t=bc(),n=e;return{on:function(){n.reset(),n=t},off:function(){n.reset(),n=e},isOn:function(){return n===t},onEvent:function(e,t){n.onEvent(e,t)},events:t.events}}var yc=function(t,n,e){var r,o,i,u=!1,a=lc.create({start:cc([]),stop:cc([])}),c=wc(),l=function(){d.stop(),c.isOn()&&(c.off(),a.trigger.stop())},f=(r=l,o=200,i=null,{cancel:function(){null!==i&&(m.clearTimeout(i),i=null)},throttle:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];null!==i&&m.clearTimeout(i),i=m.setTimeout(function(){r.apply(null,e),i=null},o)}});c.events.move.bind(function(e){n.mutate(t,e.info())});var s=function(t){return function(){var e=Array.prototype.slice.call(arguments,0);if(u)return t.apply(null,e)}},d=n.sink(fc.api({forceDrop:l,drop:s(l),move:s(function(e,t){f.cancel(),c.onEvent(e,n)}),delayDrop:s(f.throttle)}),e);return{element:d.element,go:function(e){d.start(e),c.on(),a.trigger.start()},on:function(){u=!0},off:function(){u=!1},destroy:function(){d.destroy()},events:a.registry}},xc={transform:function(e,t){var n=t!==undefined?t:{},r=n.mode!==undefined?n.mode:vc;return yc(e,r,t)}},Cc=function(e,t,n){return un(e,t,n).isSome()};function Sc(){var n,r=lc.create({drag:cc(["xDelta","yDelta","target"])}),o=S.none(),e={mutate:function(e,t){n.trigger.drag(e,t)},events:(n=lc.create({drag:cc(["xDelta","yDelta"])})).registry};return e.events.drag.bind(function(t){o.each(function(e){r.trigger.drag(t.xDelta(),t.yDelta(),e)})}),{assign:function(e){o=S.some(e)},get:function(){return o},mutate:e.mutate,events:r.registry}}var Rc=ri.resolve("resizer-bar-dragging");function Tc(e,n){var r=ko.height,t=function(o,t,i){var n=Sc(),r=xc.transform(n,{}),u=S.none(),e=function(e,t){return S.from(be(e,t))};n.events.drag.bind(function(n){e(n.target(),"data-row").each(function(e){var t=gu.getInt(n.target(),"top");ke(n.target(),"top",t+n.yDelta()+"px")}),e(n.target(),"data-column").each(function(e){var t=gu.getInt(n.target(),"left");ke(n.target(),"left",t+n.xDelta()+"px")})});var a=function(e,t){return gu.getInt(e,t)-parseInt(be(e,"data-initial-"+t),10)};r.events.stop.bind(function(){n.get().each(function(r){u.each(function(n){e(r,"data-row").each(function(e){var t=a(r,"top");ye(r,"data-initial-top"),d.trigger.adjustHeight(n,t,parseInt(e,10))}),e(r,"data-column").each(function(e){var t=a(r,"left");ye(r,"data-initial-left"),d.trigger.adjustWidth(n,t,parseInt(e,10))}),mi.refresh(o,n,i,t)})})});var c=function(e,t){d.trigger.startAdjust(),n.assign(e),pe(e,"data-initial-"+t,parseInt(Ie(e,t),10)),Yo(e,Rc),ke(e,"opacity","0.2"),r.go(o.parent())},l=pc(o.parent(),"mousedown",function(e){mi.isRowBar(e.target())&&c(e.target(),"top"),mi.isColBar(e.target())&&c(e.target(),"left")}),f=function(e){return bt(e,o.view())},s=pc(o.view(),"mouseover",function(e){"table"===ce(e.target())||Cc(e.target(),"table",f)?(u="table"===ce(e.target())?S.some(e.target()):nn(e.target(),"table",f)).each(function(e){mi.refresh(o,e,i,t)}):Oe(e.target())&&mi.destroy(o)}),d=lc.create({adjustHeight:cc(["table","delta","row"]),adjustWidth:cc(["table","delta","column"]),startAdjust:cc([])});return{destroy:function(){l.unbind(),s.unbind(),r.destroy(),mi.destroy(o)},refresh:function(e){mi.refresh(o,e,i,t)},on:r.on,off:r.off,hideBars:b(mi.hide,o),showBars:b(mi.show,o),events:d.registry}}(e,n,r),o=lc.create({beforeResize:cc(["table"]),afterResize:cc(["table"]),startDrag:cc([])});return t.events.adjustHeight.bind(function(e){o.trigger.beforeResize(e.table());var t=r.delta(e.delta());Pu(e.table(),t,e.row(),r),o.trigger.afterResize(e.table())}),t.events.startAdjust.bind(function(e){o.trigger.startDrag()}),t.events.adjustWidth.bind(function(e){o.trigger.beforeResize(e.table());var t=n.delta(e.delta(),e.table());ku(e.table(),t,e.column(),n),o.trigger.afterResize(e.table())}),{on:t.on,off:t.off,hideBars:t.hideBars,showBars:t.showBars,destroy:t.destroy,events:o.registry}}var Dc=function(e,t){return e.inline?ac(sa(e),(n=De.fromTag("div"),Pe(n,{position:"static",height:"0",width:"0",padding:"0",margin:"0",border:"0"}),kt(Ne(),n),n)):uc(De.fromDom(e.getDoc()));var n},Oc=function(e,t){e.inline&&Mt(t.parent())},Nc=function(u){var a,c,o=S.none(),i=S.none(),l=S.none(),f=/(\d+(\.\d+)?)%/,s=function(e){return"TABLE"===e.nodeName};return u.on("init",function(){var e,t=Io(wa.directionAt),n=Dc(u);if(l=S.some(n),("table"===(e=u.getParam("object_resizing",!0))||e)&&u.getParam("table_resize_bars",!0,"boolean")){var r=Tc(n,t);r.on(),r.events.startDrag.bind(function(e){o=S.some(u.selection.getRng())}),r.events.beforeResize.bind(function(e){var t=e.table().dom();Ia(u,t,da(t),ma(t))}),r.events.afterResize.bind(function(e){var t=e.table(),n=t.dom();pa(t),o.each(function(e){u.selection.setRng(e),u.focus()}),Ba(u,n,da(n),ma(n)),u.undoManager.add()}),i=S.some(r)}}),u.on("ObjectResizeStart",function(e){var t,n=e.target;s(n)&&(a=e.width,t=n,c=u.dom.getStyle(t,"width")||u.dom.getAttrib(t,"width"))}),u.on("ObjectResized",function(e){var t=e.target;if(s(t)){var n=t;if(f.test(c)){var r=parseFloat(f.exec(c)[1]),o=e.width*r/a;u.dom.setStyle(n,"width",o+"%")}else{var i=[];_a.each(n.rows,function(e){_a.each(e.cells,function(e){var t=u.dom.getStyle(e,"width",!0);i.push({cell:e,width:t})})}),_a.each(i,function(e){u.dom.setStyle(e.cell,"width",e.width),u.dom.setAttrib(e.cell,"width",null)})}}}),{lazyResize:function(){return i},lazyWire:function(){return l.getOr(uc(De.fromDom(u.getBody())))},destroy:function(){i.each(function(e){e.destroy()}),l.each(function(e){Oc(u,e)})}}},Ec=function(e){return{fold:e}},Ac=function(o){return Ec(function(e,t,n,r){return e(o)})},kc=function(o){return Ec(function(e,t,n,r){return t(o)})},Pc=function(o,i){return Ec(function(e,t,n,r){return n(o,i)})},Ic=function(o){return Ec(function(e,t,n,r){return r(o)})},Bc=function(n,e){return gn.table(n,e).bind(function(e){var t=gn.cells(e);return L(t,function(e){return bt(n,e)}).map(function(e){return{index:C(e),all:C(t)}})})},Wc=function(t,e){return Bc(t,e).fold(function(){return Ac(t)},function(e){return e.index()+1<e.all().length?Pc(t,e.all()[e.index()+1]):Ic(t)})},Mc=function(t,e){return Bc(t,e).fold(function(){return Ac()},function(e){return 0<=e.index()-1?Pc(t,e.all()[e.index()-1]):kc(t)})},_c=_r([{before:["element"]},{on:["element","offset"]},{after:["element"]}]),Lc={before:_c.before,on:_c.on,after:_c.after,cata:function(e,t,n,r){return e.fold(t,n,r)},getStart:function(e){return e.fold(o,o,o)}},jc=_r([{domRange:["rng"]},{relative:["startSitu","finishSitu"]},{exact:["start","soffset","finish","foffset"]}]),Fc=$("start","soffset","finish","foffset"),zc=function(e){var t,n=e.match({domRange:function(e){return De.fromDom(e.startContainer)},relative:function(e,t){return Lc.getStart(e)},exact:function(e,t,n,r){return e}});return t=n.dom().ownerDocument.defaultView,De.fromDom(t)},Hc=jc.relative,Uc=jc.exact,qc=function(e,t,n,r){var o,i,u,a,c,l=(i=t,u=n,a=r,(c=xt(o=e).dom().createRange()).setStart(o.dom(),i),c.setEnd(u.dom(),a),c),f=bt(e,n)&&t===r;return l.collapsed&&!f},Vc=function(e,t){e.selectNodeContents(t.dom())},Gc=function(e,t,n){var r,o,i=e.document.createRange();return r=i,t.fold(function(e){r.setStartBefore(e.dom())},function(e,t){r.setStart(e.dom(),t)},function(e){r.setStartAfter(e.dom())}),o=i,n.fold(function(e){o.setEndBefore(e.dom())},function(e,t){o.setEnd(e.dom(),t)},function(e){o.setEndAfter(e.dom())}),i},Yc=function(e,t,n,r,o){var i=e.document.createRange();return i.setStart(t.dom(),n),i.setEnd(r.dom(),o),i},Xc=function(e){return{left:C(e.left),top:C(e.top),right:C(e.right),bottom:C(e.bottom),width:C(e.width),height:C(e.height)}},Kc=_r([{ltr:["start","soffset","finish","foffset"]},{rtl:["start","soffset","finish","foffset"]}]),Jc=function(e,t,n){return t(De.fromDom(n.startContainer),n.startOffset,De.fromDom(n.endContainer),n.endOffset)},$c=function(e,t){var o,n,r,i=(o=e,t.match({domRange:function(e){return{ltr:C(e),rtl:S.none}},relative:function(e,t){return{ltr:Re(function(){return Gc(o,e,t)}),rtl:Re(function(){return S.some(Gc(o,t,e))})}},exact:function(e,t,n,r){return{ltr:Re(function(){return Yc(o,e,t,n,r)}),rtl:Re(function(){return S.some(Yc(o,n,r,e,t))})}}}));return(r=(n=i).ltr()).collapsed?n.rtl().filter(function(e){return!1===e.collapsed}).map(function(e){return Kc.rtl(De.fromDom(e.endContainer),e.endOffset,De.fromDom(e.startContainer),e.startOffset)}).getOrThunk(function(){return Jc(0,Kc.ltr,r)}):Jc(0,Kc.ltr,r)},Qc=function(i,e){return $c(i,e).match({ltr:function(e,t,n,r){var o=i.document.createRange();return o.setStart(e.dom(),t),o.setEnd(n.dom(),r),o},rtl:function(e,t,n,r){var o=i.document.createRange();return o.setStart(n.dom(),r),o.setEnd(e.dom(),t),o}})},Zc=function(e,t,n){return t>=e.left&&t<=e.right&&n>=e.top&&n<=e.bottom},el=function(n,r,e,t,o){var i=function(e){var t=n.dom().createRange();return t.setStart(r.dom(),e),t.collapse(!0),t},u=Sn(r).length,a=function(e,t,n,r,o){if(0===o)return 0;if(t===r)return o-1;for(var i=r,u=1;u<o;u++){var a=e(u),c=Math.abs(t-a.left);if(n<=a.bottom){if(n<a.top||i<c)return u-1;i=c}}return 0}(function(e){return i(e).getBoundingClientRect()},e,t,o.right,u);return i(a)},tl=function(t,n,r,o){var e=t.dom().createRange();e.selectNode(n.dom());var i=e.getClientRects();return jo(i,function(e){return Zc(e,r,o)?S.some(e):S.none()}).map(function(e){return el(t,n,r,o,e)})},nl=function(t,e,n,r){var o=t.dom().createRange(),i=Dt(e);return jo(i,function(e){return o.selectNode(e.dom()),Zc(o.getBoundingClientRect(),n,r)?rl(t,e,n,r):S.none()})},rl=function(e,t,n,r){return(me(t)?tl:nl)(e,t,n,r)},ol=function(e,t){return t-e.left<e.right-t},il=function(e,t,n){var r=e.dom().createRange();return r.selectNode(t.dom()),r.collapse(n),r},ul=function(t,e,n){var r=t.dom().createRange();r.selectNode(e.dom());var o=r.getBoundingClientRect(),i=ol(o,n);return(!0===i?En:An)(e).map(function(e){return il(t,e,i)})},al=function(e,t,n){var r=t.dom().getBoundingClientRect(),o=ol(r,n);return S.some(il(e,t,o))},cl=function(e,t,n,r){var o=e.dom().createRange();o.selectNode(t.dom());var i=o.getBoundingClientRect();return function(e,t,n,r){var o=e.dom().createRange();o.selectNode(t.dom());var i=o.getBoundingClientRect(),u=Math.max(i.left,Math.min(i.right,n)),a=Math.max(i.top,Math.min(i.bottom,r));return rl(e,t,u,a)}(e,t,Math.max(i.left,Math.min(i.right,n)),Math.max(i.top,Math.min(i.bottom,r)))},ll=document.caretPositionFromPoint?function(n,e,t){return S.from(n.dom().caretPositionFromPoint(e,t)).bind(function(e){if(null===e.offsetNode)return S.none();var t=n.dom().createRange();return t.setStart(e.offsetNode,e.offset),t.collapse(),S.some(t)})}:document.caretRangeFromPoint?function(e,t,n){return S.from(e.dom().caretRangeFromPoint(t,n))}:function(o,i,t){return De.fromPoint(o,i,t).bind(function(r){var e=function(){return e=o,n=i,(0===Dt(t=r).length?al:ul)(e,t,n);var e,t,n};return 0===Dt(r).length?e():cl(o,r,i,t).orThunk(e)})},fl=function(e,t){var n=ce(e);return"input"===n?Lc.after(e):E(["br","img"],n)?0===t?Lc.before(e):Lc.after(e):Lc.on(e,t)},sl=function(e,t){var n=e.fold(Lc.before,fl,Lc.after),r=t.fold(Lc.before,fl,Lc.after);return Hc(n,r)},dl=function(e,t,n,r){var o=fl(e,t),i=fl(n,r);return Hc(o,i)},ml=function(e,t){S.from(e.getSelection()).each(function(e){e.removeAllRanges(),e.addRange(t)})},gl=function(e,t,n,r,o){var i=Yc(e,t,n,r,o);ml(e,i)},hl=function(s,e){return $c(s,e).match({ltr:function(e,t,n,r){gl(s,e,t,n,r)},rtl:function(e,t,n,r){var o,i,u,a,c,l=s.getSelection();if(l.setBaseAndExtent)l.setBaseAndExtent(e.dom(),t,n.dom(),r);else if(l.extend)try{i=e,u=t,a=n,c=r,(o=l).collapse(i.dom(),u),o.extend(a.dom(),c)}catch(f){gl(s,n,r,e,t)}else gl(s,n,r,e,t)}})},pl=function(e){var o=zc(e).dom(),t=function(e,t,n,r){return Yc(o,e,t,n,r)},n=e.match({domRange:function(e){var t=De.fromDom(e.startContainer),n=De.fromDom(e.endContainer);return dl(t,e.startOffset,n,e.endOffset)},relative:sl,exact:dl});return $c(o,n).match({ltr:t,rtl:t})},vl=function(e){var t=De.fromDom(e.anchorNode),n=De.fromDom(e.focusNode);return qc(t,e.anchorOffset,n,e.focusOffset)?S.some(Fc(t,e.anchorOffset,n,e.focusOffset)):function(e){if(0<e.rangeCount){var t=e.getRangeAt(0),n=e.getRangeAt(e.rangeCount-1);return S.some(Fc(De.fromDom(t.startContainer),t.startOffset,De.fromDom(n.endContainer),n.endOffset))}return S.none()}(e)},bl=function(e,t){var n,r,o=(n=t,r=e.document.createRange(),Vc(r,n),r);ml(e,o)},wl=function(e){return(t=e,S.from(t.getSelection()).filter(function(e){return 0<e.rangeCount}).bind(vl)).map(function(e){return Uc(e.start(),e.soffset(),e.finish(),e.foffset())});var t},yl=function(e,t){var n,r,o,i=Qc(e,t);return r=(n=i).getClientRects(),0<(o=0<r.length?r[0]:n.getBoundingClientRect()).width||0<o.height?S.some(o).map(Xc):S.none()},xl=function(e,t,n){return r=e,o=t,i=n,u=De.fromDom(r.document),ll(u,o,i).map(function(e){return Fc(De.fromDom(e.startContainer),e.startOffset,De.fromDom(e.endContainer),e.endOffset)});var r,o,i,u},Cl=tinymce.util.Tools.resolve("tinymce.util.VK"),Sl=function(e,t,n,r){return Ol(e,t,Wc(n),r)},Rl=function(e,t,n,r){return Ol(e,t,Mc(n),r)},Tl=function(e,t){var n=Uc(t,0,t,0);return pl(n)},Dl=function(e,t){var n,r=Xt(t,"tr");return(n=r,0===n.length?S.none():S.some(n[n.length-1])).bind(function(e){return on(e,"td,th").map(function(e){return Tl(0,e)})})},Ol=function(r,e,t,o,n){return t.fold(S.none,S.none,function(e,t){return En(t).map(function(e){return Tl(0,e)})},function(n){return gn.table(n,e).bind(function(e){var t=Gr.noMenu(n);return r.undoManager.transact(function(){o.insertRowsAfter(e,t)}),Dl(0,e)})})},Nl=["table","li","dl"],El=function(t,n,r,o){if(t.keyCode===Cl.TAB){var i=sa(n),u=function(e){var t=ce(e);return bt(e,i)||E(Nl,t)},e=n.selection.getRng();if(e.collapsed){var a=De.fromDom(e.startContainer);gn.cell(a,u).each(function(e){t.preventDefault(),(t.shiftKey?Rl:Sl)(n,u,e,r,o).each(function(e){n.selection.setRng(e)})})}}},Al={response:$("selection","kill")},kl=function(t){return function(e){return e===t}},Pl=kl(38),Il=kl(40),Bl={ltr:{isBackward:kl(37),isForward:kl(39)},rtl:{isBackward:kl(39),isForward:kl(37)},isUp:Pl,isDown:Il,isNavigation:function(e){return 37<=e&&e<=40}},Wl=function(e,t){var n=Qc(e,t);return{start:C(De.fromDom(n.startContainer)),soffset:C(n.startOffset),finish:C(De.fromDom(n.endContainer)),foffset:C(n.endOffset)}},Ml=function(e,t,n,r){return{start:C(Lc.on(e,t)),finish:C(Lc.on(n,r))}};function _l(a){return{elementFromPoint:function(e,t){return De.fromPoint(De.fromDom(a.document),e,t)},getRect:function(e){return e.dom().getBoundingClientRect()},getRangedRect:function(e,t,n,r){var o=Uc(e,t,n,r);return yl(a,o).map(function(e){return K(e,c)})},getSelection:function(){return wl(a).map(function(e){return Wl(a,e)})},fromSitus:function(e){var t=Hc(e.start(),e.finish());return Wl(a,t)},situsFromPoint:function(e,t){return xl(a,e,t).map(function(e){return{start:C(Lc.on(e.start(),e.soffset())),finish:C(Lc.on(e.finish(),e.foffset()))}})},clearSelection:function(){a.getSelection().removeAllRanges()},setSelection:function(e){var t,n,r,o,i,u;t=a,n=e.start(),r=e.soffset(),o=e.finish(),i=e.foffset(),u=dl(n,r,o,i),hl(t,u)},setRelativeSelection:function(e,t){var n,r;n=a,r=sl(e,t),hl(n,r)},selectContents:function(e){bl(a,e)},getInnerHeight:function(){return a.innerHeight},getScrollY:function(){var e,t,n,r;return(e=De.fromDom(a.document),t=e!==undefined?e.dom():m.document,n=t.body.scrollLeft||t.documentElement.scrollLeft,r=t.body.scrollTop||t.documentElement.scrollTop,vo(n,r)).top()},scrollBy:function(e,t){var n,r,o;n=e,r=t,((o=De.fromDom(a.document))!==undefined?o.dom():m.document).defaultView.scrollBy(n,r)}}}mt.detect().browser.isSafari();var Ll=function(n,e,r,t,o){return bt(r,t)?S.none():Or(r,t,e).bind(function(e){var t=e.boxes().getOr([]);return 0<t.length?(o(n,t,e.start(),e.finish()),S.some(Al.response(S.some(Ml(r,0,r,Dn(r))),!0))):S.none()})},jl={sync:function(n,r,e,t,o,i,u){return bt(e,o)&&t===i?S.none():un(e,"td,th",r).bind(function(t){return un(o,"td,th",r).bind(function(e){return Ll(n,r,t,e,u)})})},detect:Ll,update:function(e,t,n,r,o){return Er(r,e,t,o.firstSelectedSelector(),o.lastSelectedSelector()).map(function(e){return o.clear(n),o.selectRange(n,e.boxes(),e.start(),e.finish()),e.boxes()})}},Fl=re(["left","top","right","bottom"],[]),zl={nu:Fl,moveUp:function(e,t){return Fl({left:e.left(),top:e.top()-t,right:e.right(),bottom:e.bottom()-t})},moveDown:function(e,t){return Fl({left:e.left(),top:e.top()+t,right:e.right(),bottom:e.bottom()+t})},moveBottomTo:function(e,t){var n=e.bottom()-e.top();return Fl({left:e.left(),top:t-n,right:e.right(),bottom:t})},moveTopTo:function(e,t){var n=e.bottom()-e.top();return Fl({left:e.left(),top:t,right:e.right(),bottom:t+n})},getTop:function(e){return e.top()},getBottom:function(e){return e.bottom()},translate:function(e,t,n){return Fl({left:e.left()+t,top:e.top()+n,right:e.right()+t,bottom:e.bottom()+n})},toString:function(e){return"("+e.left()+", "+e.top()+") -> ("+e.right()+", "+e.bottom()+")"}},Hl=function(e){return zl.nu({left:e.left,top:e.top,right:e.right,bottom:e.bottom})},Ul=function(e,t){return S.some(e.getRect(t))},ql=function(e,t,n){return de(t)?Ul(e,t).map(Hl):me(t)?(r=e,o=t,i=n,0<=i&&i<Dn(o)?r.getRangedRect(o,i,o,i+1):0<i?r.getRangedRect(o,i-1,o,i):S.none()).map(Hl):S.none();var r,o,i},Vl=function(e,t){return de(t)?Ul(e,t).map(Hl):me(t)?e.getRangedRect(t,0,t,Dn(t)).map(Hl):S.none()},Gl=$("item","mode"),Yl=function(e,t,n,r){return void 0===r&&(r=Xl),e.property().parent(t).map(function(e){return Gl(e,r)})},Xl=function(e,t,n,r){return void 0===r&&(r=Kl),n.sibling(e,t).map(function(e){return Gl(e,r)})},Kl=function(e,t,n,r){void 0===r&&(r=Kl);var o=e.property().children(t);return n.first(o).map(function(e){return Gl(e,r)})},Jl=[{current:Yl,next:Xl,fallback:S.none()},{current:Xl,next:Kl,fallback:S.some(Yl)},{current:Kl,next:Kl,fallback:S.some(Xl)}],$l=function(t,n,r,o,e){return void 0===e&&(e=Jl),_(e,function(e){return e.current===r}).bind(function(e){return e.current(t,n,o,e.next).orThunk(function(){return e.fallback.bind(function(e){return $l(t,n,e,o)})})})},Ql=function(){return{sibling:function(e,t){return e.query().prevSibling(t)},first:function(e){return 0<e.length?S.some(e[e.length-1]):S.none()}}},Zl=function(){return{sibling:function(e,t){return e.query().nextSibling(t)},first:function(e){return 0<e.length?S.some(e[0]):S.none()}}},ef=function(t,e,n,r,o,i){return $l(t,e,r,o).bind(function(e){return i(e.item())?S.none():n(e.item())?S.some(e.item()):ef(t,e.item(),n,e.mode(),o,i)})},tf=function(t){return function(e){return 0===t.property().children(e).length}},nf=function(e,t,n,r){return ef(e,t,n,Xl,Ql(),r)},rf=function(e,t,n,r){return ef(e,t,n,Xl,Zl(),r)},of=Un(),uf=function(e,t){return r=t,nf(n=of,e,tf(n),r);var n,r},af=function(e,t){return r=t,rf(n=of,e,tf(n),r);var n,r},cf=_r([{none:[]},{retry:["caret"]}]),lf=function(t,e,r){return(n=e,o=Ju,Kt(function(e){return o(e)},tn,n,o,i)).fold(C(!1),function(e){return Vl(t,e).exists(function(e){return n=e,(t=r).left()<n.left()||Math.abs(n.right()-t.left())<1||t.left()>n.right();var t,n})});var n,o,i},ff={point:zl.getTop,adjuster:function(e,t,n,r,o){var i=zl.moveUp(o,5);return Math.abs(n.top()-r.top())<1?cf.retry(i):n.bottom()<o.top()?cf.retry(i):n.bottom()===o.top()?cf.retry(zl.moveUp(o,1)):lf(e,t,o)?cf.retry(zl.translate(i,5,0)):cf.none()},move:zl.moveUp,gather:uf},sf={point:zl.getBottom,adjuster:function(e,t,n,r,o){var i=zl.moveDown(o,5);return Math.abs(n.bottom()-r.bottom())<1?cf.retry(i):n.top()>o.bottom()?cf.retry(i):n.top()===o.bottom()?cf.retry(zl.moveDown(o,1)):lf(e,t,o)?cf.retry(zl.translate(i,5,0)):cf.none()},move:zl.moveDown,gather:af},df=function(n,r,o,i,u){return 0===u?S.some(i):(c=n,l=i.left(),f=r.point(i),c.elementFromPoint(l,f).filter(function(e){return"table"===ce(e)}).isSome()?(t=i,a=u-1,df(n,e=r,o,e.move(t,5),a)):n.situsFromPoint(i.left(),r.point(i)).bind(function(e){return e.start().fold(S.none,function(t,e){return Vl(n,t,e).bind(function(e){return r.adjuster(n,t,e,o,i).fold(S.none,function(e){return df(n,r,o,e,u-1)})}).orThunk(function(){return S.some(i)})},S.none)}));var e,t,a,c,l,f},mf=function(t,n,e){var r,o,i,u=t.move(e,5),a=df(n,t,e,u,100).getOr(u);return(r=t,o=a,i=n,r.point(o)>i.getInnerHeight()?S.some(r.point(o)-i.getInnerHeight()):r.point(o)<0?S.some(-r.point(o)):S.none()).fold(function(){return n.situsFromPoint(a.left(),t.point(a))},function(e){return n.scrollBy(0,e),n.situsFromPoint(a.left(),t.point(a)-e)})},gf={tryUp:b(mf,ff),tryDown:b(mf,sf),ieTryUp:function(e,t){return e.situsFromPoint(t.left(),t.top()-5)},ieTryDown:function(e,t){return e.situsFromPoint(t.left(),t.bottom()+5)},getJumpSize:C(5)},hf=_r([{none:["message"]},{success:[]},{failedUp:["cell"]},{failedDown:["cell"]}]),pf=function(e){return un(e,"tr")},vf={verify:function(a,e,t,n,r,c,o){return un(n,"td,th",o).bind(function(u){return un(e,"td,th",o).map(function(i){return bt(u,i)?bt(n,u)&&Dn(u)===r?c(i):hf.none("in same cell"):lr.sharedOne(pf,[u,i]).fold(function(){return t=i,n=u,r=(e=a).getRect(t),(o=e.getRect(n)).right>r.left&&o.left<r.right?hf.success():c(i);var e,t,n,r,o},function(e){return c(i)})})}).getOr(hf.none("default"))},cata:function(e,t,n,r,o){return e.fold(t,n,r,o)},adt:hf},bf=$("element","offset"),wf=($("element","deltaOffset"),$("element","start","finish"),$("begin","end"),$("element","text"),$("ancestor","descendants","element","index"),$("parent","children","element","index")),yf=function(e,t){return L(e,b(bt,t))},xf=function(e){return"br"===ce(e)},Cf=function(e,t,n){return t(e,n).bind(function(e){return me(e)&&0===Sn(e).trim().length?Cf(e,t,n):S.some(e)})},Sf=function(t,e,n,r){return(o=e,i=n,Ot(o,i).filter(xf).orThunk(function(){return Ot(o,i-1).filter(xf)})).bind(function(e){return r.traverse(e).fold(function(){return Cf(e,r.gather,t).map(r.relative)},function(e){return(r=e,Ct(r).bind(function(t){var n=Dt(t);return yf(n,r).map(function(e){return wf(t,n,r,e)})})).map(function(e){return Lc.on(e.parent(),e.index())});var r})});var o,i},Rf=function(e,t,n,r){var o,i,u;return(xf(t)?(o=e,i=t,(u=r).traverse(i).orThunk(function(){return Cf(i,u.gather,o)}).map(u.relative)):Sf(e,t,n,r)).map(function(e){return{start:C(e),finish:C(e)}})},Tf=function(e){return vf.cata(e,function(e){return S.none()},function(){return S.none()},function(e){return S.some(bf(e,0))},function(e){return S.some(bf(e,Dn(e)))})},Df=mt.detect(),Of=function(r,o,i,u,a,c){return 0===c?S.none():Af(r,o,i,u,a).bind(function(e){var t=r.fromSitus(e),n=vf.verify(r,i,u,t.finish(),t.foffset(),a.failure,o);return vf.cata(n,function(){return S.none()},function(){return S.some(e)},function(e){return bt(i,e)&&0===u?Nf(r,i,u,zl.moveUp,a):Of(r,o,e,0,a,c-1)},function(e){return bt(i,e)&&u===Dn(e)?Nf(r,i,u,zl.moveDown,a):Of(r,o,e,Dn(e),a,c-1)})})},Nf=function(t,e,n,r,o){return ql(t,e,n).bind(function(e){return Ef(t,o,r(e,gf.getJumpSize()))})},Ef=function(e,t,n){return Df.browser.isChrome()||Df.browser.isSafari()||Df.browser.isFirefox()||Df.browser.isEdge()?t.otherRetry(e,n):Df.browser.isIE()?t.ieRetry(e,n):S.none()},Af=function(t,e,n,r,o){return ql(t,n,r).bind(function(e){return Ef(t,o,e)})},kf=function(t,n,r){return(o=t,i=n,u=r,o.getSelection().bind(function(r){return Rf(i,r.finish(),r.foffset(),u).fold(function(){return S.some(bf(r.finish(),r.foffset()))},function(e){var t=o.fromSitus(e),n=vf.verify(o,r.finish(),r.foffset(),t.finish(),t.foffset(),u.failure,i);return Tf(n)})})).bind(function(e){return Of(t,n,e.element(),e.offset(),r,20).map(t.fromSitus)});var o,i,u},Pf=mt.detect(),If=function(e,t){return tn(e,function(e){return Ct(e).exists(function(e){return bt(e,t)})},n).isSome();var n},Bf=function(t,r,o,e,i){return un(e,"td,th",r).bind(function(n){return un(n,"table",r).bind(function(e){return If(i,e)?kf(t,r,o).bind(function(t){return un(t.finish(),"td,th",r).map(function(e){return{start:C(n),finish:C(e),range:C(t)}})}):S.none()})})},Wf=function(e,t,n,r,o,i){return Pf.browser.isIE()?S.none():i(r,t).orThunk(function(){return Bf(e,t,n,r,o).map(function(e){var t=e.range();return Al.response(S.some(Ml(t.start(),t.soffset(),t.finish(),t.foffset())),!0)})})},Mf=function(e,t,n,r,o,i,u){return Bf(e,n,r,o,i).bind(function(e){return jl.detect(t,n,e.start(),e.finish(),u)})},_f=function(e,u){return un(e,"tr",u).bind(function(i){return un(i,"table",u).bind(function(e){var t,n,r,o=Xt(e,"tr");return bt(i,o[0])?(t=e,n=function(e){return An(e).isSome()},r=u,nf(of,t,n,r)).map(function(e){var t=Dn(e);return Al.response(S.some(Ml(e,t,e,t)),!0)}):S.none()})})},Lf=function(e,u){return un(e,"tr",u).bind(function(i){return un(i,"table",u).bind(function(e){var t,n,r,o=Xt(e,"tr");return bt(i,o[o.length-1])?(t=e,n=function(e){return En(e).isSome()},r=u,rf(of,t,n,r)).map(function(e){return Al.response(S.some(Ml(e,0,e,0)),!0)}):S.none()})})},jf=function(e,t){return un(e,"td,th",t)},Ff={down:{traverse:Tt,gather:af,relative:Lc.before,otherRetry:gf.tryDown,ieRetry:gf.ieTryDown,failure:vf.adt.failedDown},up:{traverse:Rt,gather:uf,relative:Lc.before,otherRetry:gf.tryUp,ieRetry:gf.ieTryUp,failure:vf.adt.failedUp}},zf=$("rows","cols"),Hf={mouse:function(e,t,n,r){var o,i,u,a,c,l,f=_l(e),s=(o=f,i=t,u=n,a=r,c=S.none(),l=function(){c=S.none()},{mousedown:function(e){a.clear(i),c=jf(e.target(),u)},mouseover:function(e){c.each(function(r){a.clear(i),jf(e.target(),u).each(function(n){Or(r,n,u).each(function(e){var t=e.boxes().getOr([]);(1<t.length||1===t.length&&!bt(r,n))&&(a.selectRange(i,t,e.start(),e.finish()),o.selectContents(n))})})})},mouseup:function(){c.each(l)}});return{mousedown:s.mousedown,mouseover:s.mouseover,mouseup:s.mouseup}},keyboard:function(e,c,l,f){var s=_l(e),d=function(){return f.clear(c),S.none()};return{keydown:function(e,t,n,r,o,i){var u=e.raw().which,a=!0===e.raw().shiftKey;return Nr(c,f.selectedSelector()).fold(function(){return Bl.isDown(u)&&a?b(Mf,s,c,l,Ff.down,r,t,f.selectRange):Bl.isUp(u)&&a?b(Mf,s,c,l,Ff.up,r,t,f.selectRange):Bl.isDown(u)?b(Wf,s,l,Ff.down,r,t,Lf):Bl.isUp(u)?b(Wf,s,l,Ff.up,r,t,_f):S.none},function(t){var e=function(e){return function(){return jo(e,function(e){return jl.update(e.rows(),e.cols(),c,t,f)}).fold(function(){return Ar(c,f.firstSelectedSelector(),f.lastSelectedSelector()).map(function(e){var t=Bl.isDown(u)||i.isForward(u)?Lc.after:Lc.before;return s.setRelativeSelection(Lc.on(e.first(),0),t(e.table())),f.clear(c),Al.response(S.none(),!0)})},function(e){return S.some(Al.response(S.none(),!0))})}};return Bl.isDown(u)&&a?e([zf(1,0)]):Bl.isUp(u)&&a?e([zf(-1,0)]):i.isBackward(u)&&a?e([zf(0,-1),zf(-1,0)]):i.isForward(u)&&a?e([zf(0,1),zf(1,0)]):Bl.isNavigation(u)&&!1===a?d:S.none})()},keyup:function(t,n,r,o,i){return Nr(c,f.selectedSelector()).fold(function(){var e=t.raw().which;return 0==(!0===t.raw().shiftKey)?S.none():Bl.isNavigation(e)?jl.sync(c,l,n,r,o,i,f.selectRange):S.none()},S.none)}}}},Uf=function(r,e){P(e,function(e){var t,n;n=e,qo(t=r)?t.dom().classList.remove(n):Go(t,n),Xo(t)})},qf={byClass:function(o){var t,n,i=(t=o.selected(),function(e){Yo(e,t)}),r=(n=[o.selected(),o.lastSelected(),o.firstSelected()],function(e){Uf(e,n)}),u=function(e){var t=Xt(e,o.selectedSelector());P(t,r)};return{clear:u,selectRange:function(e,t,n,r){u(e),P(t,i),Yo(n,o.firstSelected()),Yo(r,o.lastSelected())},selectedSelector:o.selectedSelector,firstSelectedSelector:o.firstSelectedSelector,lastSelectedSelector:o.lastSelectedSelector}},byAttr:function(o){var n=function(e){ye(e,o.selected()),ye(e,o.firstSelected()),ye(e,o.lastSelected())},i=function(e){pe(e,o.selected(),"1")},u=function(e){var t=Xt(e,o.selectedSelector());P(t,n)};return{clear:u,selectRange:function(e,t,n,r){u(e),P(t,i),pe(n,o.firstSelected(),"1"),pe(r,o.lastSelected(),"1")},selectedSelector:o.selectedSelector,firstSelectedSelector:o.firstSelectedSelector,lastSelectedSelector:o.lastSelectedSelector}}},Vf=function(e){return!1===Ko(De.fromDom(e.target),"ephox-snooker-resizer-bar")};function Gf(h,p){var v=re(["mousedown","mouseover","mouseup","keyup","keydown"],[]),b=S.none(),w=qf.byAttr(Mr);return h.on("init",function(e){var r=h.getWin(),o=sa(h),t=ga(h),n=Hf.mouse(r,o,t,w),a=Hf.keyboard(r,o,t,w),c=function(e,t){!0===e.raw().shiftKey&&(t.kill()&&e.kill(),t.selection().each(function(e){var t=Hc(e.start(),e.finish()),n=Qc(r,t);h.selection.setRng(n)}))},i=function(e){var t=f(e);if(t.raw().shiftKey&&Bl.isNavigation(t.raw().which)){var n=h.selection.getRng(),r=De.fromDom(n.startContainer),o=De.fromDom(n.endContainer);a.keyup(t,r,n.startOffset,o,n.endOffset).each(function(e){c(t,e)})}},u=function(e){var t=f(e);p().each(function(e){e.hideBars()});var n=h.selection.getRng(),r=De.fromDom(h.selection.getStart()),o=De.fromDom(n.startContainer),i=De.fromDom(n.endContainer),u=wa.directionAt(r).isRtl()?Bl.rtl:Bl.ltr;a.keydown(t,o,n.startOffset,i,n.endOffset,u).each(function(e){c(t,e)}),p().each(function(e){e.showBars()})},l=function(e){return e.hasOwnProperty("x")&&e.hasOwnProperty("y")},f=function(e){var t=De.fromDom(e.target),n=function(){e.stopPropagation()},r=function(){e.preventDefault()},o=x(r,n);return{target:C(t),x:C(l(e)?e.x:null),y:C(l(e)?e.y:null),stop:n,prevent:r,kill:o,raw:C(e)}},s=function(e){return 0===e.button},d=function(e){s(e)&&Vf(e)&&n.mousedown(f(e))},m=function(e){var t;(t=e).buttons!==undefined&&0==(1&t.buttons)||!Vf(e)||n.mouseover(f(e))},g=function(e){s(e)&&Vf(e)&&n.mouseup(f(e))};h.on("mousedown",d),h.on("mouseover",m),h.on("mouseup",g),h.on("keyup",i),h.on("keydown",u),h.on("nodechange",function(){var e=h.selection,t=De.fromDom(e.getStart()),n=De.fromDom(e.getEnd());lr.sharedOne(gn.table,[t,n]).fold(function(){w.clear(o)},y)}),b=S.some(v({mousedown:d,mouseover:m,mouseup:g,keyup:i,keydown:u}))}),{clear:w.clear,destroy:function(){b.each(function(e){})}}}var Yf=_a.each,Xf=function(t){var n=[];function e(e){return function(){t.execCommand(e)}}Yf("inserttable tableprops deletetable | cell row column".split(" "),function(e){"|"===e?n.push({text:"-"}):n.push(t.menuItems[e])}),t.addButton("table",{type:"menubutton",title:"Table",menu:n}),t.addButton("tableprops",{title:"Table properties",onclick:e("mceTableProps"),icon:"table"}),t.addButton("tabledelete",{title:"Delete table",onclick:e("mceTableDelete")}),t.addButton("tablecellprops",{title:"Cell properties",onclick:e("mceTableCellProps")}),t.addButton("tablemergecells",{title:"Merge cells",onclick:e("mceTableMergeCells")}),t.addButton("tablesplitcells",{title:"Split cell",onclick:e("mceTableSplitCells")}),t.addButton("tableinsertrowbefore",{title:"Insert row before",onclick:e("mceTableInsertRowBefore")}),t.addButton("tableinsertrowafter",{title:"Insert row after",onclick:e("mceTableInsertRowAfter")}),t.addButton("tabledeleterow",{title:"Delete row",onclick:e("mceTableDeleteRow")}),t.addButton("tablerowprops",{title:"Row properties",onclick:e("mceTableRowProps")}),t.addButton("tablecutrow",{title:"Cut row",onclick:e("mceTableCutRow")}),t.addButton("tablecopyrow",{title:"Copy row",onclick:e("mceTableCopyRow")}),t.addButton("tablepasterowbefore",{title:"Paste row before",onclick:e("mceTablePasteRowBefore")}),t.addButton("tablepasterowafter",{title:"Paste row after",onclick:e("mceTablePasteRowAfter")}),t.addButton("tableinsertcolbefore",{title:"Insert column before",onclick:e("mceTableInsertColBefore")}),t.addButton("tableinsertcolafter",{title:"Insert column after",onclick:e("mceTableInsertColAfter")}),t.addButton("tabledeletecol",{title:"Delete column",onclick:e("mceTableDeleteCol")})},Kf=function(t){var e,n=""===(e=t.getParam("table_toolbar",ya))||!1===e?[]:w(e)?e.split(/[ ,]/):R(e)?e:[];0<n.length&&t.addContextToolbar(function(e){return t.dom.is(e,"table")&&t.getBody().contains(e)},n.join(" "))},Jf=function(o,n){var r=S.none(),i=[],u=[],a=[],c=[],l=function(e){e.disabled(!0)},f=function(e){e.disabled(!1)},e=function(){var t=this;i.push(t),r.fold(function(){l(t)},function(e){f(t)})},t=function(){var t=this;u.push(t),r.fold(function(){l(t)},function(e){f(t)})};o.on("init",function(){o.on("nodechange",function(e){var t=S.from(o.dom.getParent(o.selection.getStart(),"th,td"));(r=t.bind(function(e){var t=De.fromDom(e);return gn.table(t).map(function(e){return Gr.forMenu(n,e,t)})})).fold(function(){P(i,l),P(u,l),P(a,l),P(c,l)},function(t){P(i,f),P(u,f),P(a,function(e){e.disabled(t.mergable().isNone())}),P(c,function(e){e.disabled(t.unmergable().isNone())})})})});var s=function(e,t,n,r){var o,i,u,a,c,l=r.getEl().getElementsByTagName("table")[0],f=r.isRtl()||"tl-tr"===r.parent().rel;for(l.nextSibling.innerHTML=t+1+" x "+(n+1),f&&(t=9-t),i=0;i<10;i++)for(o=0;o<10;o++)a=l.rows[i].childNodes[o].firstChild,c=(f?t<=o:o<=t)&&i<=n,e.dom.toggleClass(a,"mce-active",c),c&&(u=a);return u.parentNode},d=!1===o.getParam("table_grid",!0,"boolean")?{text:"Table",icon:"table",context:"table",onclick:m("mceInsertTable")}:{text:"Table",icon:"table",context:"table",ariaHideMenu:!0,onclick:function(e){e.aria&&(this.parent().hideAll(),e.stopImmediatePropagation(),o.execCommand("mceInsertTable"))},onshow:function(){s(o,0,0,this.menu.items()[0])},onhide:function(){var e=this.menu.items()[0].getEl().getElementsByTagName("a");o.dom.removeClass(e,"mce-active"),o.dom.addClass(e[0],"mce-active")},menu:[{type:"container",html:function(){var e="";e='<table role="grid" class="mce-grid mce-grid-border" aria-readonly="true">';for(var t=0;t<10;t++){e+="<tr>";for(var n=0;n<10;n++)e+='<td role="gridcell" tabindex="-1"><a id="mcegrid'+(10*t+n)+'" href="#" data-mce-x="'+n+'" data-mce-y="'+t+'"></a></td>';e+="</tr>"}return e+="</table>",e+='<div class="mce-text-center" role="presentation">1 x 1</div>'}(),onPostRender:function(){this.lastX=this.lastY=0},onmousemove:function(e){var t,n,r=e.target;"A"===r.tagName.toUpperCase()&&(t=parseInt(r.getAttribute("data-mce-x"),10),n=parseInt(r.getAttribute("data-mce-y"),10),(this.isRtl()||"tl-tr"===this.parent().rel)&&(t=9-t),t===this.lastX&&n===this.lastY||(s(o,t,n,e.control),this.lastX=t,this.lastY=n))},onclick:function(e){var t=this;"A"===e.target.tagName.toUpperCase()&&(e.preventDefault(),e.stopPropagation(),t.parent().cancel(),o.undoManager.transact(function(){ec(o,t.lastX+1,t.lastY+1)}),o.addVisual())}}]};function m(e){return function(){o.execCommand(e)}}var g={text:"Table properties",context:"table",onPostRender:e,onclick:m("mceTableProps")},h={text:"Delete table",context:"table",onPostRender:e,cmd:"mceTableDelete"},p={text:"Row",context:"table",menu:[{text:"Insert row before",onclick:m("mceTableInsertRowBefore"),onPostRender:t},{text:"Insert row after",onclick:m("mceTableInsertRowAfter"),onPostRender:t},{text:"Delete row",onclick:m("mceTableDeleteRow"),onPostRender:t},{text:"Row properties",onclick:m("mceTableRowProps"),onPostRender:t},{text:"-"},{text:"Cut row",onclick:m("mceTableCutRow"),onPostRender:t},{text:"Copy row",onclick:m("mceTableCopyRow"),onPostRender:t},{text:"Paste row before",onclick:m("mceTablePasteRowBefore"),onPostRender:t},{text:"Paste row after",onclick:m("mceTablePasteRowAfter"),onPostRender:t}]},v={text:"Column",context:"table",menu:[{text:"Insert column before",onclick:m("mceTableInsertColBefore"),onPostRender:t},{text:"Insert column after",onclick:m("mceTableInsertColAfter"),onPostRender:t},{text:"Delete column",onclick:m("mceTableDeleteCol"),onPostRender:t}]},b={separator:"before",text:"Cell",context:"table",menu:[{text:"Cell properties",onclick:m("mceTableCellProps"),onPostRender:t},{text:"Merge cells",onclick:m("mceTableMergeCells"),onPostRender:function(){var t=this;a.push(t),r.fold(function(){l(t)},function(e){t.disabled(e.mergable().isNone())})}},{text:"Split cell",onclick:m("mceTableSplitCells"),onPostRender:function(){var t=this;c.push(t),r.fold(function(){l(t)},function(e){t.disabled(e.unmergable().isNone())})}}]};o.addMenuItem("inserttable",d),o.addMenuItem("tableprops",g),o.addMenuItem("deletetable",h),o.addMenuItem("row",p),o.addMenuItem("column",v),o.addMenuItem("cell",b)},$f=function(n,r){return{insertTable:function(e,t){return ec(n,e,t)},setClipboardRows:function(e){return t=r,n=k(e,De.fromDom),void t.set(S.from(n));var t,n},getClipboardRows:function(){return r.get().fold(function(){},function(e){return k(e,function(e){return e.dom()})})}}};e.add("table",function(t){var n,r=Nc(t),e=Gf(t,r.lazyResize),o=Wa(t,r.lazyWire),i=(n=t,{get:function(){var e=sa(n);return kr(e,Mr.selectedSelector()).fold(function(){return n.selection.getStart()===undefined?jr.none():jr.single(n.selection)},function(e){return jr.multiple(e)})}}),u=Bu(S.none());return ic(t,o,e,i,u),Yr(t,i,o,e),Jf(t,i),Xf(t),Kf(t),t.on("PreInit",function(){t.serializer.addTempAttr(Mr.firstSelected()),t.serializer.addTempAttr(Mr.lastSelected())}),t.getParam("table_tab_navigation",!0,"boolean")&&t.on("keydown",function(e){El(e,t,o,r.lazyWire)}),t.on("remove",function(){r.destroy(),e.destroy()}),$f(t,u)})}(window);
\ No newline at end of file
+!function(m){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),y=function(){},x=function(n,r){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n(r.apply(null,e))}},C=function(e){return function(){return e}},o=function(e){return e};function b(r){for(var o=[],e=1;e<arguments.length;e++)o[e-1]=arguments[e];return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=o.concat(e);return r.apply(null,n)}}var t,n,r,i,g=function(n){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return!n.apply(null,e)}},f=C(!1),u=C(!0),a=function(){return c},c=(t=function(e){return e.isNone()},i={fold:function(e,t){return e()},is:f,isSome:f,isNone:u,getOr:r=function(e){return e},getOrThunk:n=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:C(null),getOrUndefined:C(undefined),or:r,orThunk:n,map:a,each:y,bind:a,exists:f,forall:u,filter:a,equals:t,equals_:t,toArray:function(){return[]},toString:C("none()")},Object.freeze&&Object.freeze(i),i),l=function(n){var e=C(n),t=function(){return o},r=function(e){return e(n)},o={fold:function(e,t){return t(n)},is:function(e){return n===e},isSome:u,isNone:f,getOr:e,getOrThunk:e,getOrDie:e,getOrNull:e,getOrUndefined:e,or:t,orThunk:t,map:function(e){return l(e(n))},each:function(e){e(n)},bind:r,exists:r,forall:r,filter:function(e){return e(n)?o:c},toArray:function(){return[n]},toString:function(){return"some("+n+")"},equals:function(e){return e.is(n)},equals_:function(e,t){return e.fold(f,function(e){return t(n,e)})}};return o},R={some:l,none:a,from:function(e){return null===e||e===undefined?c:l(e)}},s=function(t){return function(e){return function(e){if(null===e)return"null";var t=typeof e;return"object"===t&&(Array.prototype.isPrototypeOf(e)||e.constructor&&"Array"===e.constructor.name)?"array":"object"===t&&(String.prototype.isPrototypeOf(e)||e.constructor&&"String"===e.constructor.name)?"string":t}(e)===t}},d=s("string"),h=s("array"),p=s("boolean"),v=s("function"),w=s("number"),S=Array.prototype.slice,T=Array.prototype.indexOf,D=Array.prototype.push,O=function(e,t){return n=e,r=t,-1<T.call(n,r);var n,r},N=function(e,t){for(var n=0,r=e.length;n<r;n++)if(t(e[n],n))return!0;return!1},E=function(e,t){for(var n=e.length,r=new Array(n),o=0;o<n;o++){var i=e[o];r[o]=t(i,o)}return r},k=function(e,t){for(var n=0,r=e.length;n<r;n++)t(e[n],n)},A=function(e,t){for(var n=[],r=0,o=e.length;r<o;r++){var i=e[r];t(i,r)&&n.push(i)}return n},P=function(e,t,n){return function(e,t){for(var n=e.length-1;0<=n;n--)t(e[n],n)}(e,function(e){n=t(n,e)}),n},I=function(e,t,n){return k(e,function(e){n=t(n,e)}),n},B=function(e,t){for(var n=0,r=e.length;n<r;n++){var o=e[n];if(t(o,n))return R.some(o)}return R.none()},W=function(e,t){for(var n=0,r=e.length;n<r;n++)if(t(e[n],n))return R.some(n);return R.none()},M=function(e){for(var t=[],n=0,r=e.length;n<r;++n){if(!h(e[n]))throw new Error("Arr.flatten item "+n+" was not an array, input: "+e);D.apply(t,e[n])}return t},_=function(e,t){var n=E(e,t);return M(n)},L=function(e,t){for(var n=0,r=e.length;n<r;++n)if(!0!==t(e[n],n))return!1;return!0},F=function(e){var t=S.call(e,0);return t.reverse(),t},j=(v(Array.from)&&Array.from,Object.keys),z=function(e,t){for(var n=j(e),r=0,o=n.length;r<o;r++){var i=n[r];t(e[i],i)}},H=function(e,n){return U(e,function(e,t){return{k:t,v:n(e,t)}})},U=function(e,r){var o={};return z(e,function(e,t){var n=r(e,t);o[n.k]=n.v}),o},q=function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];return function(){for(var n=[],e=0;e<arguments.length;e++)n[e]=arguments[e];if(t.length!==n.length)throw new Error('Wrong number of arguments to struct. Expected "['+t.length+']", got '+n.length+" arguments");var r={};return k(t,function(e,t){r[e]=C(n[t])}),r}},V=function(e){return e.slice(0).sort()},G=function(e,t){throw new Error("All required keys ("+V(e).join(", ")+") were not specified. Specified keys were: "+V(t).join(", ")+".")},Y=function(e){throw new Error("Unsupported keys for object: "+V(e).join(", "))},X=function(t,e){if(!h(e))throw new Error("The "+t+" fields must be an array. Was: "+e+".");k(e,function(e){if(!d(e))throw new Error("The value "+e+" in the "+t+" fields was not a string.")})},K=function(e){var n=V(e);B(n,function(e,t){return t<n.length-1&&e===n[t+1]}).each(function(e){throw new Error("The field: "+e+" occurs more than once in the combined fields: ["+n.join(", ")+"].")})},J=function(o,i){var u=o.concat(i);if(0===u.length)throw new Error("You must specify at least one required or optional field.");return X("required",o),X("optional",i),K(u),function(t){var n=j(t);L(o,function(e){return O(n,e)})||G(o,n);var e=A(n,function(e){return!O(u,e)});0<e.length&&Y(e);var r={};return k(o,function(e){r[e]=C(t[e])}),k(i,function(e){r[e]=C(Object.prototype.hasOwnProperty.call(t,e)?R.some(t[e]):R.none())}),r}},$=(m.Node.ATTRIBUTE_NODE,m.Node.CDATA_SECTION_NODE,m.Node.COMMENT_NODE),Q=m.Node.DOCUMENT_NODE,Z=(m.Node.DOCUMENT_TYPE_NODE,m.Node.DOCUMENT_FRAGMENT_NODE,m.Node.ELEMENT_NODE),ee=m.Node.TEXT_NODE,te=(m.Node.PROCESSING_INSTRUCTION_NODE,m.Node.ENTITY_REFERENCE_NODE,m.Node.ENTITY_NODE,m.Node.NOTATION_NODE,"undefined"!=typeof m.window?m.window:Function("return this;")()),ne=function(e,t){return function(e,t){for(var n=t!==undefined&&null!==t?t:te,r=0;r<e.length&&n!==undefined&&null!==n;++r)n=n[e[r]];return n}(e.split("."),t)},re=function(e,t){var n=ne(e,t);if(n===undefined||null===n)throw new Error(e+" not available on this browser");return n},oe=function(e){return e.dom().nodeName.toLowerCase()},ie=function(e){return e.dom().nodeType},ue=function(t){return function(e){return ie(e)===t}},ae=function(e){return ie(e)===$||"#comment"===oe(e)},ce=ue(Z),le=ue(ee),fe=function(e,t,n){if(!(d(n)||p(n)||w(n)))throw m.console.error("Invalid call to Attr.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")},se=function(e,t,n){fe(e.dom(),t,n)},de=function(e,t){var n=e.dom();z(t,function(e,t){fe(n,t,e)})},me=function(e,t){var n=e.dom().getAttribute(t);return null===n?undefined:n},ge=function(e,t){var n=e.dom();return!(!n||!n.hasAttribute)&&n.hasAttribute(t)},he=function(e,t){e.dom().removeAttribute(t)},pe=function(e){return I(e.dom().attributes,function(e,t){return e[t.name]=t.value,e},{})},ve=function(e,t){return-1!==e.indexOf(t)},be=function(e){return e.style!==undefined&&v(e.style.getPropertyValue)},we=function(n){var r,o=!1;return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return o||(o=!0,r=n.apply(null,e)),r}},ye=function(e){if(null===e||e===undefined)throw new Error("Node cannot be null or undefined");return{dom:C(e)}},xe={fromHtml:function(e,t){var n=(t||m.document).createElement("div");if(n.innerHTML=e,!n.hasChildNodes()||1<n.childNodes.length)throw m.console.error("HTML does not have a single root node",e),new Error("HTML must have a single root node");return ye(n.childNodes[0])},fromTag:function(e,t){var n=(t||m.document).createElement(e);return ye(n)},fromText:function(e,t){var n=(t||m.document).createTextNode(e);return ye(n)},fromDom:ye,fromPoint:function(e,t,n){var r=e.dom();return R.from(r.elementFromPoint(t,n)).map(ye)}},Ce=function(e){var t=le(e)?e.dom().parentNode:e.dom();return t!==undefined&&null!==t&&t.ownerDocument.body.contains(t)},Re=we(function(){return Se(xe.fromDom(m.document))}),Se=function(e){var t=e.dom().body;if(null===t||t===undefined)throw new Error("Body is not available yet");return xe.fromDom(t)},Te=function(e,t,n){if(!d(n))throw m.console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);be(e)&&e.style.setProperty(t,n)},De=function(e,t,n){var r=e.dom();Te(r,t,n)},Oe=function(e,t){var n=e.dom();z(t,function(e,t){Te(n,t,e)})},Ne=function(e,t){var n=e.dom(),r=m.window.getComputedStyle(n).getPropertyValue(t),o=""!==r||Ce(e)?r:Ee(n,t);return null===o?undefined:o},Ee=function(e,t){return be(e)?e.style.getPropertyValue(t):""},ke=function(e,t){var n=e.dom(),r=Ee(n,t);return R.from(r).filter(function(e){return 0<e.length})},Ae=function(e,t){var n,r,o=e.dom();r=t,be(n=o)&&n.style.removeProperty(r),ge(e,"style")&&""===me(e,"style").replace(/^\s+|\s+$/g,"")&&he(e,"style")},Pe=function(){return re("Node")},Ie=function(e,t,n){return 0!=(e.compareDocumentPosition(t)&n)},Be=function(e,t){return Ie(e,t,Pe().DOCUMENT_POSITION_CONTAINED_BY)},We=function(e,t){var n=function(e,t){for(var n=0;n<e.length;n++){var r=e[n];if(r.test(t))return r}return undefined}(e,t);if(!n)return{major:0,minor:0};var r=function(e){return Number(t.replace(n,"$"+e))};return _e(r(1),r(2))},Me=function(){return _e(0,0)},_e=function(e,t){return{major:e,minor:t}},Le={nu:_e,detect:function(e,t){var n=String(t).toLowerCase();return 0===e.length?Me():We(e,n)},unknown:Me},Fe="Firefox",je=function(e,t){return function(){return t===e}},ze=function(e){var t=e.current;return{current:t,version:e.version,isEdge:je("Edge",t),isChrome:je("Chrome",t),isIE:je("IE",t),isOpera:je("Opera",t),isFirefox:je(Fe,t),isSafari:je("Safari",t)}},He={unknown:function(){return ze({current:undefined,version:Le.unknown()})},nu:ze,edge:C("Edge"),chrome:C("Chrome"),ie:C("IE"),opera:C("Opera"),firefox:C(Fe),safari:C("Safari")},Ue="Windows",qe="Android",Ve="Solaris",Ge="FreeBSD",Ye=function(e,t){return function(){return t===e}},Xe=function(e){var t=e.current;return{current:t,version:e.version,isWindows:Ye(Ue,t),isiOS:Ye("iOS",t),isAndroid:Ye(qe,t),isOSX:Ye("OSX",t),isLinux:Ye("Linux",t),isSolaris:Ye(Ve,t),isFreeBSD:Ye(Ge,t)}},Ke={unknown:function(){return Xe({current:undefined,version:Le.unknown()})},nu:Xe,windows:C(Ue),ios:C("iOS"),android:C(qe),linux:C("Linux"),osx:C("OSX"),solaris:C(Ve),freebsd:C(Ge)},Je=function(e,t){var n=String(t).toLowerCase();return B(e,function(e){return e.search(n)})},$e=function(e,n){return Je(e,n).map(function(e){var t=Le.detect(e.versionRegexes,n);return{current:e.name,version:t}})},Qe=function(e,n){return Je(e,n).map(function(e){var t=Le.detect(e.versionRegexes,n);return{current:e.name,version:t}})},Ze=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,et=function(t){return function(e){return ve(e,t)}},tt=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:function(e){return ve(e,"edge/")&&ve(e,"chrome")&&ve(e,"safari")&&ve(e,"applewebkit")}},{name:"Chrome",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,Ze],search:function(e){return ve(e,"chrome")&&!ve(e,"chromeframe")}},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:function(e){return ve(e,"msie")||ve(e,"trident")}},{name:"Opera",versionRegexes:[Ze,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:et("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:et("firefox")},{name:"Safari",versionRegexes:[Ze,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:function(e){return(ve(e,"safari")||ve(e,"mobile/"))&&ve(e,"applewebkit")}}],nt=[{name:"Windows",search:et("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:function(e){return ve(e,"iphone")||ve(e,"ipad")},versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:et("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"OSX",search:et("os x"),versionRegexes:[/.*?os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:et("linux"),versionRegexes:[]},{name:"Solaris",search:et("sunos"),versionRegexes:[]},{name:"FreeBSD",search:et("freebsd"),versionRegexes:[]}],rt={browsers:C(tt),oses:C(nt)},ot=function(e){var t,n,r,o,i,u,a,c,l,f,s,d=rt.browsers(),m=rt.oses(),g=$e(d,e).fold(He.unknown,He.nu),h=Qe(m,e).fold(Ke.unknown,Ke.nu);return{browser:g,os:h,deviceType:(n=g,r=e,o=(t=h).isiOS()&&!0===/ipad/i.test(r),i=t.isiOS()&&!o,u=t.isAndroid()&&3===t.version.major,a=t.isAndroid()&&4===t.version.major,c=o||u||a&&!0===/mobile/i.test(r),l=t.isiOS()||t.isAndroid(),f=l&&!c,s=n.isSafari()&&t.isiOS()&&!1===/safari/i.test(r),{isiPad:C(o),isiPhone:C(i),isTablet:C(c),isPhone:C(f),isTouch:C(l),isAndroid:t.isAndroid,isiOS:t.isiOS,isWebView:C(s)})}},it={detect:we(function(){var e=m.navigator.userAgent;return ot(e)})},ut=Z,at=Q,ct=function(e,t){var n=e.dom();if(n.nodeType!==ut)return!1;var r=n;if(r.matches!==undefined)return r.matches(t);if(r.msMatchesSelector!==undefined)return r.msMatchesSelector(t);if(r.webkitMatchesSelector!==undefined)return r.webkitMatchesSelector(t);if(r.mozMatchesSelector!==undefined)return r.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")},lt=function(e){return e.nodeType!==ut&&e.nodeType!==at||0===e.childElementCount},ft=function(e,t){return e.dom()===t.dom()},st=it.detect().browser.isIE()?function(e,t){return Be(e.dom(),t.dom())}:function(e,t){var n=e.dom(),r=t.dom();return n!==r&&n.contains(r)},dt=ct,mt=function(e){return xe.fromDom(e.dom().ownerDocument)},gt=function(e){return R.from(e.dom().parentNode).map(xe.fromDom)},ht=function(e,t){for(var n=v(t)?t:f,r=e.dom(),o=[];null!==r.parentNode&&r.parentNode!==undefined;){var i=r.parentNode,u=xe.fromDom(i);if(o.push(u),!0===n(u))break;r=i}return o},pt=function(e){return R.from(e.dom().previousSibling).map(xe.fromDom)},vt=function(e){return R.from(e.dom().nextSibling).map(xe.fromDom)},bt=function(e){return E(e.dom().childNodes,xe.fromDom)},wt=function(e,t){var n=e.dom().childNodes;return R.from(n[t]).map(xe.fromDom)},yt=(q("element","offset"),function(t,n){gt(t).each(function(e){e.dom().insertBefore(n.dom(),t.dom())})}),xt=function(e,t){vt(e).fold(function(){gt(e).each(function(e){Rt(e,t)})},function(e){yt(e,t)})},Ct=function(t,n){wt(t,0).fold(function(){Rt(t,n)},function(e){t.dom().insertBefore(n.dom(),e.dom())})},Rt=function(e,t){e.dom().appendChild(t.dom())},St=function(e,t){yt(e,t),Rt(t,e)},Tt=function(r,o){k(o,function(e,t){var n=0===t?r:o[t-1];xt(n,e)})},Dt=function(t,e){k(e,function(e){Rt(t,e)})},Ot=function(e){e.dom().textContent="",k(bt(e),function(e){Nt(e)})},Nt=function(e){var t=e.dom();null!==t.parentNode&&t.parentNode.removeChild(t)},Et=function(e){var t,n=bt(e);0<n.length&&(t=e,k(n,function(e){yt(t,e)})),Nt(e)},kt=(q("width","height"),q("width","height"),q("rows","columns")),At=q("row","column"),Pt=(q("x","y"),q("element","rowspan","colspan")),It=q("element","rowspan","colspan","isNew"),Bt=q("element","rowspan","colspan","row","column"),Wt=q("element","cells","section"),Mt=q("element","isNew"),_t=q("element","cells","section","isNew"),Lt=q("cells","section"),Ft=q("details","section"),jt=q("startRow","startCol","finishRow","finishCol"),zt=function(e,t){var n=[];return k(bt(e),function(e){t(e)&&(n=n.concat([e])),n=n.concat(zt(e,t))}),n},Ht=function(e,t,n){return r=function(e){return ct(e,t)},A(ht(e,n),r);var r},Ut=function(e,t){return n=function(e){return ct(e,t)},A(bt(e),n);var n},qt=function(e,t){return n=t,o=(r=e)===undefined?m.document:r.dom(),lt(o)?[]:E(o.querySelectorAll(n),xe.fromDom);var n,r,o};function Vt(e,t,n,r,o){return e(n,r)?R.some(n):v(o)&&o(n)?R.none():t(n,r,o)}var Gt,Yt,Xt,Kt=function(e,t,n){for(var r=e.dom(),o=v(n)?n:C(!1);r.parentNode;){r=r.parentNode;var i=xe.fromDom(r);if(t(i))return R.some(i);if(o(i))break}return R.none()},Jt=function(e,t,n){return Kt(e,function(e){return ct(e,t)},n)},$t=function(e,t){return n=function(e){return ct(e,t)},B(e.dom().childNodes,function(e){return n(xe.fromDom(e))}).map(xe.fromDom);var n},Qt=function(e,t){return n=t,o=(r=e)===undefined?m.document:r.dom(),lt(o)?R.none():R.from(o.querySelector(n)).map(xe.fromDom);var n,r,o},Zt=function(e,t,n){return Vt(ct,Jt,e,t,n)},en=function(e,t,n){return _(bt(e),function(e){return ct(e,t)?n(e)?[e]:[]:en(e,t,n)})},tn={firstLayer:function(e,t){return en(e,t,C(!0))},filterFirstLayer:en},nn=function(e,t,n){return void 0===n&&(n=f),n(t)?R.none():O(e,oe(t))?R.some(t):Jt(t,e.join(","),function(e){return ct(e,"table")||n(e)})},rn=function(t,e){return gt(e).map(function(e){return Ut(e,t)})},on=b(rn,"th,td"),un=b(rn,"tr"),an=function(e,t){return parseInt(me(e,t),10)},cn={cell:function(e,t){return nn(["td","th"],e,t)},firstCell:function(e){return Qt(e,"th,td")},cells:function(e){return tn.firstLayer(e,"th,td")},neighbourCells:on,table:function(e,t){return Zt(e,"table",t)},row:function(e,t){return nn(["tr"],e,t)},rows:function(e){return tn.firstLayer(e,"tr")},notCell:function(e,t){return nn(["caption","tr","tbody","tfoot","thead"],e,t)},neighbourRows:un,attr:an,grid:function(e,t,n){var r=an(e,t),o=an(e,n);return kt(r,o)}},ln=function(e){var t=cn.rows(e);return E(t,function(e){var t=e,n=gt(t).map(function(e){var t=oe(e);return"tfoot"===t||"thead"===t||"tbody"===t?t:"tbody"}).getOr("tbody"),r=E(cn.cells(e),function(e){var t=ge(e,"rowspan")?parseInt(me(e,"rowspan"),10):1,n=ge(e,"colspan")?parseInt(me(e,"colspan"),10):1;return Pt(e,t,n)});return Wt(t,r,n)})},fn=function(e,n){return E(e,function(e){var t=E(cn.cells(e),function(e){var t=ge(e,"rowspan")?parseInt(me(e,"rowspan"),10):1,n=ge(e,"colspan")?parseInt(me(e,"colspan"),10):1;return Pt(e,t,n)});return Wt(e,t,n.section())})},sn=function(e,t){return e+","+t},dn=function(e,t){var n=_(e.all(),function(e){return e.cells()});return A(n,t)},mn={generate:function(e){var l={},t=[],n=e.length,f=0;k(e,function(e,a){var c=[];k(e.cells(),function(e){for(var t=0;l[sn(a,t)]!==undefined;)t++;for(var n=Bt(e.element(),e.rowspan(),e.colspan(),a,t),r=0;r<e.colspan();r++)for(var o=0;o<e.rowspan();o++){var i=t+r,u=sn(a+o,i);l[u]=n,f=Math.max(f,i+1)}c.push(n)}),t.push(Wt(e.element(),c,e.section()))});var r=kt(n,f);return{grid:C(r),access:C(l),all:C(t)}},getAt:function(e,t,n){var r=e.access()[sn(t,n)];return r!==undefined?R.some(r):R.none()},findItem:function(e,t,n){var r=dn(e,function(e){return n(t,e.element())});return 0<r.length?R.some(r[0]):R.none()},filterItems:dn,justCells:function(e){var t=E(e.all(),function(e){return e.cells()});return M(t)}},gn=q("minRow","minCol","maxRow","maxCol"),hn=function(e,t){var n,i,r,u,a,c,l,o,f,s,d=function(e){return ct(e.element(),t)},m=ln(e),g=mn.generate(m),h=(i=d,r=(n=g).grid().columns(),u=n.grid().rows(),a=r,l=c=0,z(n.access(),function(e){if(i(e)){var t=e.row(),n=t+e.rowspan()-1,r=e.column(),o=r+e.colspan()-1;t<u?u=t:c<n&&(c=n),r<a?a=r:l<o&&(l=o)}}),gn(u,a,c,l)),p="th:not("+t+"),td:not("+t+")",v=tn.filterFirstLayer(e,"th,td",function(e){return ct(e,p)});return k(v,Nt),function(e,t,n,r){for(var o,i,u,a=t.grid().columns(),c=t.grid().rows(),l=0;l<c;l++)for(var f=!1,s=0;s<a;s++)l<n.minRow()||l>n.maxRow()||s<n.minCol()||s>n.maxCol()||(mn.getAt(t,l,s).filter(r).isNone()?(o=f,i=e[l].element(),u=xe.fromTag("td"),Rt(u,xe.fromTag("br")),(o?Rt:Ct)(i,u)):f=!0)}(m,g,h,d),o=e,f=h,s=A(tn.firstLayer(o,"tr"),function(e){return 0===e.dom().childElementCount}),k(s,Nt),f.minCol()!==f.maxCol()&&f.minRow()!==f.maxRow()||k(tn.firstLayer(o,"th,td"),function(e){he(e,"rowspan"),he(e,"colspan")}),he(o,"width"),he(o,"height"),Ae(o,"width"),Ae(o,"height"),e},pn=(Gt=le,Yt="text",{get:function(e){if(!Gt(e))throw new Error("Can only get "+Yt+" value of a "+Yt+" node");return Xt(e).getOr("")},getOption:Xt=function(e){return Gt(e)?R.from(e.dom().nodeValue):R.none()},set:function(e,t){if(!Gt(e))throw new Error("Can only set raw "+Yt+" value of a "+Yt+" node");e.dom().nodeValue=t}}),vn=function(e){return pn.get(e)},bn=function(e){return pn.getOption(e)},wn=function(e,t){pn.set(e,t)},yn=function(e){return"img"===oe(e)?1:bn(e).fold(function(){return bt(e).length},function(e){return e.length})},xn=["img","br"],Cn=function(e){return bn(e).filter(function(e){return 0!==e.trim().length||-1<e.indexOf("\xa0")}).isSome()||O(xn,oe(e))},Rn=function(e){return o=Cn,(i=function(e){for(var t=0;t<e.childNodes.length;t++){var n=xe.fromDom(e.childNodes[t]);if(o(n))return R.some(n);var r=i(e.childNodes[t]);if(r.isSome())return r}return R.none()})(e.dom());var o,i},Sn=function(e){return Tn(e,Cn)},Tn=function(e,i){var u=function(e){for(var t=bt(e),n=t.length-1;0<=n;n--){var r=t[n];if(i(r))return R.some(r);var o=u(r);if(o.isSome())return o}return R.none()};return u(e)},Dn=function(e,t){return xe.fromDom(e.dom().cloneNode(t))},On=function(e){return Dn(e,!1)},Nn=function(e){return Dn(e,!0)},En=function(e,t){var n,r,o,i,u=(n=e,r=t,o=xe.fromTag(r),i=pe(n),de(o,i),o),a=bt(Nn(e));return Dt(u,a),u},kn=function(){var e=xe.fromTag("td");return Rt(e,xe.fromTag("br")),e},An=function(e,t,n){var r=En(e,t);return z(n,function(e,t){null===e?he(r,t):se(r,t,e)}),r},Pn=function(e){return e},In=function(e){return function(){return xe.fromTag("tr",e.dom())}},Bn=function(d,e,m){return{row:In(e),cell:function(e){var r,o,i,t,n,u,a,c=mt(e.element()),l=xe.fromTag(oe(e.element()),c.dom()),f=m.getOr(["strong","em","b","i","span","font","h1","h2","h3","h4","h5","h6","p","div"]),s=0<f.length?(r=e.element(),o=l,i=f,Rn(r).map(function(e){var t=i.join(","),n=Ht(e,t,function(e){return ft(e,r)});return P(n,function(e,t){var n=On(t);return he(n,"contenteditable"),Rt(e,n),n},o)}).getOr(o)):l;return Rt(s,xe.fromTag("br")),t=e.element(),n=l,u=t.dom(),a=n.dom(),be(u)&&be(a)&&(a.style.cssText=u.style.cssText),Ae(l,"height"),1!==e.colspan()&&Ae(e.element(),"width"),d(e.element(),l),l},replace:An,gap:kn}},Wn=function(e){return{row:In(e),cell:kn,replace:Pn,gap:kn}},Mn=function(e,t){return t.column()>=e.startCol()&&t.column()+t.colspan()-1<=e.finishCol()&&t.row()>=e.startRow()&&t.row()+t.rowspan()-1<=e.finishRow()},_n=function(e,t){var n=t.column(),r=t.column()+t.colspan()-1,o=t.row(),i=t.row()+t.rowspan()-1;return n<=e.finishCol()&&r>=e.startCol()&&o<=e.finishRow()&&i>=e.startRow()},Ln=function(e,t){for(var n=!0,r=b(Mn,t),o=t.startRow();o<=t.finishRow();o++)for(var i=t.startCol();i<=t.finishCol();i++)n=n&&mn.getAt(e,o,i).exists(r);return n?R.some(t):R.none()},Fn=function(e,t,n){var r=mn.findItem(e,t,ft),o=mn.findItem(e,n,ft);return r.bind(function(r){return o.map(function(e){return t=r,n=e,jt(Math.min(t.row(),n.row()),Math.min(t.column(),n.column()),Math.max(t.row()+t.rowspan()-1,n.row()+n.rowspan()-1),Math.max(t.column()+t.colspan()-1,n.column()+n.colspan()-1));var t,n})})},jn=Fn,zn=function(t,e,n){return Fn(t,e,n).bind(function(e){return Ln(t,e)})},Hn=function(r,e,o,i){return mn.findItem(r,e,ft).bind(function(e){var t=0<o?e.row()+e.rowspan()-1:e.row(),n=0<i?e.column()+e.colspan()-1:e.column();return mn.getAt(r,t+o,n+i).map(function(e){return e.element()})})},Un=function(n,e,t){return jn(n,e,t).map(function(e){var t=mn.filterItems(n,b(_n,e));return E(t,function(e){return e.element()})})},qn=function(e,t){return mn.findItem(e,t,function(e,t){return st(t,e)}).map(function(e){return e.element()})},Vn=function(e){var t=ln(e);return mn.generate(t)},Gn=function(n,r,o){return cn.table(n).bind(function(e){var t=Vn(e);return Hn(t,n,r,o)})},Yn=function(e,t,n){var r=Vn(e);return Un(r,t,n)},Xn=function(e,t,n,r,o){var i=Vn(e),u=ft(e,n)?R.some(t):qn(i,t),a=ft(e,o)?R.some(r):qn(i,r);return u.bind(function(t){return a.bind(function(e){return Un(i,t,e)})})},Kn=function(e,t,n){var r=Vn(e);return zn(r,t,n)},Jn=["body","p","div","article","aside","figcaption","figure","footer","header","nav","section","ol","ul","li","table","thead","tbody","tfoot","caption","tr","td","th","h1","h2","h3","h4","h5","h6","blockquote","pre","address"];function $n(){return{up:C({selector:Jt,closest:Zt,predicate:Kt,all:ht}),down:C({selector:qt,predicate:zt}),styles:C({get:Ne,getRaw:ke,set:De,remove:Ae}),attrs:C({get:me,set:se,remove:he,copyTo:function(e,t){var n=pe(e);de(t,n)}}),insert:C({before:yt,after:xt,afterAll:Tt,append:Rt,appendAll:Dt,prepend:Ct,wrap:St}),remove:C({unwrap:Et,remove:Nt}),create:C({nu:xe.fromTag,clone:function(e){return xe.fromDom(e.dom().cloneNode(!1))},text:xe.fromText}),query:C({comparePosition:function(e,t){return e.dom().compareDocumentPosition(t.dom())},prevSibling:pt,nextSibling:vt}),property:C({children:bt,name:oe,parent:gt,document:function(e){return e.dom().ownerDocument},isText:le,isComment:ae,isElement:ce,getText:vn,setText:wn,isBoundary:function(e){return!!ce(e)&&("body"===oe(e)||O(Jn,oe(e)))},isEmptyTag:function(e){return!!ce(e)&&O(["br","img","hr","input"],oe(e))}}),eq:ft,is:dt}}var Qn=q("left","right"),Zn=q("first","second","splits"),er=function(e,t,n){var r=e.property().children(t);return W(r,b(e.eq,n)).map(function(e){return{before:C(r.slice(0,e)),after:C(r.slice(e+1))}})},tr=function(r,o,e,t){var n=o(r,e);return P(t,function(e,t){var n=o(r,t);return nr(r,e,n)},n)},nr=function(t,e,n){return e.bind(function(e){return n.filter(b(t.eq,e))})},rr=function(e,t){return b(e.eq,t)},or=function(t,e,n,r){void 0===r&&(r=f);var o=[e].concat(t.up().all(e)),i=[n].concat(t.up().all(n)),u=function(t){return W(t,r).fold(function(){return t},function(e){return t.slice(0,e+1)})},a=u(o),c=u(i),l=B(a,function(e){return N(c,rr(t,e))});return{firstpath:C(a),secondpath:C(c),shared:C(l)}},ir={sharedOne:function(e,t,n){return 0<n.length?tr(e,t,(r=n)[0],r.slice(1)):R.none();var r},subset:function(t,e,n){var r=or(t,e,n);return r.shared().bind(function(e){return function(o,i,e,t){var u=o.property().children(i);if(o.eq(i,e[0]))return R.some([e[0]]);if(o.eq(i,t[0]))return R.some([t[0]]);var n=function(e){var t=F(e),n=W(t,rr(o,i)).getOr(-1),r=n<t.length-1?t[n+1]:t[n];return W(u,rr(o,r))},r=n(e),a=n(t);return r.bind(function(r){return a.map(function(e){var t=Math.min(r,e),n=Math.max(r,e);return u.slice(t,n+1)})})}(t,e,r.firstpath(),r.secondpath())})},ancestors:or,breakToLeft:function(n,r,o){return er(n,r,o).map(function(e){var t=n.create().clone(r);return n.insert().appendAll(t,e.before().concat([o])),n.insert().appendAll(r,e.after()),n.insert().before(r,t),Qn(t,r)})},breakToRight:function(n,r,e){return er(n,r,e).map(function(e){var t=n.create().clone(r);return n.insert().appendAll(t,e.after()),n.insert().after(r,t),Qn(r,t)})},breakPath:function(i,e,u,a){var c=function(e,t,o){var n=Zn(e,R.none(),o);return u(e)?Zn(e,t,o):i.property().parent(e).bind(function(r){return a(i,r,e).map(function(e){var t=[{first:e.left,second:e.right}],n=u(r)?r:e.left();return c(n,R.some(e.right()),o.concat(t))})}).getOr(n)};return c(e,R.none(),[])}},ur=$n(),ar={sharedOne:function(n,e){return ir.sharedOne(ur,function(e,t){return n(t)},e)},subset:function(e,t){return ir.subset(ur,e,t)},ancestors:function(e,t,n){return ir.ancestors(ur,e,t,n)},breakToLeft:function(e,t){return ir.breakToLeft(ur,e,t)},breakToRight:function(e,t){return ir.breakToRight(ur,e,t)},breakPath:function(e,t,r){return ir.breakPath(ur,e,t,function(e,t,n){return r(t,n)})}},cr={create:J(["boxes","start","finish"],[])},lr=function(e){return Jt(e,"table")},fr=function(a,c,r){var l=function(t){return function(e){return r!==undefined&&r(e)||ft(e,t)}};return ft(a,c)?R.some(cr.create({boxes:R.some([a]),start:a,finish:c})):lr(a).bind(function(u){return lr(c).bind(function(i){if(ft(u,i))return R.some(cr.create({boxes:Yn(u,a,c),start:a,finish:c}));if(st(u,i)){var e=0<(t=Ht(c,"td,th",l(u))).length?t[t.length-1]:c;return R.some(cr.create({boxes:Xn(u,a,u,c,i),start:a,finish:e}))}if(st(i,u)){var t,n=0<(t=Ht(a,"td,th",l(i))).length?t[t.length-1]:a;return R.some(cr.create({boxes:Xn(i,a,u,c,i),start:a,finish:n}))}return ar.ancestors(a,c).shared().bind(function(e){return Zt(e,"table",r).bind(function(e){var t=Ht(c,"td,th",l(e)),n=0<t.length?t[t.length-1]:c,r=Ht(a,"td,th",l(e)),o=0<r.length?r[r.length-1]:a;return R.some(cr.create({boxes:Xn(e,a,u,c,i),start:o,finish:n}))})})})})},sr=fr,dr=function(e,t){var n=qt(e,t);return 0<n.length?R.some(n):R.none()},mr=function(e,t,n,r,o){return(i=e,u=o,B(i,function(e){return ct(e,u)})).bind(function(e){return Gn(e,t,n).bind(function(e){return n=r,Jt(t=e,"table").bind(function(e){return Qt(e,n).bind(function(e){return fr(e,t).bind(function(t){return t.boxes().map(function(e){return{boxes:C(e),start:C(t.start()),finish:C(t.finish())}})})})});var t,n})});var i,u},gr=function(e,t,r){return Qt(e,t).bind(function(n){return Qt(e,r).bind(function(t){return ar.sharedOne(lr,[n,t]).map(function(e){return{first:C(n),last:C(t),table:C(e)}})})})},hr=function(e,t){return dr(e,t)},pr=function(o,e,t){return gr(o,e,t).bind(function(n){var e=function(e){return ft(o,e)},t=Jt(n.first(),"thead,tfoot,tbody,table",e),r=Jt(n.last(),"thead,tfoot,tbody,table",e);return t.bind(function(t){return r.bind(function(e){return ft(t,e)?Kn(n.table(),n.first(),n.last()):R.none()})})})},vr="data-mce-selected",br="data-mce-first-selected",wr="data-mce-last-selected",yr={selected:C(vr),selectedSelector:C("td[data-mce-selected],th[data-mce-selected]"),attributeSelector:C("[data-mce-selected]"),firstSelected:C(br),firstSelectedSelector:C("td[data-mce-first-selected],th[data-mce-first-selected]"),lastSelected:C(wr),lastSelectedSelector:C("td[data-mce-last-selected],th[data-mce-last-selected]")},xr=function(u){if(!h(u))throw new Error("cases must be an array");if(0===u.length)throw new Error("there must be at least one case");var a=[],n={};return k(u,function(e,r){var t=j(e);if(1!==t.length)throw new Error("one and only one name per case");var o=t[0],i=e[o];if(n[o]!==undefined)throw new Error("duplicate key detected:"+o);if("cata"===o)throw new Error("cannot have a case named cata (sorry)");if(!h(i))throw new Error("case arguments must be an array");a.push(o),n[o]=function(){var e=arguments.length;if(e!==i.length)throw new Error("Wrong number of arguments to case "+o+". Expected "+i.length+" ("+i+"), got "+e);for(var n=new Array(e),t=0;t<n.length;t++)n[t]=arguments[t];return{fold:function(){if(arguments.length!==u.length)throw new Error("Wrong number of arguments to fold. Expected "+u.length+", got "+arguments.length);return arguments[r].apply(null,n)},match:function(e){var t=j(e);if(a.length!==t.length)throw new Error("Wrong number of arguments to match. Expected: "+a.join(",")+"\nActual: "+t.join(","));if(!L(a,function(e){return O(t,e)}))throw new Error("Not all branches were specified when using match. Specified: "+t.join(", ")+"\nRequired: "+a.join(", "));return e[o].apply(null,n)},log:function(e){m.console.log(e,{constructors:a,constructor:o,params:n})}}}}),n},Cr=xr([{none:[]},{multiple:["elements"]},{single:["selection"]}]),Rr={cata:function(e,t,n,r){return e.fold(t,n,r)},none:Cr.none,multiple:Cr.multiple,single:Cr.single},Sr=function(e,t){return Rr.cata(t.get(),C([]),o,C([e]))},Tr=function(n,e){return Rr.cata(e.get(),R.none,function(t,e){return 0===t.length?R.none():pr(n,yr.firstSelectedSelector(),yr.lastSelectedSelector()).bind(function(e){return 1<t.length?R.some({bounds:C(e),cells:C(t)}):R.none()})},R.none)},Dr=function(e,t){var n=Sr(e,t);return 0<n.length&&L(n,function(e){return ge(e,"rowspan")&&1<parseInt(me(e,"rowspan"),10)||ge(e,"colspan")&&1<parseInt(me(e,"colspan"),10)})?R.some(n):R.none()},Or=Sr,Nr=function(e){return{element:C(e),mergable:R.none,unmergable:R.none,selection:C([e])}},Er=q("element","clipboard","generators"),kr={noMenu:Nr,forMenu:function(e,t,n){return{element:C(n),mergable:C(Tr(t,e)),unmergable:C(Dr(n,e)),selection:C(Or(n,e))}},notCell:function(e){return Nr(e)},paste:Er,pasteRows:function(e,t,n,r,o){return{element:C(n),mergable:R.none,unmergable:R.none,selection:C(Or(n,e)),clipboard:C(r),generators:C(o)}}},Ar=function(f,e,s,d){f.on("BeforeGetContent",function(n){!0===n.selection&&Rr.cata(e.get(),y,function(e){var t;n.preventDefault(),(t=e,cn.table(t[0]).map(Nn).map(function(e){return[hn(e,yr.attributeSelector())]})).each(function(e){var t;n.content="text"===n.format?E(e,function(e){return e.dom().innerText}).join(""):(t=f,E(e,function(e){return t.selection.serializer.serialize(e.dom(),{})}).join(""))})},y)}),f.on("BeforeSetContent",function(l){!0===l.selection&&!0===l.paste&&R.from(f.dom.getParent(f.selection.getStart(),"th,td")).each(function(e){var c=xe.fromDom(e);cn.table(c).each(function(t){var e,n,r,o=A((e=l.content,(r=(n||m.document).createElement("div")).innerHTML=e,bt(xe.fromDom(r))),function(e){return"meta"!==oe(e)});if(1===o.length&&"table"===oe(o[0])){l.preventDefault();var i=xe.fromDom(f.getDoc()),u=Wn(i),a=kr.paste(c,o[0],u);s.pasteCells(t,a).each(function(e){f.selection.setRng(e),f.focus(),d.clear(t)})}})})})};function Pr(r,o){var e=function(e){var t=o(e);if(t<=0||null===t){var n=Ne(e,r);return parseFloat(n)||0}return t},i=function(o,e){return I(e,function(e,t){var n=Ne(o,t),r=n===undefined?0:parseInt(n,10);return isNaN(r)?e:e+r},0)};return{set:function(e,t){if(!w(t)&&!t.match(/^[0-9]+$/))throw new Error(r+".set accepts only positive integer values. Value was "+t);var n=e.dom();be(n)&&(n.style[r]=t+"px")},get:e,getOuter:e,aggregate:i,max:function(e,t,n){var r=i(e,n);return r<t?t-r:0}}}var Ir=Pr("height",function(e){var t=e.dom();return Ce(e)?t.getBoundingClientRect().height:t.offsetHeight}),Br=function(e){return Ir.get(e)},Wr=function(e){return Ir.getOuter(e)},Mr=Pr("width",function(e){return e.dom().offsetWidth}),_r=function(e){return Mr.get(e)},Lr=function(e){return Mr.getOuter(e)},Fr=it.detect(),jr=function(e,t,n){return r=Ne(e,t),o=n,i=parseFloat(r),isNaN(i)?o:i;var r,o,i},zr=function(e){return Fr.browser.isIE()||Fr.browser.isEdge()?(n=jr(t=e,"padding-top",0),r=jr(t,"padding-bottom",0),o=jr(t,"border-top-width",0),i=jr(t,"border-bottom-width",0),u=t.dom().getBoundingClientRect().height,"border-box"===Ne(t,"box-sizing")?u:u-n-r-(o+i)):jr(e,"height",Br(e));var t,n,r,o,i,u},Hr=/(\d+(\.\d+)?)(\w|%)*/,Ur=/(\d+(\.\d+)?)%/,qr=/(\d+(\.\d+)?)px|em/,Vr=function(e,t){De(e,"height",t+"px")},Gr=function(e,t,n,r){var o,i,u,a,c,l,f,s,d,m=parseInt(e,10);return s=l="%",d=(f=e).length-l.length,""!==s&&(f.length<s.length||f.substr(d,d+s.length)!==s)||"table"===oe(t)?m:(o=t,i=m,u=n,a=r,c=cn.table(o).map(function(e){var t=u(e);return Math.floor(i/100*t)}).getOr(i),a(o,c),c)},Yr=function(e){var t,n=ke(t=e,"height").getOrThunk(function(){return zr(t)+"px"});return n?Gr(n,e,Br,Vr):Br(e)},Xr=function(e,t){return ge(e,t)?parseInt(me(e,t),10):1},Kr=function(e){return ke(e,"width").fold(function(){return R.from(me(e,"width"))},function(e){return R.some(e)})},Jr=function(e,t){return e/t.pixelWidth()*100},$r={percentageBasedSizeRegex:C(Ur),pixelBasedSizeRegex:C(qr),setPixelWidth:function(e,t){De(e,"width",t+"px")},setPercentageWidth:function(e,t){De(e,"width",t+"%")},setHeight:Vr,getPixelWidth:function(t,n){return Kr(t).fold(function(){return _r(t)},function(e){return function(e,t,n){var r=qr.exec(t);if(null!==r)return parseInt(r[1],10);var o=Ur.exec(t);if(null!==o){var i=parseFloat(o[1]);return i/100*n.pixelWidth()}return _r(e)}(t,e,n)})},getPercentageWidth:function(t,n){return Kr(t).fold(function(){var e=_r(t);return Jr(e,n)},function(e){return function(e,t,n){var r=Ur.exec(t);if(null!==r)return parseFloat(r[1]);var o=_r(e);return Jr(o,n)}(t,e,n)})},getGenericWidth:function(e){return Kr(e).bind(function(e){var t=Hr.exec(e);return null!==t?R.some({width:C(parseFloat(t[1])),unit:C(t[3])}):R.none()})},setGenericWidth:function(e,t,n){De(e,"width",t+n)},getHeight:function(e){return n="rowspan",Yr(t=e)/Xr(t,n);var t,n},getRawWidth:Kr},Qr=function(n,r){$r.getGenericWidth(n).each(function(e){var t=e.width()/2;$r.setGenericWidth(n,t,e.unit()),$r.setGenericWidth(r,t,e.unit())})},Zr=function(n,r){return{left:C(n),top:C(r),translate:function(e,t){return Zr(n+e,r+t)}}},eo=Zr,to=function(e,t){return e!==undefined?e:t!==undefined?t:0},no=function(e){var t=e.dom().ownerDocument,n=t.body,r=t.defaultView,o=t.documentElement,i=to(r.pageYOffset,o.scrollTop),u=to(r.pageXOffset,o.scrollLeft),a=to(o.clientTop,n.clientTop),c=to(o.clientLeft,n.clientLeft);return ro(e).translate(u-c,i-a)},ro=function(e){var t,n=e.dom(),r=n.ownerDocument.body;return r===n?eo(r.offsetLeft,r.offsetTop):Ce(e)?(t=n.getBoundingClientRect(),eo(t.left,t.top)):eo(0,0)},oo=q("row","y"),io=q("col","x"),uo=function(e){return no(e).left()+Lr(e)},ao=function(e){return no(e).left()},co=function(e,t){return io(e,ao(t))},lo=function(e,t){return io(e,uo(t))},fo=function(e){return no(e).top()},so=function(e,t){return oo(e,fo(t))},mo=function(e,t){return oo(e,fo(t)+Wr(t))},go=function(n,t,r){if(0===r.length)return[];var e=E(r.slice(1),function(e,t){return e.map(function(e){return n(t,e)})}),o=r[r.length-1].map(function(e){return t(r.length-1,e)});return e.concat([o])},ho={height:{delta:o,positions:function(e){return go(so,mo,e)},edge:fo},rtl:{delta:function(e){return-e},edge:uo,positions:function(e){return go(lo,co,e)}},ltr:{delta:o,edge:ao,positions:function(e){return go(co,lo,e)}}},po={ltr:ho.ltr,rtl:ho.rtl};function vo(t){var n=function(e){return t(e).isRtl()?po.rtl:po.ltr};return{delta:function(e,t){return n(t).delta(e,t)},edge:function(e){return n(e).edge(e)},positions:function(e,t){return n(t).positions(e,t)}}}var bo=function(e){var t=ln(e);return mn.generate(t).grid()},wo=function(){return(wo=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},yo=function(e){for(var t=[],n=function(e){t.push(e)},r=0;r<e.length;r++)e[r].each(n);return t},xo=function(e,t){for(var n=0;n<e.length;n++){var r=t(e[n],n);if(r.isSome())return r}return R.none()},Co=function(e,t,n,r){n===r?he(e,t):se(e,t,n)},Ro=function(o,e){var i=[],u=[],t=function(e,t){0<e.length?function(e,t){var n=$t(o,t).getOrThunk(function(){var e=xe.fromTag(t,mt(o).dom());return Rt(o,e),e});Ot(n);var r=E(e,function(e){e.isNew()&&i.push(e.element());var t=e.element();return Ot(t),k(e.cells(),function(e){e.isNew()&&u.push(e.element()),Co(e.element(),"colspan",e.colspan(),1),Co(e.element(),"rowspan",e.rowspan(),1),Rt(t,e.element())}),t});Dt(n,r)}(e,t):$t(o,t).each(Nt)},n=[],r=[],a=[];return k(e,function(e){switch(e.section()){case"thead":n.push(e);break;case"tbody":r.push(e);break;case"tfoot":a.push(e)}}),t(n,"thead"),t(r,"tbody"),t(a,"tfoot"),{newRows:C(i),newCells:C(u)}},So=function(e){return E(e,function(e){var n=On(e.element());return k(e.cells(),function(e){var t=Nn(e.element());Co(t,"colspan",e.colspan(),1),Co(t,"rowspan",e.rowspan(),1),Rt(n,t)}),n})},To=function(e,t){var n=me(e,t);return n===undefined||""===n?[]:n.split(" ")},Do=function(e){return e.dom().classList!==undefined},Oo=function(e,t){return o=t,i=To(n=e,r="class").concat([o]),se(n,r,i.join(" ")),!0;var n,r,o,i},No=function(e,t){return o=t,0<(i=A(To(n=e,r="class"),function(e){return e!==o})).length?se(n,r,i.join(" ")):he(n,r),!1;var n,r,o,i},Eo=function(e,t){Do(e)?e.dom().classList.add(t):Oo(e,t)},ko=function(e){0===(Do(e)?e.dom().classList:To(e,"class")).length&&he(e,"class")},Ao=function(e,t){return Do(e)&&e.dom().classList.contains(t)},Po=function(e,t){for(var n=[],r=e;r<t;r++)n.push(r);return n},Io=function(t,n){if(n<0||n>=t.length-1)return R.none();var e=t[n].fold(function(){var e=F(t.slice(0,n));return xo(e,function(e,t){return e.map(function(e){return{value:e,delta:t+1}})})},function(e){return R.some({value:e,delta:0})}),r=t[n+1].fold(function(){var e=t.slice(n+1);return xo(e,function(e,t){return e.map(function(e){return{value:e,delta:t+1}})})},function(e){return R.some({value:e,delta:1})});return e.bind(function(n){return r.map(function(e){var t=e.delta+n.delta;return Math.abs(e.value-n.value)/t})})},Bo=function(e,t,n){var r=e();return B(r,t).orThunk(function(){return R.from(r[0]).orThunk(n)}).map(function(e){return e.element()})},Wo=function(n){var e=n.grid(),t=Po(0,e.columns()),r=Po(0,e.rows());return E(t,function(t){return Bo(function(){return _(r,function(e){return mn.getAt(n,e,t).filter(function(e){return e.column()===t}).fold(C([]),function(e){return[e]})})},function(e){return 1===e.colspan()},function(){return mn.getAt(n,0,t)})})},Mo=function(n){var e=n.grid(),t=Po(0,e.rows()),r=Po(0,e.columns());return E(t,function(t){return Bo(function(){return _(r,function(e){return mn.getAt(n,t,e).filter(function(e){return e.row()===t}).fold(C([]),function(e){return[e]})})},function(e){return 1===e.rowspan()},function(){return mn.getAt(n,t,0)})})},_o=function(e){var t=e.replace(/\./g,"-");return{resolve:function(e){return t+"-"+e}}},Lo={resolve:_o("ephox-snooker").resolve},Fo=function(e,t,n,r,o){var i=xe.fromTag("div");return Oe(i,{position:"absolute",left:t-r/2+"px",top:n+"px",height:o+"px",width:r+"px"}),de(i,{"data-column":e,role:"presentation"}),i},jo=function(e,t,n,r,o){var i=xe.fromTag("div");return Oe(i,{position:"absolute",left:t+"px",top:n-o/2+"px",height:o+"px",width:r+"px"}),de(i,{"data-row":e,role:"presentation"}),i},zo=Lo.resolve("resizer-bar"),Ho=Lo.resolve("resizer-rows"),Uo=Lo.resolve("resizer-cols"),qo=function(e){var t=qt(e.parent(),"."+zo);k(t,Nt)},Vo=function(n,e,r){var o=n.origin();k(e,function(e,t){e.each(function(e){var t=r(o,e);Eo(t,zo),Rt(n.parent(),t)})})},Go=function(e,t,n,r,o,i){var u,a,c,l,f=no(t),s=0<n.length?o.positions(n,t):[];u=e,a=s,c=f,l=Lr(t),Vo(u,a,function(e,t){var n=jo(t.row(),c.left()-e.left(),t.y()-e.top(),l,7);return Eo(n,Ho),n});var d,m,g,h,p=0<r.length?i.positions(r,t):[];d=e,m=p,g=f,h=Wr(t),Vo(d,m,function(e,t){var n=Fo(t.col(),t.x()-e.left(),g.top()-e.top(),7,h);return Eo(n,Uo),n})},Yo=function(e,t){var n=qt(e.parent(),"."+zo);k(n,t)},Xo=function(e,t,n,r){qo(e);var o=ln(t),i=mn.generate(o),u=Mo(i),a=Wo(i);Go(e,t,u,a,n,r)},Ko=function(e){Yo(e,function(e){De(e,"display","none")})},Jo=function(e){Yo(e,function(e){De(e,"display","block")})},$o=qo,Qo=function(e){return Ao(e,Ho)},Zo=function(e){return Ao(e,Uo)},ei=function(e,t){return Lt(t,e.section())},ti=function(e,t){return e.cells()[t]},ni={addCell:function(e,t,n){var r=e.cells(),o=r.slice(0,t),i=r.slice(t),u=o.concat([n]).concat(i);return ei(e,u)},setCells:ei,mutateCell:function(e,t,n){e.cells()[t]=n},getCell:ti,getCellElement:function(e,t){return ti(e,t).element()},mapCells:function(e,t){var n=e.cells(),r=E(n,t);return Lt(r,e.section())},cellLength:function(e){return e.cells().length}},ri=function(e,t){if(0===e.length)return 0;var n=e[0];return W(e,function(e){return!t(n.element(),e.element())}).fold(function(){return e.length},function(e){return e})},oi=function(e,t,n,r){var o,i,u,a,c=(o=e,i=t,o[i]).cells().slice(n),l=ri(c,r),f=(u=e,a=n,E(u,function(e){return ni.getCell(e,a)})).slice(t),s=ri(f,r);return{colspan:C(l),rowspan:C(s)}},ii=function(o,i){var u=E(o,function(e,t){return E(e.cells(),function(e,t){return!1})});return E(o,function(e,r){var t=_(e.cells(),function(e,t){if(!1===u[r][t]){var n=oi(o,r,t,i);return function(e,t,n,r){for(var o=e;o<e+n;o++)for(var i=t;i<t+r;i++)u[o][i]=!0}(r,t,n.rowspan(),n.colspan()),[It(e.element(),n.rowspan(),n.colspan(),e.isNew())]}return[]});return Ft(t,e.section())})},ui=function(e,t,n){for(var r=[],o=0;o<e.grid().rows();o++){for(var i=[],u=0;u<e.grid().columns();u++){var a=mn.getAt(e,o,u).map(function(e){return Mt(e.element(),n)}).getOrThunk(function(){return Mt(t.gap(),!0)});i.push(a)}var c=Lt(i,e.all()[o].section());r.push(c)}return r},ai=function(e,r){return E(e,function(e){var t,n=(t=e.details(),xo(t,function(e){return gt(e.element()).map(function(e){var t=gt(e).isNone();return Mt(e,t)})}).getOrThunk(function(){return Mt(r.row(),!0)}));return _t(n.element(),e.details(),e.section(),n.isNew())})},ci=function(e,t){var n=ii(e,ft);return ai(n,t)},li=function(e,t){var n=M(E(e.all(),function(e){return e.cells()}));return B(n,function(e){return ft(t,e.element())})},fi=function(a,c,l,f,s){return function(n,r,e,o,i){var t=ln(r),u=mn.generate(t);return c(u,e).map(function(e){var t=ui(u,o,!1),n=a(t,e,ft,s(o)),r=ci(n.grid(),o);return{grid:C(r),cursor:n.cursor}}).fold(function(){return R.none()},function(e){var t=Ro(r,e.grid());return l(r,e.grid(),i),f(r),Xo(n,r,ho.height,i),R.some({cursor:e.cursor,newRows:t.newRows,newCells:t.newCells})})}},si=function(t,e){return cn.cell(e.element()).bind(function(e){return li(t,e)})},di=function(t,e){var n=E(e.selection(),function(e){return cn.cell(e).bind(function(e){return li(t,e)})}),r=yo(n);return 0<r.length?R.some({cells:r,generators:e.generators,clipboard:e.clipboard}):R.none()},mi=function(t,e){var n=E(e.selection(),function(e){return cn.cell(e).bind(function(e){return li(t,e)})}),r=yo(n);return 0<r.length?R.some(r):R.none()},gi=function(n){return{is:function(e){return n===e},isValue:u,isError:f,getOr:C(n),getOrThunk:C(n),getOrDie:C(n),or:function(e){return gi(n)},orThunk:function(e){return gi(n)},fold:function(e,t){return t(n)},map:function(e){return gi(e(n))},mapError:function(e){return gi(n)},each:function(e){e(n)},bind:function(e){return e(n)},exists:function(e){return e(n)},forall:function(e){return e(n)},toOption:function(){return R.some(n)}}},hi=function(n){return{is:f,isValue:f,isError:u,getOr:o,getOrThunk:function(e){return e()},getOrDie:function(){return e=String(n),function(){throw new Error(e)}();var e},or:function(e){return e},orThunk:function(e){return e()},fold:function(e,t){return e(n)},map:function(e){return hi(n)},mapError:function(e){return hi(e(n))},each:y,bind:function(e){return hi(n)},exists:f,forall:u,toOption:R.none}},pi={value:gi,error:hi,fromOption:function(e,t){return e.fold(function(){return hi(t)},gi)}},vi=function(e,t){return E(e,function(){return Mt(t.cell(),!0)})},bi=function(t,e,n){return t.concat(function(e,t){for(var n=[],r=0;r<e;r++)n.push(t(r));return n}(e,function(e){return ni.setCells(t[t.length-1],vi(t[t.length-1].cells(),n))}))},wi=function(e,t,n){return E(e,function(e){return ni.setCells(e,e.cells().concat(vi(Po(0,t),n)))})},yi=function(e,t,n){if(e.row()>=t.length||e.column()>ni.cellLength(t[0]))return pi.error("invalid start address out of table bounds, row: "+e.row()+", column: "+e.column());var r=t.slice(e.row()),o=r[0].cells().slice(e.column()),i=ni.cellLength(n[0]),u=n.length;return pi.value({rowDelta:C(r.length-u),colDelta:C(o.length-i)})},xi=function(e,t){var n=ni.cellLength(e[0]),r=ni.cellLength(t[0]);return{rowDelta:C(0),colDelta:C(n-r)}},Ci=function(e,t,n){var r=t.colDelta()<0?wi:o;return(t.rowDelta()<0?bi:o)(r(e,Math.abs(t.colDelta()),n),Math.abs(t.rowDelta()),n)},Ri=function(e,t,n,r){if(0===e.length)return e;for(var o=t.startRow();o<=t.finishRow();o++)for(var i=t.startCol();i<=t.finishCol();i++)ni.mutateCell(e[o],i,Mt(r(),!1));return e},Si=function(e,t,n,r){for(var o=!0,i=0;i<e.length;i++)for(var u=0;u<ni.cellLength(e[0]);u++){var a=n(ni.getCellElement(e[i],u),t);!0===a&&!1===o?ni.mutateCell(e[i],u,Mt(r(),!0)):!0===a&&(o=!1)}return e},Ti=function(i,n,u,a){if(0<n&&n<i.length){var e=i[n-1].cells(),t=(r=u,I(e,function(e,t){return N(e,function(e){return r(e.element(),t.element())})?e:e.concat([t])},[]));k(t,function(r){for(var o=R.none(),e=function(n){for(var e=function(t){var e=i[n].cells()[t];u(e.element(),r.element())&&(o.isNone()&&(o=R.some(a())),o.each(function(e){ni.mutateCell(i[n],t,Mt(e,!0))}))},t=0;t<ni.cellLength(i[0]);t++)e(t)},t=n;t<i.length;t++)e(t)})}var r;return i},Di=function(n,r,o,i,u){return yi(n,r,o).map(function(e){var t=Ci(r,e,i);return function(e,t,n,r,o){for(var i,u,a,c,l,f=e.row(),s=e.column(),d=f+n.length,m=s+ni.cellLength(n[0]),g=f;g<d;g++)for(var h=s;h<m;h++){i=t,u=g,a=h,l=c=void 0,c=b(o,ni.getCell(i[u],a).element()),l=i[u],1<i.length&&1<ni.cellLength(l)&&(0<a&&c(ni.getCellElement(l,a-1))||a<l.cells().length-1&&c(ni.getCellElement(l,a+1))||0<u&&c(ni.getCellElement(i[u-1],a))||u<i.length-1&&c(ni.getCellElement(i[u+1],a)))&&Si(t,ni.getCellElement(t[g],h),o,r.cell);var p=ni.getCellElement(n[g-f],h-s),v=r.replace(p);ni.mutateCell(t[g],h,Mt(v,!0))}return t}(n,t,o,i,u)})},Oi=function(e,t,n,r,o){Ti(t,e,o,r.cell);var i=xi(n,t),u=Ci(n,i,r),a=xi(t,u),c=Ci(t,a,r);return c.slice(0,e).concat(u).concat(c.slice(e,c.length))},Ni=function(n,r,e,o,i){var t=n.slice(0,r),u=n.slice(r),a=ni.mapCells(n[e],function(e,t){return 0<r&&r<n.length&&o(ni.getCellElement(n[r-1],t),ni.getCellElement(n[r],t))?ni.getCell(n[r],t):Mt(i(e.element(),o),!0)});return t.concat([a]).concat(u)},Ei=function(e,n,r,o,i){return E(e,function(e){var t=0<n&&n<ni.cellLength(e)&&o(ni.getCellElement(e,n-1),ni.getCellElement(e,n))?ni.getCell(e,n):Mt(i(ni.getCellElement(e,r),o),!0);return ni.addCell(e,n,t)})},ki=function(e,r,o,i,u){var a=o+1;return E(e,function(e,t){var n=t===r?Mt(u(ni.getCellElement(e,o),i),!0):ni.getCell(e,o);return ni.addCell(e,a,n)})},Ai=function(e,t,n,r,o){var i=t+1,u=e.slice(0,i),a=e.slice(i),c=ni.mapCells(e[t],function(e,t){return t===n?Mt(o(e.element(),r),!0):e});return u.concat([c]).concat(a)},Pi=function(e,t,n){return e.slice(0,t).concat(e.slice(n+1))},Ii=function(e,n,r){var t=E(e,function(e){var t=e.cells().slice(0,n).concat(e.cells().slice(r+1));return Lt(t,e.section())});return A(t,function(e){return 0<e.cells().length})},Bi=function(e,n,r,o){return E(e,function(e){return ni.mapCells(e,function(e){return t=e,N(n,function(e){return r(t.element(),e.element())})?Mt(o(e.element(),r),!0):e;var t})})},Wi=function(e,t,n,r){return ni.getCellElement(e[t],n)!==undefined&&0<t&&r(ni.getCellElement(e[t-1],n),ni.getCellElement(e[t],n))},Mi=function(e,t,n){return 0<t&&n(ni.getCellElement(e,t-1),ni.getCellElement(e,t))},_i=function(n,r,o,e){var t=_(n,function(e,t){return Wi(n,t,r,o)||Mi(e,r,o)?[]:[ni.getCell(e,r)]});return Bi(n,t,o,e)},Li=function(n,r,o,e){var i=n[r],t=_(i.cells(),function(e,t){return Wi(n,r,t,o)||Mi(i,t,o)?[]:[e]});return Bi(n,t,o,e)},Fi=xr([{none:[]},{only:["index"]},{left:["index","next"]},{middle:["prev","index","next"]},{right:["prev","index"]}]),ji=wo({},Fi),zi=function(e,t,i,u){var n,r,a=e.slice(0),o=(r=t,0===(n=e).length?ji.none():1===n.length?ji.only(0):0===r?ji.left(0,1):r===n.length-1?ji.right(r-1,r):0<r&&r<n.length-1?ji.middle(r-1,r,r+1):ji.none()),c=function(e){return E(e,C(0))},l=C(c(a)),f=function(e,t){if(0<=i){var n=Math.max(u.minCellWidth(),a[t]-i);return c(a.slice(0,e)).concat([i,n-a[t]]).concat(c(a.slice(t+1)))}var r=Math.max(u.minCellWidth(),a[e]+i),o=a[e]-r;return c(a.slice(0,e)).concat([r-a[e],o]).concat(c(a.slice(t+1)))},s=f;return o.fold(l,function(e){return u.singleColumnWidth(a[e],i)},s,function(e,t,n){return f(t,n)},function(e,t){if(0<=i)return c(a.slice(0,t)).concat([i]);var n=Math.max(u.minCellWidth(),a[t]+i);return c(a.slice(0,t)).concat([n-a[t]])})},Hi=function(e,t){return ge(e,t)&&1<parseInt(me(e,t),10)},Ui={hasColspan:function(e){return Hi(e,"colspan")},hasRowspan:function(e){return Hi(e,"rowspan")},minWidth:C(10),minHeight:C(10),getInt:function(e,t){return parseInt(Ne(e,t),10)}},qi=function(e,t,n){return ke(e,t).fold(function(){return n(e)+"px"},function(e){return e})},Vi=function(e,t){return qi(e,"width",function(e){return $r.getPixelWidth(e,t)})},Gi=function(e){return qi(e,"height",$r.getHeight)},Yi=function(e,t,n,r,o){var i=Wo(e),u=E(i,function(e){return e.map(t.edge)});return E(i,function(e,t){return e.filter(g(Ui.hasColspan)).fold(function(){var e=Io(u,t);return r(e)},function(e){return n(e,o)})})},Xi=function(e){return e.map(function(e){return e+"px"}).getOr("")},Ki=function(e,t,n,r){var o=Mo(e),i=E(o,function(e){return e.map(t.edge)});return E(o,function(e,t){return e.filter(g(Ui.hasRowspan)).fold(function(){var e=Io(i,t);return r(e)},function(e){return n(e)})})},Ji={getRawWidths:function(e,t,n){return Yi(e,t,Vi,Xi,n)},getPixelWidths:function(e,t,n){return Yi(e,t,$r.getPixelWidth,function(e){return e.getOrThunk(n.minCellWidth)},n)},getPercentageWidths:function(e,t,n){return Yi(e,t,$r.getPercentageWidth,function(e){return e.fold(function(){return n.minCellWidth()},function(e){return e/n.pixelWidth()*100})},n)},getPixelHeights:function(e,t){return Ki(e,t,$r.getHeight,function(e){return e.getOrThunk(Ui.minHeight)})},getRawHeights:function(e,t){return Ki(e,t,Gi,Xi)}},$i=function(e,t,n){for(var r=0,o=e;o<t;o++)r+=n[o]!==undefined?n[o]:0;return r},Qi=function(e,n){var t=mn.justCells(e);return E(t,function(e){var t=$i(e.column(),e.column()+e.colspan(),n);return{element:e.element,width:C(t),colspan:e.colspan}})},Zi=function(e,n){var t=mn.justCells(e);return E(t,function(e){var t=$i(e.row(),e.row()+e.rowspan(),n);return{element:e.element,height:C(t),rowspan:e.rowspan}})},eu=function(e,n){return E(e.all(),function(e,t){return{element:e.element,height:C(n[t])}})},tu=function(e){var t=o;return{width:C(e),pixelWidth:C(e),getWidths:Ji.getPixelWidths,getCellDelta:t,singleColumnWidth:function(e,t){return[Math.max(Ui.minWidth(),e+t)-e]},minCellWidth:Ui.minWidth,setElementWidth:$r.setPixelWidth,setTableWidth:function(e,t,n){var r=P(t,function(e,t){return e+t},0);$r.setPixelWidth(e,r)}}},nu=function(e,t){var n,r,o,i,u=$r.percentageBasedSizeRegex().exec(t);if(null!==u)return n=u[1],r=e,o=parseFloat(n),i=_r(r),{width:C(o),pixelWidth:C(i),getWidths:Ji.getPercentageWidths,getCellDelta:function(e){return e/i*100},singleColumnWidth:function(e,t){return[100-e]},minCellWidth:function(){return Ui.minWidth()/i*100},setElementWidth:$r.setPercentageWidth,setTableWidth:function(e,t,n){var r=n/100*o;$r.setPercentageWidth(e,o+r)}};var a=$r.pixelBasedSizeRegex().exec(t);if(null!==a){var c=parseInt(a[1],10);return tu(c)}var l=_r(e);return tu(l)},ru=function(t){return $r.getRawWidth(t).fold(function(){var e=_r(t);return tu(e)},function(e){return nu(t,e)})},ou=function(e){return mn.generate(e)},iu=function(e){var t=ln(e);return ou(t)},uu=function(e,t,n,r){var o=ru(e),i=o.getCellDelta(t),u=iu(e),a=o.getWidths(u,r,o),c=zi(a,n,i,o),l=E(c,function(e,t){return e+a[t]}),f=Qi(u,l);k(f,function(e){o.setElementWidth(e.element(),e.width())}),n===u.grid().columns()-1&&o.setTableWidth(e,l,i)},au=function(e,n,r,t){var o=iu(e),i=Ji.getPixelHeights(o,t),u=E(i,function(e,t){return r===t?Math.max(n+e,Ui.minHeight()):e}),a=Zi(o,u),c=eu(o,u);k(c,function(e){$r.setHeight(e.element(),e.height())}),k(a,function(e){$r.setHeight(e.element(),e.height())});var l=P(u,function(e,t){return e+t},0);$r.setHeight(e,l)},cu=function(e,t,n){var r=ru(e),o=ou(t),i=r.getWidths(o,n,r),u=Qi(o,i);k(u,function(e){r.setElementWidth(e.element(),e.width())}),0<u.length&&r.setTableWidth(e,i,r.getCellDelta(0))},lu=function(e){var t=e,n=function(){return t};return{get:n,set:function(e){t=e},clone:function(){return lu(n())}}},fu=function(r,o,i){if(0===o.length)throw new Error("You must specify at least one required field.");return X("required",o),K(o),function(t){var n=j(t);L(o,function(e){return O(n,e)})||G(o,n),r(o,n);var e=A(o,function(e){return!i.validate(t[e],e)});return 0<e.length&&function(e,t){throw new Error("All values need to be of type: "+t+". Keys ("+V(e).join(", ")+") were not.")}(e,i.label),t}},su=function(t,e){var n=A(e,function(e){return!O(t,e)});0<n.length&&Y(n)},du=function(e){return fu(su,e,{validate:v,label:"function"})},mu=du(["cell","row","replace","gap"]),gu=function(e){var t=ge(e,"colspan")?parseInt(me(e,"colspan"),10):1,n=ge(e,"rowspan")?parseInt(me(e,"rowspan"),10):1;return{element:C(e),colspan:C(t),rowspan:C(n)}},hu=function(r,o){void 0===o&&(o=gu),mu(r);var n=lu(R.none()),i=function(e){var t,n=o(e);return t=n,r.cell(t)},u=function(e){var t=i(e);return n.get().isNone()&&n.set(R.some(t)),a=R.some({item:e,replacement:t}),t},a=R.none();return{getOrInit:function(t,n){return a.fold(function(){return u(t)},function(e){return n(t,e.item)?e.replacement:u(t)})},cursor:n.get}},pu=function(a,c){return function(r){var o=lu(R.none());mu(r);var i=[],u=function(e){var t={scope:a},n=r.replace(e,c,t);return i.push({item:e,sub:n}),o.get().isNone()&&o.set(R.some(n)),n};return{replaceOrInit:function(t,n){return(r=t,o=n,B(i,function(e){return o(e.item,r)})).fold(function(){return u(t)},function(e){return n(t,e.item)?e.sub:u(t)});var r,o},cursor:o.get}}},vu=function(n){mu(n);var e=lu(R.none());return{combine:function(t){return e.get().isNone()&&e.set(R.some(t)),function(){var e=n.cell({element:C(t),colspan:C(1),rowspan:C(1)});return Ae(e,"width"),Ae(t,"width"),e}},cursor:e.get}},bu=["body","p","div","article","aside","figcaption","figure","footer","header","nav","section","ol","ul","table","thead","tfoot","tbody","caption","tr","td","th","h1","h2","h3","h4","h5","h6","blockquote","pre","address"],wu=function(e,t){var n=e.property().name(t);return O(bu,n)},yu=function(e,t){return O(["br","img","hr","input"],e.property().name(t))},xu=wu,Cu=function(e,t){var n=e.property().name(t);return O(["ol","ul"],n)},Ru=yu,Su=$n(),Tu=function(e){return xu(Su,e)},Du=function(e){return Cu(Su,e)},Ou=function(e){return Ru(Su,e)},Nu=function(e){var t,i=function(e){return"br"===oe(e)},n=function(o){return Sn(o).bind(function(n){var r=vt(n).map(function(e){return!!Tu(e)||!!Ou(e)&&"img"!==oe(e)}).getOr(!1);return gt(n).map(function(e){return!0===r||"li"===oe(t=e)||Kt(t,Du).isSome()||i(n)||Tu(e)&&!ft(o,e)?[]:[xe.fromTag("br")];var t})}).getOr([])},r=0===(t=_(e,function(e){var t=bt(e);return L(t,function(e){return i(e)||le(e)&&0===vn(e).trim().length})?[]:t.concat(n(e))})).length?[xe.fromTag("br")]:t;Ot(e[0]),Dt(e[0],r)},Eu=function(e){0===cn.cells(e).length&&Nt(e)},ku=q("grid","cursor"),Au=function(e,t,n){return Pu(e,t,n).orThunk(function(){return Pu(e,0,0)})},Pu=function(e,t,n){return R.from(e[t]).bind(function(e){return R.from(e.cells()[n]).bind(function(e){return R.from(e.element())})})},Iu=function(e,t,n){return ku(e,Pu(e,t,n))},Bu=function(e){return I(e,function(e,t){return N(e,function(e){return e.row()===t.row()})?e:e.concat([t])},[]).sort(function(e,t){return e.row()-t.row()})},Wu=function(e){return I(e,function(e,t){return N(e,function(e){return e.column()===t.column()})?e:e.concat([t])},[]).sort(function(e,t){return e.column()-t.column()})},Mu=function(e,t,n){var r=fn(e,n),o=mn.generate(r);return ui(o,t,!0)},_u=cu,Lu={insertRowBefore:fi(function(e,t,n,r){var o=t.row(),i=t.row(),u=Ni(e,i,o,n,r.getOrInit);return Iu(u,i,t.column())},si,y,y,hu),insertRowsBefore:fi(function(e,t,n,r){var o=t[0].row(),i=t[0].row(),u=Bu(t),a=I(u,function(e,t){return Ni(e,i,o,n,r.getOrInit)},e);return Iu(a,i,t[0].column())},mi,y,y,hu),insertRowAfter:fi(function(e,t,n,r){var o=t.row(),i=t.row()+t.rowspan(),u=Ni(e,i,o,n,r.getOrInit);return Iu(u,i,t.column())},si,y,y,hu),insertRowsAfter:fi(function(e,t,n,r){var o=Bu(t),i=o[o.length-1].row(),u=o[o.length-1].row()+o[o.length-1].rowspan(),a=I(o,function(e,t){return Ni(e,u,i,n,r.getOrInit)},e);return Iu(a,u,t[0].column())},mi,y,y,hu),insertColumnBefore:fi(function(e,t,n,r){var o=t.column(),i=t.column(),u=Ei(e,i,o,n,r.getOrInit);return Iu(u,t.row(),i)},si,_u,y,hu),insertColumnsBefore:fi(function(e,t,n,r){var o=Wu(t),i=o[0].column(),u=o[0].column(),a=I(o,function(e,t){return Ei(e,u,i,n,r.getOrInit)},e);return Iu(a,t[0].row(),u)},mi,_u,y,hu),insertColumnAfter:fi(function(e,t,n,r){var o=t.column(),i=t.column()+t.colspan(),u=Ei(e,i,o,n,r.getOrInit);return Iu(u,t.row(),i)},si,_u,y,hu),insertColumnsAfter:fi(function(e,t,n,r){var o=t[t.length-1].column(),i=t[t.length-1].column()+t[t.length-1].colspan(),u=Wu(t),a=I(u,function(e,t){return Ei(e,i,o,n,r.getOrInit)},e);return Iu(a,t[0].row(),i)},mi,_u,y,hu),splitCellIntoColumns:fi(function(e,t,n,r){var o=ki(e,t.row(),t.column(),n,r.getOrInit);return Iu(o,t.row(),t.column())},si,_u,y,hu),splitCellIntoRows:fi(function(e,t,n,r){var o=Ai(e,t.row(),t.column(),n,r.getOrInit);return Iu(o,t.row(),t.column())},si,y,y,hu),eraseColumns:fi(function(e,t,n,r){var o=Wu(t),i=Ii(e,o[0].column(),o[o.length-1].column()),u=Au(i,t[0].row(),t[0].column());return ku(i,u)},mi,_u,Eu,hu),eraseRows:fi(function(e,t,n,r){var o=Bu(t),i=Pi(e,o[0].row(),o[o.length-1].row()),u=Au(i,t[0].row(),t[0].column());return ku(i,u)},mi,y,Eu,hu),makeColumnHeader:fi(function(e,t,n,r){var o=_i(e,t.column(),n,r.replaceOrInit);return Iu(o,t.row(),t.column())},si,y,y,pu("row","th")),unmakeColumnHeader:fi(function(e,t,n,r){var o=_i(e,t.column(),n,r.replaceOrInit);return Iu(o,t.row(),t.column())},si,y,y,pu(null,"td")),makeRowHeader:fi(function(e,t,n,r){var o=Li(e,t.row(),n,r.replaceOrInit);return Iu(o,t.row(),t.column())},si,y,y,pu("col","th")),unmakeRowHeader:fi(function(e,t,n,r){var o=Li(e,t.row(),n,r.replaceOrInit);return Iu(o,t.row(),t.column())},si,y,y,pu(null,"td")),mergeCells:fi(function(e,t,n,r){var o=t.cells();Nu(o);var i=Ri(e,t.bounds(),n,C(o[0]));return ku(i,R.from(o[0]))},function(e,t){return t.mergable()},y,y,vu),unmergeCells:fi(function(e,t,n,r){var o=P(t,function(e,t){return Si(e,t,n,r.combine(t))},e);return ku(o,R.from(t[0]))},function(e,t){return t.unmergable()},_u,y,vu),pasteCells:fi(function(e,n,t,r){var o,i,u,a,c=(o=n.clipboard(),i=n.generators(),u=ln(o),a=mn.generate(u),ui(a,i,!0)),l=At(n.row(),n.column());return Di(l,e,c,n.generators(),t).fold(function(){return ku(e,R.some(n.element()))},function(e){var t=Au(e,n.row(),n.column());return ku(e,t)})},function(t,n){return cn.cell(n.element()).bind(function(e){return li(t,e).map(function(e){return wo(wo({},e),{generators:n.generators,clipboard:n.clipboard})})})},_u,y,hu),pasteRowsBefore:fi(function(e,t,n,r){var o=e[t.cells[0].row()],i=t.cells[0].row(),u=Mu(t.clipboard(),t.generators(),o),a=Oi(i,e,u,t.generators(),n),c=Au(a,t.cells[0].row(),t.cells[0].column());return ku(a,c)},di,y,y,hu),pasteRowsAfter:fi(function(e,t,n,r){var o=e[t.cells[0].row()],i=t.cells[t.cells.length-1].row()+t.cells[t.cells.length-1].rowspan(),u=Mu(t.clipboard(),t.generators(),o),a=Oi(i,e,u,t.generators(),n),c=Au(a,t.cells[0].row(),t.cells[0].column());return ku(a,c)},di,y,y,hu)},Fu=function(e){return xe.fromDom(e.getBody())},ju=function(e){return e.getBoundingClientRect().width},zu=function(e){return e.getBoundingClientRect().height},Hu=function(t){return function(e){return ft(e,Fu(t))}},Uu=function(e){return/^[0-9]+$/.test(e)&&(e+="px"),e},qu=function(e){var t=qt(e,"td[data-mce-style],th[data-mce-style]");he(e,"data-mce-style"),k(t,function(e){he(e,"data-mce-style")})},Vu={isRtl:C(!1)},Gu={isRtl:C(!0)},Yu={directionAt:function(e){return"rtl"==("rtl"===Ne(e,"direction")?"rtl":"ltr")?Gu:Vu}},Xu=["tableprops","tabledelete","|","tableinsertrowbefore","tableinsertrowafter","tabledeleterow","|","tableinsertcolbefore","tableinsertcolafter","tabledeletecol"],Ku={"border-collapse":"collapse",width:"100%"},Ju={border:"1"},$u=function(e){return e.getParam("table_cell_advtab",!0,"boolean")},Qu=function(e){return e.getParam("table_row_advtab",!0,"boolean")},Zu=function(e){return e.getParam("table_advtab",!0,"boolean")},ea=function(e){return e.getParam("table_style_by_css",!1,"boolean")},ta=function(e){return e.getParam("table_cell_class_list",[],"array")},na=function(e){return e.getParam("table_row_class_list",[],"array")},ra=function(e){return e.getParam("table_class_list",[],"array")},oa=function(e){return!1===e.getParam("table_responsive_width")},ia=function(e,t){return e.fire("newrow",{node:t})},ua=function(e,t){return e.fire("newcell",{node:t})},aa=function(e,t,n,r){e.fire("ObjectResizeStart",{target:t,width:n,height:r})},ca=function(e,t,n,r){e.fire("ObjectResized",{target:t,width:n,height:r})},la=function(f,e){var t,n=function(e){return"table"===oe(Fu(e))},s=(t=f.getParam("table_clone_elements"),d(t)?R.some(t.split(/[ ,]/)):Array.isArray(t)?R.some(t):R.none()),r=function(u,a,c,l){return function(e,t){qu(e);var n=l(),r=xe.fromDom(f.getDoc()),o=vo(Yu.directionAt),i=Bn(c,r,s);return a(e)?u(n,e,t,i,o).bind(function(e){return k(e.newRows(),function(e){ia(f,e.dom())}),k(e.newCells(),function(e){ua(f,e.dom())}),e.cursor().map(function(e){var t=f.dom.createRng();return t.setStart(e.dom(),0),t.setEnd(e.dom(),0),t})}):R.none()}};return{deleteRow:r(Lu.eraseRows,function(e){var t=bo(e);return!1===n(f)||1<t.rows()},y,e),deleteColumn:r(Lu.eraseColumns,function(e){var t=bo(e);return!1===n(f)||1<t.columns()},y,e),insertRowsBefore:r(Lu.insertRowsBefore,u,y,e),insertRowsAfter:r(Lu.insertRowsAfter,u,y,e),insertColumnsBefore:r(Lu.insertColumnsBefore,u,Qr,e),insertColumnsAfter:r(Lu.insertColumnsAfter,u,Qr,e),mergeCells:r(Lu.mergeCells,u,y,e),unmergeCells:r(Lu.unmergeCells,u,y,e),pasteRowsBefore:r(Lu.pasteRowsBefore,u,y,e),pasteRowsAfter:r(Lu.pasteRowsAfter,u,y,e),pasteCells:r(Lu.pasteCells,u,y,e)}},fa=function(e,t,r){var n=ln(e),o=mn.generate(n);return mi(o,t).map(function(e){var t=ui(o,r,!1).slice(e[0].row(),e[e.length-1].row()+e[e.length-1].rowspan()),n=ci(t,r);return So(n)})},sa=tinymce.util.Tools.resolve("tinymce.util.Tools"),da=function(e,t,n){n&&e.formatter.apply("align"+n,{},t)},ma=function(e,t,n){n&&e.formatter.apply("valign"+n,{},t)},ga=function(t,n){sa.each("left center right".split(" "),function(e){t.formatter.remove("align"+e,{},n)})},ha=function(t,n){sa.each("top middle bottom".split(" "),function(e){t.formatter.remove("valign"+e,{},n)})},pa=function(o,e,i){var t;return t=function(e,t){for(var n=0;n<t.length;n++){var r=o.getStyle(t[n],i);if(void 0===e&&(e=r),e!==r)return""}return e}(t,o.select("td,th",e))},va=function(e,t){var n=e.dom,r=t.control.rootControl,o=r.toJSON(),i=n.parseStyle(o.style);i["border-style"]=o.borderStyle,i["border-color"]=o.borderColor,i["background-color"]=o.backgroundColor,i.width=o.width?Uu(o.width):"",i.height=o.height?Uu(o.height):"",r.find("#style").value(n.serializeStyle(n.parseStyle(n.serializeStyle(i))))},ba=function(e,t){var n=e.dom,r=t.control.rootControl,o=r.toJSON(),i=n.parseStyle(o.style);r.find("#borderStyle").value(i["border-style"]||""),r.find("#borderColor").value(i["border-color"]||""),r.find("#backgroundColor").value(i["background-color"]||""),r.find("#width").value(i.width||""),r.find("#height").value(i.height||"")},wa={createStyleForm:function(n){var e=function(){var e=n.getParam("color_picker_callback");if(e)return function(t){return e.call(n,function(e){t.control.value(e).fire("change")},t.control.value())}};return{title:"Advanced",type:"form",defaults:{onchange:b(va,n)},items:[{label:"Style",name:"style",type:"textbox",onchange:b(ba,n)},{type:"form",padding:0,formItemDefaults:{layout:"grid",alignH:["start","right"]},defaults:{size:7},items:[{label:"Border style",type:"listbox",name:"borderStyle",width:90,onselect:b(va,n),values:[{text:"Select...",value:""},{text:"Solid",value:"solid"},{text:"Dotted",value:"dotted"},{text:"Dashed",value:"dashed"},{text:"Double",value:"double"},{text:"Groove",value:"groove"},{text:"Ridge",value:"ridge"},{text:"Inset",value:"inset"},{text:"Outset",value:"outset"},{text:"None",value:"none"},{text:"Hidden",value:"hidden"}]},{label:"Border color",type:"colorbox",name:"borderColor",onaction:e()},{label:"Background color",type:"colorbox",name:"backgroundColor",onaction:e()}]}]}},buildListItems:function(e,r,t){var o=function(e,n){return n=n||[],sa.each(e,function(e){var t={text:e.text||e.title};e.menu?t.menu=o(e.menu):(t.value=e.value,r&&r(t)),n.push(t)}),n};return o(e,t||[])},updateStyleField:va,extractAdvancedStyles:function(e,t){var n=e.parseStyle(e.getAttrib(t,"style")),r={};return n["border-style"]&&(r.borderStyle=n["border-style"]),n["border-color"]&&(r.borderColor=n["border-color"]),n["background-color"]&&(r.backgroundColor=n["background-color"]),r.style=e.serializeStyle(n),r},updateAdvancedFields:ba,syncAdvancedStyleFields:function(e,t){t.control.rootControl.find("#style")[0].getEl().isEqualNode(m.document.activeElement)?ba(e,t):va(e,t)}},ya=function(r,o,e){var i,u=r.dom;function a(e,t,n){(1===o.length||n)&&u.setAttrib(e,t,n)}function c(e,t,n){(1===o.length||n)&&u.setStyle(e,t,n)}$u(r)&&wa.syncAdvancedStyleFields(r,e),i=e.control.rootControl.toJSON(),r.undoManager.transact(function(){sa.each(o,function(e){var t,n;a(e,"scope",i.scope),1===o.length?a(e,"style",i.style):(t=e,n=i.style,delete t.dataset.mceStyle,t.style.cssText+=";"+n),a(e,"class",i["class"]),c(e,"width",Uu(i.width)),c(e,"height",Uu(i.height)),i.type&&e.nodeName.toLowerCase()!==i.type&&(e=u.rename(e,i.type)),1===o.length&&(ga(r,e),ha(r,e)),i.align&&da(r,e,i.align),i.valign&&ma(r,e,i.valign)}),r.focus()})},xa=function(t){var e,n,r,o=[];if(o=t.dom.select("td[data-mce-selected],th[data-mce-selected]"),e=t.dom.getParent(t.selection.getStart(),"td,th"),!o.length&&e&&o.push(e),e=e||o[0]){var i,u,a,c;1<o.length?n={width:"",height:"",scope:"","class":"",align:"",valign:"",style:"",type:e.nodeName.toLowerCase()}:(u=e,a=(i=t).dom,c={width:a.getStyle(u,"width")||a.getAttrib(u,"width"),height:a.getStyle(u,"height")||a.getAttrib(u,"height"),scope:a.getAttrib(u,"scope"),"class":a.getAttrib(u,"class"),type:u.nodeName.toLowerCase(),style:"",align:"",valign:""},sa.each("left center right".split(" "),function(e){i.formatter.matchNode(u,"align"+e)&&(c.align=e)}),sa.each("top middle bottom".split(" "),function(e){i.formatter.matchNode(u,"valign"+e)&&(c.valign=e)}),$u(i)&&sa.extend(c,wa.extractAdvancedStyles(a,u)),n=c),0<ta(t).length&&(r={name:"class",type:"listbox",label:"Class",values:wa.buildListItems(ta(t),function(e){e.value&&(e.textStyle=function(){return t.formatter.getCssText({block:"td",classes:[e.value]})})})});var l={type:"form",layout:"flex",direction:"column",labelGapCalc:"children",padding:0,items:[{type:"form",layout:"grid",columns:2,labelGapCalc:!1,padding:0,defaults:{type:"textbox",maxWidth:50},items:[{label:"Width",name:"width",onchange:b(wa.updateStyleField,t)},{label:"Height",name:"height",onchange:b(wa.updateStyleField,t)},{label:"Cell type",name:"type",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"Cell",value:"td"},{text:"Header cell",value:"th"}]},{label:"Scope",name:"scope",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Row",value:"row"},{text:"Column",value:"col"},{text:"Row group",value:"rowgroup"},{text:"Column group",value:"colgroup"}]},{label:"H Align",name:"align",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"V Align",name:"valign",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Top",value:"top"},{text:"Middle",value:"middle"},{text:"Bottom",value:"bottom"}]}]},r]};$u(t)?t.windowManager.open({title:"Cell properties",bodyType:"tabpanel",data:n,body:[{title:"General",type:"form",items:l},wa.createStyleForm(t)],onsubmit:b(ya,t,o)}):t.windowManager.open({title:"Cell properties",data:n,body:l,onsubmit:b(ya,t,o)})}};function Ca(f,s,d,e){var m=f.dom;function g(e,t,n){(1===s.length||n)&&m.setAttrib(e,t,n)}Qu(f)&&wa.syncAdvancedStyleFields(f,e);var h=e.control.rootControl.toJSON();f.undoManager.transact(function(){sa.each(s,function(e){var t,n,r,o,i,u,a,c,l;g(e,"scope",h.scope),g(e,"style",h.style),g(e,"class",h["class"]),t=e,n="height",r=Uu(h.height),(1===s.length||r)&&m.setStyle(t,n,r),h.type!==e.parentNode.nodeName.toLowerCase()&&(o=f.dom,i=e,u=h.type,a=o.getParent(i,"table"),c=i.parentNode,(l=o.select(u,a)[0])||(l=o.create(u),a.firstChild?"CAPTION"===a.firstChild.nodeName?o.insertAfter(l,a.firstChild):a.insertBefore(l,a.firstChild):a.appendChild(l)),l.appendChild(i),c.hasChildNodes()||o.remove(c)),h.align!==d.align&&(ga(f,e),da(f,e,h.align))}),f.focus()})}var Ra=function(t){var e,n,r,o,i,u,a,c,l,f,s=t.dom,d=[];e=s.getParent(t.selection.getStart(),"table"),n=s.getParent(t.selection.getStart(),"td,th"),sa.each(e.rows,function(t){sa.each(t.cells,function(e){if(s.getAttrib(e,"data-mce-selected")||e===n)return d.push(t),!1})}),(r=d[0])&&(1<d.length?i={height:"",scope:"",style:"","class":"",align:"",type:r.parentNode.nodeName.toLowerCase()}:(c=r,l=(a=t).dom,f={height:l.getStyle(c,"height")||l.getAttrib(c,"height"),scope:l.getAttrib(c,"scope"),"class":l.getAttrib(c,"class"),align:"",style:"",type:c.parentNode.nodeName.toLowerCase()},sa.each("left center right".split(" "),function(e){a.formatter.matchNode(c,"align"+e)&&(f.align=e)}),Qu(a)&&sa.extend(f,wa.extractAdvancedStyles(l,c)),i=f),0<na(t).length&&(o={name:"class",type:"listbox",label:"Class",values:wa.buildListItems(na(t),function(e){e.value&&(e.textStyle=function(){return t.formatter.getCssText({block:"tr",classes:[e.value]})})})}),u={type:"form",columns:2,padding:0,defaults:{type:"textbox"},items:[{type:"listbox",name:"type",label:"Row type",text:"Header",maxWidth:null,values:[{text:"Header",value:"thead"},{text:"Body",value:"tbody"},{text:"Footer",value:"tfoot"}]},{type:"listbox",name:"align",label:"Alignment",text:"None",maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"Height",name:"height"},o]},Qu(t)?t.windowManager.open({title:"Row properties",data:i,bodyType:"tabpanel",body:[{title:"General",type:"form",items:u},wa.createStyleForm(t)],onsubmit:b(Ca,t,d,i)}):t.windowManager.open({title:"Row properties",data:i,body:u,onsubmit:b(Ca,t,d,i)}))},Sa=tinymce.util.Tools.resolve("tinymce.Env"),Ta={styles:{"border-collapse":"collapse",width:"100%"},attributes:{border:"1"},percentages:!0},Da=function(e,t,n,r,o){void 0===o&&(o=Ta);var i=xe.fromTag("table");Oe(i,o.styles),de(i,o.attributes);var u=xe.fromTag("tbody");Rt(i,u);for(var a=[],c=0;c<e;c++){for(var l=xe.fromTag("tr"),f=0;f<t;f++){var s=c<n||f<r?xe.fromTag("th"):xe.fromTag("td");f<r&&se(s,"scope","row"),c<n&&se(s,"scope","col"),Rt(s,xe.fromTag("br")),o.percentages&&De(s,"width",100/t+"%"),Rt(l,s)}a.push(l)}return Dt(u,a),i},Oa=function(e,t){e.selection.select(t.dom(),!0),e.selection.collapse(!0)},Na=function(r,e,t){var n,o,i=r.getParam("table_default_styles",Ku,"object"),u={styles:i,attributes:(o=r,o.getParam("table_default_attributes",Ju,"object")),percentages:(n=i.width,d(n)&&-1!==n.indexOf("%")&&!oa(r))},a=Da(t,e,0,0,u);se(a,"data-mce-id","__mce");var c,l,f,s=(c=a,l=xe.fromTag("div"),f=xe.fromDom(c.dom().cloneNode(!0)),Rt(l,f),l.dom().innerHTML);return r.insertContent(s),Qt(Fu(r),'table[data-mce-id="__mce"]').map(function(e){var t,n;return oa(r)&&De(e,"width",Ne(e,"width")),he(e,"data-mce-id"),t=r,k(qt(e,"tr"),function(e){ia(t,e.dom()),k(qt(e,"th,td"),function(e){ua(t,e.dom())})}),n=r,Qt(e,"td,th").each(b(Oa,n)),e.dom()}).getOr(null)};function Ea(e,t,n,r){if("TD"===t.tagName||"TH"===t.tagName)e.setStyle(t,n,r);else if(t.children)for(var o=0;o<t.children.length;o++)Ea(e,t.children[o],n,r)}var ka,Aa=function(e,t,n){var r,o,i=e.dom;Zu(e)&&wa.syncAdvancedStyleFields(e,n),!1===(o=n.control.rootControl.toJSON())["class"]&&delete o["class"],e.undoManager.transact(function(){t||(t=Na(e,o.cols||1,o.rows||1)),function(e,t,n){var r,o=e.dom,i={},u={};if(i["class"]=n["class"],u.height=Uu(n.height),o.getAttrib(t,"width")&&!ea(e)?i.width=(r=n.width)?r.replace(/px$/,""):"":u.width=Uu(n.width),ea(e)?(u["border-width"]=Uu(n.border),u["border-spacing"]=Uu(n.cellspacing),sa.extend(i,{"data-mce-border-color":n.borderColor,"data-mce-cell-padding":n.cellpadding,"data-mce-border":n.border})):sa.extend(i,{border:n.border,cellpadding:n.cellpadding,cellspacing:n.cellspacing}),ea(e)&&t.children)for(var a=0;a<t.children.length;a++)Ea(o,t.children[a],{"border-width":Uu(n.border),"border-color":n.borderColor,padding:Uu(n.cellpadding)});n.style?sa.extend(u,o.parseStyle(n.style)):u=sa.extend({},o.parseStyle(o.getAttrib(t,"style")),u),i.style=o.serializeStyle(u),o.setAttribs(t,i)}(e,t,o),(r=i.select("caption",t)[0])&&!o.caption&&i.remove(r),!r&&o.caption&&((r=i.create("caption")).innerHTML=Sa.ie?"\xa0":'<br data-mce-bogus="1"/>',t.insertBefore(r,t.firstChild)),ga(e,t),o.align&&da(e,t,o.align),e.focus(),e.addVisual()})},Pa=function(t,e){var n,r,o,i,u,a,c,l,f,s,d=t.dom,m={};!0===e?(n=d.getParent(t.selection.getStart(),"table"))&&(c=n,l=(a=t).dom,f={width:l.getStyle(c,"width")||l.getAttrib(c,"width"),height:l.getStyle(c,"height")||l.getAttrib(c,"height"),cellspacing:l.getStyle(c,"border-spacing")||l.getAttrib(c,"cellspacing"),cellpadding:l.getAttrib(c,"data-mce-cell-padding")||l.getAttrib(c,"cellpadding")||pa(a.dom,c,"padding"),border:l.getAttrib(c,"data-mce-border")||l.getAttrib(c,"border")||pa(a.dom,c,"border"),borderColor:l.getAttrib(c,"data-mce-border-color"),caption:!!l.select("caption",c)[0],"class":l.getAttrib(c,"class")},sa.each("left center right".split(" "),function(e){a.formatter.matchNode(c,"align"+e)&&(f.align=e)}),Zu(a)&&sa.extend(f,wa.extractAdvancedStyles(l,c)),m=f):(r={label:"Cols",name:"cols"},o={label:"Rows",name:"rows"}),0<ra(t).length&&(m["class"]&&(m["class"]=m["class"].replace(/\s*mce\-item\-table\s*/g,"")),i={name:"class",type:"listbox",label:"Class",values:wa.buildListItems(ra(t),function(e){e.value&&(e.textStyle=function(){return t.formatter.getCssText({block:"table",classes:[e.value]})})})}),u={type:"form",layout:"flex",direction:"column",labelGapCalc:"children",padding:0,items:[{type:"form",labelGapCalc:!1,padding:0,layout:"grid",columns:2,defaults:{type:"textbox",maxWidth:50},items:(s=t,s.getParam("table_appearance_options",!0,"boolean")?[r,o,{label:"Width",name:"width",onchange:b(wa.updateStyleField,t)},{label:"Height",name:"height",onchange:b(wa.updateStyleField,t)},{label:"Cell spacing",name:"cellspacing"},{label:"Cell padding",name:"cellpadding"},{label:"Border",name:"border"},{label:"Caption",name:"caption",type:"checkbox"}]:[r,o,{label:"Width",name:"width",onchange:b(wa.updateStyleField,t)},{label:"Height",name:"height",onchange:b(wa.updateStyleField,t)}])},{label:"Alignment",name:"align",type:"listbox",text:"None",values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},i]},Zu(t)?t.windowManager.open({title:"Table properties",data:m,bodyType:"tabpanel",body:[{title:"General",type:"form",items:u},wa.createStyleForm(t)],onsubmit:b(Aa,t,n)}):t.windowManager.open({title:"Table properties",data:m,body:u,onsubmit:b(Aa,t,n)})},Ia=sa.each,Ba=function(a,t,c,l,n){var r=Hu(a),e=function(e){return function(){return R.from(a.dom.getParent(a.selection.getStart(),e)).map(xe.fromDom)}},o=e("caption"),f=e("th,td"),s=function(e){return cn.table(e,r)},d=function(e){return{width:ju(e.dom()),height:ju(e.dom())}},i=function(n){f().each(function(t){s(t).each(function(i){var e=kr.forMenu(l,i,t),u=d(i);n(i,e).each(function(e){var t,n,r,o;t=a,n=u,o=d(r=i),n.width===o.width&&n.height===o.height||(aa(t,r.dom(),n.width,n.height),ca(t,r.dom(),o.width,o.height)),a.selection.setRng(e),a.focus(),c.clear(i),qu(i)})})})},u=function(e){return f().bind(function(o){return s(o).bind(function(e){var t=xe.fromDom(a.getDoc()),n=kr.forMenu(l,e,o),r=Bn(y,t,R.none());return fa(e,n,r)})})},m=function(u){n.get().each(function(e){var i=E(e,function(e){return Nn(e)});f().each(function(o){s(o).each(function(t){var e=xe.fromDom(a.getDoc()),n=Wn(e),r=kr.pasteRows(l,t,o,i,n);u(t,r).each(function(e){a.selection.setRng(e),a.focus(),c.clear(t)})})})})};Ia({mceTableSplitCells:function(){i(t.unmergeCells)},mceTableMergeCells:function(){i(t.mergeCells)},mceTableInsertRowBefore:function(){i(t.insertRowsBefore)},mceTableInsertRowAfter:function(){i(t.insertRowsAfter)},mceTableInsertColBefore:function(){i(t.insertColumnsBefore)},mceTableInsertColAfter:function(){i(t.insertColumnsAfter)},mceTableDeleteCol:function(){i(t.deleteColumn)},mceTableDeleteRow:function(){i(t.deleteRow)},mceTableCutRow:function(e){n.set(u()),i(t.deleteRow)},mceTableCopyRow:function(e){n.set(u())},mceTablePasteRowBefore:function(e){m(t.pasteRowsBefore)},mceTablePasteRowAfter:function(e){m(t.pasteRowsAfter)},mceTableDelete:function(){f().orThunk(o).each(function(e){cn.table(e,r).filter(g(r)).each(function(e){var t=xe.fromText("");xt(e,t),Nt(e);var n=a.dom.createRng();n.setStart(t.dom(),0),n.setEnd(t.dom(),0),a.selection.setRng(n)})})}},function(e,t){a.addCommand(t,e)}),Ia({mceInsertTable:b(Pa,a),mceTableProps:b(Pa,a,!0),mceTableRowProps:b(Ra,a),mceTableCellProps:b(xa,a)},function(n,e){a.addCommand(e,function(e,t){n(t)})})},Wa=function(e){var t=R.from(e.dom().documentElement).map(xe.fromDom).getOr(e);return{parent:C(t),view:C(e),origin:C(eo(0,0))}},Ma=function(e,t){return{parent:C(t),view:C(e),origin:C(eo(0,0))}},_a=function(e){var r=q.apply(null,e),o=[];return{bind:function(e){if(e===undefined)throw new Error("Event bind error: undefined handler");o.push(e)},unbind:function(t){o=A(o,function(e){return e!==t})},trigger:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=r.apply(null,e);k(o,function(e){e(n)})}}},La={create:function(e){return{registry:H(e,function(e){return{bind:e.bind,unbind:e.unbind}}),trigger:H(e,function(e){return e.trigger})}}},Fa=function(m,g){return function(e){if(m(e)){var t,n,r,o,i,u,a,c=xe.fromDom(e.target),l=function(){e.stopPropagation()},f=function(){e.preventDefault()},s=x(f,l),d=(t=c,n=e.clientX,r=e.clientY,o=l,i=f,u=s,a=e,{target:C(t),x:C(n),y:C(r),stop:o,prevent:i,kill:u,raw:C(a)});g(d)}}},ja=function(e,t,n,r){return o=e,i=t,u=!1,a=Fa(n,r),o.dom().addEventListener(i,a,u),{unbind:b(za,o,i,a,u)};var o,i,u,a},za=function(e,t,n,r){e.dom().removeEventListener(t,n,r)},Ha=C(!0),Ua=function(e,t,n){return ja(e,t,Ha,n)},qa=Object.prototype.hasOwnProperty,Va=(ka=function(e,t){return t},function(){for(var e=new Array(arguments.length),t=0;t<e.length;t++)e[t]=arguments[t];if(0===e.length)throw new Error("Can't merge zero objects");for(var n={},r=0;r<e.length;r++){var o=e[r];for(var i in o)qa.call(o,i)&&(n[i]=ka(n[i],o[i]))}return n}),Ga={resolve:_o("ephox-dragster").resolve},Ya=du(["compare","extract","mutate","sink"]),Xa=du(["element","start","stop","destroy"]),Ka=du(["forceDrop","drop","move","delayDrop"]),Ja=Ya({compare:function(e,t){return eo(t.left()-e.left(),t.top()-e.top())},extract:function(e){return R.some(eo(e.x(),e.y()))},sink:function(e,t){var n,r,o,i=(n=t,r=Va({layerClass:Ga.resolve("blocker")},n),o=xe.fromTag("div"),se(o,"role","presentation"),Oe(o,{position:"fixed",left:"0px",top:"0px",width:"100%",height:"100%"}),Eo(o,Ga.resolve("blocker")),Eo(o,r.layerClass),{element:function(){return o},destroy:function(){Nt(o)}}),u=Ua(i.element(),"mousedown",e.forceDrop),a=Ua(i.element(),"mouseup",e.drop),c=Ua(i.element(),"mousemove",e.move),l=Ua(i.element(),"mouseout",e.delayDrop);return Xa({element:i.element,start:function(e){Rt(e,i.element())},stop:function(){Nt(i.element())},destroy:function(){i.destroy(),a.unbind(),c.unbind(),l.unbind(),u.unbind()}})},mutate:function(e,t){e.mutate(t.left(),t.top())}});function $a(){var i=R.none(),u=La.create({move:_a(["info"])});return{onEvent:function(e,o){o.extract(e).each(function(e){var t,n,r;(t=o,n=e,r=i.map(function(e){return t.compare(e,n)}),i=R.some(n),r).each(function(e){u.trigger.move(e)})})},reset:function(){i=R.none()},events:u.registry}}function Qa(){var e={onEvent:y,reset:y},t=$a(),n=e;return{on:function(){n.reset(),n=t},off:function(){n.reset(),n=e},isOn:function(){return n===t},onEvent:function(e,t){n.onEvent(e,t)},events:t.events}}var Za=function(t,n,e){var r,o,i,u=!1,a=La.create({start:_a([]),stop:_a([])}),c=Qa(),l=function(){d.stop(),c.isOn()&&(c.off(),a.trigger.stop())},f=(r=l,o=200,i=null,{cancel:function(){null!==i&&(m.clearTimeout(i),i=null)},throttle:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];null!==i&&m.clearTimeout(i),i=m.setTimeout(function(){r.apply(null,e),i=null},o)}});c.events.move.bind(function(e){n.mutate(t,e.info())});var s=function(n){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];u&&n.apply(null,e)}},d=n.sink(Ka({forceDrop:l,drop:s(l),move:s(function(e){f.cancel(),c.onEvent(e,n)}),delayDrop:s(f.throttle)}),e);return{element:d.element,go:function(e){d.start(e),c.on(),a.trigger.start()},on:function(){u=!0},off:function(){u=!1},destroy:function(){d.destroy()},events:a.registry}},ec=function(e,t){void 0===t&&(t={});var n=t.mode!==undefined?t.mode:Ja;return Za(e,n,t)},tc=function(){var n,r=La.create({drag:_a(["xDelta","yDelta","target"])}),o=R.none(),e={mutate:function(e,t){n.trigger.drag(e,t)},events:(n=La.create({drag:_a(["xDelta","yDelta"])})).registry};return e.events.drag.bind(function(t){o.each(function(e){r.trigger.drag(t.xDelta(),t.yDelta(),e)})}),{assign:function(e){o=R.some(e)},get:function(){return o},mutate:e.mutate,events:r.registry}},nc=function(e){return"true"===me(e,"contenteditable")},rc=Lo.resolve("resizer-bar-dragging"),oc=function(o,t,i){var n=tc(),r=ec(n,{}),u=R.none(),e=function(e,t){return R.from(me(e,t))};n.events.drag.bind(function(n){e(n.target(),"data-row").each(function(e){var t=Ui.getInt(n.target(),"top");De(n.target(),"top",t+n.yDelta()+"px")}),e(n.target(),"data-column").each(function(e){var t=Ui.getInt(n.target(),"left");De(n.target(),"left",t+n.xDelta()+"px")})});var a=function(e,t){return Ui.getInt(e,t)-parseInt(me(e,"data-initial-"+t),10)};r.events.stop.bind(function(){n.get().each(function(r){u.each(function(n){e(r,"data-row").each(function(e){var t=a(r,"top");he(r,"data-initial-top"),m.trigger.adjustHeight(n,t,parseInt(e,10))}),e(r,"data-column").each(function(e){var t=a(r,"left");he(r,"data-initial-left"),m.trigger.adjustWidth(n,t,parseInt(e,10))}),Xo(o,n,i,t)})})});var c=function(e,t){m.trigger.startAdjust(),n.assign(e),se(e,"data-initial-"+t,parseInt(Ne(e,t),10)),Eo(e,rc),De(e,"opacity","0.2"),r.go(o.parent())},l=Ua(o.parent(),"mousedown",function(e){Qo(e.target())&&c(e.target(),"top"),Zo(e.target())&&c(e.target(),"left")}),f=function(e){return ft(e,o.view())},s=function(e){return Zt(e,"table",f).filter(function(e){return(t=e,n=f,Zt(t,"[contenteditable]",n)).exists(nc);var t,n})},d=Ua(o.view(),"mouseover",function(e){s(e.target()).fold(function(){Ce(e.target())&&$o(o)},function(e){u=R.some(e),Xo(o,e,i,t)})}),m=La.create({adjustHeight:_a(["table","delta","row"]),adjustWidth:_a(["table","delta","column"]),startAdjust:_a([])});return{destroy:function(){l.unbind(),d.unbind(),r.destroy(),$o(o)},refresh:function(e){Xo(o,e,i,t)},on:r.on,off:r.off,hideBars:b(Ko,o),showBars:b(Jo,o),events:m.registry}},ic=function(e,n){var r=ho.height,t=oc(e,n,r),o=La.create({beforeResize:_a(["table"]),afterResize:_a(["table"]),startDrag:_a([])});return t.events.adjustHeight.bind(function(e){o.trigger.beforeResize(e.table());var t=r.delta(e.delta(),e.table());au(e.table(),t,e.row(),r),o.trigger.afterResize(e.table())}),t.events.startAdjust.bind(function(e){o.trigger.startDrag()}),t.events.adjustWidth.bind(function(e){o.trigger.beforeResize(e.table());var t=n.delta(e.delta(),e.table());uu(e.table(),t,e.column(),n),o.trigger.afterResize(e.table())}),{on:t.on,off:t.off,hideBars:t.hideBars,showBars:t.showBars,destroy:t.destroy,events:o.registry}},uc=function(e,t){return e.inline?Ma(Fu(e),(n=xe.fromTag("div"),Oe(n,{position:"static",height:"0",width:"0",padding:"0",margin:"0",border:"0"}),Rt(Re(),n),n)):Wa(xe.fromDom(e.getDoc()));var n},ac=function(e,t){e.inline&&Nt(t.parent())},cc=function(u){var a,c,o=R.none(),i=R.none(),l=R.none(),f=/(\d+(\.\d+)?)%/,s=function(e){return"TABLE"===e.nodeName};return u.on("init",function(){var e,t=vo(Yu.directionAt),n=uc(u);if(l=R.some(n),("table"===(e=u.getParam("object_resizing",!0))||e)&&u.getParam("table_resize_bars",!0,"boolean")){var r=ic(n,t);r.on(),r.events.startDrag.bind(function(e){o=R.some(u.selection.getRng())}),r.events.beforeResize.bind(function(e){var t=e.table().dom();aa(u,t,ju(t),zu(t))}),r.events.afterResize.bind(function(e){var t=e.table(),n=t.dom();qu(t),o.each(function(e){u.selection.setRng(e),u.focus()}),ca(u,n,ju(n),zu(n)),u.undoManager.add()}),i=R.some(r)}}),u.on("ObjectResizeStart",function(e){var t,n=e.target;s(n)&&(a=e.width,t=n,c=u.dom.getStyle(t,"width")||u.dom.getAttrib(t,"width"))}),u.on("ObjectResized",function(e){var t=e.target;if(s(t)){var n=t;if(f.test(c)){var r=parseFloat(f.exec(c)[1]),o=e.width*r/a;u.dom.setStyle(n,"width",o+"%")}else{var i=[];sa.each(n.rows,function(e){sa.each(e.cells,function(e){var t=u.dom.getStyle(e,"width",!0);i.push({cell:e,width:t})})}),sa.each(i,function(e){u.dom.setStyle(e.cell,"width",e.width),u.dom.setAttrib(e.cell,"width",null)})}}}),{lazyResize:function(){return i},lazyWire:function(){return l.getOr(Wa(xe.fromDom(u.getBody())))},destroy:function(){i.each(function(e){e.destroy()}),l.each(function(e){ac(u,e)})}}},lc=xr([{none:["current"]},{first:["current"]},{middle:["current","target"]},{last:["current"]}]),fc=wo(wo({},lc),{none:function(e){return void 0===e&&(e=undefined),lc.none(e)}}),sc=function(n,e){return cn.table(n,e).bind(function(e){var t=cn.cells(e);return W(t,function(e){return ft(n,e)}).map(function(e){return{index:C(e),all:C(t)}})})},dc=function(t,e){return sc(t,e).fold(function(){return fc.none(t)},function(e){return e.index()+1<e.all().length?fc.middle(t,e.all()[e.index()+1]):fc.last(t)})},mc=function(t,e){return sc(t,e).fold(function(){return fc.none()},function(e){return 0<=e.index()-1?fc.middle(t,e.all()[e.index()-1]):fc.first(t)})},gc={create:q("start","soffset","finish","foffset")},hc=xr([{before:["element"]},{on:["element","offset"]},{after:["element"]}]),pc={before:hc.before,on:hc.on,after:hc.after,cata:function(e,t,n,r){return e.fold(t,n,r)},getStart:function(e){return e.fold(o,o,o)}},vc=xr([{domRange:["rng"]},{relative:["startSitu","finishSitu"]},{exact:["start","soffset","finish","foffset"]}]),bc={domRange:vc.domRange,relative:vc.relative,exact:vc.exact,exactFromRange:function(e){return vc.exact(e.start(),e.soffset(),e.finish(),e.foffset())},getWin:function(e){var t,n=e.match({domRange:function(e){return xe.fromDom(e.startContainer)},relative:function(e,t){return pc.getStart(e)},exact:function(e,t,n,r){return e}});return t=n,xe.fromDom(t.dom().ownerDocument.defaultView)},range:gc.create},wc=function(e,t){e.selectNodeContents(t.dom())},yc=function(e,t,n){var r,o,i=e.document.createRange();return r=i,t.fold(function(e){r.setStartBefore(e.dom())},function(e,t){r.setStart(e.dom(),t)},function(e){r.setStartAfter(e.dom())}),o=i,n.fold(function(e){o.setEndBefore(e.dom())},function(e,t){o.setEnd(e.dom(),t)},function(e){o.setEndAfter(e.dom())}),i},xc=function(e,t,n,r,o){var i=e.document.createRange();return i.setStart(t.dom(),n),i.setEnd(r.dom(),o),i},Cc=function(e){return{left:C(e.left),top:C(e.top),right:C(e.right),bottom:C(e.bottom),width:C(e.width),height:C(e.height)}},Rc=xr([{ltr:["start","soffset","finish","foffset"]},{rtl:["start","soffset","finish","foffset"]}]),Sc=function(e,t,n){return t(xe.fromDom(n.startContainer),n.startOffset,xe.fromDom(n.endContainer),n.endOffset)},Tc=function(e,t){var o,n,r,i=(o=e,t.match({domRange:function(e){return{ltr:C(e),rtl:R.none}},relative:function(e,t){return{ltr:we(function(){return yc(o,e,t)}),rtl:we(function(){return R.some(yc(o,t,e))})}},exact:function(e,t,n,r){return{ltr:we(function(){return xc(o,e,t,n,r)}),rtl:we(function(){return R.some(xc(o,n,r,e,t))})}}}));return(r=(n=i).ltr()).collapsed?n.rtl().filter(function(e){return!1===e.collapsed}).map(function(e){return Rc.rtl(xe.fromDom(e.endContainer),e.endOffset,xe.fromDom(e.startContainer),e.startOffset)}).getOrThunk(function(){return Sc(0,Rc.ltr,r)}):Sc(0,Rc.ltr,r)},Dc=function(i,e){return Tc(i,e).match({ltr:function(e,t,n,r){var o=i.document.createRange();return o.setStart(e.dom(),t),o.setEnd(n.dom(),r),o},rtl:function(e,t,n,r){var o=i.document.createRange();return o.setStart(n.dom(),r),o.setEnd(e.dom(),t),o}})},Oc=function(e,t,n){return t>=e.left&&t<=e.right&&n>=e.top&&n<=e.bottom},Nc=function(n,r,e,t,o){var i=function(e){var t=n.dom().createRange();return t.setStart(r.dom(),e),t.collapse(!0),t},u=vn(r).length,a=function(e,t,n,r,o){if(0===o)return 0;if(t===r)return o-1;for(var i=r,u=1;u<o;u++){var a=e(u),c=Math.abs(t-a.left);if(n<=a.bottom){if(n<a.top||i<c)return u-1;i=c}}return 0}(function(e){return i(e).getBoundingClientRect()},e,t,o.right,u);return i(a)},Ec=function(e,t,n,r){return le(t)?function(t,n,r,o){var e=t.dom().createRange();e.selectNode(n.dom());var i=e.getClientRects();return xo(i,function(e){return Oc(e,r,o)?R.some(e):R.none()}).map(function(e){return Nc(t,n,r,o,e)})}(e,t,n,r):(i=t,u=n,a=r,c=(o=e).dom().createRange(),l=bt(i),xo(l,function(e){return c.selectNode(e.dom()),Oc(c.getBoundingClientRect(),u,a)?Ec(o,e,u,a):R.none()}));var o,i,u,a,c,l},kc=function(e,t){return t-e.left<e.right-t},Ac=function(e,t,n){var r=e.dom().createRange();return r.selectNode(t.dom()),r.collapse(n),r},Pc=function(t,e,n){var r=t.dom().createRange();r.selectNode(e.dom());var o=r.getBoundingClientRect(),i=kc(o,n);return(!0===i?Rn:Sn)(e).map(function(e){return Ac(t,e,i)})},Ic=function(e,t,n){var r=t.dom().getBoundingClientRect(),o=kc(r,n);return R.some(Ac(e,t,o))},Bc=function(e,t,n,r){var o=e.dom().createRange();o.selectNode(t.dom());var i=o.getBoundingClientRect();return function(e,t,n,r){var o=e.dom().createRange();o.selectNode(t.dom());var i=o.getBoundingClientRect(),u=Math.max(i.left,Math.min(i.right,n)),a=Math.max(i.top,Math.min(i.bottom,r));return Ec(e,t,u,a)}(e,t,Math.max(i.left,Math.min(i.right,n)),Math.max(i.top,Math.min(i.bottom,r)))},Wc=document.caretPositionFromPoint?function(n,e,t){return R.from(n.dom().caretPositionFromPoint(e,t)).bind(function(e){if(null===e.offsetNode)return R.none();var t=n.dom().createRange();return t.setStart(e.offsetNode,e.offset),t.collapse(),R.some(t)})}:document.caretRangeFromPoint?function(e,t,n){return R.from(e.dom().caretRangeFromPoint(t,n))}:function(o,i,t){return xe.fromPoint(o,i,t).bind(function(r){var e=function(){return e=o,n=i,(0===bt(t=r).length?Ic:Pc)(e,t,n);var e,t,n};return 0===bt(r).length?e():Bc(o,r,i,t).orThunk(e)})},Mc=function(e,t){var n=oe(e);return"input"===n?pc.after(e):O(["br","img"],n)?0===t?pc.before(e):pc.after(e):pc.on(e,t)},_c=function(e,t){var n=e.fold(pc.before,Mc,pc.after),r=t.fold(pc.before,Mc,pc.after);return bc.relative(n,r)},Lc=function(e,t,n,r){var o=Mc(e,t),i=Mc(n,r);return bc.relative(o,i)},Fc=function(e,t,n,r){var o,i,u,a,c,l=(i=t,u=n,a=r,(c=mt(o=e).dom().createRange()).setStart(o.dom(),i),c.setEnd(u.dom(),a),c),f=ft(e,n)&&t===r;return l.collapsed&&!f},jc=function(e,t){R.from(e.getSelection()).each(function(e){e.removeAllRanges(),e.addRange(t)})},zc=function(e,t,n,r,o){var i=xc(e,t,n,r,o);jc(e,i)},Hc=function(s,e){return Tc(s,e).match({ltr:function(e,t,n,r){zc(s,e,t,n,r)},rtl:function(e,t,n,r){var o,i,u,a,c,l=s.getSelection();if(l.setBaseAndExtent)l.setBaseAndExtent(e.dom(),t,n.dom(),r);else if(l.extend)try{i=e,u=t,a=n,c=r,(o=l).collapse(i.dom(),u),o.extend(a.dom(),c)}catch(f){zc(s,n,r,e,t)}else zc(s,n,r,e,t)}})},Uc=function(e){var o=bc.getWin(e).dom(),t=function(e,t,n,r){return xc(o,e,t,n,r)},n=e.match({domRange:function(e){var t=xe.fromDom(e.startContainer),n=xe.fromDom(e.endContainer);return Lc(t,e.startOffset,n,e.endOffset)},relative:_c,exact:Lc});return Tc(o,n).match({ltr:t,rtl:t})},qc=function(e){var t=xe.fromDom(e.anchorNode),n=xe.fromDom(e.focusNode);return Fc(t,e.anchorOffset,n,e.focusOffset)?R.some(gc.create(t,e.anchorOffset,n,e.focusOffset)):function(e){if(0<e.rangeCount){var t=e.getRangeAt(0),n=e.getRangeAt(e.rangeCount-1);return R.some(gc.create(xe.fromDom(t.startContainer),t.startOffset,xe.fromDom(n.endContainer),n.endOffset))}return R.none()}(e)},Vc=function(e,t){var n,r,o=(n=t,r=e.document.createRange(),wc(r,n),r);jc(e,o)},Gc=function(e){return(t=e,R.from(t.getSelection()).filter(function(e){return 0<e.rangeCount}).bind(qc)).map(function(e){return bc.exact(e.start(),e.soffset(),e.finish(),e.foffset())});var t},Yc=function(e,t){var n,r,o,i=Dc(e,t);return r=(n=i).getClientRects(),0<(o=0<r.length?r[0]:n.getBoundingClientRect()).width||0<o.height?R.some(o).map(Cc):R.none()},Xc=function(e,t,n){return r=e,o=t,i=n,u=xe.fromDom(r.document),Wc(u,o,i).map(function(e){return gc.create(xe.fromDom(e.startContainer),e.startOffset,xe.fromDom(e.endContainer),e.endOffset)});var r,o,i,u},Kc=tinymce.util.Tools.resolve("tinymce.util.VK"),Jc=function(e,t,n,r){return el(e,t,dc(n),r)},$c=function(e,t,n,r){return el(e,t,mc(n),r)},Qc=function(e,t){var n=bc.exact(t,0,t,0);return Uc(n)},Zc=function(e,t){var n,r=qt(t,"tr");return(n=r,0===n.length?R.none():R.some(n[n.length-1])).bind(function(e){return Qt(e,"td,th").map(function(e){return Qc(0,e)})})},el=function(r,e,t,o,n){return t.fold(R.none,R.none,function(e,t){return Rn(t).map(function(e){return Qc(0,e)})},function(n){return cn.table(n,e).bind(function(e){var t=kr.noMenu(n);return r.undoManager.transact(function(){o.insertRowsAfter(e,t)}),Zc(0,e)})})},tl=["table","li","dl"],nl=function(t,n,r,o){if(t.keyCode===Kc.TAB){var i=Fu(n),u=function(e){var t=oe(e);return ft(e,i)||O(tl,t)},e=n.selection.getRng();if(e.collapsed){var a=xe.fromDom(e.startContainer);cn.cell(a,u).each(function(e){t.preventDefault(),(t.shiftKey?$c:Jc)(n,u,e,r,o).each(function(e){n.selection.setRng(e)})})}}},rl={create:q("selection","kill")},ol=function(e,t,n,r){return{start:C(pc.on(e,t)),finish:C(pc.on(n,r))}},il={convertToRange:function(e,t){var n=Dc(e,t);return gc.create(xe.fromDom(n.startContainer),n.startOffset,xe.fromDom(n.endContainer),n.endOffset)},makeSitus:ol},ul=function(n,e,r,t,o){return ft(r,t)?R.none():sr(r,t,e).bind(function(e){var t=e.boxes().getOr([]);return 0<t.length?(o(n,t,e.start(),e.finish()),R.some(rl.create(R.some(il.makeSitus(r,0,r,yn(r))),!0))):R.none()})},al={sync:function(n,r,e,t,o,i,u){return ft(e,o)&&t===i?R.none():Zt(e,"td,th",r).bind(function(t){return Zt(o,"td,th",r).bind(function(e){return ul(n,r,t,e,u)})})},detect:ul,update:function(e,t,n,r,o){return mr(r,e,t,o.firstSelectedSelector(),o.lastSelectedSelector()).map(function(e){return o.clear(n),o.selectRange(n,e.boxes(),e.start(),e.finish()),e.boxes()})}},cl=q("item","mode"),ll=function(e,t,n,r){return void 0===r&&(r=fl),e.property().parent(t).map(function(e){return cl(e,r)})},fl=function(e,t,n,r){return void 0===r&&(r=sl),n.sibling(e,t).map(function(e){return cl(e,r)})},sl=function(e,t,n,r){void 0===r&&(r=sl);var o=e.property().children(t);return n.first(o).map(function(e){return cl(e,r)})},dl=[{current:ll,next:fl,fallback:R.none()},{current:fl,next:sl,fallback:R.some(ll)},{current:sl,next:sl,fallback:R.some(fl)}],ml=function(t,n,r,o,e){return void 0===e&&(e=dl),B(e,function(e){return e.current===r}).bind(function(e){return e.current(t,n,o,e.next).orThunk(function(){return e.fallback.bind(function(e){return ml(t,n,e,o)})})})},gl=function(){return{sibling:function(e,t){return e.query().prevSibling(t)},first:function(e){return 0<e.length?R.some(e[e.length-1]):R.none()}}},hl=function(){return{sibling:function(e,t){return e.query().nextSibling(t)},first:function(e){return 0<e.length?R.some(e[0]):R.none()}}},pl=function(t,e,n,r,o,i){return ml(t,e,r,o).bind(function(e){return i(e.item())?R.none():n(e.item())?R.some(e.item()):pl(t,e.item(),n,e.mode(),o,i)})},vl=function(t){return function(e){return 0===t.property().children(e).length}},bl=function(e,t,n,r){return pl(e,t,n,fl,gl(),r)},wl=function(e,t,n,r){return pl(e,t,n,fl,hl(),r)},yl=$n(),xl=function(e,t){return r=t,bl(n=yl,e,vl(n),r);var n,r},Cl=function(e,t){return r=t,wl(n=yl,e,vl(n),r);var n,r},Rl=q("element","offset"),Sl=(q("element","deltaOffset"),q("element","start","finish"),q("begin","end"),q("element","text"),xr([{none:["message"]},{success:[]},{failedUp:["cell"]},{failedDown:["cell"]}])),Tl=function(e){return Zt(e,"tr")},Dl=wo(wo({},Sl),{verify:function(a,e,t,n,r,c,o){return Zt(n,"td,th",o).bind(function(u){return Zt(e,"td,th",o).map(function(i){return ft(u,i)?ft(n,u)&&yn(u)===r?c(i):Sl.none("in same cell"):ar.sharedOne(Tl,[u,i]).fold(function(){return t=i,n=u,r=(e=a).getRect(t),(o=e.getRect(n)).right>r.left&&o.left<r.right?Sl.success():c(i);var e,t,n,r,o},function(e){return c(i)})})}).getOr(Sl.none("default"))},cata:function(e,t,n,r,o){return e.fold(t,n,r,o)}}),Ol=(q("ancestor","descendants","element","index"),q("parent","children","element","index")),Nl=function(e,t){return W(e,b(ft,t))},El=function(e){return"br"===oe(e)},kl=function(e,t,n){return t(e,n).bind(function(e){return le(e)&&0===vn(e).trim().length?kl(e,t,n):R.some(e)})},Al=function(t,e,n,r){return(o=e,i=n,wt(o,i).filter(El).orThunk(function(){return wt(o,i-1).filter(El)})).bind(function(e){return r.traverse(e).fold(function(){return kl(e,r.gather,t).map(r.relative)},function(e){return(r=e,gt(r).bind(function(t){var n=bt(t);return Nl(n,r).map(function(e){return Ol(t,n,r,e)})})).map(function(e){return pc.on(e.parent(),e.index())});var r})});var o,i},Pl=function(e,t,n,r){var o,i,u;return(El(t)?(o=e,i=t,(u=r).traverse(i).orThunk(function(){return kl(i,u.gather,o)}).map(u.relative)):Al(e,t,n,r)).map(function(e){return{start:C(e),finish:C(e)}})},Il=function(e){return Dl.cata(e,function(e){return R.none()},function(){return R.none()},function(e){return R.some(Rl(e,0))},function(e){return R.some(Rl(e,yn(e)))})},Bl=J(["left","top","right","bottom"],[]),Wl={nu:Bl,moveUp:function(e,t){return Bl({left:e.left(),top:e.top()-t,right:e.right(),bottom:e.bottom()-t})},moveDown:function(e,t){return Bl({left:e.left(),top:e.top()+t,right:e.right(),bottom:e.bottom()+t})},moveBottomTo:function(e,t){var n=e.bottom()-e.top();return Bl({left:e.left(),top:t-n,right:e.right(),bottom:t})},moveTopTo:function(e,t){var n=e.bottom()-e.top();return Bl({left:e.left(),top:t,right:e.right(),bottom:t+n})},getTop:function(e){return e.top()},getBottom:function(e){return e.bottom()},translate:function(e,t,n){return Bl({left:e.left()+t,top:e.top()+n,right:e.right()+t,bottom:e.bottom()+n})},toString:function(e){return"("+e.left()+", "+e.top()+") -> ("+e.right()+", "+e.bottom()+")"}},Ml=function(e){return Wl.nu({left:e.left,top:e.top,right:e.right,bottom:e.bottom})},_l=function(e,t){return R.some(e.getRect(t))},Ll=function(e,t,n){return ce(t)?_l(e,t).map(Ml):le(t)?(r=e,o=t,i=n,0<=i&&i<yn(o)?r.getRangedRect(o,i,o,i+1):0<i?r.getRangedRect(o,i-1,o,i):R.none()).map(Ml):R.none();var r,o,i},Fl=function(e,t){return ce(t)?_l(e,t).map(Ml):le(t)?e.getRangedRect(t,0,t,yn(t)).map(Ml):R.none()},jl=xr([{none:[]},{retry:["caret"]}]),zl=function(t,e,r){return(n=e,o=Tu,Vt(function(e,t){return t(e)},Kt,n,o,i)).fold(C(!1),function(e){return Fl(t,e).exists(function(e){return n=e,(t=r).left()<n.left()||Math.abs(n.right()-t.left())<1||t.left()>n.right();var t,n})});var n,o,i},Hl={point:Wl.getTop,adjuster:function(e,t,n,r,o){var i=Wl.moveUp(o,5);return Math.abs(n.top()-r.top())<1?jl.retry(i):n.bottom()<o.top()?jl.retry(i):n.bottom()===o.top()?jl.retry(Wl.moveUp(o,1)):zl(e,t,o)?jl.retry(Wl.translate(i,5,0)):jl.none()},move:Wl.moveUp,gather:xl},Ul={point:Wl.getBottom,adjuster:function(e,t,n,r,o){var i=Wl.moveDown(o,5);return Math.abs(n.bottom()-r.bottom())<1?jl.retry(i):n.top()>o.bottom()?jl.retry(i):n.top()===o.bottom()?jl.retry(Wl.moveDown(o,1)):zl(e,t,o)?jl.retry(Wl.translate(i,5,0)):jl.none()},move:Wl.moveDown,gather:Cl},ql=function(n,r,o,i,u){return 0===u?R.some(i):(c=n,l=i.left(),f=r.point(i),c.elementFromPoint(l,f).filter(function(e){return"table"===oe(e)}).isSome()?(t=i,a=u-1,ql(n,e=r,o,e.move(t,5),a)):n.situsFromPoint(i.left(),r.point(i)).bind(function(e){return e.start().fold(R.none,function(t){return Fl(n,t).bind(function(e){return r.adjuster(n,t,e,o,i).fold(R.none,function(e){return ql(n,r,o,e,u-1)})}).orThunk(function(){return R.some(i)})},R.none)}));var e,t,a,c,l,f},Vl=function(t,n,e){var r,o,i,u=t.move(e,5),a=ql(n,t,e,u,100).getOr(u);return(r=t,o=a,i=n,r.point(o)>i.getInnerHeight()?R.some(r.point(o)-i.getInnerHeight()):r.point(o)<0?R.some(-r.point(o)):R.none()).fold(function(){return n.situsFromPoint(a.left(),t.point(a))},function(e){return n.scrollBy(0,e),n.situsFromPoint(a.left(),t.point(a)-e)})},Gl={tryUp:b(Vl,Hl),tryDown:b(Vl,Ul),ieTryUp:function(e,t){return e.situsFromPoint(t.left(),t.top()-5)},ieTryDown:function(e,t){return e.situsFromPoint(t.left(),t.bottom()+5)},getJumpSize:C(5)},Yl=it.detect(),Xl=function(r,o,i,u,a,c){return 0===c?R.none():$l(r,o,i,u,a).bind(function(e){var t=r.fromSitus(e),n=Dl.verify(r,i,u,t.finish(),t.foffset(),a.failure,o);return Dl.cata(n,function(){return R.none()},function(){return R.some(e)},function(e){return ft(i,e)&&0===u?Kl(r,i,u,Wl.moveUp,a):Xl(r,o,e,0,a,c-1)},function(e){return ft(i,e)&&u===yn(e)?Kl(r,i,u,Wl.moveDown,a):Xl(r,o,e,yn(e),a,c-1)})})},Kl=function(t,e,n,r,o){return Ll(t,e,n).bind(function(e){return Jl(t,o,r(e,Gl.getJumpSize()))})},Jl=function(e,t,n){return Yl.browser.isChrome()||Yl.browser.isSafari()||Yl.browser.isFirefox()||Yl.browser.isEdge()?t.otherRetry(e,n):Yl.browser.isIE()?t.ieRetry(e,n):R.none()},$l=function(t,e,n,r,o){return Ll(t,n,r).bind(function(e){return Jl(t,o,e)})},Ql=function(t,n,r){return(o=t,i=n,u=r,o.getSelection().bind(function(r){return Pl(i,r.finish(),r.foffset(),u).fold(function(){return R.some(Rl(r.finish(),r.foffset()))},function(e){var t=o.fromSitus(e),n=Dl.verify(o,r.finish(),r.foffset(),t.finish(),t.foffset(),u.failure,i);return Il(n)})})).bind(function(e){return Xl(t,n,e.element(),e.offset(),r,20).map(t.fromSitus)});var o,i,u},Zl=it.detect(),ef=function(e,t){return Kt(e,function(e){return gt(e).exists(function(e){return ft(e,t)})},n).isSome();var n},tf=function(t,r,o,e,i){return Zt(e,"td,th",r).bind(function(n){return Zt(n,"table",r).bind(function(e){return ef(i,e)?Ql(t,r,o).bind(function(t){return Zt(t.finish(),"td,th",r).map(function(e){return{start:C(n),finish:C(e),range:C(t)}})}):R.none()})})},nf=function(e,t,n,r,o,i){return Zl.browser.isIE()?R.none():i(r,t).orThunk(function(){return tf(e,t,n,r,o).map(function(e){var t=e.range();return rl.create(R.some(il.makeSitus(t.start(),t.soffset(),t.finish(),t.foffset())),!0)})})},rf=function(e,t,n,r,o,i,u){return tf(e,n,r,o,i).bind(function(e){return al.detect(t,n,e.start(),e.finish(),u)})},of=function(e,u){return Zt(e,"tr",u).bind(function(i){return Zt(i,"table",u).bind(function(e){var t,n,r,o=qt(e,"tr");return ft(i,o[0])?(t=e,n=function(e){return Sn(e).isSome()},r=u,bl(yl,t,n,r)).map(function(e){var t=yn(e);return rl.create(R.some(il.makeSitus(e,t,e,t)),!0)}):R.none()})})},uf=function(e,u){return Zt(e,"tr",u).bind(function(i){return Zt(i,"table",u).bind(function(e){var t,n,r,o=qt(e,"tr");return ft(i,o[o.length-1])?(t=e,n=function(e){return Rn(e).isSome()},r=u,wl(yl,t,n,r)).map(function(e){return rl.create(R.some(il.makeSitus(e,0,e,0)),!0)}):R.none()})})},af=function(e,t){return Zt(e,"td,th",t)},cf={down:{traverse:vt,gather:Cl,relative:pc.before,otherRetry:Gl.tryDown,ieRetry:Gl.ieTryDown,failure:Dl.failedDown},up:{traverse:pt,gather:xl,relative:pc.before,otherRetry:Gl.tryUp,ieRetry:Gl.ieTryUp,failure:Dl.failedUp}},lf=function(t){return function(e){return e===t}},ff=lf(38),sf=lf(40),df={ltr:{isBackward:lf(37),isForward:lf(39)},rtl:{isBackward:lf(39),isForward:lf(37)},isUp:ff,isDown:sf,isNavigation:function(e){return 37<=e&&e<=40}},mf=function(e){return{left:e.left(),top:e.top(),right:e.right(),bottom:e.bottom(),width:e.width(),height:e.height()}},gf=(it.detect().browser.isSafari(),function(a){return{elementFromPoint:function(e,t){return xe.fromPoint(xe.fromDom(a.document),e,t)},getRect:function(e){return e.dom().getBoundingClientRect()},getRangedRect:function(e,t,n,r){var o=bc.exact(e,t,n,r);return Yc(a,o).map(mf)},getSelection:function(){return Gc(a).map(function(e){return il.convertToRange(a,e)})},fromSitus:function(e){var t=bc.relative(e.start(),e.finish());return il.convertToRange(a,t)},situsFromPoint:function(e,t){return Xc(a,e,t).map(function(e){return ol(e.start(),e.soffset(),e.finish(),e.foffset())})},clearSelection:function(){a.getSelection().removeAllRanges()},setSelection:function(e){var t,n,r,o,i,u;t=a,n=e.start(),r=e.soffset(),o=e.finish(),i=e.foffset(),u=Lc(n,r,o,i),Hc(t,u)},setRelativeSelection:function(e,t){var n,r;n=a,r=_c(e,t),Hc(n,r)},selectContents:function(e){Vc(a,e)},getInnerHeight:function(){return a.innerHeight},getScrollY:function(){var e,t,n,r;return(e=xe.fromDom(a.document),t=e!==undefined?e.dom():m.document,n=t.body.scrollLeft||t.documentElement.scrollLeft,r=t.body.scrollTop||t.documentElement.scrollTop,eo(n,r)).top()},scrollBy:function(e,t){var n,r,o;n=e,r=t,((o=xe.fromDom(a.document))!==undefined?o.dom():m.document).defaultView.scrollBy(n,r)}}}),hf=q("rows","cols"),pf={mouse:function(e,t,n,r){var o,i,u,a,c,l,f=gf(e),s=(o=f,i=t,u=n,a=r,c=R.none(),l=function(){c=R.none()},{mousedown:function(e){a.clear(i),c=af(e.target(),u)},mouseover:function(e){c.each(function(r){a.clear(i),af(e.target(),u).each(function(n){sr(r,n,u).each(function(e){var t=e.boxes().getOr([]);(1<t.length||1===t.length&&!ft(r,n))&&(a.selectRange(i,t,e.start(),e.finish()),o.selectContents(n))})})})},mouseup:function(e){c.each(l)}});return{mousedown:s.mousedown,mouseover:s.mouseover,mouseup:s.mouseup}},keyboard:function(e,l,f,s){var d=gf(e),m=function(){return s.clear(l),R.none()};return{keydown:function(e,t,n,r,o,i){var u=e.raw(),a=u.which,c=!0===u.shiftKey;return dr(l,s.selectedSelector()).fold(function(){return df.isDown(a)&&c?b(rf,d,l,f,cf.down,r,t,s.selectRange):df.isUp(a)&&c?b(rf,d,l,f,cf.up,r,t,s.selectRange):df.isDown(a)?b(nf,d,f,cf.down,r,t,uf):df.isUp(a)?b(nf,d,f,cf.up,r,t,of):R.none},function(t){var e=function(e){return function(){return xo(e,function(e){return al.update(e.rows(),e.cols(),l,t,s)}).fold(function(){return gr(l,s.firstSelectedSelector(),s.lastSelectedSelector()).map(function(e){var t=df.isDown(a)||i.isForward(a)?pc.after:pc.before;return d.setRelativeSelection(pc.on(e.first(),0),t(e.table())),s.clear(l),rl.create(R.none(),!0)})},function(e){return R.some(rl.create(R.none(),!0))})}};return df.isDown(a)&&c?e([hf(1,0)]):df.isUp(a)&&c?e([hf(-1,0)]):i.isBackward(a)&&c?e([hf(0,-1),hf(-1,0)]):i.isForward(a)&&c?e([hf(0,1),hf(1,0)]):df.isNavigation(a)&&!1===c?m:R.none})()},keyup:function(n,r,o,i,u){return dr(l,s.selectedSelector()).fold(function(){var e=n.raw(),t=e.which;return 0==(!0===e.shiftKey)?R.none():df.isNavigation(t)?al.sync(l,f,r,o,i,u,s.selectRange):R.none()},R.none)}}}},vf=function(r,e){k(e,function(e){var t,n;n=e,Do(t=r)?t.dom().classList.remove(n):No(t,n),ko(t)})},bf={byClass:function(o){var t,n,i=(t=o.selected(),function(e){Eo(e,t)}),r=(n=[o.selected(),o.lastSelected(),o.firstSelected()],function(e){vf(e,n)}),u=function(e){var t=qt(e,o.selectedSelector());k(t,r)};return{clear:u,selectRange:function(e,t,n,r){u(e),k(t,i),Eo(n,o.firstSelected()),Eo(r,o.lastSelected())},selectedSelector:o.selectedSelector,firstSelectedSelector:o.firstSelectedSelector,lastSelectedSelector:o.lastSelectedSelector}},byAttr:function(o){var n=function(e){he(e,o.selected()),he(e,o.firstSelected()),he(e,o.lastSelected())},i=function(e){se(e,o.selected(),"1")},u=function(e){var t=qt(e,o.selectedSelector());k(t,n)};return{clear:u,selectRange:function(e,t,n,r){u(e),k(t,i),se(n,o.firstSelected(),"1"),se(r,o.lastSelected(),"1")},selectedSelector:o.selectedSelector,firstSelectedSelector:o.firstSelectedSelector,lastSelectedSelector:o.lastSelectedSelector}}},wf=function(e){return!1===Ao(xe.fromDom(e.target),"ephox-snooker-resizer-bar")};function yf(h,p){var v=J(["mousedown","mouseover","mouseup","keyup","keydown"],[]),b=R.none(),w=bf.byAttr(yr);return h.on("init",function(e){var r=h.getWin(),o=Fu(h),t=Hu(h),n=pf.mouse(r,o,t,w),a=pf.keyboard(r,o,t,w),c=function(e,t){!0===e.raw().shiftKey&&(t.kill()&&e.kill(),t.selection().each(function(e){var t=bc.relative(e.start(),e.finish()),n=Dc(r,t);h.selection.setRng(n)}))},i=function(e){var t=f(e);if(t.raw().shiftKey&&df.isNavigation(t.raw().which)){var n=h.selection.getRng(),r=xe.fromDom(n.startContainer),o=xe.fromDom(n.endContainer);a.keyup(t,r,n.startOffset,o,n.endOffset).each(function(e){c(t,e)})}},u=function(e){var t=f(e);p().each(function(e){e.hideBars()});var n=h.selection.getRng(),r=xe.fromDom(h.selection.getStart()),o=xe.fromDom(n.startContainer),i=xe.fromDom(n.endContainer),u=Yu.directionAt(r).isRtl()?df.rtl:df.ltr;a.keydown(t,o,n.startOffset,i,n.endOffset,u).each(function(e){c(t,e)}),p().each(function(e){e.showBars()})},l=function(e){return e.hasOwnProperty("x")&&e.hasOwnProperty("y")},f=function(e){var t=xe.fromDom(e.target),n=function(){e.stopPropagation()},r=function(){e.preventDefault()},o=x(r,n);return{target:C(t),x:C(l(e)?e.x:null),y:C(l(e)?e.y:null),stop:n,prevent:r,kill:o,raw:C(e)}},s=function(e){return 0===e.button},d=function(e){s(e)&&wf(e)&&n.mousedown(f(e))},m=function(e){var t;((t=e).buttons===undefined||Sa.ie&&12<=Sa.ie&&0===t.buttons||0!=(1&t.buttons))&&wf(e)&&n.mouseover(f(e))},g=function(e){s(e)&&wf(e)&&n.mouseup(f(e))};h.on("mousedown",d),h.on("mouseover",m),h.on("mouseup",g),h.on("keyup",i),h.on("keydown",u),h.on("nodechange",function(){var e=h.selection,t=xe.fromDom(e.getStart()),n=xe.fromDom(e.getEnd());ar.sharedOne(cn.table,[t,n]).fold(function(){w.clear(o)},y)}),b=R.some(v({mousedown:d,mouseover:m,mouseup:g,keyup:i,keydown:u}))}),{clear:w.clear,destroy:function(){b.each(function(e){})}}}var xf=sa.each,Cf=function(t){var n=[];function e(e){return function(){t.execCommand(e)}}xf("inserttable tableprops deletetable | cell row column".split(" "),function(e){"|"===e?n.push({text:"-"}):n.push(t.menuItems[e])}),t.addButton("table",{type:"menubutton",title:"Table",menu:n}),t.addButton("tableprops",{title:"Table properties",onclick:e("mceTableProps"),icon:"table"}),t.addButton("tabledelete",{title:"Delete table",onclick:e("mceTableDelete")}),t.addButton("tablecellprops",{title:"Cell properties",onclick:e("mceTableCellProps")}),t.addButton("tablemergecells",{title:"Merge cells",onclick:e("mceTableMergeCells")}),t.addButton("tablesplitcells",{title:"Split cell",onclick:e("mceTableSplitCells")}),t.addButton("tableinsertrowbefore",{title:"Insert row before",onclick:e("mceTableInsertRowBefore")}),t.addButton("tableinsertrowafter",{title:"Insert row after",onclick:e("mceTableInsertRowAfter")}),t.addButton("tabledeleterow",{title:"Delete row",onclick:e("mceTableDeleteRow")}),t.addButton("tablerowprops",{title:"Row properties",onclick:e("mceTableRowProps")}),t.addButton("tablecutrow",{title:"Cut row",onclick:e("mceTableCutRow")}),t.addButton("tablecopyrow",{title:"Copy row",onclick:e("mceTableCopyRow")}),t.addButton("tablepasterowbefore",{title:"Paste row before",onclick:e("mceTablePasteRowBefore")}),t.addButton("tablepasterowafter",{title:"Paste row after",onclick:e("mceTablePasteRowAfter")}),t.addButton("tableinsertcolbefore",{title:"Insert column before",onclick:e("mceTableInsertColBefore")}),t.addButton("tableinsertcolafter",{title:"Insert column after",onclick:e("mceTableInsertColAfter")}),t.addButton("tabledeletecol",{title:"Delete column",onclick:e("mceTableDeleteCol")})},Rf=function(t){var e,n=""===(e=t.getParam("table_toolbar",Xu))||!1===e?[]:d(e)?e.split(/[ ,]/):h(e)?e:[];0<n.length&&t.addContextToolbar(function(e){return t.dom.is(e,"table")&&t.getBody().contains(e)},n.join(" "))},Sf=function(o,n){var r=R.none(),i=[],u=[],a=[],c=[],l=function(e){e.disabled(!0)},f=function(e){e.disabled(!1)},e=function(){var t=this;i.push(t),r.fold(function(){l(t)},function(e){f(t)})},t=function(){var t=this;u.push(t),r.fold(function(){l(t)},function(e){f(t)})};o.on("init",function(){o.on("nodechange",function(e){var t=R.from(o.dom.getParent(o.selection.getStart(),"th,td"));(r=t.bind(function(e){var t=xe.fromDom(e);return cn.table(t).map(function(e){return kr.forMenu(n,e,t)})})).fold(function(){k(i,l),k(u,l),k(a,l),k(c,l)},function(t){k(i,f),k(u,f),k(a,function(e){e.disabled(t.mergable().isNone())}),k(c,function(e){e.disabled(t.unmergable().isNone())})})})});var s=function(e,t,n,r){var o,i,u,a,c,l=r.getEl().getElementsByTagName("table")[0],f=r.isRtl()||"tl-tr"===r.parent().rel;for(l.nextSibling.innerHTML=t+1+" x "+(n+1),f&&(t=9-t),i=0;i<10;i++)for(o=0;o<10;o++)a=l.rows[i].childNodes[o].firstChild,c=(f?t<=o:o<=t)&&i<=n,e.dom.toggleClass(a,"mce-active",c),c&&(u=a);return u.parentNode},d=!1===o.getParam("table_grid",!0,"boolean")?{text:"Table",icon:"table",context:"table",onclick:m("mceInsertTable")}:{text:"Table",icon:"table",context:"table",ariaHideMenu:!0,onclick:function(e){e.aria&&(this.parent().hideAll(),e.stopImmediatePropagation(),o.execCommand("mceInsertTable"))},onshow:function(){s(o,0,0,this.menu.items()[0])},onhide:function(){var e=this.menu.items()[0].getEl().getElementsByTagName("a");o.dom.removeClass(e,"mce-active"),o.dom.addClass(e[0],"mce-active")},menu:[{type:"container",html:function(){var e="";e='<table role="grid" class="mce-grid mce-grid-border" aria-readonly="true">';for(var t=0;t<10;t++){e+="<tr>";for(var n=0;n<10;n++)e+='<td role="gridcell" tabindex="-1"><a id="mcegrid'+(10*t+n)+'" href="#" data-mce-x="'+n+'" data-mce-y="'+t+'"></a></td>';e+="</tr>"}return e+="</table>",e+='<div class="mce-text-center" role="presentation">1 x 1</div>'}(),onPostRender:function(){this.lastX=this.lastY=0},onmousemove:function(e){var t,n,r=e.target;"A"===r.tagName.toUpperCase()&&(t=parseInt(r.getAttribute("data-mce-x"),10),n=parseInt(r.getAttribute("data-mce-y"),10),(this.isRtl()||"tl-tr"===this.parent().rel)&&(t=9-t),t===this.lastX&&n===this.lastY||(s(o,t,n,e.control),this.lastX=t,this.lastY=n))},onclick:function(e){var t=this;"A"===e.target.tagName.toUpperCase()&&(e.preventDefault(),e.stopPropagation(),t.parent().cancel(),o.undoManager.transact(function(){Na(o,t.lastX+1,t.lastY+1)}),o.addVisual())}}]};function m(e){return function(){o.execCommand(e)}}var g={text:"Table properties",context:"table",onPostRender:e,onclick:m("mceTableProps")},h={text:"Delete table",context:"table",onPostRender:e,cmd:"mceTableDelete"},p={text:"Row",context:"table",menu:[{text:"Insert row before",onclick:m("mceTableInsertRowBefore"),onPostRender:t},{text:"Insert row after",onclick:m("mceTableInsertRowAfter"),onPostRender:t},{text:"Delete row",onclick:m("mceTableDeleteRow"),onPostRender:t},{text:"Row properties",onclick:m("mceTableRowProps"),onPostRender:t},{text:"-"},{text:"Cut row",onclick:m("mceTableCutRow"),onPostRender:t},{text:"Copy row",onclick:m("mceTableCopyRow"),onPostRender:t},{text:"Paste row before",onclick:m("mceTablePasteRowBefore"),onPostRender:t},{text:"Paste row after",onclick:m("mceTablePasteRowAfter"),onPostRender:t}]},v={text:"Column",context:"table",menu:[{text:"Insert column before",onclick:m("mceTableInsertColBefore"),onPostRender:t},{text:"Insert column after",onclick:m("mceTableInsertColAfter"),onPostRender:t},{text:"Delete column",onclick:m("mceTableDeleteCol"),onPostRender:t}]},b={separator:"before",text:"Cell",context:"table",menu:[{text:"Cell properties",onclick:m("mceTableCellProps"),onPostRender:t},{text:"Merge cells",onclick:m("mceTableMergeCells"),onPostRender:function(){var t=this;a.push(t),r.fold(function(){l(t)},function(e){t.disabled(e.mergable().isNone())})}},{text:"Split cell",onclick:m("mceTableSplitCells"),onPostRender:function(){var t=this;c.push(t),r.fold(function(){l(t)},function(e){t.disabled(e.unmergable().isNone())})}}]};o.addMenuItem("inserttable",d),o.addMenuItem("tableprops",g),o.addMenuItem("deletetable",h),o.addMenuItem("row",p),o.addMenuItem("column",v),o.addMenuItem("cell",b)},Tf=function(n,r){return{insertTable:function(e,t){return Na(n,e,t)},setClipboardRows:function(e){return t=r,n=E(e,xe.fromDom),void t.set(R.from(n));var t,n},getClipboardRows:function(){return r.get().fold(function(){},function(e){return E(e,function(e){return e.dom()})})}}};e.add("table",function(t){var n,r=cc(t),e=yf(t,r.lazyResize),o=la(t,r.lazyWire),i=(n=t,{get:function(){var e=Fu(n);return hr(e,yr.selectedSelector()).fold(function(){return n.selection.getStart()===undefined?Rr.none():Rr.single(n.selection)},function(e){return Rr.multiple(e)})}}),u=lu(R.none());return Ba(t,o,e,i,u),Ar(t,i,o,e),Sf(t,i),Cf(t),Rf(t),t.on("PreInit",function(){t.serializer.addTempAttr(yr.firstSelected()),t.serializer.addTempAttr(yr.lastSelected())}),t.getParam("table_tab_navigation",!0,"boolean")&&t.on("keydown",function(e){nl(e,t,o,r.lazyWire)}),t.on("remove",function(){r.destroy(),e.destroy()}),Tf(t,u)})}(window);
\ No newline at end of file
index c94da1655a08f371c266609f29e66af5fb32f500..03f872a74e3a623f0a7248fb66bcae3fc124ab2d 100644 (file)
@@ -1 +1 @@
-!function(l){"use strict";var t,n,e,r,a,o=function(t){var n=t,e=function(){return n};return{get:e,set:function(t){n=t},clone:function(){return o(e())}}},i=tinymce.util.Tools.resolve("tinymce.PluginManager"),f=function(t){return function(){return t}},u=f(!1),s=f(!0),c=u,d=s,g=function(){return m},m=(r={fold:function(t,n){return t()},is:c,isSome:c,isNone:d,getOr:e=function(t){return t},getOrThunk:n=function(t){return t()},getOrDie:function(t){throw new Error(t||"error: getOrDie called on none.")},getOrNull:function(){return null},getOrUndefined:function(){return undefined},or:e,orThunk:n,map:g,ap:g,each:function(){},bind:g,flatten:g,exists:c,forall:d,filter:g,equals:t=function(t){return t.isNone()},equals_:t,toArray:function(){return[]},toString:f("none()")},Object.freeze&&Object.freeze(r),r),h=function(e){var t=function(){return e},n=function(){return a},r=function(t){return t(e)},a={fold:function(t,n){return n(e)},is:function(t){return e===t},isSome:d,isNone:c,getOr:t,getOrThunk:t,getOrDie:t,getOrNull:t,getOrUndefined:t,or:n,orThunk:n,map:function(t){return h(t(e))},ap:function(t){return t.fold(g,function(t){return h(t(e))})},each:function(t){t(e)},bind:r,flatten:t,exists:r,forall:r,filter:function(t){return t(e)?a:m},equals:function(t){return t.is(e)},equals_:function(t,n){return t.fold(c,function(t){return n(e,t)})},toArray:function(){return[e]},toString:function(){return"some("+e+")"}};return a},p={some:h,none:g,from:function(t){return null===t||t===undefined?m:h(t)}},v=(a="function",function(t){return function(t){if(null===t)return"null";var n=typeof t;return"object"===n&&Array.prototype.isPrototypeOf(t)?"array":"object"===n&&String.prototype.isPrototypeOf(t)?"string":n}(t)===a}),O=function(t,n){for(var e=[],r=0,a=t.length;r<a;r++){var o=t[r];n(o,r,t)&&e.push(o)}return e},y=Array.prototype.slice,P=(v(Array.from)&&Array.from,Object.hasOwnProperty),x=function(t,n){return P.call(t,n)},T=function(t){return x(t,"start")&&x(t,"end")},b=function(t){return!x(t,"end")&&!x(t,"replacement")},k=function(t){return x(t,"replacement")},C=function(t){return n=t,e=function(t,n){return t.start.length===n.start.length?0:t.start.length>n.start.length?-1:1},(r=y.call(n,0)).sort(e),r;var n,e,r},D=function(t){return{inlinePatterns:C(O(t,T)),blockPatterns:C(O(t,b)),replacementPatterns:O(t,k)}},S=function(n){return{setPatterns:function(t){n.set(D(t))},getPatterns:function(){return n.get().inlinePatterns.concat(n.get().blockPatterns,n.get().replacementPatterns)}}},A=[{start:"*",end:"*",format:"italic"},{start:"**",end:"**",format:"bold"},{start:"***",end:"***",format:["bold","italic"]},{start:"#",format:"h1"},{start:"##",format:"h2"},{start:"###",format:"h3"},{start:"####",format:"h4"},{start:"#####",format:"h5"},{start:"######",format:"h6"},{start:"1. ",cmd:"InsertOrderedList"},{start:"* ",cmd:"InsertUnorderedList"},{start:"- ",cmd:"InsertUnorderedList"}],N=function(t){var n,e,r=(n=t,e="textpattern_patterns",x(n,e)?p.from(n[e]):p.none()).getOr(A);return D(r)},R=tinymce.util.Tools.resolve("tinymce.util.Delay"),w=tinymce.util.Tools.resolve("tinymce.util.VK"),I=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),j=tinymce.util.Tools.resolve("tinymce.util.Tools"),E=function(t,n){for(var e=0;e<t.length;e++){var r=t[e];if(0===n.indexOf(r.start)&&(!r.end||n.lastIndexOf(r.end)===n.length-r.end.length))return r}},q=function(t,n,e){if(!1!==n.collapsed){var r=n.startContainer,a=r.data,o=!0===e?1:0;if(3===r.nodeType){var i=function(t,n,e,r){var a,o,i,f,u,s;for(o=0;o<t.length;o++)if((a=t[o]).end!==undefined&&(f=a,u=e,s=r,n.substr(u-f.end.length-s,f.end.length)===f.end)&&0<e-r-(i=a).end.length-i.start.length)return a}(t,a,n.startOffset,o);if(i!==undefined){var f=a.lastIndexOf(i.end,n.startOffset-o),u=a.lastIndexOf(i.start,f-i.end.length);if(f=a.indexOf(i.end,u+i.start.length),-1!==u){var s=l.document.createRange();s.setStart(r,u),s.setEnd(r,f+i.end.length);var c=E(t,s.toString());if(!(i===undefined||c!==i||r.data.length<=i.start.length+i.end.length))return{pattern:i,startOffset:u,endOffset:f}}}}}},L=function(t){return t&&3===t.nodeType},M=function(t,n,e){var r=t.dom.createRng();r.setStart(n,e),r.setEnd(n,e),t.selection.setRng(r)},U=function(n,t,e){var r=n.selection.getRng();return p.from(q(t,r,e)).map(function(t){return function(a,o,i,f){var u=j.isArray(i.pattern.format)?i.pattern.format:[i.pattern.format];if(0!==j.grep(u,function(t){var n=a.formatter.get(t);return n&&n[0].inline}).length)return a.undoManager.transact(function(){var t,n,e,r;t=o,n=i.pattern,e=i.endOffset,r=i.startOffset,(t=0<r?t.splitText(r):t).splitText(e-r+n.end.length),t.deleteData(0,n.start.length),t.deleteData(t.data.length-n.end.length,n.end.length),o=t,f&&a.selection.setCursorLocation(o.nextSibling,1),u.forEach(function(t){a.formatter.apply(t,{},o)})}),o}(n,r.startContainer,t,e)})},_=function(s,t,c){var n=s.selection.getRng(),l=n.startContainer;n.collapsed&&L(l)&&function(t,n,e){for(var r=0;r<t.length;r++){var a=e.lastIndexOf(t[r].start,n);if(-1!==a)return p.some({pattern:t[r],startOffset:a})}return p.none()}(t,n.startOffset,l.data).each(function(t){var n,e,r,a,o,i,f,u=c?p.some((n=l,r=(e=t).startOffset+e.pattern.start.length,a=n.data.slice(r,r+1),n.deleteData(r,1),a)):p.none();o=s,f=t,(i=l).deleteData(f.startOffset,f.pattern.start.length),o.insertContent(f.pattern.replacement),p.from(i.nextSibling).filter(L).each(function(t){t.insertData(0,i.data),o.dom.remove(i)}),u.each(function(t){return function(t,n){var e=t.selection.getRng(),r=e.startContainer;if(L(r)){var a=e.startOffset;r.insertData(a,n),M(t,r,a+n.length)}else{var o=t.dom.doc.createTextNode(n);e.insertNode(o),M(t,o,o.length)}}(s,t)})})},z=function(t,n,e){for(var r=0;r<t.length;r++)if(e(t[r],n))return!0},K=function(t,n){var e,r,a,o;e=t,r=n.replacementPatterns,_(e,r,!1),a=t,o=n.inlinePatterns,U(a,o,!1).each(function(t){M(a,t,t.data.length)}),function(t,n){var e,r,a,o,i,f,u,s,c,l,d;if(e=t.selection,r=t.dom,e.isCollapsed()&&(u=r.getParent(e.getStart(),"p"))){for(c=new I(u,u);i=c.next();)if(L(i)){o=i;break}if(o){if(!(s=E(n,o.data)))return;if(a=(l=e.getRng(!0)).startContainer,d=l.startOffset,o===a&&(d=Math.max(0,d-s.start.length)),j.trim(o.data).length===s.start.length)return;s.format&&(f=t.formatter.get(s.format))&&f[0].block&&(o.deleteData(0,s.start.length),t.formatter.apply(s.format,{},o),l.setStart(a,d),l.collapse(!0),e.setRng(l)),s.cmd&&t.undoManager.transact(function(){o.deleteData(0,s.start.length),t.execCommand(s.cmd)})}}}(t,n.blockPatterns)},V=function(t,n){var e,r,a,o;e=t,r=n.replacementPatterns,_(e,r,!0),a=t,o=n.inlinePatterns,U(a,o,!0).each(function(t){var n=t.data.slice(-1);if(/[\u00a0 ]/.test(n)){t.deleteData(t.data.length-1,1);var e=a.dom.doc.createTextNode(n);a.dom.insertAfter(e,t.parentNode),M(a,e,1)}})},W=function(t,n){return z(t,n,function(t,n){return t.charCodeAt(0)===n.charCode})},B=function(t,n){return z(t,n,function(t,n){return t===n.keyCode&&!1===w.modifierPressed(n)})},F=function(n,e){var r=[",",".",";",":","!","?"],a=[32];n.on("keydown",function(t){13!==t.keyCode||w.modifierPressed(t)||K(n,e.get())},!0),n.on("keyup",function(t){B(a,t)&&V(n,e.get())}),n.on("keypress",function(t){W(r,t)&&R.setEditorTimeout(n,function(){V(n,e.get())})})};i.add("textpattern",function(t){var n=o(N(t.settings));return F(t,n),S(n)})}(window);
\ No newline at end of file
+!function(l){"use strict";var t,n,e,r,a,o=function(t){var n=t,e=function(){return n};return{get:e,set:function(t){n=t},clone:function(){return o(e())}}},i=tinymce.util.Tools.resolve("tinymce.PluginManager"),f=function(){},u=function(t){return function(){return t}},s=u(!1),c=u(!0),d=function(){return g},g=(t=function(t){return t.isNone()},r={fold:function(t,n){return t()},is:s,isSome:s,isNone:c,getOr:e=function(t){return t},getOrThunk:n=function(t){return t()},getOrDie:function(t){throw new Error(t||"error: getOrDie called on none.")},getOrNull:u(null),getOrUndefined:u(undefined),or:e,orThunk:n,map:d,each:f,bind:d,exists:s,forall:c,filter:d,equals:t,equals_:t,toArray:function(){return[]},toString:u("none()")},Object.freeze&&Object.freeze(r),r),m=function(e){var t=u(e),n=function(){return a},r=function(t){return t(e)},a={fold:function(t,n){return n(e)},is:function(t){return e===t},isSome:c,isNone:s,getOr:t,getOrThunk:t,getOrDie:t,getOrNull:t,getOrUndefined:t,or:n,orThunk:n,map:function(t){return m(t(e))},each:function(t){t(e)},bind:r,exists:r,forall:r,filter:function(t){return t(e)?a:g},toArray:function(){return[e]},toString:function(){return"some("+e+")"},equals:function(t){return t.is(e)},equals_:function(t,n){return t.fold(s,function(t){return n(e,t)})}};return a},h={some:m,none:d,from:function(t){return null===t||t===undefined?g:m(t)}},p=(a="function",function(t){return function(t){if(null===t)return"null";var n=typeof t;return"object"===n&&(Array.prototype.isPrototypeOf(t)||t.constructor&&"Array"===t.constructor.name)?"array":"object"===n&&(String.prototype.isPrototypeOf(t)||t.constructor&&"String"===t.constructor.name)?"string":n}(t)===a}),v=Array.prototype.slice,y=function(t,n){for(var e=[],r=0,a=t.length;r<a;r++){var o=t[r];n(o,r)&&e.push(o)}return e},O=(p(Array.from)&&Array.from,Object.hasOwnProperty),P=function(t,n){return O.call(t,n)},x=function(t){return P(t,"start")&&P(t,"end")},T=function(t){return!P(t,"end")&&!P(t,"replacement")},b=function(t){return P(t,"replacement")},k=function(t){return n=t,e=function(t,n){return t.start.length===n.start.length?0:t.start.length>n.start.length?-1:1},(r=v.call(n,0)).sort(e),r;var n,e,r},C=function(t){return{inlinePatterns:k(y(t,x)),blockPatterns:k(y(t,T)),replacementPatterns:y(t,b)}},D=function(n){return{setPatterns:function(t){n.set(C(t))},getPatterns:function(){return function(){for(var t=0,n=0,e=arguments.length;n<e;n++)t+=arguments[n].length;var r=Array(t),a=0;for(n=0;n<e;n++)for(var o=arguments[n],i=0,f=o.length;i<f;i++,a++)r[a]=o[i];return r}(n.get().inlinePatterns,n.get().blockPatterns,n.get().replacementPatterns)}}},S=[{start:"*",end:"*",format:"italic"},{start:"**",end:"**",format:"bold"},{start:"***",end:"***",format:["bold","italic"]},{start:"#",format:"h1"},{start:"##",format:"h2"},{start:"###",format:"h3"},{start:"####",format:"h4"},{start:"#####",format:"h5"},{start:"######",format:"h6"},{start:"1. ",cmd:"InsertOrderedList"},{start:"* ",cmd:"InsertUnorderedList"},{start:"- ",cmd:"InsertUnorderedList"}],A=function(t){var n,e,r=(n=t,e="textpattern_patterns",P(n,e)?h.from(n[e]):h.none()).getOr(S);return C(r)},N=tinymce.util.Tools.resolve("tinymce.util.Delay"),R=tinymce.util.Tools.resolve("tinymce.util.VK"),w=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),I=tinymce.util.Tools.resolve("tinymce.util.Tools"),j=function(t,n){for(var e=0;e<t.length;e++){var r=t[e];if(0===n.indexOf(r.start)&&(!r.end||n.lastIndexOf(r.end)===n.length-r.end.length))return r}},E=function(t,n,e){if(!1!==n.collapsed){var r=n.startContainer,a=r.data,o=!0===e?1:0;if(3===r.nodeType){var i=function(t,n,e,r){var a,o,i,f,u,s;for(o=0;o<t.length;o++)if((a=t[o]).end!==undefined&&(f=a,u=e,s=r,n.substr(u-f.end.length-s,f.end.length)===f.end)&&0<e-r-(i=a).end.length-i.start.length)return a}(t,a,n.startOffset,o);if(i!==undefined){var f=a.lastIndexOf(i.end,n.startOffset-o),u=a.lastIndexOf(i.start,f-i.end.length);if(f=a.indexOf(i.end,u+i.start.length),-1!==u){var s=l.document.createRange();s.setStart(r,u),s.setEnd(r,f+i.end.length);var c=j(t,s.toString());if(!(i===undefined||c!==i||r.data.length<=i.start.length+i.end.length))return{pattern:i,startOffset:u,endOffset:f}}}}}},q=function(t){return t&&3===t.nodeType},L=function(t,n,e){var r=t.dom.createRng();r.setStart(n,e),r.setEnd(n,e),t.selection.setRng(r)},M=function(n,t,e){var r=n.selection.getRng();return h.from(E(t,r,e)).map(function(t){return function(a,o,i,f){var u=I.isArray(i.pattern.format)?i.pattern.format:[i.pattern.format];if(0!==I.grep(u,function(t){var n=a.formatter.get(t);return n&&n[0].inline}).length)return a.undoManager.transact(function(){var t,n,e,r;t=o,n=i.pattern,e=i.endOffset,r=i.startOffset,(t=0<r?t.splitText(r):t).splitText(e-r+n.end.length),t.deleteData(0,n.start.length),t.deleteData(t.data.length-n.end.length,n.end.length),o=t,f&&a.selection.setCursorLocation(o.nextSibling,1),u.forEach(function(t){a.formatter.apply(t,{},o)})}),o}(n,r.startContainer,t,e)})},U=function(s,t,c){var n=s.selection.getRng(),l=n.startContainer;n.collapsed&&q(l)&&function(t,n,e){for(var r=0;r<t.length;r++){var a=e.lastIndexOf(t[r].start,n);if(-1!==a)return h.some({pattern:t[r],startOffset:a})}return h.none()}(t,n.startOffset,l.data).each(function(t){var n,e,r,a,o,i,f,u=c?h.some((n=l,r=(e=t).startOffset+e.pattern.start.length,a=n.data.slice(r,r+1),n.deleteData(r,1),a)):h.none();o=s,f=t,(i=l).deleteData(f.startOffset,f.pattern.start.length),o.insertContent(f.pattern.replacement),h.from(i.nextSibling).filter(q).each(function(t){t.insertData(0,i.data),o.dom.remove(i)}),u.each(function(t){return function(t,n){var e=t.selection.getRng(),r=e.startContainer;if(q(r)){var a=e.startOffset;r.insertData(a,n),L(t,r,a+n.length)}else{var o=t.dom.doc.createTextNode(n);e.insertNode(o),L(t,o,o.length)}}(s,t)})})},_=function(t,n,e){for(var r=0;r<t.length;r++)if(e(t[r],n))return!0},z=function(t,n){var e,r,a,o;e=t,r=n.replacementPatterns,U(e,r,!1),a=t,o=n.inlinePatterns,M(a,o,!1).each(function(t){L(a,t,t.data.length)}),function(t,n){var e,r,a,o,i,f,u,s,c,l,d;if(e=t.selection,r=t.dom,e.isCollapsed()&&(u=r.getParent(e.getStart(),"p"))){for(c=new w(u,u);i=c.next();)if(q(i)){o=i;break}if(o){if(!(s=j(n,o.data)))return;if(a=(l=e.getRng(!0)).startContainer,d=l.startOffset,o===a&&(d=Math.max(0,d-s.start.length)),I.trim(o.data).length===s.start.length)return;s.format&&(f=t.formatter.get(s.format))&&f[0].block&&(o.deleteData(0,s.start.length),t.formatter.apply(s.format,{},o),l.setStart(a,d),l.collapse(!0),e.setRng(l)),s.cmd&&t.undoManager.transact(function(){o.deleteData(0,s.start.length),t.execCommand(s.cmd)})}}}(t,n.blockPatterns)},K=function(t,n){var e,r,a,o;e=t,r=n.replacementPatterns,U(e,r,!0),a=t,o=n.inlinePatterns,M(a,o,!0).each(function(t){var n=t.data.slice(-1);if(/[\u00a0 ]/.test(n)){t.deleteData(t.data.length-1,1);var e=a.dom.doc.createTextNode(n);a.dom.insertAfter(e,t.parentNode),L(a,e,1)}})},V=function(t,n){return _(t,n,function(t,n){return t.charCodeAt(0)===n.charCode})},W=function(t,n){return _(t,n,function(t,n){return t===n.keyCode&&!1===R.modifierPressed(n)})},B=function(n,e){var r=[",",".",";",":","!","?"],a=[32];n.on("keydown",function(t){13!==t.keyCode||R.modifierPressed(t)||z(n,e.get())},!0),n.on("keyup",function(t){W(a,t)&&K(n,e.get())}),n.on("keypress",function(t){V(r,t)&&N.setEditorTimeout(n,function(){K(n,e.get())})})};i.add("textpattern",function(t){var n=o(A(t.settings));return B(t,n),D(n)})}(window);
\ No newline at end of file
index 733a32a5870576990e97154263f45c1859e3e6fd..0a193452ebfdc770faec9f900111dac0ab80a32f 100644 (file)
@@ -1 +1 @@
-!function(r){"use strict";var n,e,t,o,u,i,c=function(n){var e=n,t=function(){return e};return{get:t,set:function(n){e=n},clone:function(){return c(t())}}},a=tinymce.util.Tools.resolve("tinymce.PluginManager"),f=function(n){return{isEnabled:function(){return n.get()}}},l=function(n,e){return n.fire("VisualChars",{state:e})},d={"\xa0":"nbsp","\xad":"shy"},s=function(n,e){var t,r="";for(t in n)r+=t;return new RegExp("["+r+"]",e?"g":"")},m=function(n){var e,t="";for(e in n)t&&(t+=","),t+="span.mce-"+n[e];return t},N={charMap:d,regExp:s(d),regExpGlobal:s(d,!0),selector:m(d),charMapToRegExp:s,charMapToSelector:m},E=function(n){return function(){return n}},g=E(!1),h=E(!0),v=g,T=h,p=function(){return O},O=(o={fold:function(n,e){return n()},is:v,isSome:v,isNone:T,getOr:t=function(n){return n},getOrThunk:e=function(n){return n()},getOrDie:function(n){throw new Error(n||"error: getOrDie called on none.")},getOrNull:function(){return null},getOrUndefined:function(){return undefined},or:t,orThunk:e,map:p,ap:p,each:function(){},bind:p,flatten:p,exists:v,forall:T,filter:p,equals:n=function(n){return n.isNone()},equals_:n,toArray:function(){return[]},toString:E("none()")},Object.freeze&&Object.freeze(o),o),y=function(t){var n=function(){return t},e=function(){return o},r=function(n){return n(t)},o={fold:function(n,e){return e(t)},is:function(n){return t===n},isSome:T,isNone:v,getOr:n,getOrThunk:n,getOrDie:n,getOrNull:n,getOrUndefined:n,or:e,orThunk:e,map:function(n){return y(n(t))},ap:function(n){return n.fold(p,function(n){return y(n(t))})},each:function(n){n(t)},bind:r,flatten:n,exists:r,forall:r,filter:function(n){return n(t)?o:O},equals:function(n){return n.is(t)},equals_:function(n,e){return n.fold(v,function(n){return e(t,n)})},toArray:function(){return[t]},toString:function(){return"some("+t+")"}};return o},D=function(n){return null===n||n===undefined?O:y(n)},_=(u="function",function(n){return function(n){if(null===n)return"null";var e=typeof n;return"object"===e&&Array.prototype.isPrototypeOf(n)?"array":"object"===e&&String.prototype.isPrototypeOf(n)?"string":e}(n)===u}),C=function(n,e){for(var t=0,r=n.length;t<r;t++)e(n[t],t,n)},M=(Array.prototype.slice,_(Array.from)&&Array.from,function(n){if(null===n||n===undefined)throw new Error("Node cannot be null or undefined");return{dom:E(n)}}),b={fromHtml:function(n,e){var t=(e||r.document).createElement("div");if(t.innerHTML=n,!t.hasChildNodes()||1<t.childNodes.length)throw r.console.error("HTML does not have a single root node",n),new Error("HTML must have a single root node");return M(t.childNodes[0])},fromTag:function(n,e){var t=(e||r.document).createElement(n);return M(t)},fromText:function(n,e){var t=(e||r.document).createTextNode(n);return M(t)},fromDom:M,fromPoint:function(n,e,t){var r=n.dom();return D(r.elementFromPoint(e,t)).map(M)}},k=(r.Node.ATTRIBUTE_NODE,r.Node.CDATA_SECTION_NODE,r.Node.COMMENT_NODE,r.Node.DOCUMENT_NODE,r.Node.DOCUMENT_TYPE_NODE,r.Node.DOCUMENT_FRAGMENT_NODE,r.Node.ELEMENT_NODE,r.Node.TEXT_NODE),w=(r.Node.PROCESSING_INSTRUCTION_NODE,r.Node.ENTITY_REFERENCE_NODE,r.Node.ENTITY_NODE,r.Node.NOTATION_NODE,function(n){return n.dom().nodeValue}),S=(i=k,function(n){return n.dom().nodeType===i}),x=function(n){return'<span data-mce-bogus="1" class="mce-'+N.charMap[n]+'">'+n+"</span>"},A=function(n,e){var t=[],r=function(n,e){for(var t=n.length,r=new Array(t),o=0;o<t;o++){var u=n[o];r[o]=e(u,o,n)}return r}(n.dom().childNodes,b.fromDom);return C(r,function(n){e(n)&&(t=t.concat([n])),t=t.concat(A(n,e))}),t},P={isMatch:function(n){return S(n)&&w(n)!==undefined&&N.regExp.test(w(n))},filterDescendants:A,findParentElm:function(n,e){for(;n.parentNode;){if(n.parentNode===e)return n;n=n.parentNode}},replaceWithSpans:function(n){return n.replace(N.regExpGlobal,x)}},R=function(t,n){var r,o,e=P.filterDescendants(b.fromDom(n),P.isMatch);C(e,function(n){var e=P.replaceWithSpans(w(n));for(o=t.dom.create("div",null,e);r=o.lastChild;)t.dom.insertAfter(r,n.dom());t.dom.remove(n.dom())})},I=function(e,n){var t=e.dom.select(N.selector,n);C(t,function(n){e.dom.remove(n,1)})},B=R,U=I,V=function(n){var e=n.getBody(),t=n.selection.getBookmark(),r=P.findParentElm(n.selection.getNode(),e);r=r!==undefined?r:e,I(n,r),R(n,r),n.selection.moveToBookmark(t)},j=function(n,e){var t,r=n.getBody(),o=n.selection;e.set(!e.get()),l(n,e.get()),t=o.getBookmark(),!0===e.get()?B(n,r):U(n,r),o.moveToBookmark(t)},q=function(n,e){n.addCommand("mceVisualChars",function(){j(n,e)})},G=tinymce.util.Tools.resolve("tinymce.util.Delay"),H=function(e,t){var r=G.debounce(function(){V(e)},300);!1!==e.settings.forced_root_block&&e.on("keydown",function(n){!0===t.get()&&(13===n.keyCode?V(e):r())})},L=function(n){return n.getParam("visualchars_default_state",!1)},F=function(e,t){e.on("init",function(){var n=!L(e);t.set(n),j(e,t)})},Y=function(t){return function(n){var e=n.control;t.on("VisualChars",function(n){e.active(n.state)})}};a.add("visualchars",function(n){var e,t=c(!1);return q(n,t),(e=n).addButton("visualchars",{active:!1,title:"Show invisible characters",cmd:"mceVisualChars",onPostRender:Y(e)}),e.addMenuItem("visualchars",{text:"Show invisible characters",cmd:"mceVisualChars",onPostRender:Y(e),selectable:!0,context:"view",prependToContext:!0}),H(n,t),F(n,t),f(t)})}(window);
\ No newline at end of file
+!function(r){"use strict";var n,e,t,o,i,u,c=function(n){var e=n,t=function(){return e};return{get:t,set:function(n){e=n},clone:function(){return c(t())}}},a=tinymce.util.Tools.resolve("tinymce.PluginManager"),f=function(n){return{isEnabled:function(){return n.get()}}},d=function(n,e){return n.fire("VisualChars",{state:e})},s=function(){},l=function(n){return function(){return n}},m=l(!1),N=l(!0),g=function(){return E},E=(n=function(n){return n.isNone()},o={fold:function(n,e){return n()},is:m,isSome:m,isNone:N,getOr:t=function(n){return n},getOrThunk:e=function(n){return n()},getOrDie:function(n){throw new Error(n||"error: getOrDie called on none.")},getOrNull:l(null),getOrUndefined:l(undefined),or:t,orThunk:e,map:g,each:s,bind:g,exists:m,forall:N,filter:g,equals:n,equals_:n,toArray:function(){return[]},toString:l("none()")},Object.freeze&&Object.freeze(o),o),h=function(t){var n=l(t),e=function(){return o},r=function(n){return n(t)},o={fold:function(n,e){return e(t)},is:function(n){return t===n},isSome:N,isNone:m,getOr:n,getOrThunk:n,getOrDie:n,getOrNull:n,getOrUndefined:n,or:e,orThunk:e,map:function(n){return h(n(t))},each:function(n){n(t)},bind:r,exists:r,forall:r,filter:function(n){return n(t)?o:E},toArray:function(){return[t]},toString:function(){return"some("+t+")"},equals:function(n){return n.is(t)},equals_:function(n,e){return n.fold(m,function(n){return e(t,n)})}};return o},v=function(n){return null===n||n===undefined?E:h(n)},T=(i="function",function(n){return function(n){if(null===n)return"null";var e=typeof n;return"object"===e&&(Array.prototype.isPrototypeOf(n)||n.constructor&&"Array"===n.constructor.name)?"array":"object"===e&&(String.prototype.isPrototypeOf(n)||n.constructor&&"String"===n.constructor.name)?"string":e}(n)===i}),p=(Array.prototype.slice,function(n,e){for(var t=0,r=n.length;t<r;t++)e(n[t],t)}),O=(T(Array.from)&&Array.from,function(n){if(null===n||n===undefined)throw new Error("Node cannot be null or undefined");return{dom:l(n)}}),y={fromHtml:function(n,e){var t=(e||r.document).createElement("div");if(t.innerHTML=n,!t.hasChildNodes()||1<t.childNodes.length)throw r.console.error("HTML does not have a single root node",n),new Error("HTML must have a single root node");return O(t.childNodes[0])},fromTag:function(n,e){var t=(e||r.document).createElement(n);return O(t)},fromText:function(n,e){var t=(e||r.document).createTextNode(n);return O(t)},fromDom:O,fromPoint:function(n,e,t){var r=n.dom();return v(r.elementFromPoint(e,t)).map(O)}},D=(r.Node.ATTRIBUTE_NODE,r.Node.CDATA_SECTION_NODE,r.Node.COMMENT_NODE,r.Node.DOCUMENT_NODE,r.Node.DOCUMENT_TYPE_NODE,r.Node.DOCUMENT_FRAGMENT_NODE,r.Node.ELEMENT_NODE,r.Node.TEXT_NODE),_=(r.Node.PROCESSING_INSTRUCTION_NODE,r.Node.ENTITY_REFERENCE_NODE,r.Node.ENTITY_NODE,r.Node.NOTATION_NODE,"undefined"!=typeof r.window?r.window:Function("return this;")(),function(n){return n.dom().nodeValue}),C=(u=D,function(n){return n.dom().nodeType===u}),w={"\xa0":"nbsp","\xad":"shy"},M=function(n,e){var t,r="";for(t in n)r+=t;return new RegExp("["+r+"]",e?"g":"")},b=function(n){var e,t="";for(e in n)t&&(t+=","),t+="span.mce-"+n[e];return t},k={charMap:w,regExp:M(w),regExpGlobal:M(w,!0),selector:b(w),charMapToRegExp:M,charMapToSelector:b},S=function(n){return'<span data-mce-bogus="1" class="mce-'+k.charMap[n]+'">'+n+"</span>"},A=function(n,e){var t=[],r=function(n,e){for(var t=n.length,r=new Array(t),o=0;o<t;o++){var i=n[o];r[o]=e(i,o)}return r}(n.dom().childNodes,y.fromDom);return p(r,function(n){e(n)&&(t=t.concat([n])),t=t.concat(A(n,e))}),t},x={isMatch:function(n){var e=_(n);return C(n)&&e!==undefined&&k.regExp.test(e)},filterDescendants:A,findParentElm:function(n,e){for(;n.parentNode;){if(n.parentNode===e)return n;n=n.parentNode}},replaceWithSpans:function(n){return n.replace(k.regExpGlobal,S)}},P=function(t,n){var r,o,e=x.filterDescendants(y.fromDom(n),x.isMatch);p(e,function(n){var e=x.replaceWithSpans(t.dom.encode(_(n)));for(o=t.dom.create("div",null,e);r=o.lastChild;)t.dom.insertAfter(r,n.dom());t.dom.remove(n.dom())})},R=function(e,n){var t=e.dom.select(k.selector,n);p(t,function(n){e.dom.remove(n,1)})},I=P,B=R,U=function(n){var e=n.getBody(),t=n.selection.getBookmark(),r=x.findParentElm(n.selection.getNode(),e);r=r!==undefined?r:e,R(n,r),P(n,r),n.selection.moveToBookmark(t)},V=function(n,e){var t,r=n.getBody(),o=n.selection;e.set(!e.get()),d(n,e.get()),t=o.getBookmark(),!0===e.get()?I(n,r):B(n,r),o.moveToBookmark(t)},j=function(n,e){n.addCommand("mceVisualChars",function(){V(n,e)})},q=tinymce.util.Tools.resolve("tinymce.util.Delay"),F=function(e,t){var r=q.debounce(function(){U(e)},300);!1!==e.settings.forced_root_block&&e.on("keydown",function(n){!0===t.get()&&(13===n.keyCode?U(e):r())})},G=function(n){return n.getParam("visualchars_default_state",!1)},H=function(e,t){e.on("init",function(){var n=!G(e);t.set(n),V(e,t)})},L=function(t){return function(n){var e=n.control;t.on("VisualChars",function(n){e.active(n.state)})}};a.add("visualchars",function(n){var e,t=c(!1);return j(n,t),(e=n).addButton("visualchars",{active:!1,title:"Show invisible characters",cmd:"mceVisualChars",onPostRender:L(e)}),e.addMenuItem("visualchars",{text:"Show invisible characters",cmd:"mceVisualChars",onPostRender:L(e),selectable:!0,context:"view",prependToContext:!0}),F(n,t),H(n,t),f(t)})}(window);
\ No newline at end of file
index ecaffcdc2da2500e0f4f36d4fefc107788d962bd..a05c4c1b889211b6894c10bb066589ec82380723 100644 (file)
@@ -1 +1 @@
-!function(){"use strict";var e,n,t,r,o,u=tinymce.util.Tools.resolve("tinymce.PluginManager"),T=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),i=tinymce.util.Tools.resolve("tinymce.Env"),E="[-'\\.\u2018\u2019\u2024\ufe52\uff07\uff0e]",c="[:\xb7\xb7\u05f4\u2027\ufe13\ufe55\uff1a]",a="[\xb1+*/,;;\u0589\u060c\u060d\u066c\u07f8\u2044\ufe10\ufe14\ufe50\ufe54\uff0c\uff1b]",f="[0-9\u0660-\u0669\u066b\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\uaa50-\uaa59\uabf0-\uabf9]",l="\\r",s="\\n",A="[\x0B\f\x85\u2028\u2029]",N="[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0c01-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d02\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f\u109a-\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b6-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u192b\u1930-\u193b\u19b0-\u19c0\u19c8\u19c9\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f\u1b00-\u1b04\u1b34-\u1b44\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1baa\u1be6-\u1bf3\u1c24-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2\u1dc0-\u1de6\u1dfc-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa7b\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe3-\uabea\uabec\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]",R="[\xad\u0600-\u0603\u06dd\u070f\u17b4\u17b5\u200e\u200f\u202a-\u202e\u2060-\u2064\u206a-\u206f\ufeff\ufff9-\ufffb]",d="[\u3031-\u3035\u309b\u309c\u30a0-\u30fa\u30fc-\u30ff\u31f0-\u31ff\u32d0-\u32fe\u3300-\u3357\uff66-\uff9d]",g="[=_\u203f\u2040\u2054\ufe33\ufe34\ufe4d-\ufe4f\uff3f\u2200-\u22ff<>]",p="[!-#%-*,-\\/:;?@\\[-\\]_{}\xa1\xab\xb7\xbb\xbf;\xb7\u055a-\u055f\u0589\u058a\u05be\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f3a-\u0f3d\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1361-\u1368\u1400\u166d\u166e\u169b\u169c\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cd3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205e\u207d\u207e\u208d\u208e\u3008\u3009\u2768-\u2775\u27c5\u27c6\u27e6-\u27ef\u2983-\u2998\u29d8-\u29db\u29fc\u29fd\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00-\u2e2e\u2e30\u2e31\u3001-\u3003\u3008-\u3011\u3014-\u301f\u3030\u303d\u30a0\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uabeb\ufd3e\ufd3f\ufe10-\ufe19\ufe30-\ufe52\ufe54-\ufe61\ufe63\ufe68\ufe6a\ufe6b\uff01-\uff03\uff05-\uff0a\uff0c-\uff0f\uff1a\uff1b\uff1f\uff20\uff3b-\uff3d\uff3f\uff5b\uff5d\uff5f-\uff65]",M={characterIndices:{ALETTER:0,MIDNUMLET:1,MIDLETTER:2,MIDNUM:3,NUMERIC:4,CR:5,LF:6,NEWLINE:7,EXTEND:8,FORMAT:9,KATAKANA:10,EXTENDNUMLET:11,AT:12,OTHER:13},SETS:[new RegExp("[A-Za-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f3\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u10a0-\u10c5\u10d0-\u10fa\u10fc\u1100-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1a00-\u1a16\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bc0-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u24b6-\u24e9\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2d00-\u2d25\u2d30-\u2d65\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005\u303b\u303c\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790\ua791\ua7a0-\ua7a9\ua7fa-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uffa0-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]"),new RegExp(E),new RegExp(c),new RegExp(a),new RegExp(f),new RegExp(l),new RegExp(s),new RegExp(A),new RegExp(N),new RegExp(R),new RegExp(d),new RegExp(g),new RegExp("@")],EMPTY_STRING:"",PUNCTUATION:new RegExp("^"+p+"$"),WHITESPACE:/^\s+$/},I=function(e){return function(){return e}},L=I(!1),y=I(!0),m=function(){return h},h=(r={fold:function(e,n){return e()},is:L,isSome:L,isNone:y,getOr:t=function(e){return e},getOrThunk:n=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:function(){return null},getOrUndefined:function(){return undefined},or:t,orThunk:n,map:m,ap:m,each:function(){},bind:m,flatten:m,exists:L,forall:y,filter:m,equals:e=function(e){return e.isNone()},equals_:e,toArray:function(){return[]},toString:I("none()")},Object.freeze&&Object.freeze(r),r),w=(o="function",function(e){return function(e){if(null===e)return"null";var n=typeof e;return"object"===n&&Array.prototype.isPrototypeOf(e)?"array":"object"===n&&String.prototype.isPrototypeOf(e)?"string":n}(e)===o}),U=(Array.prototype.slice,w(Array.from)&&Array.from,M.SETS),v=M.characterIndices.OTHER,x=function(e){var n,t,r=v,o=U.length;for(n=0;n<o;++n)if((t=U[n])&&t.test(e)){r=n;break}return r},D=function(e){var t,r,n=(t=x,r={},function(e){if(r[e])return r[e];var n=t(e);return r[e]=n});return function(e,n){for(var t=e.length,r=new Array(t),o=0;o<t;o++){var u=e[o];r[o]=n(u,o,e)}return r}(e.split(""),n)},C=M.characterIndices,O=function(e,n){var t,r,o=e[n],u=e[n+1];return!(n<0||n>e.length-1&&0!==n||o===C.ALETTER&&u===C.ALETTER||(r=e[n+2],o===C.ALETTER&&(u===C.MIDLETTER||u===C.MIDNUMLET||u===C.AT)&&r===C.ALETTER||(t=e[n-1],(o===C.MIDLETTER||o===C.MIDNUMLET||u===C.AT)&&u===C.ALETTER&&t===C.ALETTER||!(o!==C.NUMERIC&&o!==C.ALETTER||u!==C.NUMERIC&&u!==C.ALETTER)||(o===C.MIDNUM||o===C.MIDNUMLET)&&u===C.NUMERIC&&t===C.NUMERIC||o===C.NUMERIC&&(u===C.MIDNUM||u===C.MIDNUMLET)&&r===C.NUMERIC||o===C.EXTEND||o===C.FORMAT||t===C.EXTEND||t===C.FORMAT||u===C.EXTEND||u===C.FORMAT||o===C.CR&&u===C.LF||o!==C.NEWLINE&&o!==C.CR&&o!==C.LF&&u!==C.NEWLINE&&u!==C.CR&&u!==C.LF&&(o===C.KATAKANA&&u===C.KATAKANA||u===C.EXTENDNUMLET&&(o===C.ALETTER||o===C.NUMERIC||o===C.KATAKANA||o===C.EXTENDNUMLET)||o===C.EXTENDNUMLET&&(u===C.ALETTER||u===C.NUMERIC||u===C.KATAKANA)||o===C.AT))))},b=M.EMPTY_STRING,S=M.WHITESPACE,K=M.PUNCTUATION,P=function(e,n,t){var r=function(e,n){var t;for(t=n;t<e.length;++t){var r=e.charAt(t);if(S.test(r))break}return t}(n,t+1),o=n.substring(t+1,r);return"://"===o.substr(0,3)?{word:e+o,index:r}:{word:e,index:t}},F=function(e,n){return function(e,n){var t,r,o,u,i=0,E=D(e),T=E.length,c=[],a=[];for(n||(n={}),n.ignoreCase&&(e=e.toLowerCase()),r=n.includePunctuation,o=n.includeWhitespace;i<T;++i)if(t=e.charAt(i),c.push(t),O(E,i)){if((c=c.join(b))&&(o||!S.test(c))&&(r||!K.test(c)))if("http"===(u=c)||"https"===u){var f=P(c,e,i);a.push(f.word),i=f.index}else a.push(c);c=[]}return a}(e.replace(/\ufeff/g,""),n)},W=function(e,n){return i.ie?function(e,n){for(var t,r=n.getBlockElements(),o=n.getShortEndedElements(),u=n.getWhiteSpaceElements(),i="",E=new T(e,e);e=E.next();)3===e.nodeType?i+=e.data:(r[(t=e).nodeName]||o[t.nodeName]||u[t.nodeName])&&(i+=" ");return i}(e,n):e.innerText},X=function(e){return F((n=e,n.removed?"":W(n.getBody(),n.schema))).length;var n},k=function(e){return{getCount:function(){return X(e)}}},j=tinymce.util.Tools.resolve("tinymce.util.Delay"),_=tinymce.util.Tools.resolve("tinymce.util.I18n"),H=function(t){var r=function(e){return _.translate(["{0} words",X(e)])},o=function(){t.theme.panel.find("#wordcount").text(r(t))};t.on("init",function(){var e=t.theme.panel&&t.theme.panel.find("#statusbar")[0],n=j.debounce(o,300);e&&j.setEditorTimeout(t,function(){e.insert({type:"label",name:"wordcount",text:r(t),classes:"wordcount",disabled:t.settings.readonly},0),t.on("setcontent beforeaddundo undo redo keyup",n)},0)})};u.add("wordcount",function(e){return H(e),k(e)})}();
\ No newline at end of file
+!function(){"use strict";var e,n,t,r,o,u=tinymce.util.Tools.resolve("tinymce.PluginManager"),c=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),i=tinymce.util.Tools.resolve("tinymce.Env"),E="[-'\\.\u2018\u2019\u2024\ufe52\uff07\uff0e]",T="[:\xb7\xb7\u05f4\u2027\ufe13\ufe55\uff1a]",a="[\xb1+*/,;;\u0589\u060c\u060d\u066c\u07f8\u2044\ufe10\ufe14\ufe50\ufe54\uff0c\uff1b]",f="[0-9\u0660-\u0669\u066b\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\uaa50-\uaa59\uabf0-\uabf9]",s="\\r",l="\\n",A="[\x0B\f\x85\u2028\u2029]",N="[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0c01-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d02\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f\u109a-\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b6-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u192b\u1930-\u193b\u19b0-\u19c0\u19c8\u19c9\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f\u1b00-\u1b04\u1b34-\u1b44\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1baa\u1be6-\u1bf3\u1c24-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2\u1dc0-\u1de6\u1dfc-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa7b\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe3-\uabea\uabec\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]",R="[\xad\u0600-\u0603\u06dd\u070f\u17b4\u17b5\u200e\u200f\u202a-\u202e\u2060-\u2064\u206a-\u206f\ufeff\ufff9-\ufffb]",d="[\u3031-\u3035\u309b\u309c\u30a0-\u30fa\u30fc-\u30ff\u31f0-\u31ff\u32d0-\u32fe\u3300-\u3357\uff66-\uff9d]",g="[=_\u203f\u2040\u2054\ufe33\ufe34\ufe4d-\ufe4f\uff3f\u2200-\u22ff<>]",p="[!-#%-*,-\\/:;?@\\[-\\]_{}\xa1\xab\xb7\xbb\xbf;\xb7\u055a-\u055f\u0589\u058a\u05be\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f3a-\u0f3d\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1361-\u1368\u1400\u166d\u166e\u169b\u169c\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cd3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205e\u207d\u207e\u208d\u208e\u3008\u3009\u2768-\u2775\u27c5\u27c6\u27e6-\u27ef\u2983-\u2998\u29d8-\u29db\u29fc\u29fd\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00-\u2e2e\u2e30\u2e31\u3001-\u3003\u3008-\u3011\u3014-\u301f\u3030\u303d\u30a0\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uabeb\ufd3e\ufd3f\ufe10-\ufe19\ufe30-\ufe52\ufe54-\ufe61\ufe63\ufe68\ufe6a\ufe6b\uff01-\uff03\uff05-\uff0a\uff0c-\uff0f\uff1a\uff1b\uff1f\uff20\uff3b-\uff3d\uff3f\uff5b\uff5d\uff5f-\uff65]",M={characterIndices:{ALETTER:0,MIDNUMLET:1,MIDLETTER:2,MIDNUM:3,NUMERIC:4,CR:5,LF:6,NEWLINE:7,EXTEND:8,FORMAT:9,KATAKANA:10,EXTENDNUMLET:11,AT:12,OTHER:13},SETS:[new RegExp("[A-Za-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f3\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u10a0-\u10c5\u10d0-\u10fa\u10fc\u1100-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1a00-\u1a16\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bc0-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u24b6-\u24e9\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2d00-\u2d25\u2d30-\u2d65\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005\u303b\u303c\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790\ua791\ua7a0-\ua7a9\ua7fa-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uffa0-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]"),new RegExp(E),new RegExp(T),new RegExp(a),new RegExp(f),new RegExp(s),new RegExp(l),new RegExp(A),new RegExp(N),new RegExp(R),new RegExp(d),new RegExp(g),new RegExp("@")],EMPTY_STRING:"",PUNCTUATION:new RegExp("^"+p+"$"),WHITESPACE:/^\s+$/},I=function(){},L=function(e){return function(){return e}},m=L(!1),y=L(!0),h=function(){return w},w=(e=function(e){return e.isNone()},r={fold:function(e,n){return e()},is:m,isSome:m,isNone:y,getOr:t=function(e){return e},getOrThunk:n=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:L(null),getOrUndefined:L(undefined),or:t,orThunk:n,map:h,each:I,bind:h,exists:m,forall:y,filter:h,equals:e,equals_:e,toArray:function(){return[]},toString:L("none()")},Object.freeze&&Object.freeze(r),r),U=(o="function",function(e){return function(e){if(null===e)return"null";var n=typeof e;return"object"===n&&(Array.prototype.isPrototypeOf(e)||e.constructor&&"Array"===e.constructor.name)?"array":"object"===n&&(String.prototype.isPrototypeOf(e)||e.constructor&&"String"===e.constructor.name)?"string":n}(e)===o}),v=(Array.prototype.slice,U(Array.from)&&Array.from,M.SETS),x=M.characterIndices.OTHER,D=function(e){var n,t,r=x,o=v.length;for(n=0;n<o;++n)if((t=v[n])&&t.test(e)){r=n;break}return r},C=function(e){var t,r,n=(t=D,r={},function(e){if(r[e])return r[e];var n=t(e);return r[e]=n});return function(e,n){for(var t=e.length,r=new Array(t),o=0;o<t;o++){var u=e[o];r[o]=n(u,o)}return r}(e.split(""),n)},O=M.characterIndices,b=function(e,n){var t,r,o=e[n],u=e[n+1];return!(n<0||n>e.length-1&&0!==n||o===O.ALETTER&&u===O.ALETTER||(r=e[n+2],o===O.ALETTER&&(u===O.MIDLETTER||u===O.MIDNUMLET||u===O.AT)&&r===O.ALETTER||(t=e[n-1],(o===O.MIDLETTER||o===O.MIDNUMLET||u===O.AT)&&u===O.ALETTER&&t===O.ALETTER||!(o!==O.NUMERIC&&o!==O.ALETTER||u!==O.NUMERIC&&u!==O.ALETTER)||(o===O.MIDNUM||o===O.MIDNUMLET)&&u===O.NUMERIC&&t===O.NUMERIC||o===O.NUMERIC&&(u===O.MIDNUM||u===O.MIDNUMLET)&&r===O.NUMERIC||o===O.EXTEND||o===O.FORMAT||t===O.EXTEND||t===O.FORMAT||u===O.EXTEND||u===O.FORMAT||o===O.CR&&u===O.LF||o!==O.NEWLINE&&o!==O.CR&&o!==O.LF&&u!==O.NEWLINE&&u!==O.CR&&u!==O.LF&&(o===O.KATAKANA&&u===O.KATAKANA||u===O.EXTENDNUMLET&&(o===O.ALETTER||o===O.NUMERIC||o===O.KATAKANA||o===O.EXTENDNUMLET)||o===O.EXTENDNUMLET&&(u===O.ALETTER||u===O.NUMERIC||u===O.KATAKANA)||o===O.AT))))},S=M.EMPTY_STRING,K=M.WHITESPACE,P=M.PUNCTUATION,F=function(e,n,t){var r=function(e,n){var t;for(t=n;t<e.length;++t){var r=e.charAt(t);if(K.test(r))break}return t}(n,t+1),o=n.substring(t+1,r);return"://"===o.substr(0,3)?{word:e+o,index:r}:{word:e,index:t}},W=function(e,n){return function(e,n){var t,r,o,u,i=0,E=C(e),c=E.length,T=[],a=[];for(n||(n={}),n.ignoreCase&&(e=e.toLowerCase()),r=n.includePunctuation,o=n.includeWhitespace;i<c;++i)if(t=e.charAt(i),T.push(t),b(E,i)){if((T=T.join(S))&&(o||!K.test(T))&&(r||!P.test(T)))if("http"===(u=T)||"https"===u){var f=F(T,e,i);a.push(f.word),i=f.index}else a.push(T);T=[]}return a}(e.replace(/\ufeff/g,""),n)},X=function(e,n){return i.ie?function(e,n){for(var t,r=n.getBlockElements(),o=n.getShortEndedElements(),u=n.getWhiteSpaceElements(),i="",E=new c(e,e);e=E.next();)3===e.nodeType?i+=e.data:(r[(t=e).nodeName]||o[t.nodeName]||u[t.nodeName])&&(i+=" ");return i}(e,n):e.innerText},k=function(e){return W((n=e,n.removed?"":X(n.getBody(),n.schema))).length;var n},j=function(e){return{getCount:function(){return k(e)}}},_=tinymce.util.Tools.resolve("tinymce.util.Delay"),H=tinymce.util.Tools.resolve("tinymce.util.I18n"),z=function(t){var r=function(e){return H.translate(["{0} words",k(e)])},o=function(){t.theme.panel.find("#wordcount").text(r(t))};t.on("init",function(){var e=t.theme.panel&&t.theme.panel.find("#statusbar")[0],n=_.debounce(o,300);e&&_.setEditorTimeout(t,function(){e.insert({type:"label",name:"wordcount",text:r(t),classes:"wordcount",disabled:t.settings.readonly},0),t.on("setcontent beforeaddundo undo redo keyup",n)},0)})};u.add("wordcount",function(e){return z(e),j(e)})}();
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/Variables.less b/public/libs/tinymce/skins/dark/Variables.less
new file mode 100644 (file)
index 0000000..1c5bd20
--- /dev/null
@@ -0,0 +1,224 @@
+// Variables
+// Syntax: <control>-(<sub control>)-<bg|border|text>-(<state>)-(<extra>);
+// Example: @btn-primary-bg-hover-hlight;
+
+@prefix:                         mce;
+
+// Default font
+@font-family:                    "Helvetica Neue", Helvetica, Arial, sans-serif;
+@font-size:                      14px;
+@line-height:                    20px;
+@has-gradients:                  false;
+@has-radius:                     false;
+@has-boxshadow:                  false;
+@has-button-borders:             true;
+
+// Text colors
+@text:                           #b5b9bf;
+@text-inverse:                   #000000;
+@text-disabled:                  #6e737a;
+@text-shadow:                    0 1px 1px hsla(hue(@text-inverse), saturation(@text-inverse), lightness(@text-inverse), 0.75);
+@text-error:                     #b94a48;
+@text-warning:                   #c09853;
+@text-success:                   #468847;
+@text-link:                      #2980b9;
+
+// Button
+@btn-text:                       #b5b9bf;
+@btn-text-shadow:                #000000;
+@btn-border-top:                 rgba(0,0,0,0);
+@btn-border-right:               rgba(0,0,0,0);
+@btn-border-bottom:              rgba(0,0,0,0);
+@btn-border-left:                rgba(0,0,0,0);
+@btn-caret-border:               @btn-text;
+@btn-text-disabled:              @text-disabled;
+@btn-box-shadow:                 inset 0 1px 0 rgba(255, 255, 255, .2), 0 1px 2px rgba(0, 0, 0, .05);
+@btn-box-shadow-active:          inset 0 2px 4px rgba(0, 0, 0, .15), 0 1px 2px rgba(0, 0, 0, .05);
+@btn-box-disabled-opacity:       0.4;
+@btn-bg:                         #333;
+@btn-bg-hlight:                  #454f59;
+@btn-bg-hover:                   darken(@btn-bg, 5%);
+@btn-bg-hlight-hover:            darken(@btn-bg-hlight, 5%);
+@btn-border-hover:               darken(@btn-bg, 20%);
+@btn-border-active:              darken(@btn-bg, 20%);
+@btn-padding:                    4px 10px;
+
+@btn-primary-bg:                 #006fa6;
+@btn-primary-bg-hlight:          #005580;
+@btn-primary-bg-hover:           darken(@btn-primary-bg, 5%);
+@btn-primary-bg-hover-hlight:    darken(@btn-primary-bg-hlight, 5%);
+@btn-primary-text:               #ffffff;
+@btn-primary-text-shadow:        #333333;
+@btn-primary-border-top:         mix(@btn-border-top, @btn-primary-bg, 50%);
+@btn-primary-border-right:       mix(@btn-border-right, @btn-primary-bg, 50%);
+@btn-primary-border-bottom:      mix(@btn-border-bottom, @btn-primary-bg, 50%);
+@btn-primary-border-left:        mix(@btn-border-left, @btn-primary-bg, 50%);
+@btn-primary-border:             transparent;
+@btn-primary-border-hover:       transparent;
+
+// Button group
+@btn-group-border-width:         1px;
+
+// Menu
+@menuitem-text:                  #dddddd;
+@menu-bg:                        #2f3740;
+@menu-margin:                    -1px 0 0;
+@menu-border:                    #202a33;
+@menubar-border:                 mix(@panel-border, @panel-bg, 60%);
+@menuitem-text-inverse:          #ffffff;
+@menubar-bg-active:              darken(@btn-bg, 10%);
+@menuitem-bg-hover:              #0081C2;
+@menuitem-bg-selected:           #006fa6;
+@menuitem-bg-selected-hlight:    #005580;
+@menuitem-bg-disabled:           #CCC;
+@menuitem-caret:                 @menuitem-text;
+@menuitem-caret-selected:        @menuitem-text-inverse;
+@menuitem-separator-top:         #25313f;
+@menuitem-separator-bottom:      #424f5f;
+@menuitem-bg-active:             #0085c7;
+@menuitem-text-active:           #ffffff;
+@menuitem-preview-border-active: #08608c;
+@menubar-menubtn-text:           #b5b9bf;
+
+// Panel
+@panel-border:                   #232b33;
+@panel-bg:                       #333333;
+@panel-bg-hlight:                #404952;
+
+// Tabs
+@tab-border:                     #202a33;
+@tab-bg:                         #303942;
+@tab-bg-hover:                   #404952;
+@tab-bg-active:                  #404952;
+@tabs-bg:             #303942;
+
+// Tooltip
+@tooltip-bg:                     #000;
+@tooltip-text:                   white;
+@tooltip-font-size:              11px;
+
+// Notification
+@notification-font-size:         14px;
+@notification-bg:                #f0f0f0;
+@notification-border:            #cccccc;
+@notification-text:              #333333;
+@notification-success-bg:        #dff0d8;
+@notification-success-border:    #d6e9c6;
+@notification-success-text:      #3c763d;
+@notification-info-bg:           #d9edf7;
+@notification-info-border:       #779ecb;
+@notification-info-text:         #31708f;
+@notification-warning-bg:        #fcf8e3;
+@notification-warning-border:    #faebcc;
+@notification-warning-text:      #8a6d3b;
+@notification-error-bg:          #f2dede;
+@notification-error-border:      #ebccd1;
+@notification-error-text:        #a94442;
+
+// Infobox
+@infobox-bg:                     @notification-bg;
+@infobox-border:                 @notification-border;
+@infobox-text:                   @notification-text;
+@infobox-success-bg:             @notification-success-bg;
+@infobox-success-border:         @notification-success-border;
+@infobox-success-text:           @notification-success-text;
+@infobox-info-bg:                @notification-info-bg;
+@infobox-info-border:            @notification-info-border;
+@infobox-info-text:              @notification-info-text;
+@infobox-warning-bg:             @notification-warning-bg;
+@infobox-warning-border:         @notification-warning-border;
+@infobox-warning-text:           @notification-warning-text;
+@infobox-error-bg:               @notification-error-bg;
+@infobox-error-border:           @notification-error-border;
+@infobox-error-text:             @notification-error-text;
+
+// Window
+@window-border:                  #9e9e9e;
+@window-head-border:             @window-border;
+@window-head-close:              mix(@text, @window-bg, 60%);
+@window-head-close-hover:        mix(@text, @window-bg, 40%);
+@window-foot-border:             @window-border;
+@window-foot-bg:                 @window-bg;
+@window-fullscreen-bg:           #FFF;
+@window-modalblock-bg:           #000;
+@window-modalblock-opacity:      0.3;
+@window-box-shadow:              0 3px 7px rgba(0, 0, 0, 0.3);
+@window-bg:                      #333;
+@window-title-font-size:         20px;
+
+// Popover
+@popover-bg:                     @window-bg;
+@popover-arrow-width:            10px;
+@popover-arrow:                  @window-bg;
+@popover-arrow-outer-width:      @popover-arrow-width + 1;
+@popover-arrow-outer:            rgba(0, 0, 0, 0.25);
+
+// Floatpanel
+@floatpanel-box-shadow:          0 5px 10px rgba(0, 0, 0, .2);
+
+// Checkbox
+@checkbox-bg:                    @btn-bg;
+@checkbox-bg-hlight:             @btn-bg-hlight;
+@checkbox-box-shadow:            inset 0 1px 0 rgba(255, 255, 255, .2), 0 1px 2px rgba(0, 0, 0, .05);
+@checkbox-border:                #202a33;
+@checkbox-border-focus:          #1e7dad;
+
+// Path
+@path-text:                      @text;
+@path-bg-focus:                  #666;
+@path-text-focus:                #fff;
+
+// Textbox
+@textbox-text-placeholder:       #aaa;
+@textbox-box-shadow:             inset 0 1px 1px rgba(0, 0, 0, 0.075);
+@textbox-bg:                     #515c67;
+@textbox-border:                 #202a33;
+@textbox-border-focus:           #1e7dad;
+
+// Selectbox
+@selectbox-bg:                   @textbox-bg;
+@selectbox-border:               @textbox-border;
+
+// Throbber
+@throbber-bg:                    #fff url('img/loader.gif') no-repeat center center;
+
+// Combobox
+@combobox-border:                @textbox-border;
+@combobox-error-text:            @text-error;
+@combobox-warning-text:          @text-warning;
+@combobox-success-text:          @text-success;
+
+// Colorpicker
+@colorpicker-border:             @textbox-border;
+@colorpicker-hue-bg:             #fff;
+@colorpicker-hue-border:         #333;
+
+// Grid
+@grid-bg-active:                 @menuitem-bg-active;
+@grid-border-active:             #d6d6d6;
+@grid-border:                    #d6d6d6;
+
+// Misc
+@colorbtn-backcolor-bg:          #384552;
+@iframe-border:                  @panel-border;
+
+// Slider
+@slider-border:                  #202a33;
+@slider-bg:                      #515c67;
+@slider-handle-border:           #000000;
+@slider-handle-bg:               #454f59;
+@slider-handle-bg-focus:         #BBB;
+
+// Progress
+@progress-border:                #202a33;
+@progress-bar-bg:                #515c67;
+@progress-bar-bg-hlight:         #515c67;
+@progress-text:                  #c4c4c4;
+
+// Flow layout
+@flow-layout-spacing:            2px;
+
+// Table
+
+@table-row-even:                  #fafafa;
+@table-row-hover:                 darken(@table-row-even, 10%);
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/content.inline.min.css b/public/libs/tinymce/skins/dark/content.inline.min.css
new file mode 100644 (file)
index 0000000..c6a5bf3
--- /dev/null
@@ -0,0 +1 @@
+.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid #F00;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#3399ff !important}.mce-edit-focus{outline:1px dotted #333}.mce-resize-bar-dragging{background-color:blue;opacity:.25;filter:alpha(opacity=25);zoom:1}.mce-content-body p,.mce-content-body div,.mce-content-body h1,.mce-content-body h2,.mce-content-body h3,.mce-content-body h4,.mce-content-body h5,.mce-content-body h6{line-height:1.2em}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2d8ac7}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #7ACAFF}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2d8ac7}.mce-content-body a[data-mce-selected],.mce-content-body code[data-mce-selected]{background:#bfe6ff}.mce-content-body hr{cursor:default}
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/content.min.css b/public/libs/tinymce/skins/dark/content.min.css
new file mode 100644 (file)
index 0000000..074f713
--- /dev/null
@@ -0,0 +1 @@
+body{background-color:#FFFFFF;color:#000000;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;scrollbar-3dlight-color:#F0F0EE;scrollbar-arrow-color:#676662;scrollbar-base-color:#F0F0EE;scrollbar-darkshadow-color:#DDDDDD;scrollbar-face-color:#E0E0DD;scrollbar-highlight-color:#F0F0EE;scrollbar-shadow-color:#F0F0EE;scrollbar-track-color:#F5F5F5}td,th{font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px}.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid #F00;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#3399ff !important}.mce-edit-focus{outline:1px dotted #333}.mce-resize-bar-dragging{background-color:blue;opacity:.25;filter:alpha(opacity=25);zoom:1}.mce-content-body p,.mce-content-body div,.mce-content-body h1,.mce-content-body h2,.mce-content-body h3,.mce-content-body h4,.mce-content-body h5,.mce-content-body h6{line-height:1.2em}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2d8ac7}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #7ACAFF}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2d8ac7}.mce-content-body a[data-mce-selected],.mce-content-body code[data-mce-selected]{background:#bfe6ff}.mce-content-body hr{cursor:default}
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/fonts/readme.md b/public/libs/tinymce/skins/dark/fonts/readme.md
new file mode 100644 (file)
index 0000000..fa5d639
--- /dev/null
@@ -0,0 +1 @@
+Icons are generated and provided by the http://icomoon.io service.
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce-small.eot b/public/libs/tinymce/skins/dark/fonts/tinymce-small.eot
new file mode 100644 (file)
index 0000000..b144ba0
Binary files /dev/null and b/public/libs/tinymce/skins/dark/fonts/tinymce-small.eot differ
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce-small.json b/public/libs/tinymce/skins/dark/fonts/tinymce-small.json
new file mode 100644 (file)
index 0000000..cbc985b
--- /dev/null
@@ -0,0 +1,1277 @@
+{
+       "IcoMoonType": "selection",
+       "icons": [
+               {
+                       "icon": {
+                               "paths": [
+                                       "M704 832v-37.004c151.348-61.628 256-193.82 256-346.996 0-212.078-200.576-384-448-384s-448 171.922-448 384c0 153.176 104.654 285.368 256 346.996v37.004h-192l-64-96v224h320v-222.812c-100.9-51.362-170.666-161.54-170.666-289.188 0-176.732 133.718-320 298.666-320 164.948 0 298.666 143.268 298.666 320 0 127.648-69.766 237.826-170.666 289.188v222.812h320v-224l-64 96h-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57376,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 0,
+                               "order": 1,
+                               "prevSize": 32,
+                               "code": 57376,
+                               "name": "charmap",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 0
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M256 64v896l256-256 256 256v-896h-512zM704 789.49l-192-192-192 192v-661.49h384v661.49z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57363,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 1,
+                               "order": 2,
+                               "prevSize": 32,
+                               "code": 57363,
+                               "name": "bookmark",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 1
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M927.274 230.216l-133.49-133.488c-21.104-21.104-49.232-32.728-79.198-32.728s-58.094 11.624-79.196 32.726l-165.492 165.49c-43.668 43.668-43.668 114.724 0 158.392l2.746 2.746 67.882-67.882-2.746-2.746c-6.132-6.132-6.132-16.494 0-22.626l165.492-165.492c4.010-4.008 8.808-4.608 11.312-4.608s7.302 0.598 11.312 4.61l133.49 133.488c6.132 6.134 6.132 16.498 0.002 22.628l-165.494 165.494c-4.008 4.008-8.806 4.608-11.31 4.608s-7.302-0.6-11.312-4.612l-2.746-2.746-67.88 67.884 2.742 2.742c21.106 21.108 49.23 32.728 79.2 32.728s58.094-11.624 79.196-32.726l165.494-165.492c43.662-43.666 43.662-114.72-0.004-158.39zM551.356 600.644l-67.882 67.882 2.746 2.746c4.008 4.008 4.61 8.806 4.61 11.31 0 2.506-0.598 7.302-4.606 11.314l-165.494 165.49c-4.010 4.010-8.81 4.61-11.314 4.61s-7.304-0.6-11.314-4.61l-133.492-133.486c-4.010-4.010-4.61-8.81-4.61-11.314s0.598-7.3 4.61-11.312l165.49-165.488c4.010-4.012 8.81-4.612 11.314-4.612s7.304 0.6 11.314 4.612l2.746 2.742 67.882-67.88-2.746-2.746c-21.104-21.104-49.23-32.726-79.196-32.726s-58.092 11.624-79.196 32.726l-165.488 165.486c-21.106 21.104-32.73 49.234-32.73 79.198s11.624 58.094 32.726 79.198l133.49 133.49c21.106 21.102 49.232 32.726 79.198 32.726s58.092-11.624 79.196-32.726l165.494-165.492c21.104-21.104 32.722-49.23 32.722-79.196s-11.624-58.094-32.726-79.196l-2.744-2.746zM800 838c-9.724 0-19.45-3.708-26.87-11.13l-128-127.998c-14.844-14.84-14.844-38.898 0-53.738 14.84-14.844 38.896-14.844 53.736 0l128 128c14.844 14.84 14.844 38.896 0 53.736-7.416 7.422-17.142 11.13-26.866 11.13zM608 960c-17.674 0-32-14.326-32-32v-128c0-17.674 14.326-32 32-32s32 14.326 32 32v128c0 17.674-14.326 32-32 32zM928 640h-128c-17.674 0-32-14.326-32-32s14.326-32 32-32h128c17.674 0 32 14.326 32 32s-14.326 32-32 32zM224 186c9.724 0 19.45 3.708 26.87 11.13l128 128c14.842 14.84 14.842 38.898 0 53.738-14.84 14.844-38.898 14.844-53.738 0l-128-128c-14.842-14.84-14.842-38.898 0-53.738 7.418-7.422 17.144-11.13 26.868-11.13zM416 64c17.674 0 32 14.326 32 32v128c0 17.674-14.326 32-32 32s-32-14.326-32-32v-128c0-17.674 14.326-32 32-32zM96 384h128c17.674 0 32 14.326 32 32s-14.326 32-32 32h-128c-17.674 0-32-14.326-32-32s14.326-32 32-32z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57362,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 2,
+                               "order": 3,
+                               "prevSize": 32,
+                               "code": 57362,
+                               "name": "link",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 2
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M927.274 230.216l-133.49-133.488c-21.104-21.104-49.232-32.728-79.198-32.728s-58.094 11.624-79.196 32.726l-165.492 165.49c-43.668 43.668-43.668 114.724 0 158.392l2.746 2.746 67.882-67.882-2.746-2.746c-6.132-6.132-6.132-16.494 0-22.626l165.492-165.492c4.010-4.008 8.808-4.608 11.312-4.608s7.302 0.598 11.312 4.61l133.49 133.488c6.132 6.134 6.132 16.498 0.002 22.628l-165.494 165.494c-4.008 4.008-8.806 4.608-11.31 4.608s-7.302-0.6-11.312-4.612l-2.746-2.746-67.88 67.884 2.742 2.742c21.106 21.108 49.23 32.728 79.2 32.728s58.094-11.624 79.196-32.726l165.494-165.492c43.662-43.666 43.662-114.72-0.004-158.39zM551.356 600.644l-67.882 67.882 2.746 2.746c4.008 4.008 4.61 8.806 4.61 11.31 0 2.506-0.598 7.302-4.606 11.314l-165.494 165.49c-4.010 4.010-8.81 4.61-11.314 4.61s-7.304-0.6-11.314-4.61l-133.492-133.486c-4.010-4.010-4.61-8.81-4.61-11.314s0.598-7.3 4.61-11.312l165.49-165.488c4.010-4.012 8.81-4.612 11.314-4.612s7.304 0.6 11.314 4.612l2.746 2.742 67.882-67.88-2.746-2.746c-21.104-21.104-49.23-32.726-79.196-32.726s-58.092 11.624-79.196 32.726l-165.488 165.486c-21.106 21.104-32.73 49.234-32.73 79.198s11.624 58.094 32.726 79.198l133.49 133.49c21.106 21.102 49.232 32.726 79.198 32.726s58.092-11.624 79.196-32.726l165.494-165.492c21.104-21.104 32.722-49.23 32.722-79.196s-11.624-58.094-32.726-79.196l-2.744-2.746zM352 710c-9.724 0-19.45-3.71-26.87-11.128-14.84-14.84-14.84-38.898 0-53.738l320-320c14.84-14.84 38.896-14.84 53.736 0 14.844 14.84 14.844 38.9 0 53.74l-320 320c-7.416 7.416-17.142 11.126-26.866 11.126z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57361,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 3,
+                               "order": 4,
+                               "prevSize": 32,
+                               "code": 57361,
+                               "name": "unlink",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 3
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M576 281.326v-217.326l336.002 336-336.002 336v-222.096c-390.906-9.17-315 247.096-256 446.096-288-320-212.092-690.874 256-678.674z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57360,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 4,
+                               "order": 5,
+                               "prevSize": 32,
+                               "code": 57360,
+                               "name": "redo",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 4
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M704 960c59-199 134.906-455.266-256-446.096v222.096l-336.002-336 336.002-336v217.326c468.092-12.2 544 358.674 256 678.674z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57359,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 5,
+                               "order": 6,
+                               "prevSize": 32,
+                               "code": 57359,
+                               "name": "undo",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 5
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M256.428 424.726c105.8 0 191.572 91.17 191.572 203.638 0 112.464-85.772 203.636-191.572 203.636-105.802 0-191.572-91.17-191.572-203.636l-0.856-29.092c0-224.93 171.54-407.272 383.144-407.272v116.364c-73.1 0-141.826 30.26-193.516 85.204-9.954 10.578-19.034 21.834-27.224 33.656 9.784-1.64 19.806-2.498 30.024-2.498zM768.428 424.726c105.8 0 191.572 91.17 191.572 203.638 0 112.464-85.772 203.636-191.572 203.636-105.802 0-191.572-91.17-191.572-203.636l-0.856-29.092c0-224.93 171.54-407.272 383.144-407.272v116.364c-73.1 0-141.826 30.26-193.516 85.204-9.956 10.578-19.036 21.834-27.224 33.656 9.784-1.64 19.806-2.498 30.024-2.498z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57358,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 6,
+                               "order": 7,
+                               "prevSize": 32,
+                               "code": 57358,
+                               "name": "blockquote",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 6
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 192h896v128h-896zM384 576h576v128h-576zM384 384h576v128h-576zM64 768h896v128h-896zM64 384l224 160-224 160z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57356,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 7,
+                               "order": 8,
+                               "prevSize": 32,
+                               "code": 57356,
+                               "name": "indent",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 7
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 192h896v128h-896zM64 576h576v128h-576zM64 384h576v128h-576zM64 768h896v128h-896zM960 384l-224 160 224 160z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57357,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 8,
+                               "order": 9,
+                               "prevSize": 32,
+                               "code": 57357,
+                               "name": "outdent",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 8
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M384 128h576v128h-576zM384 448h576v128h-576zM384 768h576v128h-576zM320 530v-146h-64v-320h-128v64h64v256h-64v64h128v50l-128 60v146h128v64h-128v64h128v64h-128v64h192v-320h-128v-50z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57355,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 9,
+                               "order": 10,
+                               "prevSize": 32,
+                               "code": 57355,
+                               "name": "numlist",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 9
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M384 128h576v128h-576zM384 448h576v128h-576zM384 768h576v128h-576zM128 192c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64-35.346 0-64 28.654-64 64zM128 512c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64-35.346 0-64 28.654-64 64zM128 832c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64-35.346 0-64 28.654-64 64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57354,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 10,
+                               "order": 11,
+                               "prevSize": 32,
+                               "code": 57354,
+                               "name": "bullist",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 10
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M888 384h-56v-256h64v-64h-320v64h64v256h-256v-256h64v-64h-320v64h64v256h-56c-39.6 0-72 32.4-72 72v432c0 39.6 32.4 72 72 72h240c39.6 0 72-32.4 72-72v-312h128v312c0 39.6 32.4 72 72 72h240c39.6 0 72-32.4 72-72v-432c0-39.6-32.4-72-72-72zM348 896h-184c-19.8 0-36-14.4-36-32s16.2-32 36-32h184c19.8 0 36 14.4 36 32s-16.2 32-36 32zM544 512h-64c-17.6 0-32-14.4-32-32s14.4-32 32-32h64c17.6 0 32 14.4 32 32s-14.4 32-32 32zM860 896h-184c-19.8 0-36-14.4-36-32s16.2-32 36-32h184c19.8 0 36 14.4 36 32s-16.2 32-36 32z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57353,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 11,
+                               "order": 12,
+                               "prevSize": 32,
+                               "code": 57353,
+                               "name": "searchreplace",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 11
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M704 384v-160c0-17.6-14.4-32-32-32h-160v-64c0-35.2-28.8-64-64-64h-128c-35.204 0-64 28.8-64 64v64h-160c-17.602 0-32 14.4-32 32v512c0 17.6 14.398 32 32 32h224v192h384l192-192v-384h-192zM320 128.114c0.034-0.038 0.072-0.078 0.114-0.114h127.768c0.042 0.036 0.082 0.076 0.118 0.114l0 63.886h-128v-63.886zM192 320v-64h384v64h-384zM704 869.49v-101.49h101.49l-101.49 101.49zM832 704h-192v192h-256v-448h448v256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57352,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 12,
+                               "order": 13,
+                               "prevSize": 32,
+                               "code": 57352,
+                               "name": "paste",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 12
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M832 320h-192v-64l-192-192h-384v704h384v192h576v-448l-192-192zM832 410.51l101.49 101.49h-101.49v-101.49zM448 154.51l101.49 101.49h-101.49v-101.49zM128 128h256v192h192v384h-448v-576zM960 896h-448v-128h128v-384h128v192h192v320z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57393,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 13,
+                               "order": 14,
+                               "prevSize": 32,
+                               "code": 57393,
+                               "name": "copy",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 13
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M960 512h-265.876c-50.078-35.42-114.43-54.86-182.124-54.86-89.206 0-164.572-50.242-164.572-109.712 0-59.47 75.366-109.714 164.572-109.714 75.058 0 140.308 35.576 159.12 82.286h113.016c-7.93-50.644-37.58-97.968-84.058-132.826-50.88-38.16-117.676-59.174-188.078-59.174-70.404 0-137.196 21.014-188.074 59.174-54.788 41.090-86.212 99.502-86.212 160.254s31.424 119.164 86.212 160.254c1.956 1.466 3.942 2.898 5.946 4.316h-265.872v64h512.532c58.208 17.106 100.042 56.27 100.042 100.572 0 59.468-75.368 109.71-164.572 109.71-75.060 0-140.308-35.574-159.118-82.286h-113.016c7.93 50.64 37.582 97.968 84.060 132.826 50.876 38.164 117.668 59.18 188.072 59.18 70.402 0 137.198-21.016 188.074-59.174 54.79-41.090 86.208-99.502 86.208-160.254 0-35.298-10.654-69.792-30.294-100.572h204.012v-64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57389,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 19,
+                               "order": 15,
+                               "prevSize": 32,
+                               "code": 57389,
+                               "name": "strikethrough",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 14
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M192 832h576v64h-576v-64zM640 128v384c0 31.312-14.7 61.624-41.39 85.352-30.942 27.502-73.068 42.648-118.61 42.648-45.544 0-87.668-15.146-118.608-42.648-26.692-23.728-41.392-54.040-41.392-85.352v-384h-128v384c0 141.382 128.942 256 288 256s288-114.618 288-256v-384h-128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57388,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 20,
+                               "order": 16,
+                               "prevSize": 32,
+                               "code": 57388,
+                               "name": "underline",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 15
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M832 128v64h-144l-256 640h144v64h-448v-64h144l256-640h-144v-64h448z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57387,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 21,
+                               "order": 17,
+                               "prevSize": 32,
+                               "code": 57387,
+                               "name": "italic",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 16
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M625.442 494.182c48.074-38.15 78.558-94.856 78.558-158.182 0-114.876-100.29-208-224-208h-224v768h288c123.712 0 224-93.124 224-208 0-88.196-59.118-163.562-142.558-193.818zM384 304c0-26.51 21.49-48 48-48h67.204c42.414 0 76.796 42.98 76.796 96s-34.382 96-76.796 96h-115.204v-144zM547.2 768h-115.2c-26.51 0-48-21.49-48-48v-144h163.2c42.418 0 76.8 42.98 76.8 96s-34.382 96-76.8 96z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57386,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 22,
+                               "order": 18,
+                               "prevSize": 32,
+                               "code": 57386,
+                               "name": "bold",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 17
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M850.746 242.746l-133.492-133.49c-24.888-24.892-74.054-45.256-109.254-45.256h-416c-35.2 0-64 28.8-64 64v768c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64v-544c0-35.2-20.366-84.364-45.254-109.254zM805.49 287.998c6.792 6.796 13.792 19.162 18.894 32.002h-184.384v-184.386c12.84 5.1 25.204 12.1 32 18.896l133.49 133.488zM831.884 896h-639.77c-0.040-0.034-0.082-0.076-0.114-0.116v-767.77c0.034-0.040 0.076-0.082 0.114-0.114h383.886v256h256v511.884c-0.034 0.040-0.076 0.082-0.116 0.116z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57345,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 23,
+                               "order": 19,
+                               "prevSize": 32,
+                               "code": 57345,
+                               "name": "newdocument",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 18
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M960 880v-591.938l-223.938-224.062h-592.062c-44.182 0-80 35.816-80 80v736c0 44.184 35.818 80 80 80h736c44.184 0 80-35.816 80-80zM576 192h64v192h-64v-192zM704 832h-384v-255.882c0.034-0.042 0.076-0.082 0.116-0.118h383.77c0.040 0.036 0.082 0.076 0.116 0.118l-0.002 255.882zM832 832h-64v-256c0-35.2-28.8-64-64-64h-384c-35.2 0-64 28.8-64 64v256h-64v-640h64v192c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-171.010l128 128.072v490.938z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57344,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 24,
+                               "order": 20,
+                               "prevSize": 32,
+                               "code": 57344,
+                               "name": "save",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 19
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 192v704h896v-704h-896zM384 640v-128h256v128h-256zM640 704v128h-256v-128h256zM640 320v128h-256v-128h256zM320 320v128h-192v-128h192zM128 512h192v128h-192v-128zM704 512h192v128h-192v-128zM704 448v-128h192v128h-192zM128 704h192v128h-192v-128zM704 832v-128h192v128h-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57371,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 25,
+                               "order": 21,
+                               "prevSize": 32,
+                               "code": 57371,
+                               "name": "table",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 20
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 140c99.366 0 192.782 38.694 263.042 108.956s108.958 163.678 108.958 263.044-38.696 192.782-108.958 263.042-163.676 108.958-263.042 108.958-192.782-38.696-263.044-108.958-108.956-163.676-108.956-263.042 38.694-192.782 108.956-263.044 163.678-108.956 263.044-108.956zM512 64c-247.424 0-448 200.576-448 448s200.576 448 448 448 448-200.576 448-448-200.576-448-448-448v0zM320 384c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64-35.346 0-64 28.654-64 64zM576 384c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64-35.346 0-64 28.654-64 64zM512 656c-101.84 0-192.56-36.874-251.166-94.328 23.126 117.608 126.778 206.328 251.166 206.328 124.388 0 228.040-88.72 251.168-206.328-58.608 57.454-149.328 94.328-251.168 94.328z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57377,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 26,
+                               "order": 22,
+                               "prevSize": 32,
+                               "code": 57377,
+                               "name": "emoticons",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 21
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M480 384l-192-192 128-128h-352v352l128-128 192 192zM640 480l192-192 128 128v-352h-352l128 128-192 192zM544 640l192 192-128 128h352v-352l-128 128-192-192zM384 544l-192 192-128-128v352h352l-128-128 192-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57379,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 27,
+                               "order": 23,
+                               "prevSize": 32,
+                               "code": 57379,
+                               "name": "fullscreen",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 22
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 448h896v128h-896z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57372,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 28,
+                               "order": 24,
+                               "prevSize": 32,
+                               "code": 57372,
+                               "name": "hr",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 23
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 768h512v128h-512v-128zM768 192h-220.558l-183.766 512h-132.288l183.762-512h-223.15v-128h576v128zM929.774 896l-129.774-129.774-129.774 129.774-62.226-62.226 129.774-129.774-129.774-129.774 62.226-62.226 129.774 129.774 129.774-129.774 62.226 62.226-129.774 129.774 129.774 129.774-62.226 62.226z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57373,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 29,
+                               "order": 25,
+                               "prevSize": 32,
+                               "code": 57373,
+                               "name": "removefromat",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 24
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M256 128h512v128h-512v-128zM896 320h-768c-35.2 0-64 28.8-64 64v256c0 35.2 28.796 64 64 64h128v192h512v-192h128c35.2 0 64-28.8 64-64v-256c0-35.2-28.8-64-64-64zM704 832h-384v-256h384v256zM910.4 416c0 25.626-20.774 46.4-46.398 46.4s-46.402-20.774-46.402-46.4 20.778-46.4 46.402-46.4c25.626 0 46.398 20.774 46.398 46.4z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57378,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 30,
+                               "order": 26,
+                               "prevSize": 32,
+                               "code": 57378,
+                               "name": "print",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 25
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M384 128c-123.712 0-224 100.288-224 224s100.288 224 224 224v320h128v-640h64v640h128v-640h128v-128h-448z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57390,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 31,
+                               "order": 27,
+                               "prevSize": 32,
+                               "code": 57390,
+                               "name": "visualchars",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 26
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M448 128c-123.712 0-224 100.288-224 224s100.288 224 224 224v320h128v-640h64v640h128v-640h128v-128h-448zM64 896l224-192-224-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57391,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 32,
+                               "order": 28,
+                               "prevSize": 32,
+                               "code": 57391,
+                               "name": "ltr",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 27
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M416 704l-192-192 192-192-64-64-256 256 256 256zM672 256l-64 64 192 192-192 192 64 64 256-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57367,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 38,
+                               "order": 29,
+                               "prevSize": 32,
+                               "code": 57367,
+                               "name": "code",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 28
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M448 704h128v128h-128v-128zM704 256c35.346 0 64 28.654 64 64v166l-228 154h-92v-64l192-128v-64h-320v-128h384zM512 64c-119.666 0-232.166 46.6-316.784 131.216-84.614 84.618-131.216 197.118-131.216 316.784 0 119.664 46.602 232.168 131.216 316.784 84.618 84.616 197.118 131.216 316.784 131.216 119.664 0 232.168-46.6 316.784-131.216 84.616-84.616 131.216-197.12 131.216-316.784 0-119.666-46.6-232.166-131.216-316.784-84.616-84.616-197.12-131.216-316.784-131.216z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57366,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 39,
+                               "order": 30,
+                               "prevSize": 32,
+                               "code": 57366,
+                               "name": "help",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 29
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M896 128h-768c-35.2 0-64 28.8-64 64v640c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64v-640c0-35.2-28.8-64-64-64zM896 831.884c-0.012 0.014-0.030 0.028-0.042 0.042l-191.958-319.926-160 128-224-288-191.968 479.916c-0.010-0.010-0.022-0.022-0.032-0.032v-639.77c0.034-0.040 0.076-0.082 0.114-0.114h767.77c0.040 0.034 0.082 0.076 0.116 0.116v639.768zM640 352c0 53.019 42.981 96 96 96s96-42.981 96-96c0-53.019-42.981-96-96-96-53.019 0-96 42.981-96 96z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57364,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 40,
+                               "order": 31,
+                               "prevSize": 32,
+                               "code": 57364,
+                               "name": "image",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 30
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M896 128h-768c-35.2 0-64 28.8-64 64v640c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64v-640c0-35.2-28.8-64-64-64zM256 832h-128v-128h128v128zM256 576h-128v-128h128v128zM256 320h-128v-128h128v128zM704 832h-384v-640h384v640zM896 832h-128v-128h128v128zM896 576h-128v-128h128v128zM896 320h-128v-128h128v128zM384 320v384l288-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57365,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 41,
+                               "order": 32,
+                               "prevSize": 32,
+                               "code": 57365,
+                               "name": "media",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 31
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M77.798 304.624l81.414 50.882c50.802-81.114 128.788-143.454 221.208-174.246l-30.366-91.094c-113.748 37.898-209.728 114.626-272.256 214.458zM673.946 90.166l-30.366 91.094c92.422 30.792 170.404 93.132 221.208 174.248l81.412-50.882c-62.526-99.834-158.506-176.562-272.254-214.46zM607.974 704.008c-4.808 0-9.692-1.090-14.286-3.386l-145.688-72.844v-211.778c0-17.672 14.328-32 32-32s32 14.328 32 32v172.222l110.31 55.156c15.806 7.902 22.214 27.124 14.31 42.932-5.604 11.214-16.908 17.696-28.646 17.698zM512 192c-212.078 0-384 171.922-384 384s171.922 384 384 384c212.078 0 384-171.922 384-384 0-212.078-171.922-384-384-384zM512 864c-159.058 0-288-128.942-288-288s128.942-288 288-288c159.058 0 288 128.942 288 288 0 159.058-128.942 288-288 288z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57368,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 42,
+                               "order": 33,
+                               "prevSize": 32,
+                               "code": 57368,
+                               "name": "insertdatetime",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 32
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 455.746c45.318-49.92 97.162-92.36 153.272-125.124 90.332-52.744 192.246-80.622 294.728-80.622 102.48 0 204.396 27.878 294.726 80.624 56.112 32.764 107.956 75.204 153.274 125.124v-117.432c-33.010-28.118-68.124-53.14-104.868-74.594-105.006-61.314-223.658-93.722-343.132-93.722s-238.128 32.408-343.134 93.72c-36.742 21.454-71.856 46.478-104.866 74.596v117.43zM512 320c-183.196 0-345.838 100.556-448 256 102.162 155.448 264.804 256 448 256 183.196 0 345.838-100.552 448-256-102.162-155.444-264.804-256-448-256zM512 512c0 35.346-28.654 64-64 64s-64-28.654-64-64c0-35.348 28.654-64 64-64s64 28.652 64 64zM728.066 696.662c-67.434 39.374-140.128 59.338-216.066 59.338s-148.632-19.964-216.066-59.338c-51.554-30.104-98.616-71.31-138.114-120.662 39.498-49.35 86.56-90.558 138.116-120.66 13.276-7.752 26.758-14.74 40.426-20.982-10.512 23.742-16.362 50.008-16.362 77.642 0 106.040 85.962 192 192 192 106.040 0 192-85.96 192-192 0-27.634-5.85-53.9-16.36-77.642 13.668 6.244 27.15 13.23 40.426 20.982 51.554 30.102 98.616 71.31 138.116 120.66-39.498 49.352-86.56 90.558-138.116 120.662z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57369,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 43,
+                               "order": 34,
+                               "prevSize": 32,
+                               "code": 57369,
+                               "name": "preview",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 33
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M651.168 283.834c-24.612-81.962-28.876-91.834-107.168-91.834h-64c-79.618 0-82.664 10.152-108.418 96 0 0.002 0 0.002-0.002 0.004l-143.998 479.996h113.636l57.6-192h226.366l57.6 192h113.63l-145.246-484.166zM437.218 448l38.4-136c10.086-33.618 36.38-30 36.38-30s26.294-3.618 36.38 30h0.004l38.4 136h-149.564z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57370,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 44,
+                               "order": 35,
+                               "prevSize": 32,
+                               "code": 57370,
+                               "name": "forecolor",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 34
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M576 64c247.424 0 448 200.576 448 448s-200.576 448-448 448v-96c94.024 0 182.418-36.614 248.902-103.098 66.484-66.484 103.098-154.878 103.098-248.902 0-94.022-36.614-182.418-103.098-248.902-66.484-66.484-154.878-103.098-248.902-103.098-94.022 0-182.418 36.614-248.902 103.098-51.14 51.138-84.582 115.246-97.306 184.902h186.208l-224 256-224-256h164.57c31.060-217.102 217.738-384 443.43-384zM768 448v128h-256v-320h128v192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57384,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 45,
+                               "order": 36,
+                               "prevSize": 32,
+                               "code": 57384,
+                               "name": "restoredraft",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 35
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M1024 592.458v-160.916l-159.144-15.914c-8.186-30.042-20.088-58.548-35.21-84.98l104.596-127.838-113.052-113.050-127.836 104.596c-26.434-15.124-54.942-27.026-84.982-35.208l-15.914-159.148h-160.916l-15.914 159.146c-30.042 8.186-58.548 20.086-84.98 35.208l-127.838-104.594-113.050 113.050 104.596 127.836c-15.124 26.432-27.026 54.94-35.21 84.98l-159.146 15.916v160.916l159.146 15.914c8.186 30.042 20.086 58.548 35.21 84.982l-104.596 127.836 113.048 113.048 127.838-104.596c26.432 15.124 54.94 27.028 84.98 35.21l15.916 159.148h160.916l15.914-159.144c30.042-8.186 58.548-20.088 84.982-35.21l127.836 104.596 113.048-113.048-104.596-127.836c15.124-26.434 27.028-54.942 35.21-84.98l159.148-15.92zM704 576l-128 128h-128l-128-128v-128l128-128h128l128 128v128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57346,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 46,
+                               "order": 37,
+                               "prevSize": 32,
+                               "code": 57346,
+                               "name": "fullpage",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 36
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 206v50h128v64h-192v-146l128-60v-50h-128v-64h192v146zM676 256h-136l-188 188-188-188h-136l256 256-256 256h136l188-188 188 188h136l-256-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57375,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 47,
+                               "order": 38,
+                               "prevSize": 32,
+                               "code": 57375,
+                               "name": "superscript",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 37
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 910v50h128v64h-192v-146l128-60v-50h-128v-64h192v146zM676 256h-136l-188 188-188-188h-136l256 256-256 256h136l188-188 188 188h136l-256-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57374,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 48,
+                               "order": 39,
+                               "prevSize": 32,
+                               "code": 57374,
+                               "name": "subscript",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 38
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M704 384v-160c0-17.6-14.4-32-32-32h-160v-64c0-35.2-28.8-64-64-64h-128c-35.204 0-64 28.8-64 64v64h-160c-17.602 0-32 14.4-32 32v512c0 17.6 14.398 32 32 32h224v192h576v-576h-192zM320 128.114c0.034-0.038 0.072-0.078 0.114-0.114h127.768c0.042 0.036 0.082 0.076 0.118 0.114l0 63.886h-128v-63.886zM192 320v-64h384v64h-384zM832 896h-448v-448h448v448zM448 512v128h32l32-64h64v192h-48v64h160v-64h-48v-192h64l32 64h32v-128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "pastetext"
+                               ],
+                               "defaultCode": 57397,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 49,
+                               "order": 40,
+                               "prevSize": 32,
+                               "code": 57397,
+                               "name": "pastetext",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 39
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 256h64v64h-64zM640 384h64v64h-64zM640 512h64v64h-64zM640 640h64v64h-64zM512 512h64v64h-64zM512 640h64v64h-64zM384 640h64v64h-64zM768 384h64v64h-64zM768 512h64v64h-64zM768 640h64v64h-64zM768 768h64v64h-64zM640 768h64v64h-64zM512 768h64v64h-64zM384 768h64v64h-64zM256 768h64v64h-64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "resize",
+                                       "dots"
+                               ],
+                               "defaultCode": 57394,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 50,
+                               "order": 41,
+                               "prevSize": 32,
+                               "code": 57394,
+                               "name": "resize",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 40
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M928 128h-416l-32-64h-352l-64 128h896zM840.34 704h87.66l32-448h-896l64 640h356.080c-104.882-37.776-180.080-138.266-180.080-256 0-149.982 122.018-272 272-272 149.98 0 272 122.018 272 272 0 21.678-2.622 43.15-7.66 64zM874.996 849.75l-134.496-110.692c17.454-28.922 27.5-62.814 27.5-99.058 0-106.040-85.96-192-192-192s-192 85.96-192 192 85.96 192 192 192c36.244 0 70.138-10.046 99.058-27.5l110.692 134.496c22.962 26.678 62.118 28.14 87.006 3.252l5.492-5.492c24.888-24.888 23.426-64.044-3.252-87.006zM576 764c-68.484 0-124-55.516-124-124s55.516-124 124-124 124 55.516 124 124-55.516 124-124 124z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "browse"
+                               ],
+                               "defaultCode": 57396,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 51,
+                               "order": 42,
+                               "prevSize": 32,
+                               "code": 57396,
+                               "name": "browse",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 41
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M864.408 670.132c-46.47-46.47-106.938-68.004-161.082-62.806l-63.326-63.326 192-192c0 0 128-128 0-256l-320 320-320-320c-128 128 0 256 0 256l192 192-63.326 63.326c-54.144-5.198-114.61 16.338-161.080 62.806-74.98 74.98-85.112 186.418-22.626 248.9 62.482 62.482 173.92 52.354 248.9-22.626 46.47-46.468 68.002-106.938 62.806-161.080l63.326-63.326 63.328 63.328c-5.196 54.144 16.336 114.61 62.806 161.078 74.978 74.98 186.418 85.112 248.898 22.626 62.488-62.482 52.356-173.918-22.624-248.9zM353.124 758.578c-2.212 24.332-15.020 49.826-35.14 69.946-22.212 22.214-51.080 35.476-77.218 35.476-10.524 0-25.298-2.228-35.916-12.848-21.406-21.404-17.376-73.132 22.626-113.136 22.212-22.214 51.080-35.476 77.218-35.476 10.524 0 25.298 2.228 35.916 12.848 13.112 13.11 13.47 32.688 12.514 43.19zM512 608c-35.346 0-64-28.654-64-64s28.654-64 64-64 64 28.654 64 64-28.654 64-64 64zM819.152 851.152c-10.62 10.62-25.392 12.848-35.916 12.848-26.138 0-55.006-13.262-77.218-35.476-20.122-20.12-32.928-45.614-35.138-69.946-0.958-10.502-0.6-30.080 12.514-43.192 10.618-10.622 25.39-12.848 35.916-12.848 26.136 0 55.006 13.262 77.216 35.474 40.004 40.008 44.032 91.736 22.626 113.14z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57351,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 14,
+                               "order": 43,
+                               "prevSize": 32,
+                               "code": 57351,
+                               "name": "cut",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 42
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 192h896v128h-896zM64 576h896v128h-896zM64 384h896v128h-896zM64 768h896v128h-896z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57350,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 15,
+                               "order": 44,
+                               "prevSize": 32,
+                               "code": 57350,
+                               "name": "alignjustify",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 43
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 192h896v128h-896zM64 576h896v128h-896zM256 384h512v128h-512zM256 768h512v128h-512z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57348,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 16,
+                               "order": 45,
+                               "prevSize": 32,
+                               "code": 57348,
+                               "name": "aligncenter",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 44
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 192h896v128h-896zM64 576h896v128h-896zM384 384h576v128h-576zM384 768h576v128h-576z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57349,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 17,
+                               "order": 46,
+                               "prevSize": 32,
+                               "code": 57349,
+                               "name": "alignright",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 45
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 192h896v128h-896zM64 576h896v128h-896zM64 384h576v128h-576zM64 768h576v128h-576z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57347,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 18,
+                               "order": 47,
+                               "prevSize": 32,
+                               "code": 57347,
+                               "name": "alignleft",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 46
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M320 128c-123.712 0-224 100.288-224 224s100.288 224 224 224v320h128v-640h64v640h128v-640h128v-128h-448zM960 512l-224 192 224 192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57392,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 33,
+                               "order": 48,
+                               "prevSize": 32,
+                               "code": 57392,
+                               "name": "rtl",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 47
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 384h128v64h-128zM512 768h128v64h-128zM576 576h128v64h-128zM768 576v192h-64v64h128v-256zM384 576h128v64h-128zM320 768h128v64h-128zM320 384h128v64h-128zM192 192v256h64v-192h64v-64zM704 448h128v-256h-64v192h-64zM64 64v896h896v-896h-896zM896 896h-768v-768h768v768zM192 576v256h64v-192h64v-64zM576 192h128v64h-128zM384 192h128v64h-128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57382,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 34,
+                               "order": 49,
+                               "prevSize": 32,
+                               "code": 57382,
+                               "name": "template",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 48
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M816 64l16 384h-640l16-384h32l16 320h512l16-320h32zM208 960l-16-320h640l-16 320h-32l-16-256h-512l-16 256h-32zM64 512h128v64h-128zM256 512h128v64h-128zM448 512h128v64h-128zM640 512h128v64h-128zM832 512h128v64h-128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57383,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 35,
+                               "order": 50,
+                               "prevSize": 32,
+                               "code": 57383,
+                               "name": "pagebreak",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 49
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M960 128v-64h-192c-35.202 0-64 28.8-64 64v320c0 15.856 5.858 30.402 15.496 41.614l-303.496 260.386-142-148-82 70 224 288 416-448h128v-64h-192v-320h192zM256 512h64v-384c0-35.2-28.8-64-64-64h-128c-35.2 0-64 28.8-64 64v384h64v-192h128v192zM128 256v-128h128v128h-128zM640 448v-96c0-35.2-8.8-64-44-64 35.2 0 44-28.8 44-64v-96c0-35.2-28.8-64-64-64h-192v448h192c35.2 0 64-28.8 64-64zM448 128h128v128h-128v-128zM448 320h128v128h-128v-128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57380,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 36,
+                               "order": 51,
+                               "prevSize": 32,
+                               "code": 57380,
+                               "name": "spellcheck",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 50
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M448 512h-128v-128h128v-128h128v128h128v128h-128v128h-128v-128zM960 576v320h-896v-320h128v192h640v-192h128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57381,
+                               "grid": 0
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 37,
+                               "order": 52,
+                               "prevSize": 32,
+                               "code": 57381,
+                               "name": "nonbreaking",
+                               "ligatures": ""
+                       },
+                       "setIdx": 0,
+                       "setId": 2,
+                       "iconIdx": 51
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M256 352v-128c0-53.020 42.98-96 96-96h32v-128h-32c-123.712 0-224 100.288-224 224v128c0 53.020-42.98 96-96 96h-32v128h32c53.020 0 96 42.98 96 96v128c0 123.71 100.288 224 224 224h32v-128h-32c-53.020 0-96-42.98-96-96v-128c0-62.684-25.758-119.342-67.254-160 41.496-40.658 67.254-97.316 67.254-160z",
+                                       "M1024 352v-128c0-53.020-42.98-96-96-96h-32v-128h32c123.71 0 224 100.288 224 224v128c0 53.020 42.98 96 96 96h32v128h-32c-53.020 0-96 42.98-96 96v128c0 123.71-100.29 224-224 224h-32v-128h32c53.020 0 96-42.98 96-96v-128c0-62.684 25.758-119.342 67.254-160-41.496-40.658-67.254-97.316-67.254-160z",
+                                       "M768 320.882c0 70.692-57.308 128-128 128s-128-57.308-128-128c0-70.692 57.308-128 128-128s128 57.308 128 128z",
+                                       "M640 511.118c-70.692 0-128 57.308-128 128 0 68.732 32 123.216 130.156 127.852-29.19 41.126-73.156 57.366-130.156 62.7v76c0 0 256 22.332 256-266.55-0.25-70.694-57.306-128.002-128-128.002z"
+                               ],
+                               "width": 1280,
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "code",
+                                       "semicolon",
+                                       "curly-braces"
+                               ],
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 1,
+                               "id": 0,
+                               "prevSize": 16,
+                               "code": 58883,
+                               "name": "codesample"
+                       },
+                       "setIdx": 1,
+                       "setId": 1,
+                       "iconIdx": 0
+               }
+       ],
+       "height": 1024,
+       "metadata": {
+               "name": "tinymce-small"
+       },
+       "preferences": {
+               "showGlyphs": true,
+               "showQuickUse": true,
+               "showQuickUse2": true,
+               "showSVGs": true,
+               "fontPref": {
+                       "prefix": "icon-",
+                       "metadata": {
+                               "fontFamily": "tinymce-small",
+                               "majorVersion": 1,
+                               "minorVersion": 0
+                       },
+                       "metrics": {
+                               "emSize": 1024,
+                               "baseline": 6.25,
+                               "whitespace": 50
+                       },
+                       "showMetrics": false,
+                       "showMetadata": false,
+                       "showVersion": false,
+                       "embed": false
+               },
+               "imagePref": {
+                       "prefix": "icon-",
+                       "png": true,
+                       "useClassSelector": true,
+                       "color": 4473924,
+                       "bgColor": 16777215
+               },
+               "historySize": 100,
+               "showCodes": true
+       }
+}
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce-small.svg b/public/libs/tinymce/skins/dark/fonts/tinymce-small.svg
new file mode 100644 (file)
index 0000000..b4ee6f4
--- /dev/null
@@ -0,0 +1,63 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>Generated by IcoMoon</metadata>
+<defs>
+<font id="tinymce-small" horiz-adv-x="1024">
+<font-face units-per-em="1024" ascent="960" descent="-64" />
+<missing-glyph horiz-adv-x="1024" />
+<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
+<glyph unicode="&#xe000;" glyph-name="save" d="M960 80v591.938l-223.938 224.062h-592.062c-44.182 0-80-35.816-80-80v-736c0-44.184 35.818-80 80-80h736c44.184 0 80 35.816 80 80zM576 768h64v-192h-64v192zM704 128h-384v255.882c0.034 0.042 0.076 0.082 0.116 0.118h383.77c0.040-0.036 0.082-0.076 0.116-0.118l-0.002-255.882zM832 128h-64v256c0 35.2-28.8 64-64 64h-384c-35.2 0-64-28.8-64-64v-256h-64v640h64v-192c0-35.2 28.8-64 64-64h320c35.2 0 64 28.8 64 64v171.010l128-128.072v-490.938z" />
+<glyph unicode="&#xe001;" glyph-name="newdocument" d="M850.746 717.254l-133.492 133.49c-24.888 24.892-74.054 45.256-109.254 45.256h-416c-35.2 0-64-28.8-64-64v-768c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v544c0 35.2-20.366 84.364-45.254 109.254zM805.49 672.002c6.792-6.796 13.792-19.162 18.894-32.002h-184.384v184.386c12.84-5.1 25.204-12.1 32-18.896l133.49-133.488zM831.884 64h-639.77c-0.040 0.034-0.082 0.076-0.114 0.116v767.77c0.034 0.040 0.076 0.082 0.114 0.114h383.886v-256h256v-511.884c-0.034-0.040-0.076-0.082-0.116-0.116z" />
+<glyph unicode="&#xe002;" glyph-name="fullpage" d="M1024 367.542v160.916l-159.144 15.914c-8.186 30.042-20.088 58.548-35.21 84.98l104.596 127.838-113.052 113.050-127.836-104.596c-26.434 15.124-54.942 27.026-84.982 35.208l-15.914 159.148h-160.916l-15.914-159.146c-30.042-8.186-58.548-20.086-84.98-35.208l-127.838 104.594-113.050-113.050 104.596-127.836c-15.124-26.432-27.026-54.94-35.21-84.98l-159.146-15.916v-160.916l159.146-15.914c8.186-30.042 20.086-58.548 35.21-84.982l-104.596-127.836 113.048-113.048 127.838 104.596c26.432-15.124 54.94-27.028 84.98-35.21l15.916-159.148h160.916l15.914 159.144c30.042 8.186 58.548 20.088 84.982 35.21l127.836-104.596 113.048 113.048-104.596 127.836c15.124 26.434 27.028 54.942 35.21 84.98l159.148 15.92zM704 384l-128-128h-128l-128 128v128l128 128h128l128-128v-128z" />
+<glyph unicode="&#xe003;" glyph-name="alignleft" d="M64 768h896v-128h-896zM64 384h896v-128h-896zM64 576h576v-128h-576zM64 192h576v-128h-576z" />
+<glyph unicode="&#xe004;" glyph-name="aligncenter" d="M64 768h896v-128h-896zM64 384h896v-128h-896zM256 576h512v-128h-512zM256 192h512v-128h-512z" />
+<glyph unicode="&#xe005;" glyph-name="alignright" d="M64 768h896v-128h-896zM64 384h896v-128h-896zM384 576h576v-128h-576zM384 192h576v-128h-576z" />
+<glyph unicode="&#xe006;" glyph-name="alignjustify" d="M64 768h896v-128h-896zM64 384h896v-128h-896zM64 576h896v-128h-896zM64 192h896v-128h-896z" />
+<glyph unicode="&#xe007;" glyph-name="cut" d="M864.408 289.868c-46.47 46.47-106.938 68.004-161.082 62.806l-63.326 63.326 192 192c0 0 128 128 0 256l-320-320-320 320c-128-128 0-256 0-256l192-192-63.326-63.326c-54.144 5.198-114.61-16.338-161.080-62.806-74.98-74.98-85.112-186.418-22.626-248.9 62.482-62.482 173.92-52.354 248.9 22.626 46.47 46.468 68.002 106.938 62.806 161.080l63.326 63.326 63.328-63.328c-5.196-54.144 16.336-114.61 62.806-161.078 74.978-74.98 186.418-85.112 248.898-22.626 62.488 62.482 52.356 173.918-22.624 248.9zM353.124 201.422c-2.212-24.332-15.020-49.826-35.14-69.946-22.212-22.214-51.080-35.476-77.218-35.476-10.524 0-25.298 2.228-35.916 12.848-21.406 21.404-17.376 73.132 22.626 113.136 22.212 22.214 51.080 35.476 77.218 35.476 10.524 0 25.298-2.228 35.916-12.848 13.112-13.11 13.47-32.688 12.514-43.19zM512 352c-35.346 0-64 28.654-64 64s28.654 64 64 64 64-28.654 64-64-28.654-64-64-64zM819.152 108.848c-10.62-10.62-25.392-12.848-35.916-12.848-26.138 0-55.006 13.262-77.218 35.476-20.122 20.12-32.928 45.614-35.138 69.946-0.958 10.502-0.6 30.080 12.514 43.192 10.618 10.622 25.39 12.848 35.916 12.848 26.136 0 55.006-13.262 77.216-35.474 40.004-40.008 44.032-91.736 22.626-113.14z" />
+<glyph unicode="&#xe008;" glyph-name="paste" d="M704 576v160c0 17.6-14.4 32-32 32h-160v64c0 35.2-28.8 64-64 64h-128c-35.204 0-64-28.8-64-64v-64h-160c-17.602 0-32-14.4-32-32v-512c0-17.6 14.398-32 32-32h224v-192h384l192 192v384h-192zM320 831.886c0.034 0.038 0.072 0.078 0.114 0.114h127.768c0.042-0.036 0.082-0.076 0.118-0.114v-63.886h-128v63.886zM192 640v64h384v-64h-384zM704 90.51v101.49h101.49l-101.49-101.49zM832 256h-192v-192h-256v448h448v-256z" />
+<glyph unicode="&#xe009;" glyph-name="searchreplace" d="M888 576h-56v256h64v64h-320v-64h64v-256h-256v256h64v64h-320v-64h64v-256h-56c-39.6 0-72-32.4-72-72v-432c0-39.6 32.4-72 72-72h240c39.6 0 72 32.4 72 72v312h128v-312c0-39.6 32.4-72 72-72h240c39.6 0 72 32.4 72 72v432c0 39.6-32.4 72-72 72zM348 64h-184c-19.8 0-36 14.4-36 32s16.2 32 36 32h184c19.8 0 36-14.4 36-32s-16.2-32-36-32zM544 448h-64c-17.6 0-32 14.4-32 32s14.4 32 32 32h64c17.6 0 32-14.4 32-32s-14.4-32-32-32zM860 64h-184c-19.8 0-36 14.4-36 32s16.2 32 36 32h184c19.8 0 36-14.4 36-32s-16.2-32-36-32z" />
+<glyph unicode="&#xe00a;" glyph-name="bullist" d="M384 832h576v-128h-576zM384 512h576v-128h-576zM384 192h576v-128h-576zM128 768c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM128 448c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM128 128c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64z" />
+<glyph unicode="&#xe00b;" glyph-name="numlist" d="M384 832h576v-128h-576zM384 512h576v-128h-576zM384 192h576v-128h-576zM320 430v146h-64v320h-128v-64h64v-256h-64v-64h128v-50l-128-60v-146h128v-64h-128v-64h128v-64h-128v-64h192v320h-128v50z" />
+<glyph unicode="&#xe00c;" glyph-name="indent" d="M64 768h896v-128h-896zM384 384h576v-128h-576zM384 576h576v-128h-576zM64 192h896v-128h-896zM64 576l224-160-224-160z" />
+<glyph unicode="&#xe00d;" glyph-name="outdent" d="M64 768h896v-128h-896zM64 384h576v-128h-576zM64 576h576v-128h-576zM64 192h896v-128h-896zM960 576l-224-160 224-160z" />
+<glyph unicode="&#xe00e;" glyph-name="blockquote" d="M256.428 535.274c105.8 0 191.572-91.17 191.572-203.638 0-112.464-85.772-203.636-191.572-203.636-105.802 0-191.572 91.17-191.572 203.636l-0.856 29.092c0 224.93 171.54 407.272 383.144 407.272v-116.364c-73.1 0-141.826-30.26-193.516-85.204-9.954-10.578-19.034-21.834-27.224-33.656 9.784 1.64 19.806 2.498 30.024 2.498zM768.428 535.274c105.8 0 191.572-91.17 191.572-203.638 0-112.464-85.772-203.636-191.572-203.636-105.802 0-191.572 91.17-191.572 203.636l-0.856 29.092c0 224.93 171.54 407.272 383.144 407.272v-116.364c-73.1 0-141.826-30.26-193.516-85.204-9.956-10.578-19.036-21.834-27.224-33.656 9.784 1.64 19.806 2.498 30.024 2.498z" />
+<glyph unicode="&#xe00f;" glyph-name="undo" d="M704 0c59 199 134.906 455.266-256 446.096v-222.096l-336.002 336 336.002 336v-217.326c468.092 12.2 544-358.674 256-678.674z" />
+<glyph unicode="&#xe010;" glyph-name="redo" d="M576 678.674v217.326l336.002-336-336.002-336v222.096c-390.906 9.17-315-247.096-256-446.096-288 320-212.092 690.874 256 678.674z" />
+<glyph unicode="&#xe011;" glyph-name="unlink" d="M927.274 729.784l-133.49 133.488c-21.104 21.104-49.232 32.728-79.198 32.728s-58.094-11.624-79.196-32.726l-165.492-165.49c-43.668-43.668-43.668-114.724 0-158.392l2.746-2.746 67.882 67.882-2.746 2.746c-6.132 6.132-6.132 16.494 0 22.626l165.492 165.492c4.010 4.008 8.808 4.608 11.312 4.608s7.302-0.598 11.312-4.61l133.49-133.488c6.132-6.134 6.132-16.498 0.002-22.628l-165.494-165.494c-4.008-4.008-8.806-4.608-11.31-4.608s-7.302 0.6-11.312 4.612l-2.746 2.746-67.88-67.884 2.742-2.742c21.106-21.108 49.23-32.728 79.2-32.728s58.094 11.624 79.196 32.726l165.494 165.492c43.662 43.666 43.662 114.72-0.004 158.39zM551.356 359.356l-67.882-67.882 2.746-2.746c4.008-4.008 4.61-8.806 4.61-11.31 0-2.506-0.598-7.302-4.606-11.314l-165.494-165.49c-4.010-4.010-8.81-4.61-11.314-4.61s-7.304 0.6-11.314 4.61l-133.492 133.486c-4.010 4.010-4.61 8.81-4.61 11.314s0.598 7.3 4.61 11.312l165.49 165.488c4.010 4.012 8.81 4.612 11.314 4.612s7.304-0.6 11.314-4.612l2.746-2.742 67.882 67.88-2.746 2.746c-21.104 21.104-49.23 32.726-79.196 32.726s-58.092-11.624-79.196-32.726l-165.488-165.486c-21.106-21.104-32.73-49.234-32.73-79.198s11.624-58.094 32.726-79.198l133.49-133.49c21.106-21.102 49.232-32.726 79.198-32.726s58.092 11.624 79.196 32.726l165.494 165.492c21.104 21.104 32.722 49.23 32.722 79.196s-11.624 58.094-32.726 79.196l-2.744 2.746zM352 250c-9.724 0-19.45 3.71-26.87 11.128-14.84 14.84-14.84 38.898 0 53.738l320 320c14.84 14.84 38.896 14.84 53.736 0 14.844-14.84 14.844-38.9 0-53.74l-320-320c-7.416-7.416-17.142-11.126-26.866-11.126z" />
+<glyph unicode="&#xe012;" glyph-name="link" d="M927.274 729.784l-133.49 133.488c-21.104 21.104-49.232 32.728-79.198 32.728s-58.094-11.624-79.196-32.726l-165.492-165.49c-43.668-43.668-43.668-114.724 0-158.392l2.746-2.746 67.882 67.882-2.746 2.746c-6.132 6.132-6.132 16.494 0 22.626l165.492 165.492c4.010 4.008 8.808 4.608 11.312 4.608s7.302-0.598 11.312-4.61l133.49-133.488c6.132-6.134 6.132-16.498 0.002-22.628l-165.494-165.494c-4.008-4.008-8.806-4.608-11.31-4.608s-7.302 0.6-11.312 4.612l-2.746 2.746-67.88-67.884 2.742-2.742c21.106-21.108 49.23-32.728 79.2-32.728s58.094 11.624 79.196 32.726l165.494 165.492c43.662 43.666 43.662 114.72-0.004 158.39zM551.356 359.356l-67.882-67.882 2.746-2.746c4.008-4.008 4.61-8.806 4.61-11.31 0-2.506-0.598-7.302-4.606-11.314l-165.494-165.49c-4.010-4.010-8.81-4.61-11.314-4.61s-7.304 0.6-11.314 4.61l-133.492 133.486c-4.010 4.010-4.61 8.81-4.61 11.314s0.598 7.3 4.61 11.312l165.49 165.488c4.010 4.012 8.81 4.612 11.314 4.612s7.304-0.6 11.314-4.612l2.746-2.742 67.882 67.88-2.746 2.746c-21.104 21.104-49.23 32.726-79.196 32.726s-58.092-11.624-79.196-32.726l-165.488-165.486c-21.106-21.104-32.73-49.234-32.73-79.198s11.624-58.094 32.726-79.198l133.49-133.49c21.106-21.102 49.232-32.726 79.198-32.726s58.092 11.624 79.196 32.726l165.494 165.492c21.104 21.104 32.722 49.23 32.722 79.196s-11.624 58.094-32.726 79.196l-2.744 2.746zM800 122c-9.724 0-19.45 3.708-26.87 11.13l-128 127.998c-14.844 14.84-14.844 38.898 0 53.738 14.84 14.844 38.896 14.844 53.736 0l128-128c14.844-14.84 14.844-38.896 0-53.736-7.416-7.422-17.142-11.13-26.866-11.13zM608 0c-17.674 0-32 14.326-32 32v128c0 17.674 14.326 32 32 32s32-14.326 32-32v-128c0-17.674-14.326-32-32-32zM928 320h-128c-17.674 0-32 14.326-32 32s14.326 32 32 32h128c17.674 0 32-14.326 32-32s-14.326-32-32-32zM224 774c9.724 0 19.45-3.708 26.87-11.13l128-128c14.842-14.84 14.842-38.898 0-53.738-14.84-14.844-38.898-14.844-53.738 0l-128 128c-14.842 14.84-14.842 38.898 0 53.738 7.418 7.422 17.144 11.13 26.868 11.13zM416 896c17.674 0 32-14.326 32-32v-128c0-17.674-14.326-32-32-32s-32 14.326-32 32v128c0 17.674 14.326 32 32 32zM96 576h128c17.674 0 32-14.326 32-32s-14.326-32-32-32h-128c-17.674 0-32 14.326-32 32s14.326 32 32 32z" />
+<glyph unicode="&#xe013;" glyph-name="bookmark" d="M256 896v-896l256 256 256-256v896h-512zM704 170.51l-192 192-192-192v661.49h384v-661.49z" />
+<glyph unicode="&#xe014;" glyph-name="image" d="M896 832h-768c-35.2 0-64-28.8-64-64v-640c0-35.2 28.8-64 64-64h768c35.2 0 64 28.8 64 64v640c0 35.2-28.8 64-64 64zM896 128.116c-0.012-0.014-0.030-0.028-0.042-0.042l-191.958 319.926-160-128-224 288-191.968-479.916c-0.010 0.010-0.022 0.022-0.032 0.032v639.77c0.034 0.040 0.076 0.082 0.114 0.114h767.77c0.040-0.034 0.082-0.076 0.116-0.116v-639.768zM640 608c0-53.019 42.981-96 96-96s96 42.981 96 96c0 53.019-42.981 96-96 96s-96-42.981-96-96z" />
+<glyph unicode="&#xe015;" glyph-name="media" d="M896 832h-768c-35.2 0-64-28.8-64-64v-640c0-35.2 28.8-64 64-64h768c35.2 0 64 28.8 64 64v640c0 35.2-28.8 64-64 64zM256 128h-128v128h128v-128zM256 384h-128v128h128v-128zM256 640h-128v128h128v-128zM704 128h-384v640h384v-640zM896 128h-128v128h128v-128zM896 384h-128v128h128v-128zM896 640h-128v128h128v-128zM384 640v-384l288 192z" />
+<glyph unicode="&#xe016;" glyph-name="help" d="M448 256h128v-128h-128v128zM704 704c35.346 0 64-28.654 64-64v-166l-228-154h-92v64l192 128v64h-320v128h384zM512 896c-119.666 0-232.166-46.6-316.784-131.216-84.614-84.618-131.216-197.118-131.216-316.784 0-119.664 46.602-232.168 131.216-316.784 84.618-84.616 197.118-131.216 316.784-131.216 119.664 0 232.168 46.6 316.784 131.216s131.216 197.12 131.216 316.784c0 119.666-46.6 232.166-131.216 316.784-84.616 84.616-197.12 131.216-316.784 131.216z" />
+<glyph unicode="&#xe017;" glyph-name="code" d="M416 256l-192 192 192 192-64 64-256-256 256-256zM672 704l-64-64 192-192-192-192 64-64 256 256z" />
+<glyph unicode="&#xe018;" glyph-name="insertdatetime" d="M77.798 655.376l81.414-50.882c50.802 81.114 128.788 143.454 221.208 174.246l-30.366 91.094c-113.748-37.898-209.728-114.626-272.256-214.458zM673.946 869.834l-30.366-91.094c92.422-30.792 170.404-93.132 221.208-174.248l81.412 50.882c-62.526 99.834-158.506 176.562-272.254 214.46zM607.974 255.992c-4.808 0-9.692 1.090-14.286 3.386l-145.688 72.844v211.778c0 17.672 14.328 32 32 32s32-14.328 32-32v-172.222l110.31-55.156c15.806-7.902 22.214-27.124 14.31-42.932-5.604-11.214-16.908-17.696-28.646-17.698zM512 768c-212.078 0-384-171.922-384-384s171.922-384 384-384c212.078 0 384 171.922 384 384s-171.922 384-384 384zM512 96c-159.058 0-288 128.942-288 288s128.942 288 288 288c159.058 0 288-128.942 288-288s-128.942-288-288-288z" />
+<glyph unicode="&#xe019;" glyph-name="preview" d="M64 504.254c45.318 49.92 97.162 92.36 153.272 125.124 90.332 52.744 192.246 80.622 294.728 80.622 102.48 0 204.396-27.878 294.726-80.624 56.112-32.764 107.956-75.204 153.274-125.124v117.432c-33.010 28.118-68.124 53.14-104.868 74.594-105.006 61.314-223.658 93.722-343.132 93.722s-238.128-32.408-343.134-93.72c-36.742-21.454-71.856-46.478-104.866-74.596v-117.43zM512 640c-183.196 0-345.838-100.556-448-256 102.162-155.448 264.804-256 448-256s345.838 100.552 448 256c-102.162 155.444-264.804 256-448 256zM512 448c0-35.346-28.654-64-64-64s-64 28.654-64 64c0 35.348 28.654 64 64 64s64-28.652 64-64zM728.066 263.338c-67.434-39.374-140.128-59.338-216.066-59.338s-148.632 19.964-216.066 59.338c-51.554 30.104-98.616 71.31-138.114 120.662 39.498 49.35 86.56 90.558 138.116 120.66 13.276 7.752 26.758 14.74 40.426 20.982-10.512-23.742-16.362-50.008-16.362-77.642 0-106.040 85.962-192 192-192 106.040 0 192 85.96 192 192 0 27.634-5.85 53.9-16.36 77.642 13.668-6.244 27.15-13.23 40.426-20.982 51.554-30.102 98.616-71.31 138.116-120.66-39.498-49.352-86.56-90.558-138.116-120.662z" />
+<glyph unicode="&#xe01a;" glyph-name="forecolor" d="M651.168 676.166c-24.612 81.962-28.876 91.834-107.168 91.834h-64c-79.618 0-82.664-10.152-108.418-96 0-0.002 0-0.002-0.002-0.004l-143.998-479.996h113.636l57.6 192h226.366l57.6-192h113.63l-145.246 484.166zM437.218 512l38.4 136c10.086 33.618 36.38 30 36.38 30s26.294 3.618 36.38-30h0.004l38.4-136h-149.564z" />
+<glyph unicode="&#xe01b;" glyph-name="table" d="M64 768v-704h896v704h-896zM384 320v128h256v-128h-256zM640 256v-128h-256v128h256zM640 640v-128h-256v128h256zM320 640v-128h-192v128h192zM128 448h192v-128h-192v128zM704 448h192v-128h-192v128zM704 512v128h192v-128h-192zM128 256h192v-128h-192v128zM704 128v128h192v-128h-192z" />
+<glyph unicode="&#xe01c;" glyph-name="hr" d="M64 512h896v-128h-896z" />
+<glyph unicode="&#xe01d;" glyph-name="removeformat" d="M64 192h512v-128h-512v128zM768 768h-220.558l-183.766-512h-132.288l183.762 512h-223.15v128h576v-128zM929.774 64l-129.774 129.774-129.774-129.774-62.226 62.226 129.774 129.774-129.774 129.774 62.226 62.226 129.774-129.774 129.774 129.774 62.226-62.226-129.774-129.774 129.774-129.774-62.226-62.226z" />
+<glyph unicode="&#xe01e;" glyph-name="subscript" d="M768 50v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM676 704h-136l-188-188-188 188h-136l256-256-256-256h136l188 188 188-188h136l-256 256z" />
+<glyph unicode="&#xe01f;" glyph-name="superscript" d="M768 754v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM676 704h-136l-188-188-188 188h-136l256-256-256-256h136l188 188 188-188h136l-256 256z" />
+<glyph unicode="&#xe020;" glyph-name="charmap" d="M704 128v37.004c151.348 61.628 256 193.82 256 346.996 0 212.078-200.576 384-448 384s-448-171.922-448-384c0-153.176 104.654-285.368 256-346.996v-37.004h-192l-64 96v-224h320v222.812c-100.9 51.362-170.666 161.54-170.666 289.188 0 176.732 133.718 320 298.666 320s298.666-143.268 298.666-320c0-127.648-69.766-237.826-170.666-289.188v-222.812h320v224l-64-96h-192z" />
+<glyph unicode="&#xe021;" glyph-name="emoticons" d="M512 820c99.366 0 192.782-38.694 263.042-108.956s108.958-163.678 108.958-263.044-38.696-192.782-108.958-263.042-163.676-108.958-263.042-108.958-192.782 38.696-263.044 108.958-108.956 163.676-108.956 263.042 38.694 192.782 108.956 263.044 163.678 108.956 263.044 108.956zM512 896c-247.424 0-448-200.576-448-448s200.576-448 448-448 448 200.576 448 448-200.576 448-448 448v0zM320 576c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM576 576c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64s-64-28.654-64-64zM512 304c-101.84 0-192.56 36.874-251.166 94.328 23.126-117.608 126.778-206.328 251.166-206.328s228.040 88.72 251.168 206.328c-58.608-57.454-149.328-94.328-251.168-94.328z" />
+<glyph unicode="&#xe022;" glyph-name="print" d="M256 832h512v-128h-512v128zM896 640h-768c-35.2 0-64-28.8-64-64v-256c0-35.2 28.796-64 64-64h128v-192h512v192h128c35.2 0 64 28.8 64 64v256c0 35.2-28.8 64-64 64zM704 128h-384v256h384v-256zM910.4 544c0-25.626-20.774-46.4-46.398-46.4s-46.402 20.774-46.402 46.4 20.778 46.4 46.402 46.4c25.626 0 46.398-20.774 46.398-46.4z" />
+<glyph unicode="&#xe023;" glyph-name="fullscreen" d="M480 576l-192 192 128 128h-352v-352l128 128 192-192zM640 480l192 192 128-128v352h-352l128-128-192-192zM544 320l192-192-128-128h352v352l-128-128-192 192zM384 416l-192-192-128 128v-352h352l-128 128 192 192z" />
+<glyph unicode="&#xe024;" glyph-name="spellcheck" d="M960 832v64h-192c-35.202 0-64-28.8-64-64v-320c0-15.856 5.858-30.402 15.496-41.614l-303.496-260.386-142 148-82-70 224-288 416 448h128v64h-192v320h192zM256 448h64v384c0 35.2-28.8 64-64 64h-128c-35.2 0-64-28.8-64-64v-384h64v192h128v-192zM128 704v128h128v-128h-128zM640 512v96c0 35.2-8.8 64-44 64 35.2 0 44 28.8 44 64v96c0 35.2-28.8 64-64 64h-192v-448h192c35.2 0 64 28.8 64 64zM448 832h128v-128h-128v128zM448 640h128v-128h-128v128z" />
+<glyph unicode="&#xe025;" glyph-name="nonbreaking" d="M448 448h-128v128h128v128h128v-128h128v-128h-128v-128h-128v128zM960 384v-320h-896v320h128v-192h640v192h128z" />
+<glyph unicode="&#xe026;" glyph-name="template" d="M512 576h128v-64h-128zM512 192h128v-64h-128zM576 384h128v-64h-128zM768 384v-192h-64v-64h128v256zM384 384h128v-64h-128zM320 192h128v-64h-128zM320 576h128v-64h-128zM192 768v-256h64v192h64v64zM704 512h128v256h-64v-192h-64zM64 896v-896h896v896h-896zM896 64h-768v768h768v-768zM192 384v-256h64v192h64v64zM576 768h128v-64h-128zM384 768h128v-64h-128z" />
+<glyph unicode="&#xe027;" glyph-name="pagebreak" d="M816 896l16-384h-640l16 384h32l16-320h512l16 320h32zM208 0l-16 320h640l-16-320h-32l-16 256h-512l-16-256h-32zM64 448h128v-64h-128zM256 448h128v-64h-128zM448 448h128v-64h-128zM640 448h128v-64h-128zM832 448h128v-64h-128z" />
+<glyph unicode="&#xe028;" glyph-name="restoredraft" d="M576 896c247.424 0 448-200.576 448-448s-200.576-448-448-448v96c94.024 0 182.418 36.614 248.902 103.098s103.098 154.878 103.098 248.902c0 94.022-36.614 182.418-103.098 248.902s-154.878 103.098-248.902 103.098c-94.022 0-182.418-36.614-248.902-103.098-51.14-51.138-84.582-115.246-97.306-184.902h186.208l-224-256-224 256h164.57c31.060 217.102 217.738 384 443.43 384zM768 512v-128h-256v320h128v-192z" />
+<glyph unicode="&#xe02a;" glyph-name="bold" d="M625.442 465.818c48.074 38.15 78.558 94.856 78.558 158.182 0 114.876-100.29 208-224 208h-224v-768h288c123.712 0 224 93.124 224 208 0 88.196-59.118 163.562-142.558 193.818zM384 656c0 26.51 21.49 48 48 48h67.204c42.414 0 76.796-42.98 76.796-96s-34.382-96-76.796-96h-115.204v144zM547.2 192h-115.2c-26.51 0-48 21.49-48 48v144h163.2c42.418 0 76.8-42.98 76.8-96s-34.382-96-76.8-96z" />
+<glyph unicode="&#xe02b;" glyph-name="italic" d="M832 832v-64h-144l-256-640h144v-64h-448v64h144l256 640h-144v64h448z" />
+<glyph unicode="&#xe02c;" glyph-name="underline" d="M192 128h576v-64h-576v64zM640 832v-384c0-31.312-14.7-61.624-41.39-85.352-30.942-27.502-73.068-42.648-118.61-42.648-45.544 0-87.668 15.146-118.608 42.648-26.692 23.728-41.392 54.040-41.392 85.352v384h-128v-384c0-141.382 128.942-256 288-256s288 114.618 288 256v384h-128z" />
+<glyph unicode="&#xe02d;" glyph-name="strikethrough" d="M960 448h-265.876c-50.078 35.42-114.43 54.86-182.124 54.86-89.206 0-164.572 50.242-164.572 109.712s75.366 109.714 164.572 109.714c75.058 0 140.308-35.576 159.12-82.286h113.016c-7.93 50.644-37.58 97.968-84.058 132.826-50.88 38.16-117.676 59.174-188.078 59.174-70.404 0-137.196-21.014-188.074-59.174-54.788-41.090-86.212-99.502-86.212-160.254s31.424-119.164 86.212-160.254c1.956-1.466 3.942-2.898 5.946-4.316h-265.872v-64h512.532c58.208-17.106 100.042-56.27 100.042-100.572 0-59.468-75.368-109.71-164.572-109.71-75.060 0-140.308 35.574-159.118 82.286h-113.016c7.93-50.64 37.582-97.968 84.060-132.826 50.876-38.164 117.668-59.18 188.072-59.18 70.402 0 137.198 21.016 188.074 59.174 54.79 41.090 86.208 99.502 86.208 160.254 0 35.298-10.654 69.792-30.294 100.572h204.012v64z" />
+<glyph unicode="&#xe02e;" glyph-name="visualchars" d="M384 832c-123.712 0-224-100.288-224-224s100.288-224 224-224v-320h128v640h64v-640h128v640h128v128h-448z" />
+<glyph unicode="&#xe02f;" glyph-name="ltr" d="M448 832c-123.712 0-224-100.288-224-224s100.288-224 224-224v-320h128v640h64v-640h128v640h128v128h-448zM64 64l224 192-224 192z" />
+<glyph unicode="&#xe030;" glyph-name="rtl" d="M320 832c-123.712 0-224-100.288-224-224s100.288-224 224-224v-320h128v640h64v-640h128v640h128v128h-448zM960 448l-224-192 224-192z" />
+<glyph unicode="&#xe031;" glyph-name="copy" d="M832 640h-192v64l-192 192h-384v-704h384v-192h576v448l-192 192zM832 549.49l101.49-101.49h-101.49v101.49zM448 805.49l101.49-101.49h-101.49v101.49zM128 832h256v-192h192v-384h-448v576zM960 64h-448v128h128v384h128v-192h192v-320z" />
+<glyph unicode="&#xe032;" glyph-name="resize" d="M768 704h64v-64h-64zM640 576h64v-64h-64zM640 448h64v-64h-64zM640 320h64v-64h-64zM512 448h64v-64h-64zM512 320h64v-64h-64zM384 320h64v-64h-64zM768 576h64v-64h-64zM768 448h64v-64h-64zM768 320h64v-64h-64zM768 192h64v-64h-64zM640 192h64v-64h-64zM512 192h64v-64h-64zM384 192h64v-64h-64zM256 192h64v-64h-64z" />
+<glyph unicode="&#xe034;" glyph-name="browse" d="M928 832h-416l-32 64h-352l-64-128h896zM840.34 256h87.66l32 448h-896l64-640h356.080c-104.882 37.776-180.080 138.266-180.080 256 0 149.982 122.018 272 272 272 149.98 0 272-122.018 272-272 0-21.678-2.622-43.15-7.66-64zM874.996 110.25l-134.496 110.692c17.454 28.922 27.5 62.814 27.5 99.058 0 106.040-85.96 192-192 192s-192-85.96-192-192 85.96-192 192-192c36.244 0 70.138 10.046 99.058 27.5l110.692-134.496c22.962-26.678 62.118-28.14 87.006-3.252l5.492 5.492c24.888 24.888 23.426 64.044-3.252 87.006zM576 196c-68.484 0-124 55.516-124 124s55.516 124 124 124 124-55.516 124-124-55.516-124-124-124z" />
+<glyph unicode="&#xe035;" glyph-name="pastetext" d="M704 576v160c0 17.6-14.4 32-32 32h-160v64c0 35.2-28.8 64-64 64h-128c-35.204 0-64-28.8-64-64v-64h-160c-17.602 0-32-14.4-32-32v-512c0-17.6 14.398-32 32-32h224v-192h576v576h-192zM320 831.886c0.034 0.038 0.072 0.078 0.114 0.114h127.768c0.042-0.036 0.082-0.076 0.118-0.114v-63.886h-128v63.886zM192 640v64h384v-64h-384zM832 64h-448v448h448v-448zM448 448v-128h32l32 64h64v-192h-48v-64h160v64h-48v192h64l32-64h32v128z" />
+<glyph unicode="&#xe603;" glyph-name="codesample" d="M200.015 577.994v103.994c0 43.077 34.919 77.997 77.997 77.997h26v103.994h-26c-100.51 0-181.991-81.481-181.991-181.991v-103.994c0-43.077-34.919-77.997-77.997-77.997h-26v-103.994h26c43.077 0 77.997-34.919 77.997-77.997v-103.994c0-100.509 81.481-181.991 181.991-181.991h26v103.994h-26c-43.077 0-77.997 34.919-77.997 77.997v103.994c0 50.927-20.928 96.961-54.642 129.994 33.714 33.032 54.642 79.065 54.642 129.994zM823.985 577.994v103.994c0 43.077-34.919 77.997-77.997 77.997h-26v103.994h26c100.509 0 181.991-81.481 181.991-181.991v-103.994c0-43.077 34.919-77.997 77.997-77.997h26v-103.994h-26c-43.077 0-77.997-34.919-77.997-77.997v-103.994c0-100.509-81.482-181.991-181.991-181.991h-26v103.994h26c43.077 0 77.997 34.919 77.997 77.997v103.994c0 50.927 20.928 96.961 54.642 129.994-33.714 33.032-54.642 79.065-54.642 129.994zM615.997 603.277c0-57.435-46.56-103.994-103.994-103.994s-103.994 46.56-103.994 103.994c0 57.435 46.56 103.994 103.994 103.994s103.994-46.56 103.994-103.994zM512 448.717c-57.435 0-103.994-46.56-103.994-103.994 0-55.841 26-100.107 105.747-103.875-23.715-33.413-59.437-46.608-105.747-50.94v-61.747c0 0 207.991-18.144 207.991 216.561-0.202 57.437-46.56 103.996-103.994 103.996z" />
+</font></defs></svg>
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce-small.ttf b/public/libs/tinymce/skins/dark/fonts/tinymce-small.ttf
new file mode 100644 (file)
index 0000000..a983e2d
Binary files /dev/null and b/public/libs/tinymce/skins/dark/fonts/tinymce-small.ttf differ
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce-small.woff b/public/libs/tinymce/skins/dark/fonts/tinymce-small.woff
new file mode 100644 (file)
index 0000000..d8962df
Binary files /dev/null and b/public/libs/tinymce/skins/dark/fonts/tinymce-small.woff differ
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce.eot b/public/libs/tinymce/skins/dark/fonts/tinymce.eot
new file mode 100644 (file)
index 0000000..f99c13f
Binary files /dev/null and b/public/libs/tinymce/skins/dark/fonts/tinymce.eot differ
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce.json b/public/libs/tinymce/skins/dark/fonts/tinymce.json
new file mode 100644 (file)
index 0000000..107b741
--- /dev/null
@@ -0,0 +1,3381 @@
+{
+       "IcoMoonType": "selection",
+       "icons": [
+               {
+                       "icon": {
+                               "paths": [
+                                       "M576.234 289.27l242.712-81.432 203.584 606.784-242.712 81.432zM0 896h256v-704h-256v704zM64 320h128v64h-128v-64zM320 896h256v-704h-256v704zM384 320h128v64h-128v-64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "books",
+                                       "library",
+                                       "archive"
+                               ],
+                               "defaultCode": 57458,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 523,
+                               "id": 1722,
+                               "prevSize": 32,
+                               "code": 59665,
+                               "name": "books",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 0
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 416v192c0 17.672 14.328 32 32 32h960c17.672 0 32-14.328 32-32v-192c0-17.672-14.328-32-32-32h-960c-17.672 0-32 14.328-32 32z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "minus",
+                                       "minimize",
+                                       "subtract"
+                               ],
+                               "defaultCode": 58229,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 597,
+                               "id": 1723,
+                               "prevSize": 32,
+                               "code": 59705,
+                               "name": "minus",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 1
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M992 384h-352v-352c0-17.672-14.328-32-32-32h-192c-17.672 0-32 14.328-32 32v352h-352c-17.672 0-32 14.328-32 32v192c0 17.672 14.328 32 32 32h352v352c0 17.672 14.328 32 32 32h192c17.672 0 32-14.328 32-32v-352h352c17.672 0 32-14.328 32-32v-192c0-17.672-14.328-32-32-32z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "plus",
+                                       "add",
+                                       "sum"
+                               ],
+                               "defaultCode": 58230,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 598,
+                               "id": 1724,
+                               "prevSize": 32,
+                               "code": 59706,
+                               "name": "plus",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 2
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M521.6 44.8l-67.2 67.2-86.4-86.4-86.4 86.4 86.4 86.4-368 368 432 432 518.4-518.4-428.8-435.2zM435.2 825.6l-262.4-262.4 35.2-35.2 576-51.2-348.8 348.8zM953.6 550.4c-6.4 6.4-16 16-28.8 32-28.8 32-41.6 64-41.6 89.6v0 0 0 0 0 0 0c0 16 6.4 35.2 22.4 48 12.8 12.8 32 22.4 48 22.4s35.2-6.4 48-22.4 22.4-32 22.4-48v0 0 0 0 0 0 0c0-25.6-12.8-54.4-41.6-89.6-9.6-16-22.4-25.6-28.8-32v0z"
+                               ],
+                               "attrs": [
+                                       {}
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "fill"
+                               ],
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {}
+                       ],
+                       "properties": {
+                               "order": 599,
+                               "id": 1695,
+                               "prevSize": 32,
+                               "code": 59650,
+                               "name": "fill"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 3
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 694.4h1024v128h-1024v-128z",
+                                       "M0 928h1024v64h-1024v-64z",
+                                       "M0 393.6h1024v192h-1024v-192z",
+                                       "M0 32h1024v256h-1024v-256z"
+                               ],
+                               "attrs": [
+                                       {},
+                                       {},
+                                       {},
+                                       {}
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "borderwidth"
+                               ],
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {},
+                               {},
+                               {},
+                               {}
+                       ],
+                       "properties": {
+                               "order": 524,
+                               "id": 1696,
+                               "prevSize": 32,
+                               "code": 59651,
+                               "name": "borderwidth"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 4
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M739.2 332.8l-502.4 502.4h-185.6v-185.6l502.4-502.4 185.6 185.6zM803.2 272l-185.6-185.6 67.2-67.2c22.4-22.4 54.4-22.4 76.8 0l108.8 108.8c22.4 22.4 22.4 54.4 0 76.8l-67.2 67.2zM41.6 912h940.8v112h-940.8v-112z"
+                               ],
+                               "attrs": [
+                                       {}
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "line"
+                               ],
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {}
+                       ],
+                       "properties": {
+                               "order": 525,
+                               "id": 1697,
+                               "prevSize": 32,
+                               "code": 59652,
+                               "name": "line"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 5
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 480h1024v64h-1024v-64z",
+                                       "M304 48v339.2h-67.2v-272h-67.2v-67.2z",
+                                       "M444.8 265.6v54.4h134.4v67.2h-201.6v-153.6l134.4-64v-54.4h-134.4v-67.2h201.6v153.6z",
+                                       "M854.4 48v339.2h-204.8v-67.2h137.6v-67.2h-137.6v-70.4h137.6v-67.2h-137.6v-67.2z",
+                                       "M115.2 793.6c3.2-57.6 38.4-83.2 108.8-83.2 38.4 0 67.2 9.6 86.4 25.6s25.6 35.2 25.6 70.4v112c0 25.6 0 28.8 9.6 41.6h-73.6c-3.2-9.6-3.2-9.6-6.4-19.2-22.4 19.2-41.6 25.6-70.4 25.6-54.4 0-89.6-32-89.6-76.8s28.8-70.4 99.2-80l38.4-6.4c16-3.2 22.4-6.4 22.4-16 0-12.8-12.8-22.4-38.4-22.4s-41.6 9.6-44.8 28.8h-67.2zM262.4 844.8c-6.4 3.2-12.8 6.4-25.6 6.4l-25.6 6.4c-25.6 6.4-38.4 16-38.4 28.8 0 16 12.8 25.6 35.2 25.6s41.6-9.6 54.4-32v-35.2z",
+                                       "M390.4 624h73.6v112c22.4-16 41.6-22.4 67.2-22.4 64 0 105.6 51.2 105.6 124.8 0 76.8-44.8 134.4-108.8 134.4-32 0-48-9.6-67.2-35.2v28.8h-70.4v-342.4zM460.8 838.4c0 41.6 22.4 70.4 51.2 70.4s51.2-28.8 51.2-70.4c0-44.8-19.2-70.4-51.2-70.4-28.8 0-51.2 28.8-51.2 70.4z",
+                                       "M851.2 806.4c-3.2-22.4-19.2-35.2-44.8-35.2-32 0-51.2 25.6-51.2 70.4 0 48 19.2 73.6 51.2 73.6 25.6 0 41.6-12.8 44.8-41.6l70.4 3.2c-9.6 60.8-54.4 96-118.4 96-73.6 0-121.6-51.2-121.6-128 0-80 48-131.2 124.8-131.2 64 0 108.8 35.2 112 96h-67.2z"
+                               ],
+                               "attrs": [
+                                       {},
+                                       {},
+                                       {},
+                                       {},
+                                       {},
+                                       {},
+                                       {}
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "count"
+                               ],
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {},
+                               {},
+                               {},
+                               {},
+                               {},
+                               {},
+                               {}
+                       ],
+                       "properties": {
+                               "order": 526,
+                               "id": 1698,
+                               "prevSize": 32,
+                               "code": 59653,
+                               "name": "count"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 6
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M553.6 656l-118.4-118.4c80-89.6 137.6-195.2 172.8-304h137.6v-92.8h-326.4v-92.8h-92.8v92.8h-326.4v92.8h518.4c-32 89.6-80 176-147.2 249.6-44.8-48-80-99.2-108.8-156.8h-92.8c35.2 76.8 80 147.2 137.6 211.2l-236.8 233.6 67.2 67.2 233.6-233.6 144 144c3.2 0 38.4-92.8 38.4-92.8zM816 419.2h-92.8l-208 560h92.8l51.2-140.8h220.8l51.2 140.8h92.8l-208-560zM691.2 745.6l76.8-201.6 76.8 201.6h-153.6z"
+                               ],
+                               "attrs": [
+                                       {}
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "translate"
+                               ],
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {}
+                       ],
+                       "properties": {
+                               "order": 527,
+                               "id": 1699,
+                               "prevSize": 32,
+                               "code": 59655,
+                               "name": "translate"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 7
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M576 64h128v128h-128v-128z",
+                                       "M576 320h128v128h-128v-128z",
+                                       "M320 320h128v128h-128v-128z",
+                                       "M576 576h128v128h-128v-128z",
+                                       "M320 576h128v128h-128v-128z",
+                                       "M320 832h128v128h-128v-128z",
+                                       "M576 832h128v128h-128v-128z",
+                                       "M320 64h128v128h-128v-128z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "opacity": 1,
+                                               "visibility": false
+                                       },
+                                       {
+                                               "opacity": 1,
+                                               "visibility": false
+                                       },
+                                       {
+                                               "opacity": 1,
+                                               "visibility": false
+                                       },
+                                       {
+                                               "opacity": 1,
+                                               "visibility": false
+                                       },
+                                       {
+                                               "opacity": 1,
+                                               "visibility": false
+                                       },
+                                       {
+                                               "opacity": 1,
+                                               "visibility": false
+                                       },
+                                       {
+                                               "opacity": 1,
+                                               "visibility": false
+                                       },
+                                       {
+                                               "opacity": 1,
+                                               "visibility": false
+                                       },
+                                       {
+                                               "opacity": 1,
+                                               "visibility": false
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "drag"
+                               ],
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "opacity": 1,
+                                       "visibility": false
+                               },
+                               {
+                                       "opacity": 1,
+                                       "visibility": false
+                               },
+                               {
+                                       "opacity": 1,
+                                       "visibility": false
+                               },
+                               {
+                                       "opacity": 1,
+                                       "visibility": false
+                               },
+                               {
+                                       "opacity": 1,
+                                       "visibility": false
+                               },
+                               {
+                                       "opacity": 1,
+                                       "visibility": false
+                               },
+                               {
+                                       "opacity": 1,
+                                       "visibility": false
+                               },
+                               {
+                                       "opacity": 1,
+                                       "visibility": false
+                               },
+                               {
+                                       "opacity": 1,
+                                       "visibility": false
+                               }
+                       ],
+                       "properties": {
+                               "order": 528,
+                               "id": 1700,
+                               "prevSize": 32,
+                               "code": 59656,
+                               "name": "drag",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 8
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M1024 590.444l-512-397.426-512 397.428v-162.038l512-397.426 512 397.428zM896 576v384h-256v-256h-256v256h-256v-384l384-288z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "home"
+                               ],
+                               "defaultCode": 57345,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 529,
+                               "id": 1701,
+                               "prevSize": 32,
+                               "code": 59659,
+                               "name": "home",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 9
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M839.432 199.43c27.492 27.492 50.554 78.672 55.552 120.57h-318.984v-318.984c41.898 4.998 93.076 28.060 120.568 55.552l142.864 142.862zM512 384v-384h-368c-44 0-80 36-80 80v864c0 44 36 80 80 80h672c44 0 80-36 80-80v-560h-384zM576 768v192h-192v-192h-160l256-256 256 256h-160z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "upload"
+                               ],
+                               "defaultCode": 57474,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 530,
+                               "id": 1702,
+                               "prevSize": 32,
+                               "code": 59668,
+                               "name": "upload",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 10
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M928 64h-832c-52.8 0-96 43.2-96 96v512c0 52.8 43.2 96 96 96h160v256l307.2-256h364.8c52.8 0 96-43.2 96-96v-512c0-52.8-43.2-96-96-96zM896 640h-379.142l-196.858 174.714v-174.714h-192v-448h768v448z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "bubble"
+                               ],
+                               "defaultCode": 57703,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 600,
+                               "id": 1703,
+                               "prevSize": 32,
+                               "code": 59676,
+                               "name": "bubble",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 11
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M622.826 702.736c-22.11-3.518-22.614-64.314-22.614-64.314s64.968-64.316 79.128-150.802c38.090 0 61.618-91.946 23.522-124.296 1.59-34.054 48.96-267.324-190.862-267.324-239.822 0-192.45 233.27-190.864 267.324-38.094 32.35-14.57 124.296 23.522 124.296 14.158 86.486 79.128 150.802 79.128 150.802s-0.504 60.796-22.614 64.314c-71.22 11.332-337.172 128.634-337.172 257.264h896c0-128.63-265.952-245.932-337.174-257.264z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "user"
+                               ],
+                               "defaultCode": 57733,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 601,
+                               "id": 1704,
+                               "prevSize": 32,
+                               "code": 59677,
+                               "name": "user",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 12
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M592 448h-16v-192c0-105.87-86.13-192-192-192h-128c-105.87 0-192 86.13-192 192v192h-16c-26.4 0-48 21.6-48 48v480c0 26.4 21.6 48 48 48h544c26.4 0 48-21.6 48-48v-480c0-26.4-21.6-48-48-48zM192 256c0-35.29 28.71-64 64-64h128c35.29 0 64 28.71 64 64v192h-256v-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "lock"
+                               ],
+                               "defaultCode": 57811,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 602,
+                               "id": 1705,
+                               "prevSize": 32,
+                               "code": 59686,
+                               "name": "lock"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 13
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 64c105.87 0 192 86.13 192 192v192h-128v-192c0-35.29-28.71-64-64-64h-128c-35.29 0-64 28.71-64 64v192h16c26.4 0 48 21.6 48 48v480c0 26.4-21.6 48-48 48h-544c-26.4 0-48-21.6-48-48v-480c0-26.4 21.6-48 48-48h400v-192c0-105.87 86.13-192 192-192h128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "unlock"
+                               ],
+                               "defaultCode": 57812,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 603,
+                               "id": 1706,
+                               "prevSize": 32,
+                               "code": 59687,
+                               "name": "unlock"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 14
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M448 128v-16c0-26.4-21.6-48-48-48h-160c-26.4 0-48 21.6-48 48v16h-192v128h192v16c0 26.4 21.6 48 48 48h160c26.4 0 48-21.6 48-48v-16h576v-128h-576zM256 256v-128h128v128h-128zM832 432c0-26.4-21.6-48-48-48h-160c-26.4 0-48 21.6-48 48v16h-576v128h576v16c0 26.4 21.6 48 48 48h160c26.4 0 48-21.6 48-48v-16h192v-128h-192v-16zM640 576v-128h128v128h-128zM448 752c0-26.4-21.6-48-48-48h-160c-26.4 0-48 21.6-48 48v16h-192v128h192v16c0 26.4 21.6 48 48 48h160c26.4 0 48-21.6 48-48v-16h576v-128h-576v-16zM256 896v-128h128v128h-128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "settings"
+                               ],
+                               "defaultCode": 57819,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 604,
+                               "id": 1707,
+                               "prevSize": 32,
+                               "code": 59688,
+                               "name": "settings",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 15
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M192 1024h640l64-704h-768zM640 128v-128h-256v128h-320v192l64-64h768l64 64v-192h-320zM576 128h-128v-64h128v64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "remove2"
+                               ],
+                               "defaultCode": 57935,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 605,
+                               "id": 1708,
+                               "prevSize": 32,
+                               "code": 59690,
+                               "name": "remove2",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 16
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M384 64h256v256h-256zM384 384h256v256h-256zM384 704h256v256h-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "menu"
+                               ],
+                               "defaultCode": 58025,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 606,
+                               "id": 1709,
+                               "prevSize": 32,
+                               "code": 59693,
+                               "name": "menu",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 17
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M1009.956 915.76l-437.074-871.112c-16.742-29.766-38.812-44.648-60.882-44.648s-44.14 14.882-60.884 44.648l-437.074 871.112c-33.486 59.532-5 108.24 63.304 108.24h869.308c68.302 0 96.792-48.708 63.302-108.24zM512 896c-35.346 0-64-28.654-64-64 0-35.348 28.654-64 64-64 35.348 0 64 28.652 64 64 0 35.346-28.652 64-64 64zM556 704h-88l-20-256c0-35.346 28.654-64 64-64s64 28.654 64 64l-20 256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "warning"
+                               ],
+                               "defaultCode": 58198,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 531,
+                               "id": 1710,
+                               "prevSize": 32,
+                               "code": 59696,
+                               "name": "warning"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 18
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M448 704h128v128h-128zM704 256c35.346 0 64 28.654 64 64v192l-192 128h-128v-64l192-128v-64h-320v-128h384zM512 96c-111.118 0-215.584 43.272-294.156 121.844s-121.844 183.038-121.844 294.156c0 111.118 43.272 215.584 121.844 294.156 78.572 78.572 183.038 121.844 294.156 121.844 111.118 0 215.584-43.272 294.156-121.844 78.572-78.572 121.844-183.038 121.844-294.156 0-111.118-43.272-215.584-121.844-294.156-78.572-78.572-183.038-121.844-294.156-121.844zM512 0v0c282.77 0 512 229.23 512 512s-229.23 512-512 512c-282.77 0-512-229.23-512-512 0-282.77 229.23-512 512-512z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "question"
+                               ],
+                               "defaultCode": 58201,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 532,
+                               "id": 1711,
+                               "prevSize": 32,
+                               "code": 59697,
+                               "name": "question",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 19
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512 512-229.23 512-512-229.23-512-512-512zM512 896c-212.078 0-384-171.922-384-384s171.922-384 384-384c212.078 0 384 171.922 384 384 0 212.078-171.922 384-384 384zM768 576h-192v192h-128v-192h-192v-128h192v-192h128v192h192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "pluscircle"
+                               ],
+                               "defaultCode": 58206,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 533,
+                               "id": 1712,
+                               "prevSize": 32,
+                               "code": 59698,
+                               "name": "pluscircle",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 20
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512 512-229.23 512-512-229.23-512-512-512zM448 192h128v128h-128v-128zM640 832h-256v-64h64v-256h-64v-64h192v320h64v64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "info"
+                               ],
+                               "defaultCode": 58211,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 534,
+                               "id": 1713,
+                               "prevSize": 32,
+                               "code": 59699,
+                               "name": "info"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 21
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M1024 736 736 0h-448l-288 288v448l288 288h448l288-288v-448l-288-288zM576 832h-128v-128h128v128zM576 576h-128v-384h128v384z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "notice"
+                               ],
+                               "defaultCode": 58218,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 535,
+                               "id": 1714,
+                               "prevSize": 32,
+                               "code": 59700,
+                               "name": "notice"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 22
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 640l192 192 320-320 320 320 192-192-511.998-512z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "arrowup"
+                               ],
+                               "defaultCode": 58288,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 536,
+                               "id": 1715,
+                               "prevSize": 32,
+                               "code": 59707,
+                               "name": "arrowup",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 23
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M384 0l-192 192 320 320-320 320 192 192 512-512z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "arrowright"
+                               ],
+                               "defaultCode": 58289,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 537,
+                               "id": 1716,
+                               "prevSize": 32,
+                               "code": 59708,
+                               "name": "arrowright",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 24
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M1024 384l-192-192-320 320-320-320-192 192 512 511.998z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "arrowdown"
+                               ],
+                               "defaultCode": 58290,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 538,
+                               "id": 1717,
+                               "prevSize": 32,
+                               "code": 59709,
+                               "name": "arrowdown",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 25
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 640l-256-256-256 256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "arrowup2"
+                               ],
+                               "defaultCode": 58292,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 539,
+                               "id": 1718,
+                               "prevSize": 32,
+                               "code": 59711,
+                               "name": "arrowup2",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 26
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M256 384l256 256 256-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "arrowdown2"
+                               ],
+                               "defaultCode": 58294,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 540,
+                               "id": 1719,
+                               "prevSize": 32,
+                               "code": 59712,
+                               "name": "arrowdown2",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 27
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M256 256l256 256 256-256zM255.996 575.996l256 256 256-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "menu2"
+                               ],
+                               "defaultCode": 58393,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 541,
+                               "id": 1720,
+                               "prevSize": 32,
+                               "code": 59713,
+                               "name": "menu2",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 28
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M704 576l128-128v512h-768v-768h512l-128 128h-256v512h512zM960 64v352l-130.744-130.744-354.746 354.744h-90.51v-90.512l354.744-354.744-130.744-130.744z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "newtab"
+                               ],
+                               "defaultCode": 58492,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 607,
+                               "id": 1721,
+                               "prevSize": 32,
+                               "code": 59745,
+                               "name": "newtab",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 29
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M960 256v-64l-448 128-448-128v64l320 128v256l-128 448h64l192-448 192 448h64l-128-448v-256zM416 160q0-40 28-68t68-28 68 28 28 68-28 68-68 28-68-28-28-68z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "grid": 16,
+                               "tags": [
+                                       "a11y"
+                               ]
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 608,
+                               "id": 1694,
+                               "prevSize": 32,
+                               "code": 59648,
+                               "name": "a11y"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 30
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M892.8 982.4l-89.6-89.6c-70.4 80-172.8 131.2-288 131.2-208 0-380.8-166.4-384-377.6 0 0 0 0 0 0 0-3.2 0-3.2 0-6.4s0-3.2 0-6.4v0c0 0 0 0 0-3.2 0 0 0-3.2 0-3.2 3.2-105.6 48-211.2 105.6-304l-192-192 44.8-44.8 182.4 182.4c0 0 0 0 0 0l569.6 569.6c0 0 0 0 0 0l99.2 99.2-48 44.8zM896 633.6c0 0 0 0 0 0 0-3.2 0-6.4 0-6.4-9.6-316.8-384-627.2-384-627.2s-108.8 89.6-208 220.8l70.4 70.4c6.4-9.6 16-22.4 22.4-32 41.6-51.2 83.2-96 115.2-128v0c32 32 73.6 76.8 115.2 128 108.8 137.6 169.6 265.6 172.8 371.2 0 0 0 3.2 0 3.2v0 0c0 3.2 0 3.2 0 6.4s0 3.2 0 3.2v0 0c0 22.4-3.2 41.6-9.6 64l76.8 76.8c16-41.6 28.8-89.6 28.8-137.6 0 0 0 0 0 0 0-3.2 0-3.2 0-6.4s-0-3.2-0-6.4z"
+                               ],
+                               "attrs": [
+                                       {}
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "invert"
+                               ],
+                               "grid": 16,
+                               "defaultCode": 58882
+                       },
+                       "attrs": [
+                               {}
+                       ],
+                       "properties": {
+                               "order": 609,
+                               "id": 0,
+                               "prevSize": 32,
+                               "code": 58882,
+                               "name": "invert"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 31
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M928 128h-416l-32-64h-352l-64 128h896zM904.34 704h74.86l44.8-448h-1024l64 640h484.080c-104.882-37.776-180.080-138.266-180.080-256 0-149.982 122.018-272 272-272 149.98 0 272 122.018 272 272 0 21.678-2.622 43.15-7.66 64zM1002.996 913.75l-198.496-174.692c17.454-28.92 27.5-62.814 27.5-99.058 0-106.040-85.96-192-192-192s-192 85.96-192 192 85.96 192 192 192c36.244 0 70.138-10.046 99.058-27.5l174.692 198.496c22.962 26.678 62.118 28.14 87.006 3.252l5.492-5.492c24.888-24.888 23.426-64.044-3.252-87.006zM640 764c-68.484 0-124-55.516-124-124s55.516-124 124-124 124 55.516 124 124-55.516 124-124 124z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57396,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 0,
+                               "order": 610,
+                               "prevSize": 32,
+                               "code": 57396,
+                               "name": "browse",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 32
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 256h64v64h-64zM640 384h64v64h-64zM640 512h64v64h-64zM640 640h64v64h-64zM512 512h64v64h-64zM512 640h64v64h-64zM384 640h64v64h-64zM768 384h64v64h-64zM768 512h64v64h-64zM768 640h64v64h-64zM768 768h64v64h-64zM640 768h64v64h-64zM512 768h64v64h-64zM384 768h64v64h-64zM256 768h64v64h-64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "resize",
+                                       "dots"
+                               ],
+                               "defaultCode": 57394,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 1,
+                               "order": 611,
+                               "prevSize": 32,
+                               "code": 57394,
+                               "name": "resize",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 33
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M832 256h-192v-64l-192-192h-448v768h384v256h640v-576l-192-192zM832 346.51l101.49 101.49h-101.49v-101.49zM448 90.51l101.49 101.49h-101.49v-101.49zM64 64h320v192h192v448h-512v-640zM960 960h-512v-192h192v-448h128v192h192v448z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "copy"
+                               ],
+                               "defaultCode": 57393,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 2,
+                               "order": 612,
+                               "prevSize": 32,
+                               "code": 57393,
+                               "name": "copy",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 34
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M256 64h512v128h-128v768h-128v-768h-128v768h-128v-448c-123.712 0-224-100.288-224-224s100.288-224 224-224zM960 896l-256-224 256-224z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "rtl"
+                               ],
+                               "defaultCode": 57392,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 3,
+                               "order": 613,
+                               "prevSize": 32,
+                               "code": 57392,
+                               "name": "rtl",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 35
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M448 64h512v128h-128v768h-128v-768h-128v768h-128v-448c-123.712 0-224-100.288-224-224s100.288-224 224-224zM64 448l256 224-256 224z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "ltr"
+                               ],
+                               "defaultCode": 57391,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 4,
+                               "order": 542,
+                               "prevSize": 32,
+                               "code": 57391,
+                               "name": "ltr",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 36
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M384 64h512v128h-128v768h-128v-768h-128v768h-128v-448c-123.712 0-224-100.288-224-224s100.288-224 224-224z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "visualchars"
+                               ],
+                               "defaultCode": 57390,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 5,
+                               "order": 543,
+                               "prevSize": 32,
+                               "code": 57390,
+                               "name": "visualchars",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 37
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M731.42 517.036c63.92 47.938 100.58 116.086 100.58 186.964s-36.66 139.026-100.58 186.964c-59.358 44.518-137.284 69.036-219.42 69.036-82.138 0-160.062-24.518-219.42-69.036-63.92-47.938-100.58-116.086-100.58-186.964h128c0 69.382 87.926 128 192 128 104.074 0 192-58.618 192-128 0-69.382-87.926-128-192-128-82.138 0-160.062-24.518-219.42-69.036-63.92-47.94-100.58-116.086-100.58-186.964 0-70.878 36.66-139.024 100.58-186.964 59.358-44.518 137.282-69.036 219.42-69.036 82.136 0 160.062 24.518 219.42 69.036 63.92 47.94 100.58 116.086 100.58 186.964h-128c0-69.382-87.926-128-192-128-104.074 0-192 58.618-192 128 0 69.382 87.926 128 192 128 82.136 0 160.062 24.518 219.42 69.036zM0 512h1024v64h-1024z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "strikethrough"
+                               ],
+                               "defaultCode": 57389,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 6,
+                               "order": 544,
+                               "prevSize": 32,
+                               "code": 57389,
+                               "name": "strikethrough",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 38
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M704 64h128v416c0 159.058-143.268 288-320 288-176.73 0-320-128.942-320-288v-416h128v416c0 40.166 18.238 78.704 51.354 108.506 36.896 33.204 86.846 51.494 140.646 51.494 53.8 0 103.75-18.29 140.646-51.494 33.116-29.802 51.354-68.34 51.354-108.506v-416zM192 832h640v128h-640z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "underline"
+                               ],
+                               "defaultCode": 57388,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 7,
+                               "order": 545,
+                               "prevSize": 32,
+                               "code": 57388,
+                               "name": "underline",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 39
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M896 64v64h-128l-320 768h128v64h-448v-64h128l320-768h-128v-64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "italic"
+                               ],
+                               "defaultCode": 57387,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 8,
+                               "order": 546,
+                               "prevSize": 32,
+                               "code": 57387,
+                               "name": "italic",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 40
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M707.88 484.652c37.498-44.542 60.12-102.008 60.12-164.652 0-141.16-114.842-256-256-256h-320v896h384c141.158 0 256-114.842 256-256 0-92.956-49.798-174.496-124.12-219.348zM384 192h101.5c55.968 0 101.5 57.42 101.5 128s-45.532 128-101.5 128h-101.5v-256zM543 832h-159v-256h159c58.45 0 106 57.42 106 128s-47.55 128-106 128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "bold0"
+                               ],
+                               "defaultCode": 57386,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 9,
+                               "order": 547,
+                               "prevSize": 32,
+                               "code": 57386,
+                               "name": "bold",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 41
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M576 64c247.424 0 448 200.576 448 448s-200.576 448-448 448v-96c94.024 0 182.418-36.614 248.902-103.098 66.484-66.484 103.098-154.878 103.098-248.902 0-94.022-36.614-182.418-103.098-248.902-66.484-66.484-154.878-103.098-248.902-103.098-94.022 0-182.418 36.614-248.902 103.098-51.14 51.138-84.582 115.246-97.306 184.902h186.208l-224 256-224-256h164.57c31.060-217.102 217.738-384 443.43-384zM768 448v128h-256v-320h128v192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "restoredraft"
+                               ],
+                               "defaultCode": 57384,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 11,
+                               "order": 548,
+                               "prevSize": 32,
+                               "code": 57384,
+                               "name": "restoredraft",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 42
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 512h128v64h-128zM192 512h192v64h-192zM448 512h128v64h-128zM640 512h192v64h-192zM896 512h128v64h-128zM880 0l16 448h-768l16-448h32l16 384h640l16-384zM144 1024l-16-384h768l-16 384h-32l-16-320h-640l-16 320z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "pagebreak"
+                               ],
+                               "defaultCode": 57383,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 12,
+                               "order": 549,
+                               "prevSize": 32,
+                               "code": 57383,
+                               "name": "pagebreak",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 43
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M384 192h128v64h-128zM576 192h128v64h-128zM896 192v256h-192v-64h128v-128h-64v-64zM320 384h128v64h-128zM512 384h128v64h-128zM192 256v128h64v64h-128v-256h192v64zM384 576h128v64h-128zM576 576h128v64h-128zM896 576v256h-192v-64h128v-128h-64v-64zM320 768h128v64h-128zM512 768h128v64h-128zM192 640v128h64v64h-128v-256h192v64zM960 64h-896v896h896v-896zM1024 0v0 1024h-1024v-1024h1024z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "template"
+                               ],
+                               "defaultCode": 57382,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 13,
+                               "order": 550,
+                               "prevSize": 32,
+                               "code": 57382,
+                               "name": "template",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 44
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M448 576h-192v-128h192v-192h128v192h192v128h-192v192h-128zM1024 640v384h-1024v-384h128v256h768v-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "nonbreaking"
+                               ],
+                               "defaultCode": 57381,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 14,
+                               "order": 551,
+                               "prevSize": 32,
+                               "code": 57381,
+                               "name": "nonbreaking",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 45
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M128 256h128v192h64v-384c0-35.2-28.8-64-64-64h-128c-35.2 0-64 28.8-64 64v384h64v-192zM128 64h128v128h-128v-128zM960 64v-64h-192c-35.202 0-64 28.8-64 64v320c0 35.2 28.798 64 64 64h192v-64h-192v-320h192zM640 160v-96c0-35.2-28.8-64-64-64h-192v448h192c35.2 0 64-28.8 64-64v-96c0-35.2-8.8-64-44-64 35.2 0 44-28.8 44-64zM576 384h-128v-128h128v128zM576 192h-128v-128h128v128zM832 576l-416 448-224-288 82-70 142 148 352-302z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "spellchecker"
+                               ],
+                               "defaultCode": 57380,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 15,
+                               "order": 552,
+                               "prevSize": 32,
+                               "code": 57380,
+                               "name": "spellchecker",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 46
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M704 896h256l64-128v256h-384v-214.214c131.112-56.484 224-197.162 224-361.786 0-214.432-157.598-382.266-352-382.266-194.406 0-352 167.832-352 382.266 0 164.624 92.886 305.302 224 361.786v214.214h-384v-256l64 128h256v-32.59c-187.63-66.46-320-227.402-320-415.41 0-247.424 229.23-448 512-448 282.77 0 512 200.576 512 448 0 188.008-132.37 348.95-320 415.41v32.59z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "charmap"
+                               ],
+                               "defaultCode": 57376,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 19,
+                               "order": 614,
+                               "prevSize": 32,
+                               "code": 57376,
+                               "name": "charmap",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 47
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 206v50h128v64h-192v-146l128-60v-50h-128v-64h192v146zM676 256h-136l-188 188-188-188h-136l256 256-256 256h136l188-188 188 188h136l-256-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "sup"
+                               ],
+                               "defaultCode": 57375,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 20,
+                               "order": 615,
+                               "prevSize": 32,
+                               "code": 57375,
+                               "name": "sup",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 48
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 910v50h128v64h-192v-146l128-60v-50h-128v-64h192v146zM676 256h-136l-188 188-188-188h-136l256 256-256 256h136l188-188 188 188h136l-256-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "sub"
+                               ],
+                               "defaultCode": 57374,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 21,
+                               "order": 616,
+                               "prevSize": 32,
+                               "code": 57374,
+                               "name": "sub",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 49
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 896h576v128h-576zM192 0h704v128h-704zM277.388 832l204.688-784.164 123.85 32.328-196.25 751.836zM929.774 1024l-129.774-129.774-129.774 129.774-62.226-62.226 129.774-129.774-129.774-129.774 62.226-62.226 129.774 129.774 129.774-129.774 62.226 62.226-129.774 129.774 129.774 129.774z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "removeformat"
+                               ],
+                               "defaultCode": 57373,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 22,
+                               "order": 617,
+                               "prevSize": 32,
+                               "code": 57373,
+                               "name": "removeformat",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 50
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 448h1024v128h-1024z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "hr"
+                               ],
+                               "defaultCode": 57372,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 23,
+                               "order": 618,
+                               "prevSize": 32,
+                               "code": 57372,
+                               "name": "hr",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 51
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64v896h1024v-896h-1024zM384 640v-192h256v192h-256zM640 704v192h-256v-192h256zM640 192v192h-256v-192h256zM320 192v192h-256v-192h256zM64 448h256v192h-256v-192zM704 448h256v192h-256v-192zM704 384v-192h256v192h-256zM64 704h256v192h-256v-192zM704 896v-192h256v192h-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "table"
+                               ],
+                               "defaultCode": 57371,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 24,
+                               "order": 619,
+                               "prevSize": 32,
+                               "code": 57371,
+                               "name": "table",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 52
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M322.018 832l57.6-192h264.764l57.6 192h113.632l-191.996-640h-223.236l-192 640h113.636zM475.618 320h72.764l57.6 192h-187.964l57.6-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "forecolor"
+                               ],
+                               "defaultCode": 57370,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 25,
+                               "order": 620,
+                               "prevSize": 32,
+                               "code": 57370,
+                               "name": "forecolor",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 53
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 320c-209.368 0-395.244 100.556-512 256 116.756 155.446 302.632 256 512 256 209.368 0 395.244-100.554 512-256-116.756-155.444-302.632-256-512-256zM448 448c35.346 0 64 28.654 64 64s-28.654 64-64 64-64-28.654-64-64 28.654-64 64-64zM773.616 705.296c-39.648 20.258-81.652 35.862-124.846 46.376-44.488 10.836-90.502 16.328-136.77 16.328-46.266 0-92.282-5.492-136.768-16.324-43.194-10.518-85.198-26.122-124.846-46.376-63.020-32.202-120.222-76.41-167.64-129.298 47.418-52.888 104.62-97.1 167.64-129.298 32.336-16.522 66.242-29.946 101.082-40.040-19.888 30.242-31.468 66.434-31.468 105.336 0 106.040 85.962 192 192 192 106.038 0 192-85.96 192-192 0-38.902-11.582-75.094-31.466-105.34 34.838 10.096 68.744 23.52 101.082 40.042 63.022 32.198 120.218 76.408 167.638 129.298-47.42 52.886-104.618 97.1-167.638 129.296zM860.918 243.722c-108.72-55.554-226.112-83.722-348.918-83.722-122.806 0-240.198 28.168-348.918 83.722-58.772 30.032-113.732 67.904-163.082 112.076v109.206c55.338-58.566 120.694-107.754 192.194-144.29 99.62-50.904 207.218-76.714 319.806-76.714s220.186 25.81 319.804 76.716c71.502 36.536 136.858 85.724 192.196 144.29v-109.206c-49.35-44.174-104.308-82.046-163.082-112.078z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "preview"
+                               ],
+                               "defaultCode": 57369,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 26,
+                               "order": 553,
+                               "prevSize": 32,
+                               "code": 57369,
+                               "name": "preview",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 54
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 192c-212.076 0-384 171.922-384 384s171.922 384 384 384c212.074 0 384-171.922 384-384s-171.926-384-384-384zM715.644 779.646c-54.392 54.396-126.716 84.354-203.644 84.354s-149.25-29.958-203.646-84.354c-54.396-54.394-84.354-126.718-84.354-203.646s29.958-149.25 84.354-203.646c54.396-54.396 126.718-84.354 203.646-84.354s149.252 29.958 203.642 84.354c54.402 54.396 84.358 126.718 84.358 203.646s-29.958 149.252-84.356 203.646zM325.93 203.862l-42.94-85.878c-98.874 49.536-179.47 130.132-229.006 229.008l85.876 42.94c40.248-80.336 105.732-145.822 186.070-186.070zM884.134 389.93l85.878-42.938c-49.532-98.876-130.126-179.472-229.004-229.008l-42.944 85.878c80.338 40.248 145.824 105.732 186.070 186.068zM512 384h-64v192c0 10.11 4.7 19.11 12.022 24.972l-0.012 0.016 160 128 39.976-49.976-147.986-118.39v-176.622z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "inserttime"
+                               ],
+                               "defaultCode": 57368,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 27,
+                               "order": 554,
+                               "prevSize": 32,
+                               "code": 57368,
+                               "name": "inserttime",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 55
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M320 256l-256 256 256 256h128l-256-256 256-256zM704 256h-128l256 256-256 256h128l256-256z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "code"
+                               ],
+                               "defaultCode": 57367,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 28,
+                               "order": 555,
+                               "prevSize": 32,
+                               "code": 57367,
+                               "name": "code",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 56
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M448 704h128v128h-128zM704 256c35.346 0 64 28.654 64 64v192l-192 128h-128v-64l192-128v-64h-320v-128h384zM512 96c-111.118 0-215.584 43.272-294.156 121.844s-121.844 183.038-121.844 294.156c0 111.118 43.272 215.584 121.844 294.156 78.572 78.572 183.038 121.844 294.156 121.844 111.118 0 215.584-43.272 294.156-121.844 78.572-78.572 121.844-183.038 121.844-294.156 0-111.118-43.272-215.584-121.844-294.156-78.572-78.572-183.038-121.844-294.156-121.844zM512 0v0c282.77 0 512 229.23 512 512s-229.23 512-512 512c-282.77 0-512-229.23-512-512 0-282.77 229.23-512 512-512z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "help"
+                               ],
+                               "defaultCode": 57366,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 29,
+                               "order": 556,
+                               "prevSize": 32,
+                               "code": 57366,
+                               "name": "help",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 57
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 128v768h1024v-768h-1024zM192 832h-128v-128h128v128zM192 576h-128v-128h128v128zM192 320h-128v-128h128v128zM768 832h-512v-640h512v640zM960 832h-128v-128h128v128zM960 576h-128v-128h128v128zM960 320h-128v-128h128v128zM384 320v384l256-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "media"
+                               ],
+                               "defaultCode": 57365,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 30,
+                               "order": 557,
+                               "prevSize": 32,
+                               "code": 57365,
+                               "name": "media",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 58
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 128v832h1024v-832h-1024zM960 896h-896v-704h896v704zM704 352c0-53.019 42.981-96 96-96s96 42.981 96 96c0 53.019-42.981 96-96 96-53.019 0-96-42.981-96-96zM896 832h-768l192-512 256 320 128-96z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "image"
+                               ],
+                               "defaultCode": 57364,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 31,
+                               "order": 558,
+                               "prevSize": 32,
+                               "code": 57364,
+                               "name": "image",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 59
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M192 0v1024l320-320 320 320v-1024h-640zM768 869.49l-256-256-256 256v-805.49h512v805.49z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "anchor"
+                               ],
+                               "defaultCode": 57363,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 32,
+                               "order": 559,
+                               "prevSize": 32,
+                               "code": 57363,
+                               "name": "anchor",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 60
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M476.888 675.114c4.56 9.048 6.99 19.158 6.99 29.696 0 17.616-6.744 34.058-18.992 46.308l-163.38 163.38c-12.248 12.248-28.696 18.992-46.308 18.992s-34.060-6.744-46.308-18.992l-99.38-99.38c-12.248-12.25-18.992-28.696-18.992-46.308s6.744-34.060 18.992-46.308l163.38-163.382c12.248-12.246 28.696-18.992 46.308-18.992 10.538 0 20.644 2.43 29.696 6.988l65.338-65.336c-27.87-21.41-61.44-32.16-95.034-32.16-39.986 0-79.972 15.166-110.308 45.502l-163.38 163.382c-60.67 60.67-60.67 159.95 0 220.618l99.38 99.382c30.334 30.332 70.32 45.5 110.306 45.5 39.988 0 79.974-15.168 110.308-45.502l163.38-163.38c55.82-55.82 60.238-144.298 13.344-205.346l-65.34 65.338zM978.496 144.884l-99.38-99.382c-30.334-30.336-70.32-45.502-110.308-45.502-39.986 0-79.97 15.166-110.306 45.502l-163.382 163.382c-55.82 55.82-60.238 144.298-13.342 205.342l65.338-65.34c-4.558-9.050-6.988-19.16-6.988-29.694 0-17.616 6.744-34.060 18.992-46.308l163.382-163.382c12.246-12.248 28.694-18.994 46.306-18.994 17.616 0 34.060 6.746 46.308 18.994l99.38 99.382c12.248 12.248 18.992 28.694 18.992 46.308s-6.744 34.060-18.992 46.308l-163.38 163.382c-12.248 12.248-28.694 18.992-46.308 18.992-10.536 0-20.644-2.43-29.696-6.99l-65.338 65.338c27.872 21.41 61.44 32.16 95.034 32.16 39.988 0 79.974-15.168 110.308-45.504l163.38-163.38c60.672-60.666 60.672-159.944 0-220.614zM233.368 278.624l-191.994-191.994 45.256-45.256 191.994 191.994zM384 0h64v192h-64zM0 384h192v64h-192zM790.632 745.376l191.996 191.996-45.256 45.256-191.996-191.996zM576 832h64v192h-64zM832 576h192v64h-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "unlink"
+                               ],
+                               "defaultCode": 57362,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 33,
+                               "order": 560,
+                               "prevSize": 32,
+                               "code": 57362,
+                               "name": "unlink",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 61
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M320 704c17.6 17.6 47.274 16.726 65.942-1.942l316.118-316.116c18.668-18.668 19.54-48.342 1.94-65.942s-47.274-16.726-65.942 1.942l-316.116 316.116c-18.668 18.668-19.542 48.342-1.942 65.942zM476.888 675.112c4.56 9.050 6.99 19.16 6.99 29.696 0 17.616-6.744 34.060-18.992 46.308l-163.382 163.382c-12.248 12.248-28.694 18.992-46.308 18.992s-34.060-6.744-46.308-18.992l-99.382-99.382c-12.248-12.248-18.992-28.694-18.992-46.308s6.744-34.060 18.992-46.308l163.382-163.382c12.248-12.248 28.694-18.994 46.308-18.994 10.536 0 20.644 2.43 29.696 6.99l65.338-65.338c-27.87-21.41-61.44-32.16-95.034-32.16-39.986 0-79.972 15.166-110.308 45.502l-163.382 163.382c-60.67 60.67-60.67 159.948 0 220.618l99.382 99.382c30.334 30.332 70.32 45.5 110.306 45.5 39.988 0 79.974-15.168 110.308-45.502l163.382-163.382c55.82-55.82 60.238-144.298 13.344-205.344l-65.34 65.34zM978.498 144.884l-99.382-99.382c-30.334-30.336-70.32-45.502-110.308-45.502-39.986 0-79.972 15.166-110.308 45.502l-163.382 163.382c-55.82 55.82-60.238 144.298-13.342 205.342l65.338-65.34c-4.558-9.050-6.988-19.16-6.988-29.694 0-17.616 6.744-34.060 18.992-46.308l163.382-163.382c12.248-12.248 28.694-18.994 46.308-18.994s34.060 6.746 46.308 18.994l99.382 99.382c12.248 12.248 18.992 28.694 18.992 46.308s-6.744 34.060-18.992 46.308l-163.382 163.382c-12.248 12.248-28.694 18.992-46.308 18.992-10.536 0-20.644-2.43-29.696-6.99l-65.338 65.338c27.872 21.41 61.44 32.16 95.034 32.16 39.988 0 79.974-15.168 110.308-45.502l163.382-163.382c60.67-60.666 60.67-159.944 0-220.614z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "link"
+                               ],
+                               "defaultCode": 57361,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 34,
+                               "order": 561,
+                               "prevSize": 32,
+                               "code": 57361,
+                               "name": "link",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 62
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64h1024v128h-1024zM384 256h640v128h-640zM384 448h640v128h-640zM384 640h640v128h-640zM0 832h1024v128h-1024zM256 320v384l-256-192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "outdent"
+                               ],
+                               "defaultCode": 57357,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 38,
+                               "order": 562,
+                               "prevSize": 32,
+                               "code": 57357,
+                               "name": "outdent",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 63
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64h1024v128h-1024zM384 256h640v128h-640zM384 448h640v128h-640zM384 640h640v128h-640zM0 832h1024v128h-1024zM0 704v-384l256 192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "indent"
+                               ],
+                               "defaultCode": 57356,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 39,
+                               "order": 563,
+                               "prevSize": 32,
+                               "code": 57356,
+                               "name": "indent",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 64
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M384 832h640v128h-640zM384 448h640v128h-640zM384 64h640v128h-640zM192 0v256h-64v-192h-64v-64zM128 526v50h128v64h-192v-146l128-60v-50h-128v-64h192v146zM256 704v320h-192v-64h128v-64h-128v-64h128v-64h-128v-64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "numlist"
+                               ],
+                               "defaultCode": 57355,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 40,
+                               "order": 621,
+                               "prevSize": 32,
+                               "code": 57355,
+                               "name": "numlist",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 65
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M384 64h640v128h-640v-128zM384 448h640v128h-640v-128zM384 832h640v128h-640v-128zM0 128c0-70.692 57.308-128 128-128 70.692 0 128 57.308 128 128 0 70.692-57.308 128-128 128-70.692 0-128-57.308-128-128zM0 512c0-70.692 57.308-128 128-128 70.692 0 128 57.308 128 128 0 70.692-57.308 128-128 128-70.692 0-128-57.308-128-128zM0 896c0-70.692 57.308-128 128-128 70.692 0 128 57.308 128 128 0 70.692-57.308 128-128 128-70.692 0-128-57.308-128-128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "bullist"
+                               ],
+                               "defaultCode": 57354,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 41,
+                               "order": 622,
+                               "prevSize": 32,
+                               "code": 57354,
+                               "name": "bullist",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 66
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 0h384v64h-384zM576 0h384v64h-384zM952 320h-56v-256h-256v256h-256v-256h-256v256h-56c-39.6 0-72 32.4-72 72v560c0 39.6 32.4 72 72 72h304c39.6 0 72-32.4 72-72v-376h128v376c0 39.6 32.4 72 72 72h304c39.6 0 72-32.4 72-72v-560c0-39.6-32.4-72-72-72zM348 960h-248c-19.8 0-36-14.4-36-32s16.2-32 36-32h248c19.8 0 36 14.4 36 32s-16.2 32-36 32zM544 512h-64c-17.6 0-32-14.4-32-32s14.4-32 32-32h64c17.6 0 32 14.4 32 32s-14.4 32-32 32zM924 960h-248c-19.8 0-36-14.4-36-32s16.2-32 36-32h248c19.8 0 36 14.4 36 32s-16.2 32-36 32z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "searchreplace"
+                               ],
+                               "defaultCode": 57353,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 42,
+                               "order": 623,
+                               "prevSize": 32,
+                               "code": 57353,
+                               "name": "searchreplace",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 67
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M832 320v-160c0-17.6-14.4-32-32-32h-224v-64c0-35.2-28.8-64-64-64h-128c-35.204 0-64 28.8-64 64v64h-224c-17.602 0-32 14.4-32 32v640c0 17.6 14.398 32 32 32h288v192h448l192-192v-512h-192zM384 64.114c0.034-0.038 0.072-0.078 0.114-0.114h127.768c0.042 0.036 0.082 0.076 0.118 0.114l0 63.886h-128v-63.886zM192 256v-64h512v64h-512zM832 933.49v-101.49h101.49l-101.49 101.49zM960 768h-192v192h-320v-576h512v384z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "paste"
+                               ],
+                               "defaultCode": 57352,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 43,
+                               "order": 624,
+                               "prevSize": 32,
+                               "code": 57352,
+                               "name": "paste",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 68
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M890.774 709.154c-45.654-45.556-103.728-69.072-157.946-69.072h-29.112l-63.904-64.008 255.62-256.038c63.904-64.010 63.904-192.028 0-256.038l-383.43 384.056-383.432-384.054c-63.904 64.008-63.904 192.028 0 256.038l255.622 256.034-63.906 64.008h-29.114c-54.22 0-112.292 23.518-157.948 69.076-81.622 81.442-92.65 202.484-24.63 270.35 29.97 29.902 70.288 44.494 112.996 44.494 54.216 0 112.29-23.514 157.946-69.072 53.584-53.464 76.742-124 67.084-185.348l65.384-65.488 65.376 65.488c-9.656 61.348 13.506 131.882 67.084 185.348 45.662 45.558 103.732 69.072 157.948 69.072 42.708 0 83.024-14.592 112.994-44.496 68.020-67.866 56.988-188.908-24.632-270.35zM353.024 845.538c-7.698 17.882-19.010 34.346-33.626 48.926-14.636 14.604-31.172 25.918-49.148 33.624-16.132 6.916-32.96 10.568-48.662 10.568-15.146 0-36.612-3.402-52.862-19.612-16.136-16.104-19.52-37.318-19.52-52.288 0-15.542 3.642-32.21 10.526-48.212 7.7-17.884 19.014-34.346 33.626-48.926 14.634-14.606 31.172-25.914 49.15-33.624 16.134-6.914 32.96-10.568 48.664-10.568 15.146 0 36.612 3.4 52.858 19.614 16.134 16.098 19.522 37.316 19.522 52.284 0.002 15.542-3.638 32.216-10.528 48.214zM512.004 666.596c-49.914 0-90.376-40.532-90.376-90.526 0-49.992 40.462-90.52 90.376-90.52s90.372 40.528 90.372 90.52c0 49.998-40.46 90.526-90.372 90.526zM855.272 919.042c-16.248 16.208-37.712 19.612-52.86 19.612-15.704 0-32.53-3.652-48.666-10.568-17.972-7.706-34.508-19.020-49.142-33.624-14.614-14.58-25.926-31.042-33.626-48.926-6.886-15.998-10.526-32.672-10.526-48.212 0-14.966 3.384-36.188 19.52-52.286 16.246-16.208 37.712-19.614 52.86-19.614 15.7 0 32.53 3.654 48.66 10.568 17.978 7.708 34.516 19.018 49.15 33.624 14.61 14.58 25.924 31.042 33.626 48.926 6.884 15.998 10.526 32.67 10.526 48.212-0.002 14.97-3.39 36.186-19.522 52.288z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "cut"
+                               ],
+                               "defaultCode": 57351,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 44,
+                               "order": 625,
+                               "prevSize": 32,
+                               "code": 57351,
+                               "name": "cut",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 69
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64h1024v128h-1024zM0 256h1024v128h-1024zM0 448h1024v128h-1024zM0 640h1024v128h-1024zM0 832h1024v128h-1024z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "alignjustify"
+                               ],
+                               "defaultCode": 57350,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 45,
+                               "order": 626,
+                               "prevSize": 32,
+                               "code": 57350,
+                               "name": "alignjustify",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 70
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64h1024v128h-1024zM384 256h640v128h-640zM384 640h640v128h-640zM0 448h1024v128h-1024zM0 832h1024v128h-1024z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "alignright"
+                               ],
+                               "defaultCode": 57349,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 46,
+                               "order": 627,
+                               "prevSize": 32,
+                               "code": 57349,
+                               "name": "alignright",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 71
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64h1024v128h-1024zM192 256h640v128h-640zM192 640h640v128h-640zM0 448h1024v128h-1024zM0 832h1024v128h-1024z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "aligncenter"
+                               ],
+                               "defaultCode": 57348,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 47,
+                               "order": 564,
+                               "prevSize": 32,
+                               "code": 57348,
+                               "name": "aligncenter",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 72
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64h1024v128h-1024zM0 256h640v128h-640zM0 640h640v128h-640zM0 448h1024v128h-1024zM0 832h1024v128h-1024z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "alignleft"
+                               ],
+                               "defaultCode": 57347,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 48,
+                               "order": 565,
+                               "prevSize": 32,
+                               "code": 57347,
+                               "name": "alignleft",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 73
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M1024 592.458v-160.916l-159.144-15.914c-8.186-30.042-20.088-58.548-35.21-84.98l104.596-127.838-113.052-113.050-127.836 104.596c-26.434-15.124-54.942-27.026-84.982-35.208l-15.914-159.148h-160.916l-15.914 159.146c-30.042 8.186-58.548 20.086-84.98 35.208l-127.838-104.594-113.050 113.050 104.596 127.836c-15.124 26.432-27.026 54.94-35.21 84.98l-159.146 15.916v160.916l159.146 15.914c8.186 30.042 20.086 58.548 35.21 84.982l-104.596 127.836 113.048 113.048 127.838-104.596c26.432 15.124 54.94 27.028 84.98 35.21l15.916 159.148h160.916l15.914-159.144c30.042-8.186 58.548-20.088 84.982-35.21l127.836 104.596 113.048-113.048-104.596-127.836c15.124-26.434 27.028-54.942 35.21-84.98l159.148-15.92zM704 576l-128 128h-128l-128-128v-128l128-128h128l128 128v128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "fullpage"
+                               ],
+                               "defaultCode": 57346,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 49,
+                               "order": 566,
+                               "prevSize": 32,
+                               "code": 57346,
+                               "name": "fullpage",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 74
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M903.432 199.43l-142.864-142.862c-31.112-31.112-92.568-56.568-136.568-56.568h-480c-44 0-80 36-80 80v864c0 44 36 80 80 80h736c44 0 80-36 80-80v-608c0-44-25.456-105.458-56.568-136.57zM858.178 244.686c3.13 3.13 6.25 6.974 9.28 11.314h-163.458v-163.456c4.34 3.030 8.184 6.15 11.314 9.28l142.864 142.862zM896 944c0 8.672-7.328 16-16 16h-736c-8.672 0-16-7.328-16-16v-864c0-8.672 7.328-16 16-16h480c4.832 0 10.254 0.61 16 1.704v254.296h254.296c1.094 5.746 1.704 11.166 1.704 16v608z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "newdocument"
+                               ],
+                               "defaultCode": 57345,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 50,
+                               "order": 567,
+                               "prevSize": 32,
+                               "code": 57345,
+                               "name": "newdocument",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 75
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M896 0h-896v1024h1024v-896l-128-128zM512 128h128v256h-128v-256zM896 896h-768v-768h64v320h576v-320h74.978l53.022 53.018v714.982z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "save"
+                               ],
+                               "defaultCode": 57344,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 51,
+                               "order": 568,
+                               "prevSize": 32,
+                               "code": 57344,
+                               "name": "save",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 76
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M128 544l288 288 480-480-128-128-352 352-160-160z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "defaultCode": 57395,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 52,
+                               "order": 569,
+                               "prevSize": 32,
+                               "code": 57395,
+                               "name": "checkbox",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 77
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 512v128h32l32-64h64v256h-48v64h224v-64h-48v-256h64l32 64h32v-128zM832 320v-160c0-17.6-14.4-32-32-32h-224v-64c0-35.2-28.8-64-64-64h-128c-35.204 0-64 28.8-64 64v64h-224c-17.602 0-32 14.4-32 32v640c0 17.6 14.398 32 32 32h288v192h640v-704h-192zM384 64.114c0.034-0.038 0.072-0.078 0.114-0.114h127.768c0.042 0.036 0.082 0.076 0.118 0.114l0 63.886h-128v-63.886zM192 256v-64h512v64h-512zM960 960h-512v-576h512v576z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "pastetext"
+                               ],
+                               "defaultCode": 57397,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 53,
+                               "order": 570,
+                               "prevSize": 32,
+                               "code": 57397,
+                               "name": "pastetext",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 78
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M1024 0v384l-138.26-138.26-212 212-107.48-107.48 212-212-138.26-138.26zM245.74 138.26l212 212-107.48 107.48-212-212-138.26 138.26v-384h384zM885.74 778.26l138.26-138.26v384h-384l138.26-138.26-212-212 107.48-107.48zM457.74 673.74l-212 212 138.26 138.26h-384v-384l138.26 138.26 212-212z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "fullscreen"
+                               ],
+                               "defaultCode": 57379,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 16,
+                               "order": 571,
+                               "prevSize": 32,
+                               "code": 57379,
+                               "name": "fullscreen",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 79
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M256 64h512v128h-512zM960 256h-896c-35.2 0-64 28.8-64 64v320c0 35.2 28.796 64 64 64h192v256h512v-256h192c35.2 0 64-28.8 64-64v-320c0-35.2-28.8-64-64-64zM704 896h-384v-320h384v320zM974.4 352c0 25.626-20.774 46.4-46.398 46.4-25.626 0-46.402-20.774-46.402-46.4s20.776-46.4 46.402-46.4c25.626 0 46.398 20.774 46.398 46.4z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "print"
+                               ],
+                               "defaultCode": 57378,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 17,
+                               "order": 572,
+                               "prevSize": 32,
+                               "code": 57378,
+                               "name": "print",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 80
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 0c-282.77 0-512 229.228-512 512 0 282.77 229.228 512 512 512 282.77 0 512-229.23 512-512 0-282.772-229.23-512-512-512zM512 944c-238.586 0-432-193.412-432-432 0-238.586 193.414-432 432-432 238.59 0 432 193.414 432 432 0 238.588-193.41 432-432 432zM384 320c0 35.346-28.654 64-64 64s-64-28.654-64-64 28.654-64 64-64 64 28.654 64 64zM768 320c0 35.346-28.652 64-64 64s-64-28.654-64-64 28.652-64 64-64 64 28.654 64 64zM512 652c141.074 0 262.688-57.532 318.462-123.192-20.872 171.22-156.288 303.192-318.462 303.192-162.118 0-297.498-132.026-318.444-303.168 55.786 65.646 177.386 123.168 318.444 123.168z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "emoticons"
+                               ],
+                               "defaultCode": 57377,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 18,
+                               "order": 573,
+                               "prevSize": 32,
+                               "code": 57377,
+                               "name": "emoticons",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 81
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M225 448c123.712 0 224 100.29 224 224 0 123.712-100.288 224-224 224-123.712 0-224-100.288-224-224l-1-32c0-247.424 200.576-448 448-448v128c-85.474 0-165.834 33.286-226.274 93.726-11.634 11.636-22.252 24.016-31.83 37.020 11.438-1.8 23.16-2.746 35.104-2.746zM801 448c123.71 0 224 100.29 224 224 0 123.712-100.29 224-224 224-123.71 0-224-100.288-224-224l-1-32c0-247.424 200.576-448 448-448v128c-85.474 0-165.834 33.286-226.274 93.726-11.636 11.636-22.254 24.016-31.832 37.020 11.44-1.8 23.16-2.746 35.106-2.746z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "blockquote"
+                               ],
+                               "defaultCode": 57358,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 37,
+                               "order": 574,
+                               "prevSize": 32,
+                               "code": 57358,
+                               "name": "blockquote",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 82
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M761.862 1024c113.726-206.032 132.888-520.306-313.862-509.824v253.824l-384-384 384-384v248.372c534.962-13.942 594.57 472.214 313.862 775.628z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "undo"
+                               ],
+                               "defaultCode": 57359,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 36,
+                               "order": 628,
+                               "prevSize": 32,
+                               "code": 57359,
+                               "name": "undo",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 83
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M576 248.372v-248.372l384 384-384 384v-253.824c-446.75-10.482-427.588 303.792-313.86 509.824-280.712-303.414-221.1-789.57 313.86-775.628z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "redo"
+                               ],
+                               "defaultCode": 57360,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 35,
+                               "order": 629,
+                               "prevSize": 32,
+                               "code": 57360,
+                               "name": "redo",
+                               "ligatures": ""
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 84
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M199.995 381.998v-104.002c0-43.078 34.923-78.001 78.001-78.001h26v-104.002h-26c-100.518 0-182.003 81.485-182.003 182.003v104.002c0 43.078-34.923 78.001-78.001 78.001h-26v104.002h26c43.078 0 78.001 34.923 78.001 78.001v104.002c0 100.515 81.485 182.003 182.003 182.003h26v-104.002h-26c-43.078 0-78.001-34.923-78.001-78.001v-104.002c0-50.931-20.928-96.966-54.646-130.002 33.716-33.036 54.646-79.072 54.646-130.002z",
+                                       "M824.005 381.998v-104.002c0-43.078-34.923-78.001-78.001-78.001h-26v-104.002h26c100.515 0 182.003 81.485 182.003 182.003v104.002c0 43.078 34.923 78.001 78.001 78.001h26v104.002h-26c-43.078 0-78.001 34.923-78.001 78.001v104.002c0 100.515-81.488 182.003-182.003 182.003h-26v-104.002h26c43.078 0 78.001-34.923 78.001-78.001v-104.002c0-50.931 20.928-96.966 54.646-130.002-33.716-33.036-54.646-79.072-54.646-130.002z",
+                                       "M616.002 356.715c0 57.439-46.562 104.002-104.002 104.002s-104.002-46.562-104.002-104.002c0-57.439 46.562-104.002 104.002-104.002s104.002 46.562 104.002 104.002z",
+                                       "M512 511.283c-57.439 0-104.002 46.562-104.002 104.002 0 55.845 26 100.115 105.752 103.88-23.719 33.417-59.441 46.612-105.752 50.944v61.751c0 0 208.003 18.144 208.003-216.577-0.202-57.441-46.56-104.004-104.002-104.004z"
+                               ],
+                               "width": 1024,
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "code",
+                                       "semicolon",
+                                       "curly-braces"
+                               ],
+                               "grid": 16,
+                               "defaultCode": 58883
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 630,
+                               "id": 1,
+                               "prevSize": 32,
+                               "code": 58883,
+                               "name": "codesample"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 85
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M864.626 473.162c-65.754-183.44-205.11-348.15-352.626-473.162-147.516 125.012-286.87 289.722-352.626 473.162-40.664 113.436-44.682 236.562 12.584 345.4 65.846 125.14 198.632 205.438 340.042 205.438s274.196-80.298 340.040-205.44c57.27-108.838 53.25-231.962 12.586-345.398zM738.764 758.956c-43.802 83.252-132.812 137.044-226.764 137.044-55.12 0-108.524-18.536-152.112-50.652 13.242 1.724 26.632 2.652 40.112 2.652 117.426 0 228.668-67.214 283.402-171.242 44.878-85.292 40.978-173.848 23.882-244.338 14.558 28.15 26.906 56.198 36.848 83.932 22.606 63.062 40.024 156.34-5.368 242.604z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "drop"
+                               ],
+                               "defaultCode": 57381,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 87,
+                               "order": 631,
+                               "prevSize": 32,
+                               "code": 59701,
+                               "ligatures": "droplet, color9",
+                               "name": "drop"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 86
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 128h-512l-256 256 512 576 512-576-256-256zM512 778.666v-2.666h-2.37l-14.222-16h16.592v-16h-30.814l-14.222-16h45.036v-16h-59.258l-14.222-16h73.48v-16h-87.704l-14.222-16h101.926v-16h-116.148l-14.222-16h130.37v-16h-144.592l-14.222-16h158.814v-16h-173.038l-14.222-16h187.26v-16h-201.482l-14.222-16h215.704v-16h-229.926l-14.222-16h244.148v-16h-258.372l-14.222-16h272.594v-16h-286.816l-14.222-16h301.038v-16h-315.26l-14.222-16h329.482v-16h-343.706l-7.344-8.262 139.072-139.072h211.978v3.334h215.314l16 16h-231.314v16h247.314l16 16h-263.314v16h279.314l16 16h-295.314v16h311.314l16 16h-327.314v16h343.312l7.738 7.738-351.050 394.928z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "diamond",
+                                       "gem",
+                                       "jewelry",
+                                       "dualtone"
+                               ],
+                               "defaultCode": 57889,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 635,
+                               "order": 632,
+                               "prevSize": 32,
+                               "code": 60327,
+                               "ligatures": "diamond2, gem2",
+                               "name": "sharpen"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 87
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512 512-229.23 512-512-229.23-512-512-512zM128 512c0-212.078 171.922-384 384-384v768c-212.078 0-384-171.922-384-384z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "contrast"
+                               ],
+                               "defaultCode": 58104,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 854,
+                               "order": 633,
+                               "prevSize": 32,
+                               "code": 60628,
+                               "ligatures": "contrast",
+                               "name": "contrast"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 88
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M893.254 221.254l-90.508-90.508-290.746 290.744-290.746-290.744-90.508 90.506 290.746 290.748-290.746 290.746 90.508 90.508 290.746-290.746 290.746 290.746 90.508-90.51-290.744-290.744z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "remove"
+                               ],
+                               "defaultCode": 60778,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 24,
+                               "order": 634,
+                               "prevSize": 32,
+                               "code": 60778,
+                               "ligatures": "cross2, cancel3",
+                               "name": "remove22"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 89
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64v384c0 35.346 28.654 64 64 64s64-28.654 64-64v-229.488l677.488 677.488h-229.488c-35.346 0-64 28.652-64 64 0 35.346 28.654 64 64 64h384c35.346 0 64-28.654 64-64v-384c0-35.348-28.654-64-64-64s-64 28.652-64 64v229.488l-677.488-677.488h229.488c35.346 0 64-28.654 64-64s-28.652-64-64-64h-384c-35.346 0-64 28.654-64 64z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "resize2"
+                               ],
+                               "defaultCode": 58329,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 1097,
+                               "order": 575,
+                               "prevSize": 32,
+                               "code": 60921,
+                               "ligatures": "arrow-resize2, diagonal2",
+                               "name": "resize2"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 90
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M483.2 640l-147.2-336c-9.6-25.6-19.2-44.8-25.6-54.4s-16-12.8-25.6-12.8c-16 0-25.6 3.2-28.8 3.2v-70.4c9.6-6.4 25.6-6.4 38.4-9.6 32 0 57.6 6.4 73.6 22.4 6.4 6.4 12.8 16 19.2 25.6 6.4 12.8 12.8 25.6 16 41.6l121.6 291.2 150.4-371.2h92.8l-198.4 470.4v224h-86.4v-224z",
+                                       "M0 0v1024h1024v-1024h-1024zM960 960h-896v-896h896v896z"
+                               ],
+                               "attrs": [
+                                       {},
+                                       {}
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "gamma2"
+                               ],
+                               "grid": 16,
+                               "defaultCode": 58880
+                       },
+                       "attrs": [
+                               {},
+                               {}
+                       ],
+                       "properties": {
+                               "order": 576,
+                               "id": 1,
+                               "prevSize": 32,
+                               "code": 58880,
+                               "name": "gamma"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 91
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M627.2 880h-579.2v-396.8h579.2v396.8zM553.6 553.6h-435.2v256h435.2v-256z",
+                                       "M259.2 227.2c176-176 457.6-176 633.6 0s176 457.6 0 633.6c-121.6 121.6-297.6 160-454.4 108.8 121.6 28.8 262.4-9.6 361.6-108.8 150.4-150.4 160-384 22.4-521.6-121.6-121.6-320-128-470.4-19.2l86.4 86.4-294.4 22.4 22.4-294.4 92.8 92.8z"
+                               ],
+                               "attrs": [
+                                       {},
+                                       {}
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "orientation"
+                               ],
+                               "grid": 16,
+                               "defaultCode": 58881
+                       },
+                       "attrs": [
+                               {},
+                               {}
+                       ],
+                       "properties": {
+                               "order": 577,
+                               "id": 0,
+                               "prevSize": 32,
+                               "code": 58881,
+                               "name": "orientation"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 92
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M768 544v352h-640v-640h352l128-128h-512c-52.8 0-96 43.2-96 96v704c0 52.8 43.2 96 96 96h704c52.798 0 96-43.2 96-96v-512l-128 128z",
+                                       "M864 0l-608 608v160h160l608-608c0-96-64-160-160-160zM416 640l-48-48 480-480 48 48-480 480z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "pencil",
+                                       "write",
+                                       "edit"
+                               ],
+                               "defaultCode": 57361,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 68,
+                               "order": 578,
+                               "prevSize": 32,
+                               "code": 59669,
+                               "ligatures": "pencil7, write7",
+                               "name": "editimage"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 93
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M607.998 128.014c-212.070 0-383.986 171.916-383.986 383.986h-191.994l246.848 246.848 246.848-246.848h-191.994c0-151.478 122.798-274.276 274.276-274.276 151.48 0 274.276 122.798 274.276 274.276 0 151.48-122.796 274.276-274.276 274.276v109.71c212.070 0 383.986-171.916 383.986-383.986s-171.916-383.986-383.986-383.986z"
+                               ],
+                               "width": 1024,
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "rotate-ccw",
+                                       "ccw",
+                                       "arrow"
+                               ],
+                               "defaultCode": 60072,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 22,
+                               "order": 579,
+                               "prevSize": 32,
+                               "code": 60072,
+                               "ligatures": "rotate-ccw3, ccw4",
+                               "name": "rotateleft"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 94
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M416.002 128.014c212.070 0 383.986 171.916 383.986 383.986h191.994l-246.848 246.848-246.848-246.848h191.994c0-151.478-122.798-274.276-274.276-274.276-151.48 0-274.276 122.798-274.276 274.276 0 151.48 122.796 274.276 274.276 274.276v109.71c-212.070 0-383.986-171.916-383.986-383.986s171.916-383.986 383.986-383.986z"
+                               ],
+                               "width": 1024,
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "rotate-cw",
+                                       "cw",
+                                       "arrow"
+                               ],
+                               "defaultCode": 60073,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 580,
+                               "id": 1679,
+                               "prevSize": 32,
+                               "code": 60073,
+                               "ligatures": "rotate-cw3, cw4",
+                               "name": "rotateright"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 95
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 384h1024v-384zM1024 960v-384h-1024z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "flip-vertical",
+                                       "mirror"
+                               ],
+                               "defaultCode": 57663,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 403,
+                               "order": 581,
+                               "prevSize": 32,
+                               "code": 60074,
+                               "ligatures": "flip-vertical, mirror",
+                               "name": "flipv"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 96
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M576 0v1024h384zM0 1024h384v-1024z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "flip-horizontal",
+                                       "mirror"
+                               ],
+                               "defaultCode": 57664,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 405,
+                               "order": 582,
+                               "prevSize": 32,
+                               "code": 60076,
+                               "ligatures": "flip-horizontal, mirror3",
+                               "name": "fliph"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 97
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M992.262 871.396l-242.552-206.294c-25.074-22.566-51.89-32.926-73.552-31.926 57.256-67.068 91.842-154.078 91.842-249.176 0-212.078-171.922-384-384-384-212.076 0-384 171.922-384 384 0 212.078 171.922 384 384 384 95.098 0 182.108-34.586 249.176-91.844-1 21.662 9.36 48.478 31.926 73.552l206.294 242.552c35.322 39.246 93.022 42.554 128.22 7.356s31.892-92.898-7.354-128.22zM384 640c-141.384 0-256-114.616-256-256s114.616-256 256-256 256 114.616 256 256-114.614 256-256 256zM448 192h-128v128h-128v128h128v128h128v-128h128v-128h-128z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "zoom-in",
+                                       "magnifier",
+                                       "magnifier-plus",
+                                       "enlarge"
+                               ],
+                               "defaultCode": 57788,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 534,
+                               "order": 583,
+                               "prevSize": 32,
+                               "code": 60213,
+                               "ligatures": "zoom-in3, magnifier9",
+                               "name": "zoomin"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 98
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M992.262 871.396l-242.552-206.294c-25.074-22.566-51.89-32.926-73.552-31.926 57.256-67.068 91.842-154.078 91.842-249.176 0-212.078-171.922-384-384-384-212.076 0-384 171.922-384 384 0 212.078 171.922 384 384 384 95.098 0 182.108-34.586 249.176-91.844-1 21.662 9.36 48.478 31.926 73.552l206.294 242.552c35.322 39.246 93.022 42.554 128.22 7.356s31.892-92.898-7.354-128.22zM384 640c-141.384 0-256-114.616-256-256s114.616-256 256-256 256 114.616 256 256-114.614 256-256 256zM192 320h384v128h-384z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "zoom-out",
+                                       "magnifier",
+                                       "magnifier-minus",
+                                       "reduce"
+                               ],
+                               "defaultCode": 57789,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 535,
+                               "order": 584,
+                               "prevSize": 32,
+                               "code": 60214,
+                               "ligatures": "zoom-out3, magnifier10",
+                               "name": "zoomout"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 99
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M64 192h896v192h-896zM64 448h896v192h-896zM64 704h896v192h-896z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "menu",
+                                       "list",
+                                       "options",
+                                       "lines",
+                                       "hamburger"
+                               ],
+                               "defaultCode": 58031,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 585,
+                               "id": 1448,
+                               "prevSize": 32,
+                               "code": 60522,
+                               "ligatures": "menu3, list4",
+                               "name": "options"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 100
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M512 832c35.346 0 64 28.654 64 64v64c0 35.346-28.654 64-64 64s-64-28.654-64-64v-64c0-35.346 28.654-64 64-64zM512 192c-35.346 0-64-28.654-64-64v-64c0-35.346 28.654-64 64-64s64 28.654 64 64v64c0 35.346-28.654 64-64 64zM960 448c35.346 0 64 28.654 64 64s-28.654 64-64 64h-64c-35.348 0-64-28.654-64-64s28.652-64 64-64h64zM192 512c0 35.346-28.654 64-64 64h-64c-35.346 0-64-28.654-64-64s28.654-64 64-64h64c35.346 0 64 28.654 64 64zM828.784 738.274l45.256 45.258c24.992 24.99 24.992 65.516 0 90.508-24.994 24.992-65.518 24.992-90.51 0l-45.256-45.256c-24.992-24.99-24.992-65.516 0-90.51 24.994-24.992 65.518-24.992 90.51-0zM195.216 285.726l-45.256-45.256c-24.994-24.994-24.994-65.516 0-90.51s65.516-24.994 90.51 0l45.256 45.256c24.994 24.994 24.994 65.516 0 90.51s-65.516 24.994-90.51 0zM828.784 285.726c-24.992 24.992-65.516 24.992-90.51 0-24.992-24.994-24.992-65.516 0-90.51l45.256-45.254c24.992-24.994 65.516-24.994 90.51 0 24.992 24.994 24.992 65.516 0 90.51l-45.256 45.254zM195.216 738.274c24.992-24.992 65.518-24.992 90.508 0 24.994 24.994 24.994 65.52 0 90.51l-45.254 45.256c-24.994 24.992-65.516 24.992-90.51 0s-24.994-65.518 0-90.508l45.256-45.258z",
+                                       "M512 256c-141.384 0-256 114.616-256 256 0 141.382 114.616 256 256 256 141.382 0 256-114.618 256-256 0-141.384-114.616-256-256-256zM512 672c-88.366 0-160-71.634-160-160s71.634-160 160-160 160 71.634 160 160-71.634 160-160 160z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "sun",
+                                       "weather"
+                               ],
+                               "defaultCode": 58094,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 844,
+                               "order": 635,
+                               "prevSize": 32,
+                               "code": 60620,
+                               "ligatures": "sun2, weather21",
+                               "name": "sun"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 101
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M715.812 64.48c-60.25-34.784-124.618-55.904-189.572-64.48 122.936 160.082 144.768 384.762 37.574 570.42-107.2 185.67-312.688 279.112-512.788 252.68 39.898 51.958 90.376 97.146 150.628 131.934 245.908 141.974 560.37 57.72 702.344-188.198 141.988-245.924 57.732-560.372-188.186-702.356z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "moon",
+                                       "night",
+                                       "sleep"
+                               ],
+                               "defaultCode": 58105,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 855,
+                               "order": 636,
+                               "prevSize": 32,
+                               "code": 60621,
+                               "ligatures": "moon, night",
+                               "name": "moon"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 102
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M672 1024l192-192-320-320 320-320-192-192-512 512z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "arrow-left",
+                                       "left",
+                                       "previous"
+                               ],
+                               "defaultCode": 58291,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 1056,
+                               "order": 637,
+                               "prevSize": 32,
+                               "code": 60864,
+                               "ligatures": "arrow-left, left4",
+                               "name": "arrowleft"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 103
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M832 256l192-192-64-64-192 192h-448v-192h-128v192h-192v128h192v512h512v192h128v-192h192v-128h-192v-448zM320 320h320l-320 320v-320zM384 704l320-320v320h-320z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "crop",
+                                       "resize",
+                                       "cut"
+                               ],
+                               "defaultCode": 58428,
+                               "grid": 16
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "id": 1201,
+                               "order": 638,
+                               "prevSize": 32,
+                               "code": 61048,
+                               "ligatures": "crop, resize",
+                               "name": "crop"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 104
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64v896h1024v-896h-1024zM640 704v192h-256v-192h256zM640 192v192h-256v-192h256zM320 192v192h-256v-192h256zM704 384v-192h256v192h-256zM64 704h256v192h-256v-192zM704 896v-192h256v192h-256z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tablerowprops"
+                               ],
+                               "defaultCode": 58880,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1680,
+                               "order": 639,
+                               "prevSize": 32,
+                               "code": 58884,
+                               "name": "tablerowprops"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 105
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64v896h1024v-896h-1024zM640 704v192h-256v-192h256zM640 192v192h-256v-192h256zM320 192v192h-256v-192h256zM64 448h256v192h-256v-192zM704 448h256v192h-256v-192zM704 384v-192h256v192h-256zM64 704h256v192h-256v-192zM704 896v-192h256v192h-256z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tablecellprops"
+                               ],
+                               "defaultCode": 58881,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1681,
+                               "order": 640,
+                               "prevSize": 32,
+                               "code": 58885,
+                               "name": "tablecellprops"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 106
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64v832h1024v-832h-1024zM320 832h-256v-192h256v192zM320 576h-256v-192h256v192zM640 832h-256v-192h256v192zM640 576h-256v-192h256v192zM960 832h-256v-192h256v192zM960 576h-256v-192h256v192zM960 320h-896v-192h896v192z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "table2"
+                               ],
+                               "defaultCode": 58882,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1682,
+                               "order": 641,
+                               "prevSize": 32,
+                               "code": 58886,
+                               "name": "table2"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 107
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64v896h1024v-896h-1024zM384 896v-448h576v448h-576zM640 192v192h-256v-192h256zM320 192v192h-256v-192h256zM64 448h256v192h-256v-192zM704 384v-192h256v192h-256zM64 704h256v192h-256v-192z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tablemergecells"
+                               ],
+                               "defaultCode": 58884,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1683,
+                               "order": 586,
+                               "prevSize": 32,
+                               "code": 58887,
+                               "name": "tablemergecells"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 108
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M320 771.2v-182.4h-182.4v-89.6h182.4v-182.4h86.4v182.4h185.6v89.6h-185.6v182.4zM0 64v896h1024v-896h-1024zM640 896h-576v-704h576v704zM960 896h-256v-192h256v192zM960 640h-256v-192h256v192zM960 384h-256v-192h256v192z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tableinsertcolbefore"
+                               ],
+                               "defaultCode": 58885,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1684,
+                               "order": 587,
+                               "prevSize": 32,
+                               "code": 58888,
+                               "name": "tableinsertcolbefore"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 109
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M704 316.8v182.4h182.4v89.6h-182.4v182.4h-86.4v-182.4h-185.6v-89.6h185.6v-182.4zM0 64v896h1024v-896h-1024zM320 896h-256v-192h256v192zM320 640h-256v-192h256v192zM320 384h-256v-192h256v192zM960 896h-576v-704h576v704z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tableinsertcolafter"
+                               ],
+                               "defaultCode": 58886,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1685,
+                               "order": 588,
+                               "prevSize": 32,
+                               "code": 58889,
+                               "name": "tableinsertcolafter"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 110
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M691.2 451.2h-144v144h-70.4v-144h-144v-67.2h144v-144h70.4v144h144zM0 64v896h1024v-896h-1024zM320 896h-256v-192h256v192zM640 896h-256v-192h256v192zM960 896h-256v-192h256v192zM960 643.2h-896v-451.2h896v451.2z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tableinsertrowbefore"
+                               ],
+                               "defaultCode": 58887,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1686,
+                               "order": 589,
+                               "prevSize": 32,
+                               "code": 58890,
+                               "name": "tableinsertrowbefore"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 111
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M332.8 636.8h144v-144h70.4v144h144v67.2h-144v144h-70.4v-144h-144zM0 64v896h1024v-896h-1024zM384 192h256v192h-256v-192zM64 192h256v192h-256v-192zM960 896h-896v-451.2h896v451.2zM960 384h-256v-192h256v192z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tableinsertrowafter"
+                               ],
+                               "defaultCode": 58888,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1687,
+                               "order": 590,
+                               "prevSize": 32,
+                               "code": 58891,
+                               "name": "tableinsertrowafter"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 112
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64v896h1024v-896h-1024zM384 192h256v192h-256v-192zM320 896h-256v-192h256v192zM320 640h-256v-192h256v192zM320 384h-256v-192h256v192zM960 896h-576v-448h576v448zM960 384h-256v-192h256v192zM864 803.2l-60.8 60.8-131.2-131.2-131.2 131.2-60.8-60.8 131.2-131.2-131.2-131.2 60.8-60.8 131.2 131.2 131.2-131.2 60.8 60.8-131.2 131.2z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tablesplitcells"
+                               ],
+                               "defaultCode": 58890,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1688,
+                               "order": 591,
+                               "prevSize": 32,
+                               "code": 58893,
+                               "name": "tablesplitcells"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 113
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64h1024v896h-1024v-896zM60.8 192v704h899.2v-704h-899.2zM809.6 748.8l-96 96-204.8-204.8-204.8 204.8-96-96 204.8-204.8-204.8-204.8 96-96 204.8 204.8 204.8-204.8 96 96-204.8 204.8z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tabledelete"
+                               ],
+                               "defaultCode": 58891,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1689,
+                               "order": 592,
+                               "prevSize": 32,
+                               "code": 58894,
+                               "name": "tabledelete"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 114
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64v832h1024v-832h-1024zM640 832h-256v-192h256v192zM640 576h-256v-192h256v192zM640 320h-256v-192h256v192zM960 832h-256v-192h256v192zM960 576h-256v-192h256v192zM960 320h-256v-192h256v192z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tableleftheader"
+                               ],
+                               "defaultCode": 58922,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1690,
+                               "order": 593,
+                               "prevSize": 32,
+                               "code": 58922,
+                               "name": "tableleftheader"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 115
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M0 64v832h1024v-832h-1024zM320 832h-256v-192h256v192zM320 576h-256v-192h256v192zM640 832h-256v-192h256v192zM640 576h-256v-192h256v192zM960 832h-256v-192h256v192zM960 576h-256v-192h256v192z"
+                               ],
+                               "attrs": [
+                                       {
+                                               "fill": "rgb(0, 0, 0)"
+                                       }
+                               ],
+                               "isMulticolor": false,
+                               "tags": [
+                                       "tabletopheader"
+                               ],
+                               "defaultCode": 58923,
+                               "grid": 16
+                       },
+                       "attrs": [
+                               {
+                                       "fill": "rgb(0, 0, 0)"
+                               }
+                       ],
+                       "properties": {
+                               "id": 1691,
+                               "order": 594,
+                               "prevSize": 32,
+                               "code": 58923,
+                               "name": "tabletopheader"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 116
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M886.4 387.2l-156.8 156.8 160 160-76.8 76.8-160-160-156.8 156.8-76.8-73.6 160-160-163.2-163.2 76.8-76.8 163.2 163.2 156.8-156.8 73.6 76.8zM0 64v896h1024v-896h-1024zM960 384h-22.4l-64 64h86.4v192h-89.6l64 64h25.6v192h-896v-192h310.4l64-64h-374.4v-192h371.2l-64-64h-307.2v-192h896v192z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "grid": 16,
+                               "tags": [
+                                       "tabledeleterow"
+                               ],
+                               "defaultCode": 59392
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 595,
+                               "id": 1693,
+                               "prevSize": 32,
+                               "code": 59392,
+                               "name": "tabledeleterow"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 117
+               },
+               {
+                       "icon": {
+                               "paths": [
+                                       "M320 460.8l64 64v12.8l-64 64v-140.8zM640 537.6l64 64v-137.6l-64 64v9.6zM1024 64v896h-1024v-896h1024zM960 192h-256v51.2l-12.8-12.8-51.2 51.2v-89.6h-256v89.6l-51.2-51.2-12.8 12.8v-51.2h-256v704h256v-118.4l35.2 35.2 28.8-28.8v115.2h256v-115.2l48 48 16-16v83.2h256v-707.2zM672 297.6l-156.8 156.8-163.2-163.2-76.8 76.8 163.2 163.2-156.8 156.8 76.8 76.8 156.8-156.8 160 160 76.8-76.8-160-160 156.8-156.8-76.8-76.8z"
+                               ],
+                               "attrs": [],
+                               "isMulticolor": false,
+                               "grid": 16,
+                               "tags": [
+                                       "tabledeletecol"
+                               ],
+                               "defaultCode": 59393
+                       },
+                       "attrs": [],
+                       "properties": {
+                               "order": 596,
+                               "id": 1692,
+                               "prevSize": 32,
+                               "code": 59393,
+                               "name": "tabledeletecol"
+                       },
+                       "setIdx": 2,
+                       "setId": 1,
+                       "iconIdx": 118
+               }
+       ],
+       "height": 1024,
+       "metadata": {
+               "name": "tinymce"
+       },
+       "preferences": {
+               "showGlyphs": true,
+               "showQuickUse": true,
+               "showQuickUse2": true,
+               "showSVGs": true,
+               "fontPref": {
+                       "prefix": "icon-",
+                       "metadata": {
+                               "fontFamily": "tinymce",
+                               "majorVersion": 1,
+                               "minorVersion": 0
+                       },
+                       "metrics": {
+                               "emSize": 1024,
+                               "baseline": 6.25,
+                               "whitespace": 50
+                       },
+                       "resetPoint": 59649,
+                       "embed": false
+               },
+               "imagePref": {
+                       "prefix": "icon-",
+                       "png": true,
+                       "useClassSelector": true,
+                       "color": 4473924,
+                       "bgColor": 16777215
+               },
+               "historySize": 100,
+               "gridSize": 16,
+               "showGrid": true,
+               "showCodes": true,
+               "showLiga": false
+       }
+}
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce.svg b/public/libs/tinymce/skins/dark/fonts/tinymce.svg
new file mode 100644 (file)
index 0000000..5727cea
--- /dev/null
@@ -0,0 +1,131 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>Generated by IcoMoon</metadata>
+<defs>
+<font id="tinymce" horiz-adv-x="1024">
+<font-face units-per-em="1024" ascent="960" descent="-64" />
+<missing-glyph horiz-adv-x="1024" />
+<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
+<glyph unicode="&#xe000;" glyph-name="save" d="M896 960h-896v-1024h1024v896l-128 128zM512 832h128v-256h-128v256zM896 64h-768v768h64v-320h576v320h74.978l53.022-53.018v-714.982z" />
+<glyph unicode="&#xe001;" glyph-name="newdocument" d="M903.432 760.57l-142.864 142.862c-31.112 31.112-92.568 56.568-136.568 56.568h-480c-44 0-80-36-80-80v-864c0-44 36-80 80-80h736c44 0 80 36 80 80v608c0 44-25.456 105.458-56.568 136.57zM858.178 715.314c3.13-3.13 6.25-6.974 9.28-11.314h-163.458v163.456c4.34-3.030 8.184-6.15 11.314-9.28l142.864-142.862zM896 16c0-8.672-7.328-16-16-16h-736c-8.672 0-16 7.328-16 16v864c0 8.672 7.328 16 16 16h480c4.832 0 10.254-0.61 16-1.704v-254.296h254.296c1.094-5.746 1.704-11.166 1.704-16v-608z" />
+<glyph unicode="&#xe002;" glyph-name="fullpage" d="M1024 367.542v160.916l-159.144 15.914c-8.186 30.042-20.088 58.548-35.21 84.98l104.596 127.838-113.052 113.050-127.836-104.596c-26.434 15.124-54.942 27.026-84.982 35.208l-15.914 159.148h-160.916l-15.914-159.146c-30.042-8.186-58.548-20.086-84.98-35.208l-127.838 104.594-113.050-113.050 104.596-127.836c-15.124-26.432-27.026-54.94-35.21-84.98l-159.146-15.916v-160.916l159.146-15.914c8.186-30.042 20.086-58.548 35.21-84.982l-104.596-127.836 113.048-113.048 127.838 104.596c26.432-15.124 54.94-27.028 84.98-35.21l15.916-159.148h160.916l15.914 159.144c30.042 8.186 58.548 20.088 84.982 35.21l127.836-104.596 113.048 113.048-104.596 127.836c15.124 26.434 27.028 54.942 35.21 84.98l159.148 15.92zM704 384l-128-128h-128l-128 128v128l128 128h128l128-128v-128z" />
+<glyph unicode="&#xe003;" glyph-name="alignleft" d="M0 896h1024v-128h-1024zM0 704h640v-128h-640zM0 320h640v-128h-640zM0 512h1024v-128h-1024zM0 128h1024v-128h-1024z" />
+<glyph unicode="&#xe004;" glyph-name="aligncenter" d="M0 896h1024v-128h-1024zM192 704h640v-128h-640zM192 320h640v-128h-640zM0 512h1024v-128h-1024zM0 128h1024v-128h-1024z" />
+<glyph unicode="&#xe005;" glyph-name="alignright" d="M0 896h1024v-128h-1024zM384 704h640v-128h-640zM384 320h640v-128h-640zM0 512h1024v-128h-1024zM0 128h1024v-128h-1024z" />
+<glyph unicode="&#xe006;" glyph-name="alignjustify" d="M0 896h1024v-128h-1024zM0 704h1024v-128h-1024zM0 512h1024v-128h-1024zM0 320h1024v-128h-1024zM0 128h1024v-128h-1024z" />
+<glyph unicode="&#xe007;" glyph-name="cut" d="M890.774 250.846c-45.654 45.556-103.728 69.072-157.946 69.072h-29.112l-63.904 64.008 255.62 256.038c63.904 64.010 63.904 192.028 0 256.038l-383.43-384.056-383.432 384.054c-63.904-64.008-63.904-192.028 0-256.038l255.622-256.034-63.906-64.008h-29.114c-54.22 0-112.292-23.518-157.948-69.076-81.622-81.442-92.65-202.484-24.63-270.35 29.97-29.902 70.288-44.494 112.996-44.494 54.216 0 112.29 23.514 157.946 69.072 53.584 53.464 76.742 124 67.084 185.348l65.384 65.488 65.376-65.488c-9.656-61.348 13.506-131.882 67.084-185.348 45.662-45.558 103.732-69.072 157.948-69.072 42.708 0 83.024 14.592 112.994 44.496 68.020 67.866 56.988 188.908-24.632 270.35zM353.024 114.462c-7.698-17.882-19.010-34.346-33.626-48.926-14.636-14.604-31.172-25.918-49.148-33.624-16.132-6.916-32.96-10.568-48.662-10.568-15.146 0-36.612 3.402-52.862 19.612-16.136 16.104-19.52 37.318-19.52 52.288 0 15.542 3.642 32.21 10.526 48.212 7.7 17.884 19.014 34.346 33.626 48.926 14.634 14.606 31.172 25.914 49.15 33.624 16.134 6.914 32.96 10.568 48.664 10.568 15.146 0 36.612-3.4 52.858-19.614 16.134-16.098 19.522-37.316 19.522-52.284 0.002-15.542-3.638-32.216-10.528-48.214zM512.004 293.404c-49.914 0-90.376 40.532-90.376 90.526 0 49.992 40.462 90.52 90.376 90.52s90.372-40.528 90.372-90.52c0-49.998-40.46-90.526-90.372-90.526zM855.272 40.958c-16.248-16.208-37.712-19.612-52.86-19.612-15.704 0-32.53 3.652-48.666 10.568-17.972 7.706-34.508 19.020-49.142 33.624-14.614 14.58-25.926 31.042-33.626 48.926-6.886 15.998-10.526 32.672-10.526 48.212 0 14.966 3.384 36.188 19.52 52.286 16.246 16.208 37.712 19.614 52.86 19.614 15.7 0 32.53-3.654 48.66-10.568 17.978-7.708 34.516-19.018 49.15-33.624 14.61-14.58 25.924-31.042 33.626-48.926 6.884-15.998 10.526-32.67 10.526-48.212-0.002-14.97-3.39-36.186-19.522-52.288z" />
+<glyph unicode="&#xe008;" glyph-name="paste" d="M832 640v160c0 17.6-14.4 32-32 32h-224v64c0 35.2-28.8 64-64 64h-128c-35.204 0-64-28.8-64-64v-64h-224c-17.602 0-32-14.4-32-32v-640c0-17.6 14.398-32 32-32h288v-192h448l192 192v512h-192zM384 895.886c0.034 0.038 0.072 0.078 0.114 0.114h127.768c0.042-0.036 0.082-0.076 0.118-0.114v-63.886h-128v63.886zM192 704v64h512v-64h-512zM832 26.51v101.49h101.49l-101.49-101.49zM960 192h-192v-192h-320v576h512v-384z" />
+<glyph unicode="&#xe009;" glyph-name="searchreplace" d="M64 960h384v-64h-384zM576 960h384v-64h-384zM952 640h-56v256h-256v-256h-256v256h-256v-256h-56c-39.6 0-72-32.4-72-72v-560c0-39.6 32.4-72 72-72h304c39.6 0 72 32.4 72 72v376h128v-376c0-39.6 32.4-72 72-72h304c39.6 0 72 32.4 72 72v560c0 39.6-32.4 72-72 72zM348 0h-248c-19.8 0-36 14.4-36 32s16.2 32 36 32h248c19.8 0 36-14.4 36-32s-16.2-32-36-32zM544 448h-64c-17.6 0-32 14.4-32 32s14.4 32 32 32h64c17.6 0 32-14.4 32-32s-14.4-32-32-32zM924 0h-248c-19.8 0-36 14.4-36 32s16.2 32 36 32h248c19.8 0 36-14.4 36-32s-16.2-32-36-32z" />
+<glyph unicode="&#xe00a;" glyph-name="bullist" d="M384 896h640v-128h-640v128zM384 512h640v-128h-640v128zM384 128h640v-128h-640v128zM0 832c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128zM0 448c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128zM0 64c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128z" />
+<glyph unicode="&#xe00b;" glyph-name="numlist" d="M384 128h640v-128h-640zM384 512h640v-128h-640zM384 896h640v-128h-640zM192 960v-256h-64v192h-64v64zM128 434v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM256 256v-320h-192v64h128v64h-128v64h128v64h-128v64z" />
+<glyph unicode="&#xe00c;" glyph-name="indent" d="M0 896h1024v-128h-1024zM384 704h640v-128h-640zM384 512h640v-128h-640zM384 320h640v-128h-640zM0 128h1024v-128h-1024zM0 256v384l256-192z" />
+<glyph unicode="&#xe00d;" glyph-name="outdent" d="M0 896h1024v-128h-1024zM384 704h640v-128h-640zM384 512h640v-128h-640zM384 320h640v-128h-640zM0 128h1024v-128h-1024zM256 640v-384l-256 192z" />
+<glyph unicode="&#xe00e;" glyph-name="blockquote" d="M225 512c123.712 0 224-100.29 224-224 0-123.712-100.288-224-224-224s-224 100.288-224 224l-1 32c0 247.424 200.576 448 448 448v-128c-85.474 0-165.834-33.286-226.274-93.726-11.634-11.636-22.252-24.016-31.83-37.020 11.438 1.8 23.16 2.746 35.104 2.746zM801 512c123.71 0 224-100.29 224-224 0-123.712-100.29-224-224-224s-224 100.288-224 224l-1 32c0 247.424 200.576 448 448 448v-128c-85.474 0-165.834-33.286-226.274-93.726-11.636-11.636-22.254-24.016-31.832-37.020 11.44 1.8 23.16 2.746 35.106 2.746z" />
+<glyph unicode="&#xe00f;" glyph-name="undo" d="M761.862-64c113.726 206.032 132.888 520.306-313.862 509.824v-253.824l-384 384 384 384v-248.372c534.962 13.942 594.57-472.214 313.862-775.628z" />
+<glyph unicode="&#xe010;" glyph-name="redo" d="M576 711.628v248.372l384-384-384-384v253.824c-446.75 10.482-427.588-303.792-313.86-509.824-280.712 303.414-221.1 789.57 313.86 775.628z" />
+<glyph unicode="&#xe011;" glyph-name="link" d="M320 256c17.6-17.6 47.274-16.726 65.942 1.942l316.118 316.116c18.668 18.668 19.54 48.342 1.94 65.942s-47.274 16.726-65.942-1.942l-316.116-316.116c-18.668-18.668-19.542-48.342-1.942-65.942zM476.888 284.888c4.56-9.050 6.99-19.16 6.99-29.696 0-17.616-6.744-34.060-18.992-46.308l-163.382-163.382c-12.248-12.248-28.694-18.992-46.308-18.992s-34.060 6.744-46.308 18.992l-99.382 99.382c-12.248 12.248-18.992 28.694-18.992 46.308s6.744 34.060 18.992 46.308l163.382 163.382c12.248 12.248 28.694 18.994 46.308 18.994 10.536 0 20.644-2.43 29.696-6.99l65.338 65.338c-27.87 21.41-61.44 32.16-95.034 32.16-39.986 0-79.972-15.166-110.308-45.502l-163.382-163.382c-60.67-60.67-60.67-159.948 0-220.618l99.382-99.382c30.334-30.332 70.32-45.5 110.306-45.5 39.988 0 79.974 15.168 110.308 45.502l163.382 163.382c55.82 55.82 60.238 144.298 13.344 205.344l-65.34-65.34zM978.498 815.116l-99.382 99.382c-30.334 30.336-70.32 45.502-110.308 45.502-39.986 0-79.972-15.166-110.308-45.502l-163.382-163.382c-55.82-55.82-60.238-144.298-13.342-205.342l65.338 65.34c-4.558 9.050-6.988 19.16-6.988 29.694 0 17.616 6.744 34.060 18.992 46.308l163.382 163.382c12.248 12.248 28.694 18.994 46.308 18.994s34.060-6.746 46.308-18.994l99.382-99.382c12.248-12.248 18.992-28.694 18.992-46.308s-6.744-34.060-18.992-46.308l-163.382-163.382c-12.248-12.248-28.694-18.992-46.308-18.992-10.536 0-20.644 2.43-29.696 6.99l-65.338-65.338c27.872-21.41 61.44-32.16 95.034-32.16 39.988 0 79.974 15.168 110.308 45.502l163.382 163.382c60.67 60.666 60.67 159.944 0 220.614z" />
+<glyph unicode="&#xe012;" glyph-name="unlink" d="M476.888 284.886c4.56-9.048 6.99-19.158 6.99-29.696 0-17.616-6.744-34.058-18.992-46.308l-163.38-163.38c-12.248-12.248-28.696-18.992-46.308-18.992s-34.060 6.744-46.308 18.992l-99.38 99.38c-12.248 12.25-18.992 28.696-18.992 46.308s6.744 34.060 18.992 46.308l163.38 163.382c12.248 12.246 28.696 18.992 46.308 18.992 10.538 0 20.644-2.43 29.696-6.988l65.338 65.336c-27.87 21.41-61.44 32.16-95.034 32.16-39.986 0-79.972-15.166-110.308-45.502l-163.38-163.382c-60.67-60.67-60.67-159.95 0-220.618l99.38-99.382c30.334-30.332 70.32-45.5 110.306-45.5 39.988 0 79.974 15.168 110.308 45.502l163.38 163.38c55.82 55.82 60.238 144.298 13.344 205.346l-65.34-65.338zM978.496 815.116l-99.38 99.382c-30.334 30.336-70.32 45.502-110.308 45.502-39.986 0-79.97-15.166-110.306-45.502l-163.382-163.382c-55.82-55.82-60.238-144.298-13.342-205.342l65.338 65.34c-4.558 9.050-6.988 19.16-6.988 29.694 0 17.616 6.744 34.060 18.992 46.308l163.382 163.382c12.246 12.248 28.694 18.994 46.306 18.994 17.616 0 34.060-6.746 46.308-18.994l99.38-99.382c12.248-12.248 18.992-28.694 18.992-46.308s-6.744-34.060-18.992-46.308l-163.38-163.382c-12.248-12.248-28.694-18.992-46.308-18.992-10.536 0-20.644 2.43-29.696 6.99l-65.338-65.338c27.872-21.41 61.44-32.16 95.034-32.16 39.988 0 79.974 15.168 110.308 45.504l163.38 163.38c60.672 60.666 60.672 159.944 0 220.614zM233.368 681.376l-191.994 191.994 45.256 45.256 191.994-191.994zM384 960h64v-192h-64zM0 576h192v-64h-192zM790.632 214.624l191.996-191.996-45.256-45.256-191.996 191.996zM576 128h64v-192h-64zM832 384h192v-64h-192z" />
+<glyph unicode="&#xe013;" glyph-name="anchor" d="M192 960v-1024l320 320 320-320v1024h-640zM768 90.51l-256 256-256-256v805.49h512v-805.49z" />
+<glyph unicode="&#xe014;" glyph-name="image" d="M0 832v-832h1024v832h-1024zM960 64h-896v704h896v-704zM704 608c0 53.019 42.981 96 96 96s96-42.981 96-96c0-53.019-42.981-96-96-96s-96 42.981-96 96zM896 128h-768l192 512 256-320 128 96z" />
+<glyph unicode="&#xe015;" glyph-name="media" d="M0 832v-768h1024v768h-1024zM192 128h-128v128h128v-128zM192 384h-128v128h128v-128zM192 640h-128v128h128v-128zM768 128h-512v640h512v-640zM960 128h-128v128h128v-128zM960 384h-128v128h128v-128zM960 640h-128v128h128v-128zM384 640v-384l256 192z" />
+<glyph unicode="&#xe016;" glyph-name="help" d="M448 256h128v-128h-128zM704 704c35.346 0 64-28.654 64-64v-192l-192-128h-128v64l192 128v64h-320v128h384zM512 864c-111.118 0-215.584-43.272-294.156-121.844s-121.844-183.038-121.844-294.156c0-111.118 43.272-215.584 121.844-294.156s183.038-121.844 294.156-121.844c111.118 0 215.584 43.272 294.156 121.844s121.844 183.038 121.844 294.156c0 111.118-43.272 215.584-121.844 294.156s-183.038 121.844-294.156 121.844zM512 960v0c282.77 0 512-229.23 512-512s-229.23-512-512-512c-282.77 0-512 229.23-512 512s229.23 512 512 512z" />
+<glyph unicode="&#xe017;" glyph-name="code" d="M320 704l-256-256 256-256h128l-256 256 256 256zM704 704h-128l256-256-256-256h128l256 256z" />
+<glyph unicode="&#xe018;" glyph-name="inserttime" d="M512 768c-212.076 0-384-171.922-384-384s171.922-384 384-384c212.074 0 384 171.922 384 384s-171.926 384-384 384zM715.644 180.354c-54.392-54.396-126.716-84.354-203.644-84.354s-149.25 29.958-203.646 84.354c-54.396 54.394-84.354 126.718-84.354 203.646s29.958 149.25 84.354 203.646c54.396 54.396 126.718 84.354 203.646 84.354s149.252-29.958 203.642-84.354c54.402-54.396 84.358-126.718 84.358-203.646s-29.958-149.252-84.356-203.646zM325.93 756.138l-42.94 85.878c-98.874-49.536-179.47-130.132-229.006-229.008l85.876-42.94c40.248 80.336 105.732 145.822 186.070 186.070zM884.134 570.070l85.878 42.938c-49.532 98.876-130.126 179.472-229.004 229.008l-42.944-85.878c80.338-40.248 145.824-105.732 186.070-186.068zM512 576h-64v-192c0-10.11 4.7-19.11 12.022-24.972l-0.012-0.016 160-128 39.976 49.976-147.986 118.39v176.622z" />
+<glyph unicode="&#xe019;" glyph-name="preview" d="M512 640c-209.368 0-395.244-100.556-512-256 116.756-155.446 302.632-256 512-256s395.244 100.554 512 256c-116.756 155.444-302.632 256-512 256zM448 512c35.346 0 64-28.654 64-64s-28.654-64-64-64-64 28.654-64 64 28.654 64 64 64zM773.616 254.704c-39.648-20.258-81.652-35.862-124.846-46.376-44.488-10.836-90.502-16.328-136.77-16.328-46.266 0-92.282 5.492-136.768 16.324-43.194 10.518-85.198 26.122-124.846 46.376-63.020 32.202-120.222 76.41-167.64 129.298 47.418 52.888 104.62 97.1 167.64 129.298 32.336 16.522 66.242 29.946 101.082 40.040-19.888-30.242-31.468-66.434-31.468-105.336 0-106.040 85.962-192 192-192s192 85.96 192 192c0 38.902-11.582 75.094-31.466 105.34 34.838-10.096 68.744-23.52 101.082-40.042 63.022-32.198 120.218-76.408 167.638-129.298-47.42-52.886-104.618-97.1-167.638-129.296zM860.918 716.278c-108.72 55.554-226.112 83.722-348.918 83.722s-240.198-28.168-348.918-83.722c-58.772-30.032-113.732-67.904-163.082-112.076v-109.206c55.338 58.566 120.694 107.754 192.194 144.29 99.62 50.904 207.218 76.714 319.806 76.714s220.186-25.81 319.804-76.716c71.502-36.536 136.858-85.724 192.196-144.29v109.206c-49.35 44.174-104.308 82.046-163.082 112.078z" />
+<glyph unicode="&#xe01a;" glyph-name="forecolor" d="M322.018 128l57.6 192h264.764l57.6-192h113.632l-191.996 640h-223.236l-192-640h113.636zM475.618 640h72.764l57.6-192h-187.964l57.6 192z" />
+<glyph unicode="&#xe01b;" glyph-name="table" d="M0 896v-896h1024v896h-1024zM384 320v192h256v-192h-256zM640 256v-192h-256v192h256zM640 768v-192h-256v192h256zM320 768v-192h-256v192h256zM64 512h256v-192h-256v192zM704 512h256v-192h-256v192zM704 576v192h256v-192h-256zM64 256h256v-192h-256v192zM704 64v192h256v-192h-256z" />
+<glyph unicode="&#xe01c;" glyph-name="hr" d="M0 512h1024v-128h-1024z" />
+<glyph unicode="&#xe01d;" glyph-name="removeformat" d="M0 64h576v-128h-576zM192 960h704v-128h-704zM277.388 128l204.688 784.164 123.85-32.328-196.25-751.836zM929.774-64l-129.774 129.774-129.774-129.774-62.226 62.226 129.774 129.774-129.774 129.774 62.226 62.226 129.774-129.774 129.774 129.774 62.226-62.226-129.774-129.774 129.774-129.774z" />
+<glyph unicode="&#xe01e;" glyph-name="sub" d="M768 50v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM676 704h-136l-188-188-188 188h-136l256-256-256-256h136l188 188 188-188h136l-256 256z" />
+<glyph unicode="&#xe01f;" glyph-name="sup" d="M768 754v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM676 704h-136l-188-188-188 188h-136l256-256-256-256h136l188 188 188-188h136l-256 256z" />
+<glyph unicode="&#xe020;" glyph-name="charmap" d="M704 64h256l64 128v-256h-384v214.214c131.112 56.484 224 197.162 224 361.786 0 214.432-157.598 382.266-352 382.266-194.406 0-352-167.832-352-382.266 0-164.624 92.886-305.302 224-361.786v-214.214h-384v256l64-128h256v32.59c-187.63 66.46-320 227.402-320 415.41 0 247.424 229.23 448 512 448s512-200.576 512-448c0-188.008-132.37-348.95-320-415.41v-32.59z" />
+<glyph unicode="&#xe021;" glyph-name="emoticons" d="M512 960c-282.77 0-512-229.228-512-512 0-282.77 229.228-512 512-512 282.77 0 512 229.23 512 512 0 282.772-229.23 512-512 512zM512 16c-238.586 0-432 193.412-432 432 0 238.586 193.414 432 432 432 238.59 0 432-193.414 432-432 0-238.588-193.41-432-432-432zM384 640c0-35.346-28.654-64-64-64s-64 28.654-64 64 28.654 64 64 64 64-28.654 64-64zM768 640c0-35.346-28.652-64-64-64s-64 28.654-64 64 28.652 64 64 64 64-28.654 64-64zM512 308c141.074 0 262.688 57.532 318.462 123.192-20.872-171.22-156.288-303.192-318.462-303.192-162.118 0-297.498 132.026-318.444 303.168 55.786-65.646 177.386-123.168 318.444-123.168z" />
+<glyph unicode="&#xe022;" glyph-name="print" d="M256 896h512v-128h-512zM960 704h-896c-35.2 0-64-28.8-64-64v-320c0-35.2 28.796-64 64-64h192v-256h512v256h192c35.2 0 64 28.8 64 64v320c0 35.2-28.8 64-64 64zM704 64h-384v320h384v-320zM974.4 608c0-25.626-20.774-46.4-46.398-46.4-25.626 0-46.402 20.774-46.402 46.4s20.776 46.4 46.402 46.4c25.626 0 46.398-20.774 46.398-46.4z" />
+<glyph unicode="&#xe023;" glyph-name="fullscreen" d="M1024 960v-384l-138.26 138.26-212-212-107.48 107.48 212 212-138.26 138.26zM245.74 821.74l212-212-107.48-107.48-212 212-138.26-138.26v384h384zM885.74 181.74l138.26 138.26v-384h-384l138.26 138.26-212 212 107.48 107.48zM457.74 286.26l-212-212 138.26-138.26h-384v384l138.26-138.26 212 212z" />
+<glyph unicode="&#xe024;" glyph-name="spellchecker" d="M128 704h128v-192h64v384c0 35.2-28.8 64-64 64h-128c-35.2 0-64-28.8-64-64v-384h64v192zM128 896h128v-128h-128v128zM960 896v64h-192c-35.202 0-64-28.8-64-64v-320c0-35.2 28.798-64 64-64h192v64h-192v320h192zM640 800v96c0 35.2-28.8 64-64 64h-192v-448h192c35.2 0 64 28.8 64 64v96c0 35.2-8.8 64-44 64 35.2 0 44 28.8 44 64zM576 576h-128v128h128v-128zM576 768h-128v128h128v-128zM832 384l-416-448-224 288 82 70 142-148 352 302z" />
+<glyph unicode="&#xe025;" glyph-name="nonbreaking" d="M448 384h-192v128h192v192h128v-192h192v-128h-192v-192h-128zM1024 320v-384h-1024v384h128v-256h768v256z" />
+<glyph unicode="&#xe026;" glyph-name="template" d="M384 768h128v-64h-128zM576 768h128v-64h-128zM896 768v-256h-192v64h128v128h-64v64zM320 576h128v-64h-128zM512 576h128v-64h-128zM192 704v-128h64v-64h-128v256h192v-64zM384 384h128v-64h-128zM576 384h128v-64h-128zM896 384v-256h-192v64h128v128h-64v64zM320 192h128v-64h-128zM512 192h128v-64h-128zM192 320v-128h64v-64h-128v256h192v-64zM960 896h-896v-896h896v896zM1024 960v0-1024h-1024v1024h1024z" />
+<glyph unicode="&#xe027;" glyph-name="pagebreak" d="M0 448h128v-64h-128zM192 448h192v-64h-192zM448 448h128v-64h-128zM640 448h192v-64h-192zM896 448h128v-64h-128zM880 960l16-448h-768l16 448h32l16-384h640l16 384zM144-64l-16 384h768l-16-384h-32l-16 320h-640l-16-320z" />
+<glyph unicode="&#xe028;" glyph-name="restoredraft" d="M576 896c247.424 0 448-200.576 448-448s-200.576-448-448-448v96c94.024 0 182.418 36.614 248.902 103.098s103.098 154.878 103.098 248.902c0 94.022-36.614 182.418-103.098 248.902s-154.878 103.098-248.902 103.098c-94.022 0-182.418-36.614-248.902-103.098-51.14-51.138-84.582-115.246-97.306-184.902h186.208l-224-256-224 256h164.57c31.060 217.102 217.738 384 443.43 384zM768 512v-128h-256v320h128v-192z" />
+<glyph unicode="&#xe02a;" glyph-name="bold" d="M707.88 475.348c37.498 44.542 60.12 102.008 60.12 164.652 0 141.16-114.842 256-256 256h-320v-896h384c141.158 0 256 114.842 256 256 0 92.956-49.798 174.496-124.12 219.348zM384 768h101.5c55.968 0 101.5-57.42 101.5-128s-45.532-128-101.5-128h-101.5v256zM543 128h-159v256h159c58.45 0 106-57.42 106-128s-47.55-128-106-128z" />
+<glyph unicode="&#xe02b;" glyph-name="italic" d="M896 896v-64h-128l-320-768h128v-64h-448v64h128l320 768h-128v64z" />
+<glyph unicode="&#xe02c;" glyph-name="underline" d="M704 896h128v-416c0-159.058-143.268-288-320-288-176.73 0-320 128.942-320 288v416h128v-416c0-40.166 18.238-78.704 51.354-108.506 36.896-33.204 86.846-51.494 140.646-51.494s103.75 18.29 140.646 51.494c33.116 29.802 51.354 68.34 51.354 108.506v416zM192 128h640v-128h-640z" />
+<glyph unicode="&#xe02d;" glyph-name="strikethrough" d="M731.42 442.964c63.92-47.938 100.58-116.086 100.58-186.964s-36.66-139.026-100.58-186.964c-59.358-44.518-137.284-69.036-219.42-69.036-82.138 0-160.062 24.518-219.42 69.036-63.92 47.938-100.58 116.086-100.58 186.964h128c0-69.382 87.926-128 192-128s192 58.618 192 128c0 69.382-87.926 128-192 128-82.138 0-160.062 24.518-219.42 69.036-63.92 47.94-100.58 116.086-100.58 186.964s36.66 139.024 100.58 186.964c59.358 44.518 137.282 69.036 219.42 69.036 82.136 0 160.062-24.518 219.42-69.036 63.92-47.94 100.58-116.086 100.58-186.964h-128c0 69.382-87.926 128-192 128s-192-58.618-192-128c0-69.382 87.926-128 192-128 82.136 0 160.062-24.518 219.42-69.036zM0 448h1024v-64h-1024z" />
+<glyph unicode="&#xe02e;" glyph-name="visualchars" d="M384 896h512v-128h-128v-768h-128v768h-128v-768h-128v448c-123.712 0-224 100.288-224 224s100.288 224 224 224z" />
+<glyph unicode="&#xe02f;" glyph-name="ltr" d="M448 896h512v-128h-128v-768h-128v768h-128v-768h-128v448c-123.712 0-224 100.288-224 224s100.288 224 224 224zM64 512l256-224-256-224z" />
+<glyph unicode="&#xe030;" glyph-name="rtl" d="M256 896h512v-128h-128v-768h-128v768h-128v-768h-128v448c-123.712 0-224 100.288-224 224s100.288 224 224 224zM960 64l-256 224 256 224z" />
+<glyph unicode="&#xe031;" glyph-name="copy" d="M832 704h-192v64l-192 192h-448v-768h384v-256h640v576l-192 192zM832 613.49l101.49-101.49h-101.49v101.49zM448 869.49l101.49-101.49h-101.49v101.49zM64 896h320v-192h192v-448h-512v640zM960 0h-512v192h192v448h128v-192h192v-448z" />
+<glyph unicode="&#xe032;" glyph-name="resize" d="M768 704h64v-64h-64zM640 576h64v-64h-64zM640 448h64v-64h-64zM640 320h64v-64h-64zM512 448h64v-64h-64zM512 320h64v-64h-64zM384 320h64v-64h-64zM768 576h64v-64h-64zM768 448h64v-64h-64zM768 320h64v-64h-64zM768 192h64v-64h-64zM640 192h64v-64h-64zM512 192h64v-64h-64zM384 192h64v-64h-64zM256 192h64v-64h-64z" />
+<glyph unicode="&#xe033;" glyph-name="checkbox" d="M128 416l288-288 480 480-128 128-352-352-160 160z" />
+<glyph unicode="&#xe034;" glyph-name="browse" d="M928 832h-416l-32 64h-352l-64-128h896zM904.34 256h74.86l44.8 448h-1024l64-640h484.080c-104.882 37.776-180.080 138.266-180.080 256 0 149.982 122.018 272 272 272 149.98 0 272-122.018 272-272 0-21.678-2.622-43.15-7.66-64zM1002.996 46.25l-198.496 174.692c17.454 28.92 27.5 62.814 27.5 99.058 0 106.040-85.96 192-192 192s-192-85.96-192-192 85.96-192 192-192c36.244 0 70.138 10.046 99.058 27.5l174.692-198.496c22.962-26.678 62.118-28.14 87.006-3.252l5.492 5.492c24.888 24.888 23.426 64.044-3.252 87.006zM640 196c-68.484 0-124 55.516-124 124s55.516 124 124 124 124-55.516 124-124-55.516-124-124-124z" />
+<glyph unicode="&#xe035;" glyph-name="pastetext" d="M512 448v-128h32l32 64h64v-256h-48v-64h224v64h-48v256h64l32-64h32v128zM832 640v160c0 17.6-14.4 32-32 32h-224v64c0 35.2-28.8 64-64 64h-128c-35.204 0-64-28.8-64-64v-64h-224c-17.602 0-32-14.4-32-32v-640c0-17.6 14.398-32 32-32h288v-192h640v704h-192zM384 895.886c0.034 0.038 0.072 0.078 0.114 0.114h127.768c0.042-0.036 0.082-0.076 0.118-0.114v-63.886h-128v63.886zM192 704v64h512v-64h-512zM960 0h-512v576h512v-576z" />
+<glyph unicode="&#xe600;" glyph-name="gamma" d="M483.2 320l-147.2 336c-9.6 25.6-19.2 44.8-25.6 54.4s-16 12.8-25.6 12.8c-16 0-25.6-3.2-28.8-3.2v70.4c9.6 6.4 25.6 6.4 38.4 9.6 32 0 57.6-6.4 73.6-22.4 6.4-6.4 12.8-16 19.2-25.6 6.4-12.8 12.8-25.6 16-41.6l121.6-291.2 150.4 371.2h92.8l-198.4-470.4v-224h-86.4v224zM0 960v-1024h1024v1024h-1024zM960 0h-896v896h896v-896z" />
+<glyph unicode="&#xe601;" glyph-name="orientation" d="M627.2 80h-579.2v396.8h579.2v-396.8zM553.6 406.4h-435.2v-256h435.2v256zM259.2 732.8c176 176 457.6 176 633.6 0s176-457.6 0-633.6c-121.6-121.6-297.6-160-454.4-108.8 121.6-28.8 262.4 9.6 361.6 108.8 150.4 150.4 160 384 22.4 521.6-121.6 121.6-320 128-470.4 19.2l86.4-86.4-294.4-22.4 22.4 294.4 92.8-92.8z" />
+<glyph unicode="&#xe602;" glyph-name="invert" d="M892.8-22.4l-89.6 89.6c-70.4-80-172.8-131.2-288-131.2-208 0-380.8 166.4-384 377.6 0 0 0 0 0 0 0 3.2 0 3.2 0 6.4s0 3.2 0 6.4v0c0 0 0 0 0 3.2 0 0 0 3.2 0 3.2 3.2 105.6 48 211.2 105.6 304l-192 192 44.8 44.8 182.4-182.4c0 0 0 0 0 0l569.6-569.6c0 0 0 0 0 0l99.2-99.2-48-44.8zM896 326.4c0 0 0 0 0 0 0 3.2 0 6.4 0 6.4-9.6 316.8-384 627.2-384 627.2s-108.8-89.6-208-220.8l70.4-70.4c6.4 9.6 16 22.4 22.4 32 41.6 51.2 83.2 96 115.2 128v0c32-32 73.6-76.8 115.2-128 108.8-137.6 169.6-265.6 172.8-371.2 0 0 0-3.2 0-3.2v0 0c0-3.2 0-3.2 0-6.4s0-3.2 0-3.2v0 0c0-22.4-3.2-41.6-9.6-64l76.8-76.8c16 41.6 28.8 89.6 28.8 137.6 0 0 0 0 0 0 0 3.2 0 3.2 0 6.4s0 3.2 0 6.4z" />
+<glyph unicode="&#xe603;" glyph-name="codesample" d="M199.995 578.002v104.002c0 43.078 34.923 78.001 78.001 78.001h26v104.002h-26c-100.518 0-182.003-81.485-182.003-182.003v-104.002c0-43.078-34.923-78.001-78.001-78.001h-26v-104.002h26c43.078 0 78.001-34.923 78.001-78.001v-104.002c0-100.515 81.485-182.003 182.003-182.003h26v104.002h-26c-43.078 0-78.001 34.923-78.001 78.001v104.002c0 50.931-20.928 96.966-54.646 130.002 33.716 33.036 54.646 79.072 54.646 130.002zM824.005 578.002v104.002c0 43.078-34.923 78.001-78.001 78.001h-26v104.002h26c100.515 0 182.003-81.485 182.003-182.003v-104.002c0-43.078 34.923-78.001 78.001-78.001h26v-104.002h-26c-43.078 0-78.001-34.923-78.001-78.001v-104.002c0-100.515-81.488-182.003-182.003-182.003h-26v104.002h26c43.078 0 78.001 34.923 78.001 78.001v104.002c0 50.931 20.928 96.966 54.646 130.002-33.716 33.036-54.646 79.072-54.646 130.002zM616.002 603.285c0-57.439-46.562-104.002-104.002-104.002s-104.002 46.562-104.002 104.002c0 57.439 46.562 104.002 104.002 104.002s104.002-46.562 104.002-104.002zM512 448.717c-57.439 0-104.002-46.562-104.002-104.002 0-55.845 26-100.115 105.752-103.88-23.719-33.417-59.441-46.612-105.752-50.944v-61.751c0 0 208.003-18.144 208.003 216.577-0.202 57.441-46.56 104.004-104.002 104.004z" />
+<glyph unicode="&#xe604;" glyph-name="tablerowprops" d="M0 896v-896h1024v896h-1024zM640 256v-192h-256v192h256zM640 768v-192h-256v192h256zM320 768v-192h-256v192h256zM704 576v192h256v-192h-256zM64 256h256v-192h-256v192zM704 64v192h256v-192h-256z" />
+<glyph unicode="&#xe605;" glyph-name="tablecellprops" d="M0 896v-896h1024v896h-1024zM640 256v-192h-256v192h256zM640 768v-192h-256v192h256zM320 768v-192h-256v192h256zM64 512h256v-192h-256v192zM704 512h256v-192h-256v192zM704 576v192h256v-192h-256zM64 256h256v-192h-256v192zM704 64v192h256v-192h-256z" />
+<glyph unicode="&#xe606;" glyph-name="table2" d="M0 896v-832h1024v832h-1024zM320 128h-256v192h256v-192zM320 384h-256v192h256v-192zM640 128h-256v192h256v-192zM640 384h-256v192h256v-192zM960 128h-256v192h256v-192zM960 384h-256v192h256v-192zM960 640h-896v192h896v-192z" />
+<glyph unicode="&#xe607;" glyph-name="tablemergecells" d="M0 896v-896h1024v896h-1024zM384 64v448h576v-448h-576zM640 768v-192h-256v192h256zM320 768v-192h-256v192h256zM64 512h256v-192h-256v192zM704 576v192h256v-192h-256zM64 256h256v-192h-256v192z" />
+<glyph unicode="&#xe608;" glyph-name="tableinsertcolbefore" d="M320 188.8v182.4h-182.4v89.6h182.4v182.4h86.4v-182.4h185.6v-89.6h-185.6v-182.4zM0 896v-896h1024v896h-1024zM640 64h-576v704h576v-704zM960 64h-256v192h256v-192zM960 320h-256v192h256v-192zM960 576h-256v192h256v-192z" />
+<glyph unicode="&#xe609;" glyph-name="tableinsertcolafter" d="M704 643.2v-182.4h182.4v-89.6h-182.4v-182.4h-86.4v182.4h-185.6v89.6h185.6v182.4zM0 896v-896h1024v896h-1024zM320 64h-256v192h256v-192zM320 320h-256v192h256v-192zM320 576h-256v192h256v-192zM960 64h-576v704h576v-704z" />
+<glyph unicode="&#xe60a;" glyph-name="tableinsertrowbefore" d="M691.2 508.8h-144v-144h-70.4v144h-144v67.2h144v144h70.4v-144h144zM0 896v-896h1024v896h-1024zM320 64h-256v192h256v-192zM640 64h-256v192h256v-192zM960 64h-256v192h256v-192zM960 316.8h-896v451.2h896v-451.2z" />
+<glyph unicode="&#xe60b;" glyph-name="tableinsertrowafter" d="M332.8 323.2h144v144h70.4v-144h144v-67.2h-144v-144h-70.4v144h-144zM0 896v-896h1024v896h-1024zM384 768h256v-192h-256v192zM64 768h256v-192h-256v192zM960 64h-896v451.2h896v-451.2zM960 576h-256v192h256v-192z" />
+<glyph unicode="&#xe60d;" glyph-name="tablesplitcells" d="M0 896v-896h1024v896h-1024zM384 768h256v-192h-256v192zM320 64h-256v192h256v-192zM320 320h-256v192h256v-192zM320 576h-256v192h256v-192zM960 64h-576v448h576v-448zM960 576h-256v192h256v-192zM864 156.8l-60.8-60.8-131.2 131.2-131.2-131.2-60.8 60.8 131.2 131.2-131.2 131.2 60.8 60.8 131.2-131.2 131.2 131.2 60.8-60.8-131.2-131.2z" />
+<glyph unicode="&#xe60e;" glyph-name="tabledelete" d="M0 896h1024v-896h-1024v896zM60.8 768v-704h899.2v704h-899.2zM809.6 211.2l-96-96-204.8 204.8-204.8-204.8-96 96 204.8 204.8-204.8 204.8 96 96 204.8-204.8 204.8 204.8 96-96-204.8-204.8z" />
+<glyph unicode="&#xe62a;" glyph-name="tableleftheader" d="M0 896v-832h1024v832h-1024zM640 128h-256v192h256v-192zM640 384h-256v192h256v-192zM640 640h-256v192h256v-192zM960 128h-256v192h256v-192zM960 384h-256v192h256v-192zM960 640h-256v192h256v-192z" />
+<glyph unicode="&#xe62b;" glyph-name="tabletopheader" d="M0 896v-832h1024v832h-1024zM320 128h-256v192h256v-192zM320 384h-256v192h256v-192zM640 128h-256v192h256v-192zM640 384h-256v192h256v-192zM960 128h-256v192h256v-192zM960 384h-256v192h256v-192z" />
+<glyph unicode="&#xe800;" glyph-name="tabledeleterow" d="M886.4 572.8l-156.8-156.8 160-160-76.8-76.8-160 160-156.8-156.8-76.8 73.6 160 160-163.2 163.2 76.8 76.8 163.2-163.2 156.8 156.8 73.6-76.8zM0 896v-896h1024v896h-1024zM960 576h-22.4l-64-64h86.4v-192h-89.6l64-64h25.6v-192h-896v192h310.4l64 64h-374.4v192h371.2l-64 64h-307.2v192h896v-192z" />
+<glyph unicode="&#xe801;" glyph-name="tabledeletecol" d="M320 499.2l64-64v-12.8l-64-64v140.8zM640 422.4l64-64v137.6l-64-64v-9.6zM1024 896v-896h-1024v896h1024zM960 768h-256v-51.2l-12.8 12.8-51.2-51.2v89.6h-256v-89.6l-51.2 51.2-12.8-12.8v51.2h-256v-704h256v118.4l35.2-35.2 28.8 28.8v-115.2h256v115.2l48-48 16 16v-83.2h256v707.2zM672 662.4l-156.8-156.8-163.2 163.2-76.8-76.8 163.2-163.2-156.8-156.8 76.8-76.8 156.8 156.8 160-160 76.8 76.8-160 160 156.8 156.8-76.8 76.8z" />
+<glyph unicode="&#xe900;" glyph-name="a11y" d="M960 704v64l-448-128-448 128v-64l320-128v-256l-128-448h64l192 448 192-448h64l-128 448v256zM416 800q0 40 28 68t68 28 68-28 28-68-28-68-68-28-68 28-28 68z" />
+<glyph unicode="&#xe901;" glyph-name="toc" d="M0 896h128v-128h-128v128zM192 896h832v-128h-832v128zM192 704h128v-128h-128v128zM384 704h640v-128h-640v128zM384 512h128v-128h-128v128zM576 512h448v-128h-448v128zM0 320h128v-128h-128v128zM192 320h832v-128h-832v128zM192 128h128v-128h-128v128zM384 128h640v-128h-640v128z" />
+<glyph unicode="&#xe902;" glyph-name="fill" d="M521.6 915.2l-67.2-67.2-86.4 86.4-86.4-86.4 86.4-86.4-368-368 432-432 518.4 518.4-428.8 435.2zM435.2 134.4l-262.4 262.4 35.2 35.2 576 51.2-348.8-348.8zM953.6 409.6c-6.4-6.4-16-16-28.8-32-28.8-32-41.6-64-41.6-89.6v0 0 0 0 0 0 0c0-16 6.4-35.2 22.4-48 12.8-12.8 32-22.4 48-22.4s35.2 6.4 48 22.4 22.4 32 22.4 48v0 0 0 0 0 0 0c0 25.6-12.8 54.4-41.6 89.6-9.6 16-22.4 25.6-28.8 32v0z" />
+<glyph unicode="&#xe903;" glyph-name="borderwidth" d="M0 265.6h1024v-128h-1024v128zM0 32h1024v-64h-1024v64zM0 566.4h1024v-192h-1024v192zM0 928h1024v-256h-1024v256z" />
+<glyph unicode="&#xe904;" glyph-name="line" d="M739.2 627.2l-502.4-502.4h-185.6v185.6l502.4 502.4 185.6-185.6zM803.2 688l-185.6 185.6 67.2 67.2c22.4 22.4 54.4 22.4 76.8 0l108.8-108.8c22.4-22.4 22.4-54.4 0-76.8l-67.2-67.2zM41.6 48h940.8v-112h-940.8v112z" />
+<glyph unicode="&#xe905;" glyph-name="count" d="M0 480h1024v-64h-1024v64zM304 912v-339.2h-67.2v272h-67.2v67.2zM444.8 694.4v-54.4h134.4v-67.2h-201.6v153.6l134.4 64v54.4h-134.4v67.2h201.6v-153.6zM854.4 912v-339.2h-204.8v67.2h137.6v67.2h-137.6v70.4h137.6v67.2h-137.6v67.2zM115.2 166.4c3.2 57.6 38.4 83.2 108.8 83.2 38.4 0 67.2-9.6 86.4-25.6s25.6-35.2 25.6-70.4v-112c0-25.6 0-28.8 9.6-41.6h-73.6c-3.2 9.6-3.2 9.6-6.4 19.2-22.4-19.2-41.6-25.6-70.4-25.6-54.4 0-89.6 32-89.6 76.8s28.8 70.4 99.2 80l38.4 6.4c16 3.2 22.4 6.4 22.4 16 0 12.8-12.8 22.4-38.4 22.4s-41.6-9.6-44.8-28.8h-67.2zM262.4 115.2c-6.4-3.2-12.8-6.4-25.6-6.4l-25.6-6.4c-25.6-6.4-38.4-16-38.4-28.8 0-16 12.8-25.6 35.2-25.6s41.6 9.6 54.4 32v35.2zM390.4 336h73.6v-112c22.4 16 41.6 22.4 67.2 22.4 64 0 105.6-51.2 105.6-124.8 0-76.8-44.8-134.4-108.8-134.4-32 0-48 9.6-67.2 35.2v-28.8h-70.4v342.4zM460.8 121.6c0-41.6 22.4-70.4 51.2-70.4s51.2 28.8 51.2 70.4c0 44.8-19.2 70.4-51.2 70.4-28.8 0-51.2-28.8-51.2-70.4zM851.2 153.6c-3.2 22.4-19.2 35.2-44.8 35.2-32 0-51.2-25.6-51.2-70.4 0-48 19.2-73.6 51.2-73.6 25.6 0 41.6 12.8 44.8 41.6l70.4-3.2c-9.6-60.8-54.4-96-118.4-96-73.6 0-121.6 51.2-121.6 128 0 80 48 131.2 124.8 131.2 64 0 108.8-35.2 112-96h-67.2z" />
+<glyph unicode="&#xe906;" glyph-name="reload" d="M889.68 793.68c-93.608 102.216-228.154 166.32-377.68 166.32-282.77 0-512-229.23-512-512h96c0 229.75 186.25 416 416 416 123.020 0 233.542-53.418 309.696-138.306l-149.696-149.694h352v352l-134.32-134.32zM928 448c0-229.75-186.25-416-416-416-123.020 0-233.542 53.418-309.694 138.306l149.694 149.694h-352v-352l134.32 134.32c93.608-102.216 228.154-166.32 377.68-166.32 282.77 0 512 229.23 512 512h-96z" />
+<glyph unicode="&#xe907;" glyph-name="translate" d="M553.6 304l-118.4 118.4c80 89.6 137.6 195.2 172.8 304h137.6v92.8h-326.4v92.8h-92.8v-92.8h-326.4v-92.8h518.4c-32-89.6-80-176-147.2-249.6-44.8 48-80 99.2-108.8 156.8h-92.8c35.2-76.8 80-147.2 137.6-211.2l-236.8-233.6 67.2-67.2 233.6 233.6 144-144c3.2 0 38.4 92.8 38.4 92.8zM816 540.8h-92.8l-208-560h92.8l51.2 140.8h220.8l51.2-140.8h92.8l-208 560zM691.2 214.4l76.8 201.6 76.8-201.6h-153.6z" />
+<glyph unicode="&#xe908;" glyph-name="drag" d="M576 896h128v-128h-128v128zM576 640h128v-128h-128v128zM320 640h128v-128h-128v128zM576 384h128v-128h-128v128zM320 384h128v-128h-128v128zM320 128h128v-128h-128v128zM576 128h128v-128h-128v128zM320 896h128v-128h-128v128z" />
+<glyph unicode="&#xe90b;" glyph-name="home" d="M1024 369.556l-512 397.426-512-397.428v162.038l512 397.426 512-397.428zM896 384v-384h-256v256h-256v-256h-256v384l384 288z" />
+<glyph unicode="&#xe911;" glyph-name="books" d="M576.234 670.73l242.712 81.432 203.584-606.784-242.712-81.432zM0 64h256v704h-256v-704zM64 640h128v-64h-128v64zM320 64h256v704h-256v-704zM384 640h128v-64h-128v64z" />
+<glyph unicode="&#xe914;" glyph-name="upload" d="M839.432 760.57c27.492-27.492 50.554-78.672 55.552-120.57h-318.984v318.984c41.898-4.998 93.076-28.060 120.568-55.552l142.864-142.862zM512 576v384h-368c-44 0-80-36-80-80v-864c0-44 36-80 80-80h672c44 0 80 36 80 80v560h-384zM576 192v-192h-192v192h-160l256 256 256-256h-160z" />
+<glyph unicode="&#xe915;" glyph-name="editimage" d="M768 416v-352h-640v640h352l128 128h-512c-52.8 0-96-43.2-96-96v-704c0-52.8 43.2-96 96-96h704c52.798 0 96 43.2 96 96v512l-128-128zM864 960l-608-608v-160h160l608 608c0 96-64 160-160 160zM416 320l-48 48 480 480 48-48-480-480z" />
+<glyph unicode="&#xe91c;" glyph-name="bubble" d="M928 896h-832c-52.8 0-96-43.2-96-96v-512c0-52.8 43.2-96 96-96h160v-256l307.2 256h364.8c52.8 0 96 43.2 96 96v512c0 52.8-43.2 96-96 96zM896 320h-379.142l-196.858-174.714v174.714h-192v448h768v-448z" />
+<glyph unicode="&#xe91d;" glyph-name="user" d="M622.826 257.264c-22.11 3.518-22.614 64.314-22.614 64.314s64.968 64.316 79.128 150.802c38.090 0 61.618 91.946 23.522 124.296 1.59 34.054 48.96 267.324-190.862 267.324s-192.45-233.27-190.864-267.324c-38.094-32.35-14.57-124.296 23.522-124.296 14.158-86.486 79.128-150.802 79.128-150.802s-0.504-60.796-22.614-64.314c-71.22-11.332-337.172-128.634-337.172-257.264h896c0 128.63-265.952 245.932-337.174 257.264z" />
+<glyph unicode="&#xe926;" glyph-name="lock" d="M592 512h-16v192c0 105.87-86.13 192-192 192h-128c-105.87 0-192-86.13-192-192v-192h-16c-26.4 0-48-21.6-48-48v-480c0-26.4 21.6-48 48-48h544c26.4 0 48 21.6 48 48v480c0 26.4-21.6 48-48 48zM192 704c0 35.29 28.71 64 64 64h128c35.29 0 64-28.71 64-64v-192h-256v192z" />
+<glyph unicode="&#xe927;" glyph-name="unlock" d="M768 896c105.87 0 192-86.13 192-192v-192h-128v192c0 35.29-28.71 64-64 64h-128c-35.29 0-64-28.71-64-64v-192h16c26.4 0 48-21.6 48-48v-480c0-26.4-21.6-48-48-48h-544c-26.4 0-48 21.6-48 48v480c0 26.4 21.6 48 48 48h400v192c0 105.87 86.13 192 192 192h128z" />
+<glyph unicode="&#xe928;" glyph-name="settings" d="M448 832v16c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-192v-128h192v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h576v128h-576zM256 704v128h128v-128h-128zM832 528c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-576v-128h576v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h192v128h-192v16zM640 384v128h128v-128h-128zM448 208c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-192v-128h192v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h576v128h-576v16zM256 64v128h128v-128h-128z" />
+<glyph unicode="&#xe92a;" glyph-name="remove2" d="M192-64h640l64 704h-768zM640 832v128h-256v-128h-320v-192l64 64h768l64-64v192h-320zM576 832h-128v64h128v-64z" />
+<glyph unicode="&#xe92d;" glyph-name="menu" d="M384 896h256v-256h-256zM384 576h256v-256h-256zM384 256h256v-256h-256z" />
+<glyph unicode="&#xe930;" glyph-name="warning" d="M1009.956 44.24l-437.074 871.112c-16.742 29.766-38.812 44.648-60.882 44.648s-44.14-14.882-60.884-44.648l-437.074-871.112c-33.486-59.532-5-108.24 63.304-108.24h869.308c68.302 0 96.792 48.708 63.302 108.24zM512 64c-35.346 0-64 28.654-64 64 0 35.348 28.654 64 64 64 35.348 0 64-28.652 64-64 0-35.346-28.652-64-64-64zM556 256h-88l-20 256c0 35.346 28.654 64 64 64s64-28.654 64-64l-20-256z" />
+<glyph unicode="&#xe931;" glyph-name="question" d="M448 256h128v-128h-128zM704 704c35.346 0 64-28.654 64-64v-192l-192-128h-128v64l192 128v64h-320v128h384zM512 864c-111.118 0-215.584-43.272-294.156-121.844s-121.844-183.038-121.844-294.156c0-111.118 43.272-215.584 121.844-294.156s183.038-121.844 294.156-121.844c111.118 0 215.584 43.272 294.156 121.844s121.844 183.038 121.844 294.156c0 111.118-43.272 215.584-121.844 294.156s-183.038 121.844-294.156 121.844zM512 960v0c282.77 0 512-229.23 512-512s-229.23-512-512-512c-282.77 0-512 229.23-512 512s229.23 512 512 512z" />
+<glyph unicode="&#xe932;" glyph-name="pluscircle" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 64c-212.078 0-384 171.922-384 384s171.922 384 384 384c212.078 0 384-171.922 384-384s-171.922-384-384-384zM768 384h-192v-192h-128v192h-192v128h192v192h128v-192h192z" />
+<glyph unicode="&#xe933;" glyph-name="info" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM448 768h128v-128h-128v128zM640 128h-256v64h64v256h-64v64h192v-320h64v-64z" />
+<glyph unicode="&#xe934;" glyph-name="notice" d="M1024 224l-288 736h-448l-288-288v-448l288-288h448l288 288v448l-288 288zM576 128h-128v128h128v-128zM576 384h-128v384h128v-384z" />
+<glyph unicode="&#xe935;" glyph-name="drop" d="M864.626 486.838c-65.754 183.44-205.11 348.15-352.626 473.162-147.516-125.012-286.87-289.722-352.626-473.162-40.664-113.436-44.682-236.562 12.584-345.4 65.846-125.14 198.632-205.438 340.042-205.438s274.196 80.298 340.040 205.44c57.27 108.838 53.25 231.962 12.586 345.398zM738.764 201.044c-43.802-83.252-132.812-137.044-226.764-137.044-55.12 0-108.524 18.536-152.112 50.652 13.242-1.724 26.632-2.652 40.112-2.652 117.426 0 228.668 67.214 283.402 171.242 44.878 85.292 40.978 173.848 23.882 244.338 14.558-28.15 26.906-56.198 36.848-83.932 22.606-63.062 40.024-156.34-5.368-242.604z" />
+<glyph unicode="&#xe939;" glyph-name="minus" d="M0 544v-192c0-17.672 14.328-32 32-32h960c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32h-960c-17.672 0-32-14.328-32-32z" />
+<glyph unicode="&#xe93a;" glyph-name="plus" d="M992 576h-352v352c0 17.672-14.328 32-32 32h-192c-17.672 0-32-14.328-32-32v-352h-352c-17.672 0-32-14.328-32-32v-192c0-17.672 14.328-32 32-32h352v-352c0-17.672 14.328-32 32-32h192c17.672 0 32 14.328 32 32v352h352c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32z" />
+<glyph unicode="&#xe93b;" glyph-name="arrowup" d="M0 320l192-192 320 320 320-320 192 192-511.998 512z" />
+<glyph unicode="&#xe93c;" glyph-name="arrowright" d="M384 960l-192-192 320-320-320-320 192-192 512 512z" />
+<glyph unicode="&#xe93d;" glyph-name="arrowdown" d="M1024 576l-192 192-320-320-320 320-192-192 512-511.998z" />
+<glyph unicode="&#xe93f;" glyph-name="arrowup2" d="M768 320l-256 256-256-256z" />
+<glyph unicode="&#xe940;" glyph-name="arrowdown2" d="M256 576l256-256 256 256z" />
+<glyph unicode="&#xe941;" glyph-name="menu2" d="M256 704l256-256 256 256zM255.996 384.004l256-256 256 256z" />
+<glyph unicode="&#xe961;" glyph-name="newtab" d="M704 384l128 128v-512h-768v768h512l-128-128h-256v-512h512zM960 896v-352l-130.744 130.744-354.746-354.744h-90.51v90.512l354.744 354.744-130.744 130.744z" />
+<glyph unicode="&#xeaa8;" glyph-name="rotateleft" d="M607.998 831.986c-212.070 0-383.986-171.916-383.986-383.986h-191.994l246.848-246.848 246.848 246.848h-191.994c0 151.478 122.798 274.276 274.276 274.276 151.48 0 274.276-122.798 274.276-274.276 0-151.48-122.796-274.276-274.276-274.276v-109.71c212.070 0 383.986 171.916 383.986 383.986s-171.916 383.986-383.986 383.986z" />
+<glyph unicode="&#xeaa9;" glyph-name="rotateright" d="M416.002 831.986c212.070 0 383.986-171.916 383.986-383.986h191.994l-246.848-246.848-246.848 246.848h191.994c0 151.478-122.798 274.276-274.276 274.276-151.48 0-274.276-122.798-274.276-274.276 0-151.48 122.796-274.276 274.276-274.276v-109.71c-212.070 0-383.986 171.916-383.986 383.986s171.916 383.986 383.986 383.986z" />
+<glyph unicode="&#xeaaa;" glyph-name="flipv" d="M0 576h1024v384zM1024 0v384h-1024z" />
+<glyph unicode="&#xeaac;" glyph-name="fliph" d="M576 960v-1024h384zM0-64h384v1024z" />
+<glyph unicode="&#xeb35;" glyph-name="zoomin" d="M992.262 88.604l-242.552 206.294c-25.074 22.566-51.89 32.926-73.552 31.926 57.256 67.068 91.842 154.078 91.842 249.176 0 212.078-171.922 384-384 384-212.076 0-384-171.922-384-384s171.922-384 384-384c95.098 0 182.108 34.586 249.176 91.844-1-21.662 9.36-48.478 31.926-73.552l206.294-242.552c35.322-39.246 93.022-42.554 128.22-7.356s31.892 92.898-7.354 128.22zM384 320c-141.384 0-256 114.616-256 256s114.616 256 256 256 256-114.616 256-256-114.614-256-256-256zM448 768h-128v-128h-128v-128h128v-128h128v128h128v128h-128z" />
+<glyph unicode="&#xeb36;" glyph-name="zoomout" d="M992.262 88.604l-242.552 206.294c-25.074 22.566-51.89 32.926-73.552 31.926 57.256 67.068 91.842 154.078 91.842 249.176 0 212.078-171.922 384-384 384-212.076 0-384-171.922-384-384s171.922-384 384-384c95.098 0 182.108 34.586 249.176 91.844-1-21.662 9.36-48.478 31.926-73.552l206.294-242.552c35.322-39.246 93.022-42.554 128.22-7.356s31.892 92.898-7.354 128.22zM384 320c-141.384 0-256 114.616-256 256s114.616 256 256 256 256-114.616 256-256-114.614-256-256-256zM192 640h384v-128h-384z" />
+<glyph unicode="&#xeba7;" glyph-name="sharpen" d="M768 832h-512l-256-256 512-576 512 576-256 256zM512 181.334v2.666h-2.37l-14.222 16h16.592v16h-30.814l-14.222 16h45.036v16h-59.258l-14.222 16h73.48v16h-87.704l-14.222 16h101.926v16h-116.148l-14.222 16h130.37v16h-144.592l-14.222 16h158.814v16h-173.038l-14.222 16h187.26v16h-201.482l-14.222 16h215.704v16h-229.926l-14.222 16h244.148v16h-258.372l-14.222 16h272.594v16h-286.816l-14.222 16h301.038v16h-315.26l-14.222 16h329.482v16h-343.706l-7.344 8.262 139.072 139.072h211.978v-3.334h215.314l16-16h-231.314v-16h247.314l16-16h-263.314v-16h279.314l16-16h-295.314v-16h311.314l16-16h-327.314v-16h343.312l7.738-7.738-351.050-394.928z" />
+<glyph unicode="&#xec6a;" glyph-name="options" d="M64 768h896v-192h-896zM64 512h896v-192h-896zM64 256h896v-192h-896z" />
+<glyph unicode="&#xeccc;" glyph-name="sun" d="M512 128c35.346 0 64-28.654 64-64v-64c0-35.346-28.654-64-64-64s-64 28.654-64 64v64c0 35.346 28.654 64 64 64zM512 768c-35.346 0-64 28.654-64 64v64c0 35.346 28.654 64 64 64s64-28.654 64-64v-64c0-35.346-28.654-64-64-64zM960 512c35.346 0 64-28.654 64-64s-28.654-64-64-64h-64c-35.348 0-64 28.654-64 64s28.652 64 64 64h64zM192 448c0-35.346-28.654-64-64-64h-64c-35.346 0-64 28.654-64 64s28.654 64 64 64h64c35.346 0 64-28.654 64-64zM828.784 221.726l45.256-45.258c24.992-24.99 24.992-65.516 0-90.508-24.994-24.992-65.518-24.992-90.51 0l-45.256 45.256c-24.992 24.99-24.992 65.516 0 90.51 24.994 24.992 65.518 24.992 90.51 0zM195.216 674.274l-45.256 45.256c-24.994 24.994-24.994 65.516 0 90.51s65.516 24.994 90.51 0l45.256-45.256c24.994-24.994 24.994-65.516 0-90.51s-65.516-24.994-90.51 0zM828.784 674.274c-24.992-24.992-65.516-24.992-90.51 0-24.992 24.994-24.992 65.516 0 90.51l45.256 45.254c24.992 24.994 65.516 24.994 90.51 0 24.992-24.994 24.992-65.516 0-90.51l-45.256-45.254zM195.216 221.726c24.992 24.992 65.518 24.992 90.508 0 24.994-24.994 24.994-65.52 0-90.51l-45.254-45.256c-24.994-24.992-65.516-24.992-90.51 0s-24.994 65.518 0 90.508l45.256 45.258zM512 704c-141.384 0-256-114.616-256-256 0-141.382 114.616-256 256-256 141.382 0 256 114.618 256 256 0 141.384-114.616 256-256 256zM512 288c-88.366 0-160 71.634-160 160s71.634 160 160 160 160-71.634 160-160-71.634-160-160-160z" />
+<glyph unicode="&#xeccd;" glyph-name="moon" d="M715.812 895.52c-60.25 34.784-124.618 55.904-189.572 64.48 122.936-160.082 144.768-384.762 37.574-570.42-107.2-185.67-312.688-279.112-512.788-252.68 39.898-51.958 90.376-97.146 150.628-131.934 245.908-141.974 560.37-57.72 702.344 188.198 141.988 245.924 57.732 560.372-188.186 702.356z" />
+<glyph unicode="&#xecd4;" glyph-name="contrast" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM128 448c0 212.078 171.922 384 384 384v-768c-212.078 0-384 171.922-384 384z" />
+<glyph unicode="&#xed6a;" glyph-name="remove22" d="M893.254 738.746l-90.508 90.508-290.746-290.744-290.746 290.744-90.508-90.506 290.746-290.748-290.746-290.746 90.508-90.508 290.746 290.746 290.746-290.746 90.508 90.51-290.744 290.744z" />
+<glyph unicode="&#xedc0;" glyph-name="arrowleft" d="M672-64l192 192-320 320 320 320-192 192-512-512z" />
+<glyph unicode="&#xedf9;" glyph-name="resize2" d="M0 896v-384c0-35.346 28.654-64 64-64s64 28.654 64 64v229.488l677.488-677.488h-229.488c-35.346 0-64-28.652-64-64 0-35.346 28.654-64 64-64h384c35.346 0 64 28.654 64 64v384c0 35.348-28.654 64-64 64s-64-28.652-64-64v-229.488l-677.488 677.488h229.488c35.346 0 64 28.654 64 64s-28.652 64-64 64h-384c-35.346 0-64-28.654-64-64z" />
+<glyph unicode="&#xee78;" glyph-name="crop" d="M832 704l192 192-64 64-192-192h-448v192h-128v-192h-192v-128h192v-512h512v-192h128v192h192v128h-192v448zM320 640h320l-320-320v320zM384 256l320 320v-320h-320z" />
+</font></defs></svg>
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce.ttf b/public/libs/tinymce/skins/dark/fonts/tinymce.ttf
new file mode 100644 (file)
index 0000000..16536bf
Binary files /dev/null and b/public/libs/tinymce/skins/dark/fonts/tinymce.ttf differ
diff --git a/public/libs/tinymce/skins/dark/fonts/tinymce.woff b/public/libs/tinymce/skins/dark/fonts/tinymce.woff
new file mode 100644 (file)
index 0000000..74b50f4
Binary files /dev/null and b/public/libs/tinymce/skins/dark/fonts/tinymce.woff differ
diff --git a/public/libs/tinymce/skins/dark/img/anchor.gif b/public/libs/tinymce/skins/dark/img/anchor.gif
new file mode 100644 (file)
index 0000000..606348c
Binary files /dev/null and b/public/libs/tinymce/skins/dark/img/anchor.gif differ
diff --git a/public/libs/tinymce/skins/dark/img/loader.gif b/public/libs/tinymce/skins/dark/img/loader.gif
new file mode 100644 (file)
index 0000000..c69e937
Binary files /dev/null and b/public/libs/tinymce/skins/dark/img/loader.gif differ
diff --git a/public/libs/tinymce/skins/dark/img/object.gif b/public/libs/tinymce/skins/dark/img/object.gif
new file mode 100644 (file)
index 0000000..cccd7f0
Binary files /dev/null and b/public/libs/tinymce/skins/dark/img/object.gif differ
diff --git a/public/libs/tinymce/skins/dark/img/trans.gif b/public/libs/tinymce/skins/dark/img/trans.gif
new file mode 100644 (file)
index 0000000..3884865
Binary files /dev/null and b/public/libs/tinymce/skins/dark/img/trans.gif differ
diff --git a/public/libs/tinymce/skins/dark/skin.ie7.min.css b/public/libs/tinymce/skins/dark/skin.ie7.min.css
new file mode 100644 (file)
index 0000000..bde3628
--- /dev/null
@@ -0,0 +1 @@
+.mce-container,.mce-container *,.mce-widget,.mce-widget *,.mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#b5b9bf;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-widget button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:inherit !important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}div.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #232b33;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#454f59}.mce-grid td.mce-grid-cell div{border:1px solid #d6d6d6;width:15px;height:15px;margin:0px;cursor:pointer}.mce-grid td.mce-grid-cell div:focus{border-color:#d6d6d6}.mce-grid td.mce-grid-cell div[disabled]{cursor:not-allowed}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover,.mce-grid a:focus{border-color:#d6d6d6}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#d6d6d6;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#d6d6d6;background:#0085c7}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%}.mce-colorbtn-trans div{text-align:center;vertical-align:middle;font-weight:bold;font-size:20px;line-height:16px;color:#cbced2}.mce-monospace{font-family:"Courier New",Courier,monospace}.mce-toolbar-grp{padding-bottom:2px}.mce-toolbar-grp .mce-flow-layout-item{margin-bottom:0}.mce-rtl .mce-wordcount{left:0;right:auto}.mce-croprect-container{position:absolute;top:0;left:0}.mce-croprect-handle{position:absolute;top:0;left:0;width:20px;height:20px;border:2px solid white}.mce-croprect-handle-nw{border-width:2px 0 0 2px;margin:-2px 0 0 -2px;cursor:nw-resize;top:100px;left:100px}.mce-croprect-handle-ne{border-width:2px 2px 0 0;margin:-2px 0 0 -20px;cursor:ne-resize;top:100px;left:200px}.mce-croprect-handle-sw{border-width:0 0 2px 2px;margin:-20px 2px 0 -2px;cursor:sw-resize;top:200px;left:100px}.mce-croprect-handle-se{border-width:0 2px 2px 0;margin:-20px 0 0 -20px;cursor:se-resize;top:200px;left:200px}.mce-croprect-handle-move{position:absolute;cursor:move;border:0}.mce-croprect-block{opacity:.3;filter:alpha(opacity=30);zoom:1;position:absolute;background:black}.mce-croprect-handle:focus{border-color:#1e7dad}.mce-croprect-handle-move:focus{outline:1px solid #1e7dad}.mce-imagepanel{overflow:auto;background:black}.mce-imagepanel img{position:absolute}.mce-imagetool.mce-btn .mce-ico{display:block;width:20px;height:20px;text-align:center;line-height:20px;font-size:20px;padding:5px}.mce-arrow-up{margin-top:3px}.mce-arrow-down{margin-top:-3px}.mce-arrow:before,.mce-arrow:after{position:absolute;left:50%;display:block;width:0;height:0;border-style:solid;border-color:transparent;content:""}.mce-arrow.mce-arrow-up:before{top:-9px;border-bottom-color:#232b33;border-width:0 9px 9px;margin-left:-9px}.mce-arrow.mce-arrow-down:before{bottom:-9px;border-top-color:#232b33;border-width:9px 9px 0;margin-left:-9px}.mce-arrow.mce-arrow-up:after{top:-8px;border-bottom-color:#383c3f;border-width:0 8px 8px;margin-left:-8px}.mce-arrow.mce-arrow-down:after{bottom:-8px;border-top-color:#383c3f;border-width:8px 8px 0;margin-left:-8px}.mce-arrow.mce-arrow-left:before,.mce-arrow.mce-arrow-left:after{margin:0}.mce-arrow.mce-arrow-left:before{left:20px}.mce-arrow.mce-arrow-left:after{left:21px}.mce-arrow.mce-arrow-right:before,.mce-arrow.mce-arrow-right:after{left:auto;margin:0}.mce-arrow.mce-arrow-right:before{right:20px}.mce-arrow.mce-arrow-right:after{right:21px}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1}.mce-scroll{position:relative}.mce-panel{border:0 solid #303133;border:0 solid #232b33;background-color:#383c3f}.mce-floatpanel{position:absolute}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;top:0;left:0;background:#333;border:1px solid #232b33;border:1px solid rgba(0,0,0,0.25)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px;*margin-top:0}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#232b33;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#333}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#333;position:fixed;top:0;left:0;opacity:0;transform:scale(.1);transition:transform 100ms ease-in,opacity 150ms ease-in}.mce-window.mce-in{transform:scale(1);opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #9e9e9e;position:relative}.mce-window-head .mce-close{position:absolute;right:0;top:0;height:38px;width:38px;text-align:center;cursor:pointer}.mce-window-head .mce-close i{color:#818387}.mce-close:hover i{color:#67696b}.mce-window-head .mce-title{line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:20px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:#333;border-top:1px solid #9e9e9e}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window-body .mce-listbox{border-color:#000}.mce-rtl .mce-window-head .mce-close{position:absolute;right:auto;left:15px}.mce-rtl .mce-window-head .mce-dragh{left:auto;right:0}.mce-rtl .mce-window-head .mce-title{direction:rtl;text-align:right}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-n .mce-tooltip-arrow{top:0px;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:none;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-progress{display:inline-block;position:relative;height:20px}.mce-progress .mce-bar-container{display:inline-block;width:100px;height:100%;margin-right:8px;border:1px solid #202a33;overflow:hidden}.mce-progress .mce-text{display:inline-block;margin-top:auto;margin-bottom:auto;font-size:14px;width:40px;color:#c4c4c4}.mce-bar{display:block;width:0%;height:100%;background-color:#515c67;-webkit-transition:width .2s ease;transition:width .2s ease}.mce-notification{position:absolute;background-color:#f0f0f0;padding:5px;margin-top:5px;border-width:1px;border-style:solid;border-color:#ccc;transition:transform 100ms ease-in,opacity 150ms ease-in;opacity:0}.mce-notification.mce-in{opacity:1}.mce-notification-success{background-color:#dff0d8;border-color:#d6e9c6}.mce-notification-info{background-color:#d9edf7;border-color:#779ecb}.mce-notification-warning{background-color:#fcf8e3;border-color:#faebcc}.mce-notification-error{background-color:#f2dede;border-color:#ebccd1}.mce-notification.mce-has-close{padding-right:15px}.mce-notification .mce-ico{margin-top:5px}.mce-notification-inner{display:inline-block;font-size:14px;margin:5px 8px 4px 8px;text-align:center;white-space:normal;color:#31708f}.mce-notification-inner a{text-decoration:underline;cursor:pointer}.mce-notification .mce-progress{margin-right:8px}.mce-notification .mce-progress .mce-text{margin-top:5px}.mce-notification *,.mce-notification .mce-progress .mce-text{color:#333}.mce-notification .mce-progress .mce-bar-container{border-color:#ccc}.mce-notification .mce-progress .mce-bar-container .mce-bar{background-color:#333}.mce-notification-success *,.mce-notification-success .mce-progress .mce-text{color:#3c763d}.mce-notification-success .mce-progress .mce-bar-container{border-color:#d6e9c6}.mce-notification-success .mce-progress .mce-bar-container .mce-bar{background-color:#3c763d}.mce-notification-info *,.mce-notification-info .mce-progress .mce-text{color:#31708f}.mce-notification-info .mce-progress .mce-bar-container{border-color:#779ecb}.mce-notification-info .mce-progress .mce-bar-container .mce-bar{background-color:#31708f}.mce-notification-warning *,.mce-notification-warning .mce-progress .mce-text{color:#8a6d3b}.mce-notification-warning .mce-progress .mce-bar-container{border-color:#faebcc}.mce-notification-warning .mce-progress .mce-bar-container .mce-bar{background-color:#8a6d3b}.mce-notification-error *,.mce-notification-error .mce-progress .mce-text{color:#a94442}.mce-notification-error .mce-progress .mce-bar-container{border-color:#ebccd1}.mce-notification-error .mce-progress .mce-bar-container .mce-bar{background-color:#a94442}.mce-notification .mce-close{position:absolute;top:6px;right:8px;font-size:20px;font-weight:bold;line-height:20px;color:#818387;cursor:pointer;height:20px;overflow:hidden}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-btn{border:1px solid #242424;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0);position:relative;text-shadow:0 1px 1px rgba(0,0,0,0.75);display:inline-block;*display:inline;*zoom:1;background-color:#3a3e42}.mce-btn:hover,.mce-btn:focus{color:#b5b9bf;background-color:#2e3135;border-color:#000}.mce-btn.mce-disabled button,.mce-btn.mce-disabled:hover button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{background-color:#262a2d;border-color:#000}.mce-btn:active{background-color:#2b2e32;border-color:#000}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#b5b9bf;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #000}.mce-primary{min-width:50px;color:#fff;border:1px solid transparent;border-color:transparent;background-color:#006597}.mce-primary:hover,.mce-primary:focus{background-color:#00547d;border-color:transparent}.mce-primary.mce-disabled button,.mce-primary.mce-disabled:hover button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-primary.mce-active,.mce-primary.mce-active:hover,.mce-primary:not(.mce-disabled):active{background-color:#004364}.mce-primary button,.mce-primary button i{color:#fff;text-shadow:1px 1px #333}.mce-btn .mce-txt{font-size:inherit;line-height:inherit;color:inherit}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:1px 5px;font-size:12px;*padding-bottom:2px}.mce-btn-small i{line-height:20px;vertical-align:top;*line-height:18px}.mce-btn .mce-caret{margin-top:8px;margin-left:0}.mce-btn-small .mce-caret{margin-top:8px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #b5b9bf;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#6e737a}.mce-caret.mce-up{border-bottom:4px solid #b5b9bf;border-top:0}.mce-btn-flat{border:0;background:transparent;filter:none}.mce-btn-flat:hover,.mce-btn-flat.mce-active,.mce-btn-flat:focus,.mce-btn-flat:active{border:0;background:#1a1a1a;filter:none}.mce-btn-has-text .mce-ico{padding-right:5px}.mce-rtl .mce-btn button{direction:rtl}.mce-btn-group .mce-btn{border-width:1px;margin:0;margin-left:2px}.mce-btn-group .mce-btn{border-left-width:0;border-right-width:0;margin-left:0}.mce-btn-group:not(:first-child){padding-left:1px;margin-left:1px}.mce-btn-group .mce-first{border-left:1px solid rgba(0,0,0,0);margin-left:0}.mce-btn-group .mce-last{border-right:1px solid rgba(0,0,0,0)}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-rtl .mce-btn-group .mce-btn{margin-left:0;margin-right:2px}.mce-rtl .mce-btn-group .mce-first{margin-right:0}.mce-rtl .mce-btn-group:not(:first-child){border-left:none;border-right:1px solid #0d0d0d;padding-right:4px;margin-right:4px}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #202a33;background-color:#3a3e42;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0;overflow:hidden}.mce-checked i.mce-i-checkbox{color:#b5b9bf;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox,.mce-checkbox.mce-focus i.mce-i-checkbox{border:1px solid #1e7dad}.mce-checkbox.mce-disabled .mce-label,.mce-checkbox.mce-disabled i.mce-i-checkbox{color:#67696b}.mce-checkbox .mce-label{vertical-align:middle}.mce-rtl .mce-checkbox{direction:rtl;text-align:right}.mce-rtl i.mce-i-checkbox{margin:0 0 0 3px}.mce-combobox{display:inline-block;*display:inline;*zoom:1;*height:32px}.mce-combobox input{border:1px solid #202a33;border-right-color:#202a33;height:28px}.mce-combobox.mce-disabled input{color:#79818a}.mce-combobox .mce-btn{border:1px solid #202a33;border-left:0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox.mce-disabled .mce-btn button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-colorbox i{border:1px solid #202a33;width:14px;height:14px}.mce-colorbutton .mce-ico{position:relative}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:6px;padding-left:6px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-17px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-16px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;padding-right:4px;border-left:1px solid transparent}.mce-colorbutton:hover .mce-open{border-color:#000}.mce-colorbutton.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-colorbutton{direction:rtl}.mce-rtl .mce-colorbutton .mce-preview{margin-left:0;padding-right:0;padding-left:3px}.mce-rtl .mce-colorbutton.mce-btn-small .mce-preview{margin-left:0;padding-right:0;padding-left:2px}.mce-rtl .mce-colorbutton .mce-open{padding-left:4px;padding-right:4px;border-left:0}.mce-colorpicker{position:relative;width:250px;height:220px}.mce-colorpicker-sv{position:absolute;top:0;left:0;width:90%;height:100%;border:1px solid #202a33;cursor:crosshair;overflow:hidden}.mce-colorpicker-h-chunk{width:100%}.mce-colorpicker-overlay1,.mce-colorpicker-overlay2{width:100%;height:100%;position:absolute;top:0;left:0}.mce-colorpicker-overlay1{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr='#ffffff', endColorstr='#00ffffff');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')";background:linear-gradient(to right, #fff, rgba(255,255,255,0))}.mce-colorpicker-overlay2{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#00000000', endColorstr='#000000');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')";background:linear-gradient(to bottom, rgba(0,0,0,0), #000)}.mce-colorpicker-selector1{background:none;position:absolute;width:12px;height:12px;margin:-8px 0 0 -8px;border:1px solid black;border-radius:50%}.mce-colorpicker-selector2{position:absolute;width:10px;height:10px;border:1px solid white;border-radius:50%}.mce-colorpicker-h{position:absolute;top:0;right:0;width:6.5%;height:100%;border:1px solid #202a33;cursor:crosshair}.mce-colorpicker-h-marker{margin-top:-4px;position:absolute;top:0;left:-1px;width:100%;border:1px solid #333;background:#fff;height:4px;z-index:100}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#b5b9bf}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:#666;color:#fff}.mce-path .mce-divider{display:inline}.mce-disabled .mce-path-item{color:#6e737a}.mce-rtl .mce-path{direction:rtl}.mce-fieldset{border:0 solid #9E9E9E}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-rtl .mce-flow-layout{text-align:right;direction:rtl}.mce-rtl .mce-flow-layout-item{margin:2px 2px 2px 0}.mce-rtl .mce-flow-layout-item.mce-last{margin-left:2px}.mce-iframe{border:0 solid #232b33;width:100%;height:100%}.mce-infobox{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(0,0,0,0.75);overflow:hidden;border:1px solid red}.mce-infobox div{display:block;margin:5px}.mce-infobox div button{position:absolute;top:50%;right:4px;cursor:pointer;margin-top:-8px;display:none}.mce-infobox div button:focus{outline:2px solid #000}.mce-infobox.mce-has-help div{margin-right:25px}.mce-infobox.mce-has-help button{display:block}.mce-infobox.mce-success{background:#dff0d8;border-color:#d6e9c6}.mce-infobox.mce-success div{color:#3c763d}.mce-infobox.mce-warning{background:#fcf8e3;border-color:#faebcc}.mce-infobox.mce-warning div{color:#8a6d3b}.mce-infobox.mce-error{background:#f2dede;border-color:#ebccd1}.mce-infobox.mce-error div{color:#a94442}.mce-rtl .mce-infobox div{text-align:right;direction:rtl}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(0,0,0,0.75);overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label.mce-disabled{color:#6e737a}.mce-label.mce-multiline{white-space:pre-wrap}.mce-label.mce-success{color:#468847}.mce-label.mce-warning{color:#c09853}.mce-label.mce-error{color:#b94a48}.mce-rtl .mce-label{text-align:right;direction:rtl}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;filter:none}.mce-menubar .mce-menubtn button{color:#b5b9bf}.mce-menubar{border:1px solid #292e33}.mce-menubar .mce-menubtn button span{color:#b5b9bf}.mce-menubar .mce-caret{border-top-color:#b5b9bf}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:#000;background:#2f3740;filter:none}.mce-menubtn button{color:#b5b9bf}.mce-menubtn.mce-btn-small span{font-size:12px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-menubtn .mce-caret{*margin-top:6px}.mce-rtl .mce-menubtn button{direction:rtl;text-align:right}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:normal;line-height:20px;color:#ddd;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item .mce-ico,.mce-menu-item .mce-text{color:#ddd}.mce-menu-item.mce-disabled .mce-text,.mce-menu-item.mce-disabled .mce-ico{color:#75797f}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text,.mce-menu-item:focus .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:#fff}.mce-menu-item.mce-disabled:hover{background:#ccc}.mce-menu-shortcut{display:inline-block;color:#75797f}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 15px 0 20px}.mce-menu-item:hover .mce-menu-shortcut,.mce-menu-item.mce-selected .mce-menu-shortcut,.mce-menu-item:focus .mce-menu-shortcut{color:#fff}.mce-menu-item .mce-caret{margin-top:4px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #ddd}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret,.mce-menu-item:hover .mce-caret{border-left-color:#fff}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item-normal.mce-active{background-color:#0085c7}.mce-menu-item-preview.mce-active{border-left:5px solid #08608c}.mce-menu-item-normal.mce-active .mce-text{color:#fff}.mce-menu-item-normal.mce-active:hover .mce-text,.mce-menu-item-normal.mce-active:hover .mce-ico{color:#fff}.mce-menu-item-normal.mce-active:focus .mce-text,.mce-menu-item-normal.mce-active:focus .mce-ico{color:#fff}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#006597}div.mce-menu .mce-menu-item-sep,.mce-menu-item-sep:hover{border:0;padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#25313f;border-bottom:1px solid #424f5f;cursor:default;filter:none}.mce-menu.mce-rtl{direction:rtl}.mce-rtl .mce-menu-item{text-align:right;direction:rtl;padding:6px 12px 6px 15px}.mce-menu-align.mce-rtl .mce-menu-shortcut,.mce-menu-align.mce-rtl .mce-caret{right:auto;left:0}.mce-rtl .mce-menu-item .mce-caret{margin-left:6px;margin-right:0;border-right:4px solid #ddd;border-left:0}.mce-rtl .mce-menu-item.mce-selected .mce-caret,.mce-rtl .mce-menu-item:focus .mce-caret,.mce-rtl .mce-menu-item:hover .mce-caret{border-left-color:transparent;border-right-color:#fff}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}.mce-throbber-inline{position:static;height:50px}.mce-menu .mce-throbber-inline{height:25px;background-size:contain}.mce-menu{position:absolute;left:0;top:0;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:-1px 0 0;min-width:160px;background:#2f3740;border:1px solid #0d2133;border:1px solid #202a33;z-index:1002;max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-rtl .mce-listbox .mce-caret{right:auto;left:8px}.mce-rtl .mce-listbox button{padding-right:10px;padding-left:20px}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#b5b9bf}.mce-selectbox{background:#515c67;border:1px solid #202a33}.mce-slider{border:1px solid #202a33;background:#515c67;width:100px;height:10px;position:relative;display:block}.mce-slider.mce-vertical{width:10px;height:100px}.mce-slider-handle{border:1px solid #000;background:#454f59;display:block;width:13px;height:13px;position:absolute;top:0;left:0;margin-left:-1px;margin-top:-2px}.mce-slider-handle:focus{background:#bbb}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#000}.mce-splitbtn button{padding-right:4px;padding-left:8px}.mce-splitbtn .mce-open{padding-right:4px;padding-left:4px}.mce-splitbtn .mce-open.mce-active{background-color:#262a2d;outline:1px solid #000}.mce-splitbtn.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-splitbtn{direction:rtl;text-align:right}.mce-rtl .mce-splitbtn button{padding-right:4px;padding-left:4px}.mce-rtl .mce-splitbtn .mce-open{border-left:0}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #202a33}.mce-tabs,.mce-tabs+.mce-container-body{background:#303942}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #202a33;border-width:0 1px 0 0;background:#303942;padding:8px;text-shadow:0 1px 1px rgba(0,0,0,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#404952}.mce-tab.mce-active{background:#404952;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-rtl .mce-tabs{text-align:right;direction:rtl}.mce-rtl .mce-tab{border-width:0 0 0 1px}.mce-textbox{background:#515c67;border:1px solid #202a33;display:inline-block;-webkit-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#b5b9bf}.mce-textbox:focus,.mce-textbox.mce-focus{border-color:#1e7dad}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px;height:auto}.mce-textbox.mce-disabled{color:#79818a}.mce-rtl .mce-textbox{text-align:right;direction:rtl}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce';font-style:normal;font-weight:normal;font-size:16px;line-height:16px;vertical-align:text-top;-webkit-font-smoothing:antialiased;display:inline-block;background:transparent center center;width:16px;height:16px;color:#b5b9bf;-ie7-icon:' '}.mce-btn-small .mce-ico{font-family:'tinymce-small'}.mce-ico,i.mce-i-checkbox{zoom:expression(this.runtimeStyle['zoom'] = '1', this.innerHTML = this.currentStyle['-ie7-icon'].substr(1, 1) + '&nbsp;')}.mce-i-save{-ie7-icon:"\e000"}.mce-i-newdocument{-ie7-icon:"\e001"}.mce-i-fullpage{-ie7-icon:"\e002"}.mce-i-alignleft{-ie7-icon:"\e003"}.mce-i-aligncenter{-ie7-icon:"\e004"}.mce-i-alignright{-ie7-icon:"\e005"}.mce-i-alignjustify{-ie7-icon:"\e006"}.mce-i-alignnone{-ie7-icon:"\e003"}.mce-i-cut{-ie7-icon:"\e007"}.mce-i-paste{-ie7-icon:"\e008"}.mce-i-searchreplace{-ie7-icon:"\e009"}.mce-i-bullist{-ie7-icon:"\e00a"}.mce-i-numlist{-ie7-icon:"\e00b"}.mce-i-indent{-ie7-icon:"\e00c"}.mce-i-outdent{-ie7-icon:"\e00d"}.mce-i-blockquote{-ie7-icon:"\e00e"}.mce-i-undo{-ie7-icon:"\e00f"}.mce-i-redo{-ie7-icon:"\e010"}.mce-i-link{-ie7-icon:"\e011"}.mce-i-unlink{-ie7-icon:"\e012"}.mce-i-anchor{-ie7-icon:"\e013"}.mce-i-image{-ie7-icon:"\e014"}.mce-i-media{-ie7-icon:"\e015"}.mce-i-help{-ie7-icon:"\e016"}.mce-i-code{-ie7-icon:"\e017"}.mce-i-insertdatetime{-ie7-icon:"\e018"}.mce-i-preview{-ie7-icon:"\e019"}.mce-i-forecolor{-ie7-icon:"\e01a"}.mce-i-backcolor{-ie7-icon:"\e01a"}.mce-i-table{-ie7-icon:"\e01b"}.mce-i-hr{-ie7-icon:"\e01c"}.mce-i-removeformat{-ie7-icon:"\e01d"}.mce-i-subscript{-ie7-icon:"\e01e"}.mce-i-superscript{-ie7-icon:"\e01f"}.mce-i-charmap{-ie7-icon:"\e020"}.mce-i-emoticons{-ie7-icon:"\e021"}.mce-i-print{-ie7-icon:"\e022"}.mce-i-fullscreen{-ie7-icon:"\e023"}.mce-i-spellchecker{-ie7-icon:"\e024"}.mce-i-nonbreaking{-ie7-icon:"\e025"}.mce-i-template{-ie7-icon:"\e026"}.mce-i-pagebreak{-ie7-icon:"\e027"}.mce-i-restoredraft{-ie7-icon:"\e028"}.mce-i-untitled{-ie7-icon:"\e029"}.mce-i-bold{-ie7-icon:"\e02a"}.mce-i-italic{-ie7-icon:"\e02b"}.mce-i-underline{-ie7-icon:"\e02c"}.mce-i-strikethrough{-ie7-icon:"\e02d"}.mce-i-visualchars{-ie7-icon:"\e02e"}.mce-i-ltr{-ie7-icon:"\e02f"}.mce-i-rtl{-ie7-icon:"\e030"}.mce-i-copy{-ie7-icon:"\e031"}.mce-i-resize{-ie7-icon:"\e032"}.mce-i-browse{-ie7-icon:"\e034"}.mce-i-pastetext{-ie7-icon:"\e035"}.mce-i-rotateleft{-ie7-icon:"\eaa8"}.mce-i-rotateright{-ie7-icon:"\eaa9"}.mce-i-crop{-ie7-icon:"\ee78"}.mce-i-editimage{-ie7-icon:"\e914"}.mce-i-options{-ie7-icon:"\ec6a"}.mce-i-flipv{-ie7-icon:"\eaaa"}.mce-i-fliph{-ie7-icon:"\eaac"}.mce-i-zoomin{-ie7-icon:"\eb35"}.mce-i-zoomout{-ie7-icon:"\eb36"}.mce-i-sun{-ie7-icon:"\eccc"}.mce-i-moon{-ie7-icon:"\eccd"}.mce-i-arrowleft{-ie7-icon:"\edc0"}.mce-i-arrowright{-ie7-icon:"\edb8"}.mce-i-drop{-ie7-icon:"\e934"}.mce-i-contrast{-ie7-icon:"\ecd4"}.mce-i-sharpen{-ie7-icon:"\eba7"}.mce-i-palette{-ie7-icon:"\e92a"}.mce-i-resize2{-ie7-icon:"\edf9"}.mce-i-orientation{-ie7-icon:"\e601"}.mce-i-invert{-ie7-icon:"\e602"}.mce-i-gamma{-ie7-icon:"\e600"}.mce-i-remove{-ie7-icon:"\ed6a"}.mce-i-codesample{-ie7-icon:"\e603"}.mce-i-checkbox,.mce-i-selected{-ie7-icon:"\e033"}.mce-i-selected{visibility:hidden}.mce-i-backcolor{background:#BBB}
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/skin.json b/public/libs/tinymce/skins/dark/skin.json
new file mode 100644 (file)
index 0000000..5ccf810
--- /dev/null
@@ -0,0 +1,79 @@
+{
+       "skin-name": "dark",
+       "preview-bg": "#222222",
+       "text": "#b5b9bf",
+       "text-inverse": "#000000",
+       "text-disabled": "#6e737a",
+       "has-gradients": false,
+       "has-radius": false,
+       "has-boxshadow": false,
+       "has-button-borders": true,
+       "btn-text": "#b5b9bf",
+       "btn-text-shadow": "#000000",
+       "btn-bg": "#333",
+       "btn-bg-hlight": "#454f59",
+       "btn-border-top": "rgba(0,0,0,0)",
+       "btn-border-right": "rgba(0,0,0,0)",
+       "btn-border-bottom": "rgba(0,0,0,0)",
+       "btn-border-left": "rgba(0,0,0,0)",
+       "btn-split-border": "#202a33",
+       "btn-primary-text": "#ffffff",
+       "btn-primary-text-shadow": "#333333",
+       "btn-primary-bg": "#006fa6",
+       "btn-primary-bg-hlight": "#005580",
+       "btn-padding": "4px 10px",
+       "menu-bg": "#2f3740",
+       "menu-border": "#202a33",
+       "menuitem-text": "#dddddd",
+       "menuitem-bg-selected": "#006fa6",
+       "menuitem-bg-selected-hlight": "#005580",
+       "menuitem-separator-top": "#25313f",
+       "menuitem-separator-bottom": "#424f5f",
+       "menuitem-text-inverse": "#ffffff",
+       "menuitem-bg-active": "#0085c7",
+       "menuitem-text-active": "#ffffff",
+       "menuitem-preview-border-active": "#08608c",
+       "menubar-menubtn-text": "#b5b9bf",
+       "checkbox-border": "#202a33",
+       "checkbox-border-focus": "#1e7dad",
+       "panel-border": "#232b33",
+       "panel-bg": "#333333",
+       "panel-bg-hlight": "#404952",
+       "textbox-bg": "#515c67",
+       "textbox-border": "#202a33",
+       "textbox-border-focus": "#1e7dad",
+       "window-bg": "#333",
+       "window-border": "#9e9e9e",
+       "tab-bg": "#303942",
+       "tab-bg-hover": "#404952",
+       "tab-bg-active": "#404952",
+       "tab-border": "#202a33",
+       "tabs-bg": "#303942",
+       "notification-bg": "#f0f0f0",
+       "notification-border": "#cccccc",
+       "notification-text": "#333333",
+       "notification-success-bg": "#dff0d8",
+       "notification-success-border": "#d6e9c6",
+       "notification-success-text": "#3c763d",
+       "notification-info-bg": "#d9edf7",
+       "notification-info-border": "#779ecb",
+       "notification-info-text": "#31708f",
+       "notification-warning-bg": "#fcf8e3",
+       "notification-warning-border": "#faebcc",
+       "notification-warning-text": "#8a6d3b",
+       "notification-error-bg": "#f2dede",
+       "notification-error-border": "#ebccd1",
+       "notification-error-text": "#a94442",
+       "progress-bar-bg": "#515c67",
+       "progress-bar-bg-hlight": "#515c67",
+       "progress-border": "#202a33",
+       "progress-text": "#c4c4c4",
+       "progress-text-shadow": "#000000",
+       "slider-bg": "#515c67",
+       "slider-border": "#202a33",
+       "slider-handle-bg": "#454f59",
+       "slider-handle-border": "#000000",
+       "colorbtn-backcolor-bg": "#384552",
+       "grid-border": "#d6d6d6",
+       "grid-border-active": "#d6d6d6"
+}
\ No newline at end of file
diff --git a/public/libs/tinymce/skins/dark/skin.min.css b/public/libs/tinymce/skins/dark/skin.min.css
new file mode 100644 (file)
index 0000000..fa1ed84
--- /dev/null
@@ -0,0 +1 @@
+.mce-container,.mce-container *,.mce-widget,.mce-widget *,.mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#b5b9bf;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-widget button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:inherit !important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}div.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #232b33;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#454f59}.mce-grid td.mce-grid-cell div{border:1px solid #d6d6d6;width:15px;height:15px;margin:0px;cursor:pointer}.mce-grid td.mce-grid-cell div:focus{border-color:#d6d6d6}.mce-grid td.mce-grid-cell div[disabled]{cursor:not-allowed}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover,.mce-grid a:focus{border-color:#d6d6d6}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#d6d6d6;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#d6d6d6;background:#0085c7}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%}.mce-colorbtn-trans div{text-align:center;vertical-align:middle;font-weight:bold;font-size:20px;line-height:16px;color:#cbced2}.mce-monospace{font-family:"Courier New",Courier,monospace}.mce-toolbar-grp{padding-bottom:2px}.mce-toolbar-grp .mce-flow-layout-item{margin-bottom:0}.mce-rtl .mce-wordcount{left:0;right:auto}.mce-container b{font-weight:bold}.mce-container p{margin-bottom:5px}.mce-container a{cursor:pointer;color:#2980b9}.mce-container a:hover{text-decoration:underline}.mce-container ul{margin-left:15px}.mce-container .mce-table-striped{border-collapse:collapse;margin:10px}.mce-container .mce-table-striped thead>tr{background-color:#fafafa}.mce-container .mce-table-striped thead>tr th{font-weight:bold}.mce-container .mce-table-striped td,.mce-container .mce-table-striped th{padding:5px}.mce-container .mce-table-striped tr:nth-child(even){background-color:#fafafa}.mce-container .mce-table-striped tbody>tr:hover{background-color:#e1e1e1}.mce-branding-powered-by{background-color:#383c3f;position:absolute;right:0;bottom:0;width:91px;height:9px;margin-right:-1px;margin-bottom:-1px;border:1px solid #202a33;border-width:1px 1px 0 1px;padding:6px 6px 0 6px;background-image:url('data:image/gif;base64,R0lGODlhXwAJAIABAIiIiAAAACH5BAEKAAEALAAAAABfAAkAAAJxhBGpy+2PUnzqGNpmPNJqDIZSJY4m+KXLF3At2V6xPFfuvMF6J6fINTnhTr9XcaRC6pKvFYlZjDIszaXRSA3ijlXo9AlWindaldSJthJ55XAz6+ZWbVCOdojP77p8J8vlUSI4SHEnaEiYqOhARdhIWAAAOw');background-repeat:no-repeat;background-position:center center}.mce-croprect-container{position:absolute;top:0;left:0}.mce-croprect-handle{position:absolute;top:0;left:0;width:20px;height:20px;border:2px solid white}.mce-croprect-handle-nw{border-width:2px 0 0 2px;margin:-2px 0 0 -2px;cursor:nw-resize;top:100px;left:100px}.mce-croprect-handle-ne{border-width:2px 2px 0 0;margin:-2px 0 0 -20px;cursor:ne-resize;top:100px;left:200px}.mce-croprect-handle-sw{border-width:0 0 2px 2px;margin:-20px 2px 0 -2px;cursor:sw-resize;top:200px;left:100px}.mce-croprect-handle-se{border-width:0 2px 2px 0;margin:-20px 0 0 -20px;cursor:se-resize;top:200px;left:200px}.mce-croprect-handle-move{position:absolute;cursor:move;border:0}.mce-croprect-block{opacity:.3;filter:alpha(opacity=30);zoom:1;position:absolute;background:black}.mce-croprect-handle:focus{border-color:#1e7dad}.mce-croprect-handle-move:focus{outline:1px solid #1e7dad}.mce-imagepanel{overflow:auto;background:black}.mce-imagepanel-bg{position:absolute;background:url('data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==')}.mce-imagepanel img{position:absolute}.mce-imagetool.mce-btn .mce-ico{display:block;width:20px;height:20px;text-align:center;line-height:20px;font-size:20px;padding:5px}.mce-arrow-up{margin-top:12px}.mce-arrow-down{margin-top:-12px}.mce-arrow:before,.mce-arrow:after{position:absolute;left:50%;display:block;width:0;height:0;border-style:solid;border-color:transparent;content:""}.mce-arrow.mce-arrow-up:before{top:-9px;border-bottom-color:#232b33;border-width:0 9px 9px;margin-left:-9px}.mce-arrow.mce-arrow-down:before{bottom:-9px;border-top-color:#232b33;border-width:9px 9px 0;margin-left:-9px}.mce-arrow.mce-arrow-up:after{top:-8px;border-bottom-color:#383c3f;border-width:0 8px 8px;margin-left:-8px}.mce-arrow.mce-arrow-down:after{bottom:-8px;border-top-color:#383c3f;border-width:8px 8px 0;margin-left:-8px}.mce-arrow.mce-arrow-left:before,.mce-arrow.mce-arrow-left:after{margin:0}.mce-arrow.mce-arrow-left:before{left:8px}.mce-arrow.mce-arrow-left:after{left:9px}.mce-arrow.mce-arrow-right:before,.mce-arrow.mce-arrow-right:after{left:auto;margin:0}.mce-arrow.mce-arrow-right:before{right:8px}.mce-arrow.mce-arrow-right:after{right:9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left:before{left:-9px;top:50%;border-right-color:#232b33;border-width:9px 9px 9px 0;margin-top:-9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left:after{left:-8px;top:50%;border-right-color:#383c3f;border-width:8px 8px 8px 0;margin-top:-8px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left{margin-left:12px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right:before{right:-9px;top:50%;border-left-color:#232b33;border-width:9px 0 9px 9px;margin-top:-9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right:after{right:-8px;top:50%;border-left-color:#383c3f;border-width:8px 0 8px 8px;margin-top:-8px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right{margin-left:-14px}.mce-edit-aria-container>.mce-container-body{display:flex}.mce-edit-aria-container>.mce-container-body .mce-edit-area{flex:1}.mce-edit-aria-container>.mce-container-body .mce-sidebar>.mce-container-body{display:flex;align-items:stretch;height:100%}.mce-edit-aria-container>.mce-container-body .mce-sidebar-panel{min-width:250px;max-width:250px;position:relative}.mce-edit-aria-container>.mce-container-body .mce-sidebar-panel>.mce-container-body{position:absolute;width:100%;height:100%;overflow:auto;top:0;left:0}.mce-sidebar-toolbar{border:0 solid #232b33;border-left-width:1px}.mce-sidebar-toolbar .mce-btn.mce-active,.mce-sidebar-toolbar .mce-btn.mce-active:hover{border:1px solid transparent;border-color:transparent;background-color:#006597}.mce-sidebar-toolbar .mce-btn.mce-active button,.mce-sidebar-toolbar .mce-btn.mce-active:hover button,.mce-sidebar-toolbar .mce-btn.mce-active button i,.mce-sidebar-toolbar .mce-btn.mce-active:hover button i{color:#fff;text-shadow:1px 1px #333}.mce-sidebar-panel{border:0 solid #232b33;border-left-width:1px}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1}.mce-scroll{position:relative}.mce-panel{border:0 solid #303133;border:0 solid #232b33;background-color:#383c3f}.mce-floatpanel{position:absolute}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;top:0;left:0;background:#333;border:1px solid #232b33;border:1px solid rgba(0,0,0,0.25)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px;*margin-top:0}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#232b33;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#333}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#333;position:fixed;top:0;left:0;opacity:0;transform:scale(.1);transition:transform 100ms ease-in,opacity 150ms ease-in}.mce-window.mce-in{transform:scale(1);opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #9e9e9e;position:relative}.mce-window-head .mce-close{position:absolute;right:0;top:0;height:38px;width:38px;text-align:center;cursor:pointer}.mce-window-head .mce-close i{color:#818387}.mce-close:hover i{color:#67696b}.mce-window-head .mce-title{line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:20px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:#333;border-top:1px solid #9e9e9e}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window-body .mce-listbox{border-color:#000}.mce-rtl .mce-window-head .mce-close{position:absolute;right:auto;left:15px}.mce-rtl .mce-window-head .mce-dragh{left:auto;right:0}.mce-rtl .mce-window-head .mce-title{direction:rtl;text-align:right}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-ne,.mce-tooltip-se{margin-left:14px}.mce-tooltip-n .mce-tooltip-arrow{top:0px;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:none;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-progress{display:inline-block;position:relative;height:20px}.mce-progress .mce-bar-container{display:inline-block;width:100px;height:100%;margin-right:8px;border:1px solid #202a33;overflow:hidden}.mce-progress .mce-text{display:inline-block;margin-top:auto;margin-bottom:auto;font-size:14px;width:40px;color:#c4c4c4}.mce-bar{display:block;width:0%;height:100%;background-color:#515c67;-webkit-transition:width .2s ease;transition:width .2s ease}.mce-notification{position:absolute;background-color:#f0f0f0;padding:5px;margin-top:5px;border-width:1px;border-style:solid;border-color:#ccc;transition:transform 100ms ease-in,opacity 150ms ease-in;opacity:0}.mce-notification.mce-in{opacity:1}.mce-notification-success{background-color:#dff0d8;border-color:#d6e9c6}.mce-notification-info{background-color:#d9edf7;border-color:#779ecb}.mce-notification-warning{background-color:#fcf8e3;border-color:#faebcc}.mce-notification-error{background-color:#f2dede;border-color:#ebccd1}.mce-notification.mce-has-close{padding-right:15px}.mce-notification .mce-ico{margin-top:5px}.mce-notification-inner{display:inline-block;font-size:14px;margin:5px 8px 4px 8px;text-align:center;white-space:normal;color:#31708f}.mce-notification-inner a{text-decoration:underline;cursor:pointer}.mce-notification .mce-progress{margin-right:8px}.mce-notification .mce-progress .mce-text{margin-top:5px}.mce-notification *,.mce-notification .mce-progress .mce-text{color:#333}.mce-notification .mce-progress .mce-bar-container{border-color:#ccc}.mce-notification .mce-progress .mce-bar-container .mce-bar{background-color:#333}.mce-notification-success *,.mce-notification-success .mce-progress .mce-text{color:#3c763d}.mce-notification-success .mce-progress .mce-bar-container{border-color:#d6e9c6}.mce-notification-success .mce-progress .mce-bar-container .mce-bar{background-color:#3c763d}.mce-notification-info *,.mce-notification-info .mce-progress .mce-text{color:#31708f}.mce-notification-info .mce-progress .mce-bar-container{border-color:#779ecb}.mce-notification-info .mce-progress .mce-bar-container .mce-bar{background-color:#31708f}.mce-notification-warning *,.mce-notification-warning .mce-progress .mce-text{color:#8a6d3b}.mce-notification-warning .mce-progress .mce-bar-container{border-color:#faebcc}.mce-notification-warning .mce-progress .mce-bar-container .mce-bar{background-color:#8a6d3b}.mce-notification-error *,.mce-notification-error .mce-progress .mce-text{color:#a94442}.mce-notification-error .mce-progress .mce-bar-container{border-color:#ebccd1}.mce-notification-error .mce-progress .mce-bar-container .mce-bar{background-color:#a94442}.mce-notification .mce-close{position:absolute;top:6px;right:8px;font-size:20px;font-weight:bold;line-height:20px;color:#818387;cursor:pointer;height:20px;overflow:hidden}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-btn{border:1px solid #242424;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0);position:relative;text-shadow:0 1px 1px rgba(0,0,0,0.75);display:inline-block;*display:inline;*zoom:1;background-color:#3a3e42}.mce-btn:hover,.mce-btn:focus{color:#b5b9bf;background-color:#2e3135;border-color:#000}.mce-btn.mce-disabled button,.mce-btn.mce-disabled:hover button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{background-color:#262a2d;border-color:#000}.mce-btn:active{background-color:#2b2e32;border-color:#000}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#b5b9bf;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #000}.mce-primary.mce-btn-has-text{min-width:50px}.mce-primary{color:#fff;border:1px solid transparent;border-color:transparent;background-color:#006597}.mce-primary:hover,.mce-primary:focus{background-color:#00547d;border-color:transparent}.mce-primary.mce-disabled button,.mce-primary.mce-disabled:hover button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-primary.mce-active,.mce-primary.mce-active:hover,.mce-primary:not(.mce-disabled):active{background-color:#004364}.mce-primary button,.mce-primary button i{color:#fff;text-shadow:1px 1px #333}.mce-btn .mce-txt{font-size:inherit;line-height:inherit;color:inherit}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:1px 5px;font-size:12px;*padding-bottom:2px}.mce-btn-small i{line-height:20px;vertical-align:top;*line-height:18px}.mce-btn .mce-caret{margin-top:8px;margin-left:0}.mce-btn-small .mce-caret{margin-top:8px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #b5b9bf;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#6e737a}.mce-caret.mce-up{border-bottom:4px solid #b5b9bf;border-top:0}.mce-btn-flat{border:0;background:transparent;filter:none}.mce-btn-flat:hover,.mce-btn-flat.mce-active,.mce-btn-flat:focus,.mce-btn-flat:active{border:0;background:#1a1a1a;filter:none}.mce-btn-has-text .mce-ico{padding-right:5px}.mce-rtl .mce-btn button{direction:rtl}.mce-btn-group .mce-btn{border-width:1px;margin:0;margin-left:2px}.mce-btn-group .mce-btn{border-left-width:0;border-right-width:0;margin-left:0}.mce-btn-group:not(:first-child){padding-left:1px;margin-left:1px}.mce-btn-group .mce-first{border-left:1px solid rgba(0,0,0,0);margin-left:0}.mce-btn-group .mce-last{border-right:1px solid rgba(0,0,0,0)}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-rtl .mce-btn-group .mce-btn{margin-left:0;margin-right:2px}.mce-rtl .mce-btn-group .mce-first{margin-right:0}.mce-rtl .mce-btn-group:not(:first-child){border-left:none;border-right:1px solid #0d0d0d;padding-right:4px;margin-right:4px}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #202a33;background-color:#3a3e42;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0;overflow:hidden}.mce-checked i.mce-i-checkbox{color:#b5b9bf;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox,.mce-checkbox.mce-focus i.mce-i-checkbox{border:1px solid #1e7dad}.mce-checkbox.mce-disabled .mce-label,.mce-checkbox.mce-disabled i.mce-i-checkbox{color:#67696b}.mce-checkbox .mce-label{vertical-align:middle}.mce-rtl .mce-checkbox{direction:rtl;text-align:right}.mce-rtl i.mce-i-checkbox{margin:0 0 0 3px}.mce-combobox{position:relative;display:inline-block;*display:inline;*zoom:1;*height:32px}.mce-combobox input{border:1px solid #202a33;border-right-color:#202a33;height:28px}.mce-combobox.mce-disabled input{color:#79818a}.mce-combobox .mce-btn{border:1px solid #202a33;border-left:0;margin:0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox.mce-disabled .mce-btn button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-combobox .mce-status{position:absolute;right:2px;top:50%;line-height:16px;margin-top:-8px;font-size:12px;width:15px;height:15px;text-align:center;cursor:pointer}.mce-combobox.mce-has-status input{padding-right:20px}.mce-combobox.mce-has-open .mce-status{right:37px}.mce-combobox .mce-status.mce-i-warning{color:#c09853}.mce-combobox .mce-status.mce-i-checkmark{color:#468847}.mce-menu.mce-combobox-menu{border-top:0;margin-top:0;max-height:200px}.mce-menu.mce-combobox-menu .mce-menu-item{padding:4px 6px 4px 4px;font-size:11px}.mce-menu.mce-combobox-menu .mce-menu-item-sep{padding:0}.mce-menu.mce-combobox-menu .mce-text{font-size:11px}.mce-menu.mce-combobox-menu .mce-menu-item-link,.mce-menu.mce-combobox-menu .mce-menu-item-link b{font-size:11px}.mce-menu.mce-combobox-menu .mce-text b{font-size:11px}.mce-colorbox i{border:1px solid #202a33;width:14px;height:14px}.mce-colorbutton .mce-ico{position:relative}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:6px;padding-left:6px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-17px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-16px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;padding-right:4px;border-left:1px solid transparent}.mce-colorbutton:hover .mce-open{border-color:#000}.mce-colorbutton.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-colorbutton{direction:rtl}.mce-rtl .mce-colorbutton .mce-preview{margin-left:0;padding-right:0;padding-left:3px}.mce-rtl .mce-colorbutton.mce-btn-small .mce-preview{margin-left:0;padding-right:0;padding-left:2px}.mce-rtl .mce-colorbutton .mce-open{padding-left:4px;padding-right:4px;border-left:0}.mce-colorpicker{position:relative;width:250px;height:220px}.mce-colorpicker-sv{position:absolute;top:0;left:0;width:90%;height:100%;border:1px solid #202a33;cursor:crosshair;overflow:hidden}.mce-colorpicker-h-chunk{width:100%}.mce-colorpicker-overlay1,.mce-colorpicker-overlay2{width:100%;height:100%;position:absolute;top:0;left:0}.mce-colorpicker-overlay1{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr='#ffffff', endColorstr='#00ffffff');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')";background:linear-gradient(to right, #fff, rgba(255,255,255,0))}.mce-colorpicker-overlay2{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#00000000', endColorstr='#000000');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')";background:linear-gradient(to bottom, rgba(0,0,0,0), #000)}.mce-colorpicker-selector1{background:none;position:absolute;width:12px;height:12px;margin:-8px 0 0 -8px;border:1px solid black;border-radius:50%}.mce-colorpicker-selector2{position:absolute;width:10px;height:10px;border:1px solid white;border-radius:50%}.mce-colorpicker-h{position:absolute;top:0;right:0;width:6.5%;height:100%;border:1px solid #202a33;cursor:crosshair}.mce-colorpicker-h-marker{margin-top:-4px;position:absolute;top:0;left:-1px;width:100%;border:1px solid #333;background:#fff;height:4px;z-index:100}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#b5b9bf}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:#666;color:#fff}.mce-path .mce-divider{display:inline}.mce-disabled .mce-path-item{color:#6e737a}.mce-rtl .mce-path{direction:rtl}.mce-fieldset{border:0 solid #9E9E9E}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-rtl .mce-flow-layout{text-align:right;direction:rtl}.mce-rtl .mce-flow-layout-item{margin:2px 2px 2px 0}.mce-rtl .mce-flow-layout-item.mce-last{margin-left:2px}.mce-iframe{border:0 solid #232b33;width:100%;height:100%}.mce-infobox{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(0,0,0,0.75);overflow:hidden;border:1px solid red}.mce-infobox div{display:block;margin:5px}.mce-infobox div button{position:absolute;top:50%;right:4px;cursor:pointer;margin-top:-8px;display:none}.mce-infobox div button:focus{outline:2px solid #000}.mce-infobox.mce-has-help div{margin-right:25px}.mce-infobox.mce-has-help button{display:block}.mce-infobox.mce-success{background:#dff0d8;border-color:#d6e9c6}.mce-infobox.mce-success div{color:#3c763d}.mce-infobox.mce-warning{background:#fcf8e3;border-color:#faebcc}.mce-infobox.mce-warning div{color:#8a6d3b}.mce-infobox.mce-error{background:#f2dede;border-color:#ebccd1}.mce-infobox.mce-error div{color:#a94442}.mce-rtl .mce-infobox div{text-align:right;direction:rtl}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(0,0,0,0.75);overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label.mce-disabled{color:#6e737a}.mce-label.mce-multiline{white-space:pre-wrap}.mce-label.mce-success{color:#468847}.mce-label.mce-warning{color:#c09853}.mce-label.mce-error{color:#b94a48}.mce-rtl .mce-label{text-align:right;direction:rtl}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;filter:none}.mce-menubar .mce-menubtn button{color:#b5b9bf}.mce-menubar{border:1px solid #292e33}.mce-menubar .mce-menubtn button span{color:#b5b9bf}.mce-menubar .mce-caret{border-top-color:#b5b9bf}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:#000;background:#2f3740;filter:none}.mce-menubtn button{color:#b5b9bf}.mce-menubtn.mce-btn-small span{font-size:12px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-menubtn .mce-caret{*margin-top:6px}.mce-rtl .mce-menubtn button{direction:rtl;text-align:right}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:normal;line-height:20px;color:#ddd;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item .mce-ico,.mce-menu-item .mce-text{color:#ddd}.mce-menu-item.mce-disabled .mce-text,.mce-menu-item.mce-disabled .mce-ico{color:#75797f}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text,.mce-menu-item:focus .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:#fff}.mce-menu-item.mce-disabled:hover{background:#ccc}.mce-menu-shortcut{display:inline-block;color:#75797f}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 15px 0 20px}.mce-menu-item:hover .mce-menu-shortcut,.mce-menu-item.mce-selected .mce-menu-shortcut,.mce-menu-item:focus .mce-menu-shortcut{color:#fff}.mce-menu-item .mce-caret{margin-top:4px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #ddd}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret,.mce-menu-item:hover .mce-caret{border-left-color:#fff}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item-normal.mce-active{background-color:#0085c7}.mce-menu-item-preview.mce-active{border-left:5px solid #08608c}.mce-menu-item-normal.mce-active .mce-text{color:#fff}.mce-menu-item-normal.mce-active:hover .mce-text,.mce-menu-item-normal.mce-active:hover .mce-ico{color:#fff}.mce-menu-item-normal.mce-active:focus .mce-text,.mce-menu-item-normal.mce-active:focus .mce-ico{color:#fff}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#006597}.mce-menu-item-link{color:#093;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mce-menu-item-link b{color:#093}.mce-menu-item-ellipsis{display:block;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mce-menu-item:hover *,.mce-menu-item.mce-selected *,.mce-menu-item:focus *{color:#fff}div.mce-menu .mce-menu-item-sep,.mce-menu-item-sep:hover{border:0;padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#25313f;border-bottom:1px solid #424f5f;cursor:default;filter:none}div.mce-menu .mce-menu-item b{font-weight:bold}.mce-menu-item-indent-1{padding-left:20px}.mce-menu-item-indent-2{padding-left:35px}.mce-menu-item-indent-2{padding-left:35px}.mce-menu-item-indent-3{padding-left:40px}.mce-menu-item-indent-4{padding-left:45px}.mce-menu-item-indent-5{padding-left:50px}.mce-menu-item-indent-6{padding-left:55px}.mce-menu.mce-rtl{direction:rtl}.mce-rtl .mce-menu-item{text-align:right;direction:rtl;padding:6px 12px 6px 15px}.mce-menu-align.mce-rtl .mce-menu-shortcut,.mce-menu-align.mce-rtl .mce-caret{right:auto;left:0}.mce-rtl .mce-menu-item .mce-caret{margin-left:6px;margin-right:0;border-right:4px solid #ddd;border-left:0}.mce-rtl .mce-menu-item.mce-selected .mce-caret,.mce-rtl .mce-menu-item:focus .mce-caret,.mce-rtl .mce-menu-item:hover .mce-caret{border-left-color:transparent;border-right-color:#fff}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}.mce-throbber-inline{position:static;height:50px}.mce-menu .mce-throbber-inline{height:25px;background-size:contain}.mce-menu{position:absolute;left:0;top:0;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:-1px 0 0;min-width:160px;background:#2f3740;border:1px solid #0d2133;border:1px solid #202a33;z-index:1002;max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-rtl .mce-listbox .mce-caret{right:auto;left:8px}.mce-rtl .mce-listbox button{padding-right:10px;padding-left:20px}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#b5b9bf}.mce-selectbox{background:#515c67;border:1px solid #202a33}.mce-slider{border:1px solid #202a33;background:#515c67;width:100px;height:10px;position:relative;display:block}.mce-slider.mce-vertical{width:10px;height:100px}.mce-slider-handle{border:1px solid #000;background:#454f59;display:block;width:13px;height:13px;position:absolute;top:0;left:0;margin-left:-1px;margin-top:-2px}.mce-slider-handle:focus{background:#bbb}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#000}.mce-splitbtn button{padding-right:4px;padding-left:8px}.mce-splitbtn .mce-open{padding-right:4px;padding-left:4px}.mce-splitbtn .mce-open.mce-active{background-color:#262a2d;outline:1px solid #000}.mce-splitbtn.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-splitbtn{direction:rtl;text-align:right}.mce-rtl .mce-splitbtn button{padding-right:4px;padding-left:4px}.mce-rtl .mce-splitbtn .mce-open{border-left:0}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #202a33}.mce-tabs,.mce-tabs+.mce-container-body{background:#303942}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #202a33;border-width:0 1px 0 0;background:#303942;padding:8px;text-shadow:0 1px 1px rgba(0,0,0,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#404952}.mce-tab.mce-active{background:#404952;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-rtl .mce-tabs{text-align:right;direction:rtl}.mce-rtl .mce-tab{border-width:0 0 0 1px}.mce-textbox{background:#515c67;border:1px solid #202a33;display:inline-block;-webkit-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#b5b9bf}.mce-textbox:focus,.mce-textbox.mce-focus{border-color:#1e7dad}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px;height:auto}.mce-textbox.mce-disabled{color:#79818a}.mce-rtl .mce-textbox{text-align:right;direction:rtl}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce',Arial;font-style:normal;font-weight:normal;font-variant:normal;font-size:16px;line-height:16px;speak:none;vertical-align:text-top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;background:transparent center center;background-size:cover;width:16px;height:16px;color:#b5b9bf}.mce-btn-small .mce-ico{font-family:'tinymce-small',Arial}.mce-i-save:before{content:"\e000"}.mce-i-newdocument:before{content:"\e001"}.mce-i-fullpage:before{content:"\e002"}.mce-i-alignleft:before{content:"\e003"}.mce-i-aligncenter:before{content:"\e004"}.mce-i-alignright:before{content:"\e005"}.mce-i-alignjustify:before{content:"\e006"}.mce-i-alignnone:before{content:"\e003"}.mce-i-cut:before{content:"\e007"}.mce-i-paste:before{content:"\e008"}.mce-i-searchreplace:before{content:"\e009"}.mce-i-bullist:before{content:"\e00a"}.mce-i-numlist:before{content:"\e00b"}.mce-i-indent:before{content:"\e00c"}.mce-i-outdent:before{content:"\e00d"}.mce-i-blockquote:before{content:"\e00e"}.mce-i-undo:before{content:"\e00f"}.mce-i-redo:before{content:"\e010"}.mce-i-link:before{content:"\e011"}.mce-i-unlink:before{content:"\e012"}.mce-i-anchor:before{content:"\e013"}.mce-i-image:before{content:"\e014"}.mce-i-media:before{content:"\e015"}.mce-i-help:before{content:"\e016"}.mce-i-code:before{content:"\e017"}.mce-i-insertdatetime:before{content:"\e018"}.mce-i-preview:before{content:"\e019"}.mce-i-forecolor:before{content:"\e01a"}.mce-i-backcolor:before{content:"\e01a"}.mce-i-table:before{content:"\e01b"}.mce-i-hr:before{content:"\e01c"}.mce-i-removeformat:before{content:"\e01d"}.mce-i-subscript:before{content:"\e01e"}.mce-i-superscript:before{content:"\e01f"}.mce-i-charmap:before{content:"\e020"}.mce-i-emoticons:before{content:"\e021"}.mce-i-print:before{content:"\e022"}.mce-i-fullscreen:before{content:"\e023"}.mce-i-spellchecker:before{content:"\e024"}.mce-i-nonbreaking:before{content:"\e025"}.mce-i-template:before{content:"\e026"}.mce-i-pagebreak:before{content:"\e027"}.mce-i-restoredraft:before{content:"\e028"}.mce-i-bold:before{content:"\e02a"}.mce-i-italic:before{content:"\e02b"}.mce-i-underline:before{content:"\e02c"}.mce-i-strikethrough:before{content:"\e02d"}.mce-i-visualchars:before{content:"\e02e"}.mce-i-visualblocks:before{content:"\e02e"}.mce-i-ltr:before{content:"\e02f"}.mce-i-rtl:before{content:"\e030"}.mce-i-copy:before{content:"\e031"}.mce-i-resize:before{content:"\e032"}.mce-i-browse:before{content:"\e034"}.mce-i-pastetext:before{content:"\e035"}.mce-i-rotateleft:before{content:"\eaa8"}.mce-i-rotateright:before{content:"\eaa9"}.mce-i-crop:before{content:"\ee78"}.mce-i-editimage:before{content:"\e915"}.mce-i-options:before{content:"\ec6a"}.mce-i-flipv:before{content:"\eaaa"}.mce-i-fliph:before{content:"\eaac"}.mce-i-zoomin:before{content:"\eb35"}.mce-i-zoomout:before{content:"\eb36"}.mce-i-sun:before{content:"\eccc"}.mce-i-moon:before{content:"\eccd"}.mce-i-arrowleft:before{content:"\edc0"}.mce-i-arrowright:before{content:"\e93c"}.mce-i-drop:before{content:"\e935"}.mce-i-contrast:before{content:"\ecd4"}.mce-i-sharpen:before{content:"\eba7"}.mce-i-resize2:before{content:"\edf9"}.mce-i-orientation:before{content:"\e601"}.mce-i-invert:before{content:"\e602"}.mce-i-gamma:before{content:"\e600"}.mce-i-remove:before{content:"\ed6a"}.mce-i-tablerowprops:before{content:"\e604"}.mce-i-tablecellprops:before{content:"\e605"}.mce-i-table2:before{content:"\e606"}.mce-i-tablemergecells:before{content:"\e607"}.mce-i-tableinsertcolbefore:before{content:"\e608"}.mce-i-tableinsertcolafter:before{content:"\e609"}.mce-i-tableinsertrowbefore:before{content:"\e60a"}.mce-i-tableinsertrowafter:before{content:"\e60b"}.mce-i-tablesplitcells:before{content:"\e60d"}.mce-i-tabledelete:before{content:"\e60e"}.mce-i-tableleftheader:before{content:"\e62a"}.mce-i-tabletopheader:before{content:"\e62b"}.mce-i-tabledeleterow:before{content:"\e800"}.mce-i-tabledeletecol:before{content:"\e801"}.mce-i-codesample:before{content:"\e603"}.mce-i-fill:before{content:"\e902"}.mce-i-borderwidth:before{content:"\e903"}.mce-i-line:before{content:"\e904"}.mce-i-count:before{content:"\e905"}.mce-i-translate:before{content:"\e907"}.mce-i-drag:before{content:"\e908"}.mce-i-home:before{content:"\e90b"}.mce-i-upload:before{content:"\e914"}.mce-i-bubble:before{content:"\e91c"}.mce-i-user:before{content:"\e91d"}.mce-i-lock:before{content:"\e926"}.mce-i-unlock:before{content:"\e927"}.mce-i-settings:before{content:"\e928"}.mce-i-remove2:before{content:"\e92a"}.mce-i-menu:before{content:"\e92d"}.mce-i-warning:before{content:"\e930"}.mce-i-question:before{content:"\e931"}.mce-i-pluscircle:before{content:"\e932"}.mce-i-info:before{content:"\e933"}.mce-i-notice:before{content:"\e934"}.mce-i-arrowup:before{content:"\e93b"}.mce-i-arrowdown:before{content:"\e93d"}.mce-i-arrowup2:before{content:"\e93f"}.mce-i-arrowdown2:before{content:"\e940"}.mce-i-menu2:before{content:"\e941"}.mce-i-newtab:before{content:"\e961"}.mce-i-a11y:before{content:"\e900"}.mce-i-plus:before{content:"\e93a"}.mce-i-insert:before{content:"\e93a"}.mce-i-minus:before{content:"\e939"}.mce-i-books:before{content:"\e911"}.mce-i-reload:before{content:"\e906"}.mce-i-toc:before{content:"\e901"}.mce-i-checkmark:before{content:"\e033"}.mce-i-checkbox:before,.mce-i-selected:before{content:"\e033"}.mce-i-insert{font-size:14px}.mce-i-selected{visibility:hidden}i.mce-i-backcolor{text-shadow:none;background:#384552}
\ No newline at end of file
index e4a77ff459acdcd6a0061873f25fe4444bad9502..aa3697c6f76038fd8e77d13e2ea9d4751782b079 100644 (file)
@@ -1 +1 @@
-.word-wrap{word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid rgba(208,2,27,0.5);cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#2276d2 !important}.mce-edit-focus{outline:1px dotted #333}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2276d2}.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,.mce-content-body.mce-content-readonly *[contentEditable=true]:hover{outline:none}.mce-content-body *[data-mce-selected="inline-boundary"]{background:#bfe6ff}.mce-content-body .mce-item-anchor[data-mce-selected]{background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-content-body hr{cursor:default}.mce-content-body table{-webkit-nbsp-mode:normal}.ephox-snooker-resizer-bar{background-color:#2276d2;opacity:0}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:.2}.mce-content-body{line-height:1.3}
\ No newline at end of file
+.word-wrap{word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid rgba(208,2,27,0.5);cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#2276d2 !important}.mce-edit-focus{outline:1px dotted #333}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2276d2}.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,.mce-content-body.mce-content-readonly *[contentEditable=true]:hover{outline:none}.mce-content-body *[data-mce-selected="inline-boundary"]{background:#bfe6ff}.mce-content-body .mce-item-anchor[data-mce-selected]{background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-content-body hr{cursor:default}.mce-content-body table{-webkit-nbsp-mode:normal}.ephox-snooker-resizer-bar{background-color:#2276d2;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:.2}.mce-content-body{line-height:1.3}
\ No newline at end of file
index 1434177df569bc667a5679303a7f0cbe31a4906d..c04313684de9bb6bb11bae0cb4e1646d07507d76 100644 (file)
@@ -1 +1 @@
-body{background-color:#FFFFFF;color:#000000;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;line-height:1.3;scrollbar-3dlight-color:#F0F0EE;scrollbar-arrow-color:#676662;scrollbar-base-color:#F0F0EE;scrollbar-darkshadow-color:#DDDDDD;scrollbar-face-color:#E0E0DD;scrollbar-highlight-color:#F0F0EE;scrollbar-shadow-color:#F0F0EE;scrollbar-track-color:#F5F5F5}td,th{font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px}.word-wrap{word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid rgba(208,2,27,0.5);cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#2276d2 !important}.mce-edit-focus{outline:1px dotted #333}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2276d2}.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,.mce-content-body.mce-content-readonly *[contentEditable=true]:hover{outline:none}.mce-content-body *[data-mce-selected="inline-boundary"]{background:#bfe6ff}.mce-content-body .mce-item-anchor[data-mce-selected]{background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-content-body hr{cursor:default}.mce-content-body table{-webkit-nbsp-mode:normal}.ephox-snooker-resizer-bar{background-color:#2276d2;opacity:0}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:.2}
\ No newline at end of file
+body{background-color:#FFFFFF;color:#000000;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;line-height:1.3;scrollbar-3dlight-color:#F0F0EE;scrollbar-arrow-color:#676662;scrollbar-base-color:#F0F0EE;scrollbar-darkshadow-color:#DDDDDD;scrollbar-face-color:#E0E0DD;scrollbar-highlight-color:#F0F0EE;scrollbar-shadow-color:#F0F0EE;scrollbar-track-color:#F5F5F5}td,th{font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px}.word-wrap{word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid rgba(208,2,27,0.5);cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#2276d2 !important}.mce-edit-focus{outline:1px dotted #333}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2276d2}.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,.mce-content-body.mce-content-readonly *[contentEditable=true]:hover{outline:none}.mce-content-body *[data-mce-selected="inline-boundary"]{background:#bfe6ff}.mce-content-body .mce-item-anchor[data-mce-selected]{background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-content-body hr{cursor:default}.mce-content-body table{-webkit-nbsp-mode:normal}.ephox-snooker-resizer-bar{background-color:#2276d2;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:.2}
\ No newline at end of file
index f963a66a976bf5271b13b7891d78e431b6c3427d..f14c3686af0dd84b0ad5d46b76c5c414c4511fb6 100644 (file)
@@ -1,2 +1,2 @@
-// 4.9.4 (2019-03-20)
-!function(H){"use strict";var o=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t]},j=function(n,r){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n(r.apply(null,e))}},q=function(e){return function(){return e}},$=function(e){return e};function d(r){for(var o=[],e=1;e<arguments.length;e++)o[e-1]=arguments[e];return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=o.concat(e);return r.apply(null,n)}}var e,t,n,r,i,a,u,s,c,l,f,m,g,p,h,v,b,y=function(n){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return!n.apply(null,e)}},C=q(!1),x=q(!0),w=C,N=x,E=function(){return S},S=(r={fold:function(e,t){return e()},is:w,isSome:w,isNone:N,getOr:n=function(e){return e},getOrThunk:t=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:function(){return null},getOrUndefined:function(){return undefined},or:n,orThunk:t,map:E,ap:E,each:function(){},bind:E,flatten:E,exists:w,forall:N,filter:E,equals:e=function(e){return e.isNone()},equals_:e,toArray:function(){return[]},toString:q("none()")},Object.freeze&&Object.freeze(r),r),k=function(n){var e=function(){return n},t=function(){return o},r=function(e){return e(n)},o={fold:function(e,t){return t(n)},is:function(e){return n===e},isSome:N,isNone:w,getOr:e,getOrThunk:e,getOrDie:e,getOrNull:e,getOrUndefined:e,or:t,orThunk:t,map:function(e){return k(e(n))},ap:function(e){return e.fold(E,function(e){return k(e(n))})},each:function(e){e(n)},bind:r,flatten:e,exists:r,forall:r,filter:function(e){return e(n)?o:S},equals:function(e){return e.is(n)},equals_:function(e,t){return e.fold(w,function(e){return t(n,e)})},toArray:function(){return[n]},toString:function(){return"some("+n+")"}};return o},A={some:k,none:E,from:function(e){return null===e||e===undefined?S:k(e)}},T=function(t){return function(e){return function(e){if(null===e)return"null";var t=typeof e;return"object"===t&&Array.prototype.isPrototypeOf(e)?"array":"object"===t&&String.prototype.isPrototypeOf(e)?"string":t}(e)===t}},R=T("string"),_=T("object"),D=T("array"),B=T("null"),O=T("boolean"),P=T("function"),L=T("number"),I=(i=Array.prototype.indexOf)===undefined?function(e,t){return Y(e,t)}:function(e,t){return i.call(e,t)},M=function(e,t){return-1<I(e,t)},W=function(e,t){for(var n=e.length,r=new Array(n),o=0;o<n;o++){var i=e[o];r[o]=t(i,o,e)}return r},F=function(e,t){for(var n=0,r=e.length;n<r;n++)t(e[n],n,e)},K=function(e,t){for(var n=[],r=[],o=0,i=e.length;o<i;o++){var a=e[o];(t(a,o,e)?n:r).push(a)}return{pass:n,fail:r}},z=function(e,t){for(var n=[],r=0,o=e.length;r<o;r++){var i=e[r];t(i,r,e)&&n.push(i)}return n},U=function(e,t,n){return F(e,function(e){n=t(n,e)}),n},V=function(e,t){for(var n=0,r=e.length;n<r;n++){var o=e[n];if(t(o,n,e))return A.some(o)}return A.none()},X=function(e,t){for(var n=0,r=e.length;n<r;n++)if(t(e[n],n,e))return A.some(n);return A.none()},Y=function(e,t){for(var n=0,r=e.length;n<r;++n)if(e[n]===t)return n;return-1},G=Array.prototype.push,J=function(e,t){return function(e){for(var t=[],n=0,r=e.length;n<r;++n){if(!Array.prototype.isPrototypeOf(e[n]))throw new Error("Arr.flatten item "+n+" was not an array, input: "+e);G.apply(t,e[n])}return t}(W(e,t))},Q=function(e,t){for(var n=0,r=e.length;n<r;++n)if(!0!==t(e[n],n,e))return!1;return!0},Z=Array.prototype.slice,ee=function(e,t){return z(e,function(e){return!M(t,e)})},te=function(e){return 0===e.length?A.none():A.some(e[0])},ne=function(e){return 0===e.length?A.none():A.some(e[e.length-1])},re=P(Array.from)?Array.from:function(e){return Z.call(e)},oe="undefined"!=typeof H.window?H.window:Function("return this;")(),ie=function(e,t){return function(e,t){for(var n=t!==undefined&&null!==t?t:oe,r=0;r<e.length&&n!==undefined&&null!==n;++r)n=n[e[r]];return n}(e.split("."),t)},ae={getOrDie:function(e,t){var n=ie(e,t);if(n===undefined||null===n)throw e+" not available on this browser";return n}},ue=function(){return ae.getOrDie("URL")},se={createObjectURL:function(e){return ue().createObjectURL(e)},revokeObjectURL:function(e){ue().revokeObjectURL(e)}},ce=H.navigator,le=ce.userAgent,fe=function(e){return"matchMedia"in H.window&&H.matchMedia(e).matches};g=/Android/.test(le),u=(u=!(a=/WebKit/.test(le))&&/MSIE/gi.test(le)&&/Explorer/gi.test(ce.appName))&&/MSIE (\w+)\./.exec(le)[1],s=-1!==le.indexOf("Trident/")&&(-1!==le.indexOf("rv:")||-1!==ce.appName.indexOf("Netscape"))&&11,c=-1!==le.indexOf("Edge/")&&!u&&!s&&12,u=u||s||c,l=!a&&!s&&/Gecko/.test(le),f=-1!==le.indexOf("Mac"),m=/(iPad|iPhone)/.test(le),p="FormData"in H.window&&"FileReader"in H.window&&"URL"in H.window&&!!se.createObjectURL,h=fe("only screen and (max-device-width: 480px)")&&(g||m),v=fe("only screen and (min-width: 800px)")&&(g||m),b=-1!==le.indexOf("Windows Phone"),c&&(a=!1);var de,me={opera:!1,webkit:a,ie:u,gecko:l,mac:f,iOS:m,android:g,contentEditable:!m||p||534<=parseInt(le.match(/AppleWebKit\/(\d*)/)[1],10),transparentSrc:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",caretAfter:8!==u,range:H.window.getSelection&&"Range"in H.window,documentMode:u&&!c?H.document.documentMode||7:10,fileApi:p,ceFalse:!1===u||8<u,cacheSuffix:null,container:null,overrideViewPort:null,experimentalShadowDom:!1,canHaveCSP:!1===u||11<u,desktop:!h&&!v,windowsPhone:b},ge=window.Promise?window.Promise:function(){function r(e,t){return function(){e.apply(t,arguments)}}var e=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i=function(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],l(e,r(o,this),r(u,this))},t=i.immediateFn||"function"==typeof setImmediate&&setImmediate||function(e){setTimeout(e,1)};function a(r){var o=this;null!==this._state?t(function(){var e=o._state?r.onFulfilled:r.onRejected;if(null!==e){var t;try{t=e(o._value)}catch(n){return void r.reject(n)}r.resolve(t)}else(o._state?r.resolve:r.reject)(o._value)}):this._deferreds.push(r)}function o(e){try{if(e===this)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var t=e.then;if("function"==typeof t)return void l(r(t,e),r(o,this),r(u,this))}this._state=!0,this._value=e,s.call(this)}catch(n){u.call(this,n)}}function u(e){this._state=!1,this._value=e,s.call(this)}function s(){for(var e=0,t=this._deferreds.length;e<t;e++)a.call(this,this._deferreds[e]);this._deferreds=null}function c(e,t,n,r){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.resolve=n,this.reject=r}function l(e,t,n){var r=!1;try{e(function(e){r||(r=!0,t(e))},function(e){r||(r=!0,n(e))})}catch(o){if(r)return;r=!0,n(o)}}return i.prototype["catch"]=function(e){return this.then(null,e)},i.prototype.then=function(n,r){var o=this;return new i(function(e,t){a.call(o,new c(n,r,e,t))})},i.all=function(){var s=Array.prototype.slice.call(1===arguments.length&&e(arguments[0])?arguments[0]:arguments);return new i(function(o,i){if(0===s.length)return o([]);var a=s.length;function u(t,e){try{if(e&&("object"==typeof e||"function"==typeof e)){var n=e.then;if("function"==typeof n)return void n.call(e,function(e){u(t,e)},i)}s[t]=e,0==--a&&o(s)}catch(r){i(r)}}for(var e=0;e<s.length;e++)u(e,s[e])})},i.resolve=function(t){return t&&"object"==typeof t&&t.constructor===i?t:new i(function(e){e(t)})},i.reject=function(n){return new i(function(e,t){t(n)})},i.race=function(o){return new i(function(e,t){for(var n=0,r=o.length;n<r;n++)o[n].then(e,t)})},i}(),pe=function(e,t){return"number"!=typeof t&&(t=0),setTimeout(e,t)},he=function(e,t){return"number"!=typeof t&&(t=1),setInterval(e,t)},ve=function(t,n){var r,e;return(e=function(){var e=arguments;clearTimeout(r),r=pe(function(){t.apply(this,e)},n)}).stop=function(){clearTimeout(r)},e},be={requestAnimationFrame:function(e,t){de?de.then(e):de=new ge(function(e){t||(t=H.document.body),function(e,t){var n,r=H.window.requestAnimationFrame,o=["ms","moz","webkit"];for(n=0;n<o.length&&!r;n++)r=H.window[o[n]+"RequestAnimationFrame"];r||(r=function(e){H.window.setTimeout(e,0)}),r(e,t)}(e,t)}).then(e)},setTimeout:pe,setInterval:he,setEditorTimeout:function(e,t,n){return pe(function(){e.removed||t()},n)},setEditorInterval:function(e,t,n){var r;return r=he(function(){e.removed?clearInterval(r):t()},n)},debounce:ve,throttle:ve,clearInterval:function(e){return clearInterval(e)},clearTimeout:function(e){return clearTimeout(e)}},ye=/^(?:mouse|contextmenu)|click/,Ce={keyLocation:1,layerX:1,layerY:1,returnValue:1,webkitMovementX:1,webkitMovementY:1,keyIdentifier:1},xe=function(){return!1},we=function(){return!0},Ne=function(e,t,n,r){e.addEventListener?e.addEventListener(t,n,r||!1):e.attachEvent&&e.attachEvent("on"+t,n)},Ee=function(e,t,n,r){e.removeEventListener?e.removeEventListener(t,n,r||!1):e.detachEvent&&e.detachEvent("on"+t,n)},Se=function(e,t){var n,r,o=t||{};for(n in e)Ce[n]||(o[n]=e[n]);if(o.target||(o.target=o.srcElement||H.document),me.experimentalShadowDom&&(o.target=function(e,t){if(e.composedPath){var n=e.composedPath();if(n&&0<n.length)return n[0]}return t}(e,o.target)),e&&ye.test(e.type)&&e.pageX===undefined&&e.clientX!==undefined){var i=o.target.ownerDocument||H.document,a=i.documentElement,u=i.body;o.pageX=e.clientX+(a&&a.scrollLeft||u&&u.scrollLeft||0)-(a&&a.clientLeft||u&&u.clientLeft||0),o.pageY=e.clientY+(a&&a.scrollTop||u&&u.scrollTop||0)-(a&&a.clientTop||u&&u.clientTop||0)}return o.preventDefault=function(){o.isDefaultPrevented=we,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},o.stopPropagation=function(){o.isPropagationStopped=we,e&&(e.stopPropagation?e.stopPropagation():e.cancelBubble=!0)},!(o.stopImmediatePropagation=function(){o.isImmediatePropagationStopped=we,o.stopPropagation()})==((r=o).isDefaultPrevented===we||r.isDefaultPrevented===xe)&&(o.isDefaultPrevented=xe,o.isPropagationStopped=xe,o.isImmediatePropagationStopped=xe),"undefined"==typeof o.metaKey&&(o.metaKey=!1),o},ke=function(e,t,n){var r=e.document,o={type:"ready"};if(n.domLoaded)t(o);else{var i=function(){return"complete"===r.readyState||"interactive"===r.readyState&&r.body},a=function(){n.domLoaded||(n.domLoaded=!0,t(o))},u=function(){i()&&(Ee(r,"readystatechange",u),a())},s=function(){try{r.documentElement.doScroll("left")}catch(e){return void be.setTimeout(s)}a()};!r.addEventListener||me.ie&&me.ie<11?(Ne(r,"readystatechange",u),r.documentElement.doScroll&&e.self===e.top&&s()):i()?a():Ne(e,"DOMContentLoaded",a),Ne(e,"load",a)}},Te=function(){var m,g,p,h,v,b=this,y={};g="mce-data-"+(+new Date).toString(32),h="onmouseenter"in H.document.documentElement,p="onfocusin"in H.document.documentElement,v={mouseenter:"mouseover",mouseleave:"mouseout"},m=1,b.domLoaded=!1,b.events=y;var C=function(e,t){var n,r,o,i,a=y[t];if(n=a&&a[e.type])for(r=0,o=n.length;r<o;r++)if((i=n[r])&&!1===i.func.call(i.scope,e)&&e.preventDefault(),e.isImmediatePropagationStopped())return};b.bind=function(e,t,n,r){var o,i,a,u,s,c,l,f=H.window,d=function(e){C(Se(e||f.event),o)};if(e&&3!==e.nodeType&&8!==e.nodeType){for(e[g]?o=e[g]:(o=m++,e[g]=o,y[o]={}),r=r||e,a=(t=t.split(" ")).length;a--;)c=d,s=l=!1,"DOMContentLoaded"===(u=t[a])&&(u="ready"),b.domLoaded&&"ready"===u&&"complete"===e.readyState?n.call(r,Se({type:u})):(h||(s=v[u])&&(c=function(e){var t,n;if(t=e.currentTarget,(n=e.relatedTarget)&&t.contains)n=t.contains(n);else for(;n&&n!==t;)n=n.parentNode;n||((e=Se(e||f.event)).type="mouseout"===e.type?"mouseleave":"mouseenter",e.target=t,C(e,o))}),p||"focusin"!==u&&"focusout"!==u||(l=!0,s="focusin"===u?"focus":"blur",c=function(e){(e=Se(e||f.event)).type="focus"===e.type?"focusin":"focusout",C(e,o)}),(i=y[o][u])?"ready"===u&&b.domLoaded?n({type:u}):i.push({func:n,scope:r}):(y[o][u]=i=[{func:n,scope:r}],i.fakeName=s,i.capture=l,i.nativeHandler=c,"ready"===u?ke(e,c,b):Ne(e,s||u,c,l)));return e=i=0,n}},b.unbind=function(e,t,n){var r,o,i,a,u,s;if(!e||3===e.nodeType||8===e.nodeType)return b;if(r=e[g]){if(s=y[r],t){for(i=(t=t.split(" ")).length;i--;)if(o=s[u=t[i]]){if(n)for(a=o.length;a--;)if(o[a].func===n){var c=o.nativeHandler,l=o.fakeName,f=o.capture;(o=o.slice(0,a).concat(o.slice(a+1))).nativeHandler=c,o.fakeName=l,o.capture=f,s[u]=o}n&&0!==o.length||(delete s[u],Ee(e,o.fakeName||u,o.nativeHandler,o.capture))}}else{for(u in s)o=s[u],Ee(e,o.fakeName||u,o.nativeHandler,o.capture);s={}}for(u in s)return b;delete y[r];try{delete e[g]}catch(d){e[g]=null}}return b},b.fire=function(e,t,n){var r;if(!e||3===e.nodeType||8===e.nodeType)return b;for((n=Se(null,n)).type=t,n.target=e;(r=e[g])&&C(n,r),(e=e.parentNode||e.ownerDocument||e.defaultView||e.parentWindow)&&!n.isPropagationStopped(););return b},b.clean=function(e){var t,n,r=b.unbind;if(!e||3===e.nodeType||8===e.nodeType)return b;if(e[g]&&r(e),e.getElementsByTagName||(e=e.document),e&&e.getElementsByTagName)for(r(e),t=(n=e.getElementsByTagName("*")).length;t--;)(e=n[t])[g]&&r(e);return b},b.destroy=function(){y={}},b.cancel=function(e){return e&&(e.preventDefault(),e.stopImmediatePropagation()),!1}};Te.Event=new Te,Te.Event.bind(H.window,"ready",function(){});var Ae,Re,_e,De,Be,Oe,Pe,Le,Ie,Me,Fe,ze,Ue,Ve,He,je,qe,$e,We="sizzle"+-new Date,Ke=H.window.document,Xe=0,Ye=0,Ge=At(),Je=At(),Qe=At(),Ze=function(e,t){return e===t&&(Fe=!0),0},et=typeof undefined,tt={}.hasOwnProperty,nt=[],rt=nt.pop,ot=nt.push,it=nt.push,at=nt.slice,ut=nt.indexOf||function(e){for(var t=0,n=this.length;t<n;t++)if(this[t]===e)return t;return-1},st="[\\x20\\t\\r\\n\\f]",ct="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",lt="\\["+st+"*("+ct+")(?:"+st+"*([*^$|!~]?=)"+st+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+ct+"))|)"+st+"*\\]",ft=":("+ct+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+lt+")*)|.*)\\)|)",dt=new RegExp("^"+st+"+|((?:^|[^\\\\])(?:\\\\.)*)"+st+"+$","g"),mt=new RegExp("^"+st+"*,"+st+"*"),gt=new RegExp("^"+st+"*([>+~]|"+st+")"+st+"*"),pt=new RegExp("="+st+"*([^\\]'\"]*?)"+st+"*\\]","g"),ht=new RegExp(ft),vt=new RegExp("^"+ct+"$"),bt={ID:new RegExp("^#("+ct+")"),CLASS:new RegExp("^\\.("+ct+")"),TAG:new RegExp("^("+ct+"|[*])"),ATTR:new RegExp("^"+lt),PSEUDO:new RegExp("^"+ft),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+st+"*(even|odd|(([+-]|)(\\d*)n|)"+st+"*(?:([+-]|)"+st+"*(\\d+)|))"+st+"*\\)|)","i"),bool:new RegExp("^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$","i"),needsContext:new RegExp("^"+st+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+st+"*((?:-\\d)?\\d*)"+st+"*\\)|)(?=[^-]|$)","i")},yt=/^(?:input|select|textarea|button)$/i,Ct=/^h\d$/i,xt=/^[^{]+\{\s*\[native \w/,wt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Nt=/[+~]/,Et=/'|\\/g,St=new RegExp("\\\\([\\da-f]{1,6}"+st+"?|("+st+")|.)","ig"),kt=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)};try{it.apply(nt=at.call(Ke.childNodes),Ke.childNodes),nt[Ke.childNodes.length].nodeType}catch(jN){it={apply:nt.length?function(e,t){ot.apply(e,at.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}var Tt=function(e,t,n,r){var o,i,a,u,s,c,l,f,d,m;if((t?t.ownerDocument||t:Ke)!==Ue&&ze(t),n=n||[],!e||"string"!=typeof e)return n;if(1!==(u=(t=t||Ue).nodeType)&&9!==u)return[];if(He&&!r){if(o=wt.exec(e))if(a=o[1]){if(9===u){if(!(i=t.getElementById(a))||!i.parentNode)return n;if(i.id===a)return n.push(i),n}else if(t.ownerDocument&&(i=t.ownerDocument.getElementById(a))&&$e(t,i)&&i.id===a)return n.push(i),n}else{if(o[2])return it.apply(n,t.getElementsByTagName(e)),n;if((a=o[3])&&Re.getElementsByClassName)return it.apply(n,t.getElementsByClassName(a)),n}if(Re.qsa&&(!je||!je.test(e))){if(f=l=We,d=t,m=9===u&&e,1===u&&"object"!==t.nodeName.toLowerCase()){for(c=Oe(e),(l=t.getAttribute("id"))?f=l.replace(Et,"\\$&"):t.setAttribute("id",f),f="[id='"+f+"'] ",s=c.length;s--;)c[s]=f+It(c[s]);d=Nt.test(e)&&Pt(t.parentNode)||t,m=c.join(",")}if(m)try{return it.apply(n,d.querySelectorAll(m)),n}catch(g){}finally{l||t.removeAttribute("id")}}}return Le(e.replace(dt,"$1"),t,n,r)};function At(){var r=[];return function e(t,n){return r.push(t+" ")>_e.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function Rt(e){return e[We]=!0,e}function _t(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||1<<31)-(~e.sourceIndex||1<<31);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function Dt(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function Bt(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function Ot(a){return Rt(function(i){return i=+i,Rt(function(e,t){for(var n,r=a([],e.length,i),o=r.length;o--;)e[n=r[o]]&&(e[n]=!(t[n]=e[n]))})})}function Pt(e){return e&&typeof e.getElementsByTagName!==et&&e}for(Ae in Re=Tt.support={},Be=Tt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},ze=Tt.setDocument=function(e){var t,s=e?e.ownerDocument||e:Ke,n=s.defaultView;return s!==Ue&&9===s.nodeType&&s.documentElement?(Ve=(Ue=s).documentElement,He=!Be(s),n&&n!==function(e){try{return e.top}catch(t){}return null}(n)&&(n.addEventListener?n.addEventListener("unload",function(){ze()},!1):n.attachEvent&&n.attachEvent("onunload",function(){ze()})),Re.attributes=!0,Re.getElementsByTagName=!0,Re.getElementsByClassName=xt.test(s.getElementsByClassName),Re.getById=!0,_e.find.ID=function(e,t){if(typeof t.getElementById!==et&&He){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},_e.filter.ID=function(e){var t=e.replace(St,kt);return function(e){return e.getAttribute("id")===t}},_e.find.TAG=Re.getElementsByTagName?function(e,t){if(typeof t.getElementsByTagName!==et)return t.getElementsByTagName(e)}:function(e,t){var n,r=[],o=0,i=t.getElementsByTagName(e);if("*"===e){for(;n=i[o++];)1===n.nodeType&&r.push(n);return r}return i},_e.find.CLASS=Re.getElementsByClassName&&function(e,t){if(He)return t.getElementsByClassName(e)},qe=[],je=[],Re.disconnectedMatch=!0,je=je.length&&new RegExp(je.join("|")),qe=qe.length&&new RegExp(qe.join("|")),t=xt.test(Ve.compareDocumentPosition),$e=t||xt.test(Ve.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},Ze=t?function(e,t){if(e===t)return Fe=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!Re.sortDetached&&t.compareDocumentPosition(e)===n?e===s||e.ownerDocument===Ke&&$e(Ke,e)?-1:t===s||t.ownerDocument===Ke&&$e(Ke,t)?1:Me?ut.call(Me,e)-ut.call(Me,t):0:4&n?-1:1)}:function(e,t){if(e===t)return Fe=!0,0;var n,r=0,o=e.parentNode,i=t.parentNode,a=[e],u=[t];if(!o||!i)return e===s?-1:t===s?1:o?-1:i?1:Me?ut.call(Me,e)-ut.call(Me,t):0;if(o===i)return _t(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;a[r]===u[r];)r++;return r?_t(a[r],u[r]):a[r]===Ke?-1:u[r]===Ke?1:0},s):Ue},Tt.matches=function(e,t){return Tt(e,null,null,t)},Tt.matchesSelector=function(e,t){if((e.ownerDocument||e)!==Ue&&ze(e),t=t.replace(pt,"='$1']"),Re.matchesSelector&&He&&(!qe||!qe.test(t))&&(!je||!je.test(t)))try{var n=(void 0).call(e,t);if(n||Re.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(jN){}return 0<Tt(t,Ue,null,[e]).length},Tt.contains=function(e,t){return(e.ownerDocument||e)!==Ue&&ze(e),$e(e,t)},Tt.attr=function(e,t){(e.ownerDocument||e)!==Ue&&ze(e);var n=_e.attrHandle[t.toLowerCase()],r=n&&tt.call(_e.attrHandle,t.toLowerCase())?n(e,t,!He):undefined;return r!==undefined?r:Re.attributes||!He?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},Tt.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},Tt.uniqueSort=function(e){var t,n=[],r=0,o=0;if(Fe=!Re.detectDuplicates,Me=!Re.sortStable&&e.slice(0),e.sort(Ze),Fe){for(;t=e[o++];)t===e[o]&&(r=n.push(o));for(;r--;)e.splice(n[r],1)}return Me=null,e},De=Tt.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=De(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r++];)n+=De(t);return n},(_e=Tt.selectors={cacheLength:50,createPseudo:Rt,match:bt,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(St,kt),e[3]=(e[3]||e[4]||e[5]||"").replace(St,kt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||Tt.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&Tt.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return bt.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&ht.test(n)&&(t=Oe(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(St,kt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=Ge[e+" "];return t||(t=new RegExp("(^|"+st+")"+e+"("+st+"|$)"))&&Ge(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==et&&e.getAttribute("class")||"")})},ATTR:function(n,r,o){return function(e){var t=Tt.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===o:"!="===r?t!==o:"^="===r?o&&0===t.indexOf(o):"*="===r?o&&-1<t.indexOf(o):"$="===r?o&&t.slice(-o.length)===o:"~="===r?-1<(" "+t+" ").indexOf(o):"|="===r&&(t===o||t.slice(0,o.length+1)===o+"-"))}},CHILD:function(m,e,t,g,p){var h="nth"!==m.slice(0,3),v="last"!==m.slice(-4),b="of-type"===e;return 1===g&&0===p?function(e){return!!e.parentNode}:function(e,t,n){var r,o,i,a,u,s,c=h!==v?"nextSibling":"previousSibling",l=e.parentNode,f=b&&e.nodeName.toLowerCase(),d=!n&&!b;if(l){if(h){for(;c;){for(i=e;i=i[c];)if(b?i.nodeName.toLowerCase()===f:1===i.nodeType)return!1;s=c="only"===m&&!s&&"nextSibling"}return!0}if(s=[v?l.firstChild:l.lastChild],v&&d){for(u=(r=(o=l[We]||(l[We]={}))[m]||[])[0]===Xe&&r[1],a=r[0]===Xe&&r[2],i=u&&l.childNodes[u];i=++u&&i&&i[c]||(a=u=0)||s.pop();)if(1===i.nodeType&&++a&&i===e){o[m]=[Xe,u,a];break}}else if(d&&(r=(e[We]||(e[We]={}))[m])&&r[0]===Xe)a=r[1];else for(;(i=++u&&i&&i[c]||(a=u=0)||s.pop())&&((b?i.nodeName.toLowerCase()!==f:1!==i.nodeType)||!++a||(d&&((i[We]||(i[We]={}))[m]=[Xe,a]),i!==e)););return(a-=p)===g||a%g==0&&0<=a/g}}},PSEUDO:function(e,i){var t,a=_e.pseudos[e]||_e.setFilters[e.toLowerCase()]||Tt.error("unsupported pseudo: "+e);return a[We]?a(i):1<a.length?(t=[e,e,"",i],_e.setFilters.hasOwnProperty(e.toLowerCase())?Rt(function(e,t){for(var n,r=a(e,i),o=r.length;o--;)e[n=ut.call(e,r[o])]=!(t[n]=r[o])}):function(e){return a(e,0,t)}):a}},pseudos:{not:Rt(function(e){var r=[],o=[],u=Pe(e.replace(dt,"$1"));return u[We]?Rt(function(e,t,n,r){for(var o,i=u(e,null,r,[]),a=e.length;a--;)(o=i[a])&&(e[a]=!(t[a]=o))}):function(e,t,n){return r[0]=e,u(r,null,n,o),!o.pop()}}),has:Rt(function(t){return function(e){return 0<Tt(t,e).length}}),contains:Rt(function(t){return t=t.replace(St,kt),function(e){return-1<(e.textContent||e.innerText||De(e)).indexOf(t)}}),lang:Rt(function(n){return vt.test(n||"")||Tt.error("unsupported lang: "+n),n=n.replace(St,kt).toLowerCase(),function(e){var t;do{if(t=He?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=H.window.location&&H.window.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===Ve},focus:function(e){return e===Ue.activeElement&&(!Ue.hasFocus||Ue.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return!1===e.disabled},disabled:function(e){return!0===e.disabled},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!_e.pseudos.empty(e)},header:function(e){return Ct.test(e.nodeName)},input:function(e){return yt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:Ot(function(){return[0]}),last:Ot(function(e,t){return[t-1]}),eq:Ot(function(e,t,n){return[n<0?n+t:n]}),even:Ot(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:Ot(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:Ot(function(e,t,n){for(var r=n<0?n+t:n;0<=--r;)e.push(r);return e}),gt:Ot(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=_e.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})_e.pseudos[Ae]=Dt(Ae);for(Ae in{submit:!0,reset:!0})_e.pseudos[Ae]=Bt(Ae);function Lt(){}function It(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function Mt(a,e,t){var u=e.dir,s=t&&"parentNode"===u,c=Ye++;return e.first?function(e,t,n){for(;e=e[u];)if(1===e.nodeType||s)return a(e,t,n)}:function(e,t,n){var r,o,i=[Xe,c];if(n){for(;e=e[u];)if((1===e.nodeType||s)&&a(e,t,n))return!0}else for(;e=e[u];)if(1===e.nodeType||s){if((r=(o=e[We]||(e[We]={}))[u])&&r[0]===Xe&&r[1]===c)return i[2]=r[2];if((o[u]=i)[2]=a(e,t,n))return!0}}}function Ft(o){return 1<o.length?function(e,t,n){for(var r=o.length;r--;)if(!o[r](e,t,n))return!1;return!0}:o[0]}function zt(e,t,n,r,o){for(var i,a=[],u=0,s=e.length,c=null!=t;u<s;u++)(i=e[u])&&(n&&!n(i,r,o)||(a.push(i),c&&t.push(u)));return a}function Ut(m,g,p,h,v,e){return h&&!h[We]&&(h=Ut(h)),v&&!v[We]&&(v=Ut(v,e)),Rt(function(e,t,n,r){var o,i,a,u=[],s=[],c=t.length,l=e||function(e,t,n){for(var r=0,o=t.length;r<o;r++)Tt(e,t[r],n);return n}(g||"*",n.nodeType?[n]:n,[]),f=!m||!e&&g?l:zt(l,u,m,n,r),d=p?v||(e?m:c||h)?[]:t:f;if(p&&p(f,d,n,r),h)for(o=zt(d,s),h(o,[],n,r),i=o.length;i--;)(a=o[i])&&(d[s[i]]=!(f[s[i]]=a));if(e){if(v||m){if(v){for(o=[],i=d.length;i--;)(a=d[i])&&o.push(f[i]=a);v(null,d=[],o,r)}for(i=d.length;i--;)(a=d[i])&&-1<(o=v?ut.call(e,a):u[i])&&(e[o]=!(t[o]=a))}}else d=zt(d===t?d.splice(c,d.length):d),v?v(null,t,d,r):it.apply(t,d)})}function Vt(e){for(var r,t,n,o=e.length,i=_e.relative[e[0].type],a=i||_e.relative[" "],u=i?1:0,s=Mt(function(e){return e===r},a,!0),c=Mt(function(e){return-1<ut.call(r,e)},a,!0),l=[function(e,t,n){return!i&&(n||t!==Ie)||((r=t).nodeType?s(e,t,n):c(e,t,n))}];u<o;u++)if(t=_e.relative[e[u].type])l=[Mt(Ft(l),t)];else{if((t=_e.filter[e[u].type].apply(null,e[u].matches))[We]){for(n=++u;n<o&&!_e.relative[e[n].type];n++);return Ut(1<u&&Ft(l),1<u&&It(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(dt,"$1"),t,u<n&&Vt(e.slice(u,n)),n<o&&Vt(e=e.slice(n)),n<o&&It(e))}l.push(t)}return Ft(l)}Lt.prototype=_e.filters=_e.pseudos,_e.setFilters=new Lt,Oe=Tt.tokenize=function(e,t){var n,r,o,i,a,u,s,c=Je[e+" "];if(c)return t?0:c.slice(0);for(a=e,u=[],s=_e.preFilter;a;){for(i in n&&!(r=mt.exec(a))||(r&&(a=a.slice(r[0].length)||a),u.push(o=[])),n=!1,(r=gt.exec(a))&&(n=r.shift(),o.push({value:n,type:r[0].replace(dt," ")}),a=a.slice(n.length)),_e.filter)!(r=bt[i].exec(a))||s[i]&&!(r=s[i](r))||(n=r.shift(),o.push({value:n,type:i,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?Tt.error(e):Je(e,u).slice(0)},Pe=Tt.compile=function(e,t){var n,h,v,b,y,r,o=[],i=[],a=Qe[e+" "];if(!a){for(t||(t=Oe(e)),n=t.length;n--;)(a=Vt(t[n]))[We]?o.push(a):i.push(a);(a=Qe(e,(h=i,b=0<(v=o).length,y=0<h.length,r=function(e,t,n,r,o){var i,a,u,s=0,c="0",l=e&&[],f=[],d=Ie,m=e||y&&_e.find.TAG("*",o),g=Xe+=null==d?1:Math.random()||.1,p=m.length;for(o&&(Ie=t!==Ue&&t);c!==p&&null!=(i=m[c]);c++){if(y&&i){for(a=0;u=h[a++];)if(u(i,t,n)){r.push(i);break}o&&(Xe=g)}b&&((i=!u&&i)&&s--,e&&l.push(i))}if(s+=c,b&&c!==s){for(a=0;u=v[a++];)u(l,f,t,n);if(e){if(0<s)for(;c--;)l[c]||f[c]||(f[c]=rt.call(r));f=zt(f)}it.apply(r,f),o&&!e&&0<f.length&&1<s+v.length&&Tt.uniqueSort(r)}return o&&(Xe=g,Ie=d),l},b?Rt(r):r))).selector=e}return a},Le=Tt.select=function(e,t,n,r){var o,i,a,u,s,c="function"==typeof e&&e,l=!r&&Oe(e=c.selector||e);if(n=n||[],1===l.length){if(2<(i=l[0]=l[0].slice(0)).length&&"ID"===(a=i[0]).type&&Re.getById&&9===t.nodeType&&He&&_e.relative[i[1].type]){if(!(t=(_e.find.ID(a.matches[0].replace(St,kt),t)||[])[0]))return n;c&&(t=t.parentNode),e=e.slice(i.shift().value.length)}for(o=bt.needsContext.test(e)?0:i.length;o--&&(a=i[o],!_e.relative[u=a.type]);)if((s=_e.find[u])&&(r=s(a.matches[0].replace(St,kt),Nt.test(i[0].type)&&Pt(t.parentNode)||t))){if(i.splice(o,1),!(e=r.length&&It(i)))return it.apply(n,r),n;break}}return(c||Pe(e,l))(r,t,!He,n,Nt.test(e)&&Pt(t.parentNode)||t),n},Re.sortStable=We.split("").sort(Ze).join("")===We,Re.detectDuplicates=!!Fe,ze(),Re.sortDetached=!0;var Ht=Array.isArray,jt=function(e,t,n){var r,o;if(!e)return 0;if(n=n||e,e.length!==undefined){for(r=0,o=e.length;r<o;r++)if(!1===t.call(n,e[r],r,e))return 0}else for(r in e)if(e.hasOwnProperty(r)&&!1===t.call(n,e[r],r,e))return 0;return 1},qt=function(e,t,n){var r,o;for(r=0,o=e.length;r<o;r++)if(t.call(n,e[r],r,e))return r;return-1},$t={isArray:Ht,toArray:function(e){var t,n,r=e;if(!Ht(e))for(r=[],t=0,n=e.length;t<n;t++)r[t]=e[t];return r},each:jt,map:function(n,r){var o=[];return jt(n,function(e,t){o.push(r(e,t,n))}),o},filter:function(n,r){var o=[];return jt(n,function(e,t){r&&!r(e,t,n)||o.push(e)}),o},indexOf:function(e,t){var n,r;if(e)for(n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},reduce:function(e,t,n,r){var o=0;for(arguments.length<3&&(n=e[0]);o<e.length;o++)n=t.call(r,n,e[o],o);return n},findIndex:qt,find:function(e,t,n){var r=qt(e,t,n);return-1!==r?e[r]:undefined},last:function(e){return e[e.length-1]}},Wt=/^\s*|\s*$/g,Kt=function(e){return null===e||e===undefined?"":(""+e).replace(Wt,"")},Xt=function(e,t){return t?!("array"!==t||!$t.isArray(e))||typeof e===t:e!==undefined},Yt=function(e,n,r,o){o=o||this,e&&(r&&(e=e[r]),$t.each(e,function(e,t){if(!1===n.call(o,e,t,r))return!1;Yt(e,n,r,o)}))},Gt={trim:Kt,isArray:$t.isArray,is:Xt,toArray:$t.toArray,makeMap:function(e,t,n){var r;for(t=t||",","string"==typeof(e=e||[])&&(e=e.split(t)),n=n||{},r=e.length;r--;)n[e[r]]={};return n},each:$t.each,map:$t.map,grep:$t.filter,inArray:$t.indexOf,hasOwn:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},extend:function(e,t){for(var n,r,o,i=[],a=2;a<arguments.length;a++)i[a-2]=arguments[a];var u,s=arguments;for(n=1,r=s.length;n<r;n++)for(o in t=s[n])t.hasOwnProperty(o)&&(u=t[o])!==undefined&&(e[o]=u);return e},create:function(e,t,n){var r,o,i,a,u,s=this,c=0;if(e=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(e),i=e[3].match(/(^|\.)(\w+)$/i)[2],!(o=s.createNS(e[3].replace(/\.\w+$/,""),n))[i]){if("static"===e[2])return o[i]=t,void(this.onCreate&&this.onCreate(e[2],e[3],o[i]));t[i]||(t[i]=function(){},c=1),o[i]=t[i],s.extend(o[i].prototype,t),e[5]&&(r=s.resolve(e[5]).prototype,a=e[5].match(/\.(\w+)$/i)[1],u=o[i],o[i]=c?function(){return r[a].apply(this,arguments)}:function(){return this.parent=r[a],u.apply(this,arguments)},o[i].prototype[i]=o[i],s.each(r,function(e,t){o[i].prototype[t]=r[t]}),s.each(t,function(e,t){r[t]?o[i].prototype[t]=function(){return this.parent=r[t],e.apply(this,arguments)}:t!==i&&(o[i].prototype[t]=e)})),s.each(t["static"],function(e,t){o[i][t]=e})}},walk:Yt,createNS:function(e,t){var n,r;for(t=t||H.window,e=e.split("."),n=0;n<e.length;n++)t[r=e[n]]||(t[r]={}),t=t[r];return t},resolve:function(e,t){var n,r;for(t=t||H.window,n=0,r=(e=e.split(".")).length;n<r&&(t=t[e[n]]);n++);return t},explode:function(e,t){return!e||Xt(e,"array")?e:$t.map(e.split(t||","),Kt)},_addCacheSuffix:function(e){var t=me.cacheSuffix;return t&&(e+=(-1===e.indexOf("?")?"?":"&")+t),e}},Jt=H.document,Qt=Array.prototype.push,Zt=Array.prototype.slice,en=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,tn=Te.Event,nn=Gt.makeMap("children,contents,next,prev"),rn=function(e){return void 0!==e},on=function(e){return"string"==typeof e},an=function(e,t){var n,r,o;for(o=(t=t||Jt).createElement("div"),n=t.createDocumentFragment(),o.innerHTML=e;r=o.firstChild;)n.appendChild(r);return n},un=function(e,t,n,r){var o;if(on(t))t=an(t,xn(e[0]));else if(t.length&&!t.nodeType){if(t=hn.makeArray(t),r)for(o=t.length-1;0<=o;o--)un(e,t[o],n,r);else for(o=0;o<t.length;o++)un(e,t[o],n,r);return e}if(t.nodeType)for(o=e.length;o--;)n.call(e[o],t);return e},sn=function(e,t){return e&&t&&-1!==(" "+e.className+" ").indexOf(" "+t+" ")},cn=function(e,t,n){var r,o;return t=hn(t)[0],e.each(function(){var e=this;n&&r===e.parentNode||(r=e.parentNode,o=t.cloneNode(!1),e.parentNode.insertBefore(o,e)),o.appendChild(e)}),e},ln=Gt.makeMap("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"," "),fn=Gt.makeMap("checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected"," "),dn={"for":"htmlFor","class":"className",readonly:"readOnly"},mn={"float":"cssFloat"},gn={},pn={},hn=function(e,t){return new hn.fn.init(e,t)},vn=/^\s*|\s*$/g,bn=function(e){return null===e||e===undefined?"":(""+e).replace(vn,"")},yn=function(e,t){var n,r,o,i;if(e)if((n=e.length)===undefined){for(r in e)if(e.hasOwnProperty(r)&&(i=e[r],!1===t.call(i,r,i)))break}else for(o=0;o<n&&(i=e[o],!1!==t.call(i,o,i));o++);return e},Cn=function(e,n){var r=[];return yn(e,function(e,t){n(t,e)&&r.push(t)}),r},xn=function(e){return e?9===e.nodeType?e:e.ownerDocument:Jt};hn.fn=hn.prototype={constructor:hn,selector:"",context:null,length:0,init:function(e,t){var n,r,o=this;if(!e)return o;if(e.nodeType)return o.context=o[0]=e,o.length=1,o;if(t&&t.nodeType)o.context=t;else{if(t)return hn(e).attr(t);o.context=t=H.document}if(on(e)){if(!(n="<"===(o.selector=e).charAt(0)&&">"===e.charAt(e.length-1)&&3<=e.length?[null,e,null]:en.exec(e)))return hn(t).find(e);if(n[1])for(r=an(e,xn(t)).firstChild;r;)Qt.call(o,r),r=r.nextSibling;else{if(!(r=xn(t).getElementById(n[2])))return o;if(r.id!==n[2])return o.find(e);o.length=1,o[0]=r}}else this.add(e,!1);return o},toArray:function(){return Gt.toArray(this)},add:function(e,t){var n,r,o=this;if(on(e))return o.add(hn(e));if(!1!==t)for(n=hn.unique(o.toArray().concat(hn.makeArray(e))),o.length=n.length,r=0;r<n.length;r++)o[r]=n[r];else Qt.apply(o,hn.makeArray(e));return o},attr:function(t,n){var e,r=this;if("object"==typeof t)yn(t,function(e,t){r.attr(e,t)});else{if(!rn(n)){if(r[0]&&1===r[0].nodeType){if((e=gn[t])&&e.get)return e.get(r[0],t);if(fn[t])return r.prop(t)?t:undefined;null===(n=r[0].getAttribute(t,2))&&(n=undefined)}return n}this.each(function(){var e;if(1===this.nodeType){if((e=gn[t])&&e.set)return void e.set(this,n);null===n?this.removeAttribute(t,2):this.setAttribute(t,n,2)}})}return r},removeAttr:function(e){return this.attr(e,null)},prop:function(e,t){var n=this;if("object"==typeof(e=dn[e]||e))yn(e,function(e,t){n.prop(e,t)});else{if(!rn(t))return n[0]&&n[0].nodeType&&e in n[0]?n[0][e]:t;this.each(function(){1===this.nodeType&&(this[e]=t)})}return n},css:function(n,r){var e,o,i=this,t=function(e){return e.replace(/-(\D)/g,function(e,t){return t.toUpperCase()})},a=function(e){return e.replace(/[A-Z]/g,function(e){return"-"+e})};if("object"==typeof n)yn(n,function(e,t){i.css(e,t)});else if(rn(r))n=t(n),"number"!=typeof r||ln[n]||(r=r.toString()+"px"),i.each(function(){var e=this.style;if((o=pn[n])&&o.set)o.set(this,r);else{try{this.style[mn[n]||n]=r}catch(t){}null!==r&&""!==r||(e.removeProperty?e.removeProperty(a(n)):e.removeAttribute(n))}});else{if(e=i[0],(o=pn[n])&&o.get)return o.get(e);if(!e.ownerDocument.defaultView)return e.currentStyle?e.currentStyle[t(n)]:"";try{return e.ownerDocument.defaultView.getComputedStyle(e,null).getPropertyValue(a(n))}catch(u){return undefined}}return i},remove:function(){for(var e,t=this.length;t--;)e=this[t],tn.clean(e),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var e,t=this.length;t--;)for(e=this[t];e.firstChild;)e.removeChild(e.firstChild);return this},html:function(e){var t,n=this;if(rn(e)){t=n.length;try{for(;t--;)n[t].innerHTML=e}catch(r){hn(n[t]).empty().append(e)}return n}return n[0]?n[0].innerHTML:""},text:function(e){var t,n=this;if(rn(e)){for(t=n.length;t--;)"innerText"in n[t]?n[t].innerText=e:n[0].textContent=e;return n}return n[0]?n[0].innerText||n[0].textContent:""},append:function(){return un(this,arguments,function(e){(1===this.nodeType||this.host&&1===this.host.nodeType)&&this.appendChild(e)})},prepend:function(){return un(this,arguments,function(e){(1===this.nodeType||this.host&&1===this.host.nodeType)&&this.insertBefore(e,this.firstChild)},!0)},before:function(){return this[0]&&this[0].parentNode?un(this,arguments,function(e){this.parentNode.insertBefore(e,this)}):this},after:function(){return this[0]&&this[0].parentNode?un(this,arguments,function(e){this.parentNode.insertBefore(e,this.nextSibling)},!0):this},appendTo:function(e){return hn(e).append(this),this},prependTo:function(e){return hn(e).prepend(this),this},replaceWith:function(e){return this.before(e).remove()},wrap:function(e){return cn(this,e)},wrapAll:function(e){return cn(this,e,!0)},wrapInner:function(e){return this.each(function(){hn(this).contents().wrapAll(e)}),this},unwrap:function(){return this.parent().each(function(){hn(this).replaceWith(this.childNodes)})},clone:function(){var e=[];return this.each(function(){e.push(this.cloneNode(!0))}),hn(e)},addClass:function(e){return this.toggleClass(e,!0)},removeClass:function(e){return this.toggleClass(e,!1)},toggleClass:function(o,i){var e=this;return"string"!=typeof o||(-1!==o.indexOf(" ")?yn(o.split(" "),function(){e.toggleClass(this,i)}):e.each(function(e,t){var n,r;(r=sn(t,o))!==i&&(n=t.className,r?t.className=bn((" "+n+" ").replace(" "+o+" "," ")):t.className+=n?" "+o:o)})),e},hasClass:function(e){return sn(this[0],e)},each:function(e){return yn(this,e)},on:function(e,t){return this.each(function(){tn.bind(this,e,t)})},off:function(e,t){return this.each(function(){tn.unbind(this,e,t)})},trigger:function(e){return this.each(function(){"object"==typeof e?tn.fire(this,e.type,e):tn.fire(this,e)})},show:function(){return this.css("display","")},hide:function(){return this.css("display","none")},slice:function(){return new hn(Zt.apply(this,arguments))},eq:function(e){return-1===e?this.slice(e):this.slice(e,+e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},find:function(e){var t,n,r=[];for(t=0,n=this.length;t<n;t++)hn.find(e,this[t],r);return hn(r)},filter:function(n){return hn("function"==typeof n?Cn(this.toArray(),function(e,t){return n(t,e)}):hn.filter(n,this.toArray()))},closest:function(n){var r=[];return n instanceof hn&&(n=n[0]),this.each(function(e,t){for(;t;){if("string"==typeof n&&hn(t).is(n)){r.push(t);break}if(t===n){r.push(t);break}t=t.parentNode}}),hn(r)},offset:function(e){var t,n,r,o,i=0,a=0;return e?this.css(e):((t=this[0])&&(r=(n=t.ownerDocument).documentElement,t.getBoundingClientRect&&(i=(o=t.getBoundingClientRect()).left+(r.scrollLeft||n.body.scrollLeft)-r.clientLeft,a=o.top+(r.scrollTop||n.body.scrollTop)-r.clientTop)),{left:i,top:a})},push:Qt,sort:[].sort,splice:[].splice},Gt.extend(hn,{extend:Gt.extend,makeArray:function(e){return(t=e)&&t===t.window||e.nodeType?[e]:Gt.toArray(e);var t},inArray:function(e,t){var n;if(t.indexOf)return t.indexOf(e);for(n=t.length;n--;)if(t[n]===e)return n;return-1},isArray:Gt.isArray,each:yn,trim:bn,grep:Cn,find:Tt,expr:Tt.selectors,unique:Tt.uniqueSort,text:Tt.getText,contains:Tt.contains,filter:function(e,t,n){var r=t.length;for(n&&(e=":not("+e+")");r--;)1!==t[r].nodeType&&t.splice(r,1);return t=1===t.length?hn.find.matchesSelector(t[0],e)?[t[0]]:[]:hn.find.matches(e,t)}});var wn=function(e,t,n){var r=[],o=e[t];for("string"!=typeof n&&n instanceof hn&&(n=n[0]);o&&9!==o.nodeType;){if(n!==undefined){if(o===n)break;if("string"==typeof n&&hn(o).is(n))break}1===o.nodeType&&r.push(o),o=o[t]}return r},Nn=function(e,t,n,r){var o=[];for(r instanceof hn&&(r=r[0]);e;e=e[t])if(!n||e.nodeType===n){if(r!==undefined){if(e===r)break;if("string"==typeof r&&hn(e).is(r))break}o.push(e)}return o},En=function(e,t,n){for(e=e[t];e;e=e[t])if(e.nodeType===n)return e;return null};yn({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return wn(e,"parentNode")},next:function(e){return En(e,"nextSibling",1)},prev:function(e){return En(e,"previousSibling",1)},children:function(e){return Nn(e.firstChild,"nextSibling",1)},contents:function(e){return Gt.toArray(("iframe"===e.nodeName?e.contentDocument||e.contentWindow.document:e).childNodes)}},function(e,r){hn.fn[e]=function(t){var n=[];return this.each(function(){var e=r.call(n,this,t,n);e&&(hn.isArray(e)?n.push.apply(n,e):n.push(e))}),1<this.length&&(nn[e]||(n=hn.unique(n)),0===e.indexOf("parents")&&(n=n.reverse())),n=hn(n),t?n.filter(t):n}}),yn({parentsUntil:function(e,t){return wn(e,"parentNode",t)},nextUntil:function(e,t){return Nn(e,"nextSibling",1,t).slice(1)},prevUntil:function(e,t){return Nn(e,"previousSibling",1,t).slice(1)}},function(r,o){hn.fn[r]=function(t,e){var n=[];return this.each(function(){var e=o.call(n,this,t,n);e&&(hn.isArray(e)?n.push.apply(n,e):n.push(e))}),1<this.length&&(n=hn.unique(n),0!==r.indexOf("parents")&&"prevUntil"!==r||(n=n.reverse())),n=hn(n),e?n.filter(e):n}}),hn.fn.is=function(e){return!!e&&0<this.filter(e).length},hn.fn.init.prototype=hn.fn,hn.overrideDefaults=function(n){var r,o=function(e,t){return r=r||n(),0===arguments.length&&(e=r.element),t||(t=r.context),new o.fn.init(e,t)};return hn.extend(o,this),o};var Sn=function(n,r,e){yn(e,function(e,t){n[e]=n[e]||{},n[e][r]=t})};me.ie&&me.ie<8&&(Sn(gn,"get",{maxlength:function(e){var t=e.maxLength;return 2147483647===t?undefined:t},size:function(e){var t=e.size;return 20===t?undefined:t},"class":function(e){return e.className},style:function(e){var t=e.style.cssText;return 0===t.length?undefined:t}}),Sn(gn,"set",{"class":function(e,t){e.className=t},style:function(e,t){e.style.cssText=t}})),me.ie&&me.ie<9&&(mn["float"]="styleFloat",Sn(pn,"set",{opacity:function(e,t){var n=e.style;null===t||""===t?n.removeAttribute("filter"):(n.zoom=1,n.filter="alpha(opacity="+100*t+")")}})),hn.attrHooks=gn,hn.cssHooks=pn;var kn,Tn,An,Rn=function(e,t){var n=function(e,t){for(var n=0;n<e.length;n++){var r=e[n];if(r.test(t))return r}return undefined}(e,t);if(!n)return{major:0,minor:0};var r=function(e){return Number(t.replace(n,"$"+e))};return Dn(r(1),r(2))},_n=function(){return Dn(0,0)},Dn=function(e,t){return{major:e,minor:t}},Bn={nu:Dn,detect:function(e,t){var n=String(t).toLowerCase();return 0===e.length?_n():Rn(e,n)},unknown:_n},On="Firefox",Pn=function(e,t){return function(){return t===e}},Ln=function(e){var t=e.current;return{current:t,version:e.version,isEdge:Pn("Edge",t),isChrome:Pn("Chrome",t),isIE:Pn("IE",t),isOpera:Pn("Opera",t),isFirefox:Pn(On,t),isSafari:Pn("Safari",t)}},In={unknown:function(){return Ln({current:undefined,version:Bn.unknown()})},nu:Ln,edge:q("Edge"),chrome:q("Chrome"),ie:q("IE"),opera:q("Opera"),firefox:q(On),safari:q("Safari")},Mn="Windows",Fn="Android",zn="Solaris",Un="FreeBSD",Vn=function(e,t){return function(){return t===e}},Hn=function(e){var t=e.current;return{current:t,version:e.version,isWindows:Vn(Mn,t),isiOS:Vn("iOS",t),isAndroid:Vn(Fn,t),isOSX:Vn("OSX",t),isLinux:Vn("Linux",t),isSolaris:Vn(zn,t),isFreeBSD:Vn(Un,t)}},jn={unknown:function(){return Hn({current:undefined,version:Bn.unknown()})},nu:Hn,windows:q(Mn),ios:q("iOS"),android:q(Fn),linux:q("Linux"),osx:q("OSX"),solaris:q(zn),freebsd:q(Un)},qn=function(e,t){var n=String(t).toLowerCase();return V(e,function(e){return e.search(n)})},$n=function(e,n){return qn(e,n).map(function(e){var t=Bn.detect(e.versionRegexes,n);return{current:e.name,version:t}})},Wn=function(e,n){return qn(e,n).map(function(e){var t=Bn.detect(e.versionRegexes,n);return{current:e.name,version:t}})},Kn=function(e,t){return-1!==e.indexOf(t)},Xn=function(e){return e.replace(/^\s+|\s+$/g,"")},Yn=function(e){return e.replace(/\s+$/g,"")},Gn=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,Jn=function(t){return function(e){return Kn(e,t)}},Qn=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:function(e){return Kn(e,"edge/")&&Kn(e,"chrome")&&Kn(e,"safari")&&Kn(e,"applewebkit")}},{name:"Chrome",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,Gn],search:function(e){return Kn(e,"chrome")&&!Kn(e,"chromeframe")}},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:function(e){return Kn(e,"msie")||Kn(e,"trident")}},{name:"Opera",versionRegexes:[Gn,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:Jn("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:Jn("firefox")},{name:"Safari",versionRegexes:[Gn,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:function(e){return(Kn(e,"safari")||Kn(e,"mobile/"))&&Kn(e,"applewebkit")}}],Zn=[{name:"Windows",search:Jn("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:function(e){return Kn(e,"iphone")||Kn(e,"ipad")},versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:Jn("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"OSX",search:Jn("os x"),versionRegexes:[/.*?os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:Jn("linux"),versionRegexes:[]},{name:"Solaris",search:Jn("sunos"),versionRegexes:[]},{name:"FreeBSD",search:Jn("freebsd"),versionRegexes:[]}],er={browsers:q(Qn),oses:q(Zn)},tr=function(e){var t,n,r,o,i,a,u,s,c,l,f,d=er.browsers(),m=er.oses(),g=$n(d,e).fold(In.unknown,In.nu),p=Wn(m,e).fold(jn.unknown,jn.nu);return{browser:g,os:p,deviceType:(n=g,r=e,o=(t=p).isiOS()&&!0===/ipad/i.test(r),i=t.isiOS()&&!o,a=t.isAndroid()&&3===t.version.major,u=t.isAndroid()&&4===t.version.major,s=o||a||u&&!0===/mobile/i.test(r),c=t.isiOS()||t.isAndroid(),l=c&&!s,f=n.isSafari()&&t.isiOS()&&!1===/safari/i.test(r),{isiPad:q(o),isiPhone:q(i),isTablet:q(s),isPhone:q(l),isTouch:q(c),isAndroid:t.isAndroid,isiOS:t.isiOS,isWebView:q(f)})}},nr={detect:(kn=function(){var e=H.navigator.userAgent;return tr(e)},An=!1,function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return An||(An=!0,Tn=kn.apply(null,e)),Tn})},rr=function(e){if(null===e||e===undefined)throw new Error("Node cannot be null or undefined");return{dom:q(e)}},or={fromHtml:function(e,t){var n=(t||H.document).createElement("div");if(n.innerHTML=e,!n.hasChildNodes()||1<n.childNodes.length)throw H.console.error("HTML does not have a single root node",e),new Error("HTML must have a single root node");return rr(n.childNodes[0])},fromTag:function(e,t){var n=(t||H.document).createElement(e);return rr(n)},fromText:function(e,t){var n=(t||H.document).createTextNode(e);return rr(n)},fromDom:rr,fromPoint:function(e,t,n){var r=e.dom();return A.from(r.elementFromPoint(t,n)).map(rr)}},ir=(H.Node.ATTRIBUTE_NODE,H.Node.CDATA_SECTION_NODE,H.Node.COMMENT_NODE,H.Node.DOCUMENT_NODE),ar=(H.Node.DOCUMENT_TYPE_NODE,H.Node.DOCUMENT_FRAGMENT_NODE,H.Node.ELEMENT_NODE),ur=H.Node.TEXT_NODE,sr=(H.Node.PROCESSING_INSTRUCTION_NODE,H.Node.ENTITY_REFERENCE_NODE,H.Node.ENTITY_NODE,H.Node.NOTATION_NODE,function(e){return e.dom().nodeName.toLowerCase()}),cr=function(t){return function(e){return e.dom().nodeType===t}},lr=cr(ar),fr=cr(ur),dr=Object.keys,mr=Object.hasOwnProperty,gr=function(e,t){for(var n=dr(e),r=0,o=n.length;r<o;r++){var i=n[r];t(e[i],i,e)}},pr=function(r,o){var i={};return gr(r,function(e,t){var n=o(e,t,r);i[n.k]=n.v}),i},hr=function(e){return e.style!==undefined},vr=function(e,t,n){if(!(R(n)||O(n)||L(n)))throw H.console.error("Invalid call to Attr.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")},br=function(e,t,n){vr(e.dom(),t,n)},yr=function(e,t){var n=e.dom();gr(t,function(e,t){vr(n,t,e)})},Cr=function(e,t){var n=e.dom().getAttribute(t);return null===n?undefined:n},xr=function(e,t){e.dom().removeAttribute(t)},wr=function(e,t){var n=e.dom();gr(t,function(e,t){!function(e,t,n){if(!R(n))throw H.console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);hr(e)&&e.style.setProperty(t,n)}(n,t,e)})},Nr=function(e,t){var n,r,o=e.dom(),i=H.window.getComputedStyle(o).getPropertyValue(t),a=""!==i||(r=fr(n=e)?n.dom().parentNode:n.dom())!==undefined&&null!==r&&r.ownerDocument.body.contains(r)?i:Er(o,t);return null===a?undefined:a},Er=function(e,t){return hr(e)?e.style.getPropertyValue(t):""},Sr=function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];return function(){for(var n=[],e=0;e<arguments.length;e++)n[e]=arguments[e];if(t.length!==n.length)throw new Error('Wrong number of arguments to struct. Expected "['+t.length+']", got '+n.length+" arguments");var r={};return F(t,function(e,t){r[e]=q(n[t])}),r}},kr=function(e,t){for(var n=[],r=function(e){return n.push(e),t(e)},o=t(e);(o=o.bind(r)).isSome(););return n},Tr=function(){return ae.getOrDie("Node")},Ar=function(e,t,n){return 0!=(e.compareDocumentPosition(t)&n)},Rr=function(e,t){return Ar(e,t,Tr().DOCUMENT_POSITION_CONTAINED_BY)},_r=ar,Dr=ir,Br=function(e,t){var n=e.dom();if(n.nodeType!==_r)return!1;if(n.matches!==undefined)return n.matches(t);if(n.msMatchesSelector!==undefined)return n.msMatchesSelector(t);if(n.webkitMatchesSelector!==undefined)return n.webkitMatchesSelector(t);if(n.mozMatchesSelector!==undefined)return n.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")},Or=function(e){return e.nodeType!==_r&&e.nodeType!==Dr||0===e.childElementCount},Pr=function(e,t){return e.dom()===t.dom()},Lr=nr.detect().browser.isIE()?function(e,t){return Rr(e.dom(),t.dom())}:function(e,t){var n=e.dom(),r=t.dom();return n!==r&&n.contains(r)},Ir=function(e){return or.fromDom(e.dom().ownerDocument)},Mr=function(e){var t=e.dom();return A.from(t.parentNode).map(or.fromDom)},Fr=function(e){var t=e.dom();return A.from(t.previousSibling).map(or.fromDom)},zr=function(e){var t=e.dom();return A.from(t.nextSibling).map(or.fromDom)},Ur=function(e){return t=kr(e,Fr),(n=Z.call(t,0)).reverse(),n;var t,n},Vr=function(e){return kr(e,zr)},Hr=function(e){var t=e.dom();return W(t.childNodes,or.fromDom)},jr=function(e,t){var n=e.dom().childNodes;return A.from(n[t]).map(or.fromDom)},qr=function(e){return jr(e,0)},$r=function(e){return jr(e,e.dom().childNodes.length-1)},Wr=(Sr("element","offset"),nr.detect().browser),Kr=function(e){return V(e,lr)},Xr={getPos:function(e,t,n){var r,o,i,a=0,u=0,s=e.ownerDocument;if(n=n||e,t){if(n===e&&t.getBoundingClientRect&&"static"===Nr(or.fromDom(e),"position"))return{x:a=(o=t.getBoundingClientRect()).left+(s.documentElement.scrollLeft||e.scrollLeft)-s.documentElement.clientLeft,y:u=o.top+(s.documentElement.scrollTop||e.scrollTop)-s.documentElement.clientTop};for(r=t;r&&r!==n&&r.nodeType;)a+=r.offsetLeft||0,u+=r.offsetTop||0,r=r.offsetParent;for(r=t.parentNode;r&&r!==n&&r.nodeType;)a-=r.scrollLeft||0,u-=r.scrollTop||0,r=r.parentNode;u+=(i=or.fromDom(t),Wr.isFirefox()&&"table"===sr(i)?Kr(Hr(i)).filter(function(e){return"caption"===sr(e)}).bind(function(o){return Kr(Vr(o)).map(function(e){var t=e.dom().offsetTop,n=o.dom().offsetTop,r=o.dom().offsetHeight;return t<=n?-r:0})}).getOr(0):0)}return{x:a,y:u}}},Yr=function(e){var n=A.none(),t=[],r=function(e){o()?a(e):t.push(e)},o=function(){return n.isSome()},i=function(e){F(e,a)},a=function(t){n.each(function(e){H.setTimeout(function(){t(e)},0)})};return e(function(e){n=A.some(e),i(t),t=[]}),{get:r,map:function(n){return Yr(function(t){r(function(e){t(n(e))})})},isReady:o}},Gr={nu:Yr,pure:function(t){return Yr(function(e){e(t)})}},Jr=function(t){var e=function(e){var r;t((r=e,function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=this;H.setTimeout(function(){r.apply(n,e)},0)}))},n=function(){return Gr.nu(e)};return{map:function(r){return Jr(function(n){e(function(e){var t=r(e);n(t)})})},bind:function(n){return Jr(function(t){e(function(e){n(e).get(t)})})},anonBind:function(n){return Jr(function(t){e(function(e){n.get(t)})})},toLazy:n,toCached:function(){var t=null;return Jr(function(e){null===t&&(t=n()),t.get(e)})},get:e}},Qr={nu:Jr,pure:function(t){return Jr(function(e){e(t)})}},Zr=function(a,e){return e(function(r){var o=[],i=0;0===a.length?r([]):F(a,function(e,t){var n;e.get((n=t,function(e){o[n]=e,++i>=a.length&&r(o)}))})})},eo=function(e){return Zr(e,Qr.nu)},to=function(n){return{is:function(e){return n===e},isValue:x,isError:C,getOr:q(n),getOrThunk:q(n),getOrDie:q(n),or:function(e){return to(n)},orThunk:function(e){return to(n)},fold:function(e,t){return t(n)},map:function(e){return to(e(n))},mapError:function(e){return to(n)},each:function(e){e(n)},bind:function(e){return e(n)},exists:function(e){return e(n)},forall:function(e){return e(n)},toOption:function(){return A.some(n)}}},no=function(n){return{is:C,isValue:C,isError:x,getOr:$,getOrThunk:function(e){return e()},getOrDie:function(){return e=String(n),function(){throw new Error(e)}();var e},or:function(e){return e},orThunk:function(e){return e()},fold:function(e,t){return e(n)},map:function(e){return no(n)},mapError:function(e){return no(e(n))},each:o,bind:function(e){return no(n)},exists:C,forall:x,toOption:A.none}},ro={value:to,error:no};function oo(e,u){var t=e,n=function(e,t,n,r){var o,i;if(e){if(!r&&e[t])return e[t];if(e!==u){if(o=e[n])return o;for(i=e.parentNode;i&&i!==u;i=i.parentNode)if(o=i[n])return o}}};this.current=function(){return t},this.next=function(e){return t=n(t,"firstChild","nextSibling",e)},this.prev=function(e){return t=n(t,"lastChild","previousSibling",e)},this.prev2=function(e){return t=function(e,t,n,r){var o,i,a;if(e){if(o=e[n],u&&o===u)return;if(o){if(!r)for(a=o[t];a;a=a[t])if(!a[t])return a;return o}if((i=e.parentNode)&&i!==u)return i}}(t,"lastChild","previousSibling",e)}}var io,ao,uo,so=function(t){var n;return function(e){return(n=n||function(e,t){for(var n={},r=0,o=e.length;r<o;r++){var i=e[r];n[String(i)]=t(i,r)}return n}(t,q(!0))).hasOwnProperty(sr(e))}},co=so(["h1","h2","h3","h4","h5","h6"]),lo=so(["article","aside","details","div","dt","figcaption","footer","form","fieldset","header","hgroup","html","main","nav","section","summary","body","p","dl","multicol","dd","figure","address","center","blockquote","h1","h2","h3","h4","h5","h6","listing","xmp","pre","plaintext","menu","dir","ul","ol","li","hr","table","tbody","thead","tfoot","th","tr","td","caption"]),fo=function(e){return lr(e)&&!lo(e)},mo=function(e){return lr(e)&&"br"===sr(e)},go=so(["h1","h2","h3","h4","h5","h6","p","div","address","pre","form","blockquote","center","dir","fieldset","header","footer","article","section","hgroup","aside","nav","figure"]),po=so(["ul","ol","dl"]),ho=so(["li","dd","dt"]),vo=so(["area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param","embed","source","wbr","track"]),bo=so(["thead","tbody","tfoot"]),yo=so(["td","th"]),Co=so(["pre","script","textarea","style"]),xo=function(t){return function(e){return!!e&&e.nodeType===t}},wo=xo(1),No=function(e){var r=e.toLowerCase().split(" ");return function(e){var t,n;if(e&&e.nodeType)for(n=e.nodeName.toLowerCase(),t=0;t<r.length;t++)if(n===r[t])return!0;return!1}},Eo=function(t){return function(e){if(wo(e)){if(e.contentEditable===t)return!0;if(e.getAttribute("data-mce-contenteditable")===t)return!0}return!1}},So=xo(3),ko=xo(8),To=xo(9),Ao=xo(11),Ro=No("br"),_o=Eo("true"),Do=Eo("false"),Bo={isText:So,isElement:wo,isComment:ko,isDocument:To,isDocumentFragment:Ao,isBr:Ro,isContentEditableTrue:_o,isContentEditableFalse:Do,matchNodeNames:No,hasPropValue:function(t,n){return function(e){return wo(e)&&e[t]===n}},hasAttribute:function(t,e){return function(e){return wo(e)&&e.hasAttribute(t)}},hasAttributeValue:function(t,n){return function(e){return wo(e)&&e.getAttribute(t)===n}},matchStyleValues:function(r,e){var o=e.toLowerCase().split(" ");return function(e){var t;if(wo(e))for(t=0;t<o.length;t++){var n=e.ownerDocument.defaultView.getComputedStyle(e,null);if((n?n.getPropertyValue(r):null)===o[t])return!0}return!1}},isBogus:function(e){return wo(e)&&e.hasAttribute("data-mce-bogus")},isBogusAll:function(e){return wo(e)&&"all"===e.getAttribute("data-mce-bogus")},isTable:function(e){return wo(e)&&"TABLE"===e.tagName}},Oo=function(e){return e&&"SPAN"===e.tagName&&"bookmark"===e.getAttribute("data-mce-type")},Po=function(e,t){var n,r=t.childNodes;if(!Bo.isElement(t)||!Oo(t)){for(n=r.length-1;0<=n;n--)Po(e,r[n]);if(!1===Bo.isDocument(t)){if(Bo.isText(t)&&0<t.nodeValue.length){var o=Gt.trim(t.nodeValue).length;if(e.isBlock(t.parentNode)||0<o)return;if(0===o&&(a=(i=t).previousSibling&&"SPAN"===i.previousSibling.nodeName,u=i.nextSibling&&"SPAN"===i.nextSibling.nodeName,a&&u))return}else if(Bo.isElement(t)&&(1===(r=t.childNodes).length&&Oo(r[0])&&t.parentNode.insertBefore(r[0],t),r.length||vo(or.fromDom(t))))return;e.remove(t)}var i,a,u;return t}},Lo={trimNode:Po},Io=Gt.makeMap,Mo=/[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,Fo=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,zo=/[<>&\"\']/g,Uo=/&#([a-z0-9]+);?|&([a-z0-9]+);/gi,Vo={128:"\u20ac",130:"\u201a",131:"\u0192",132:"\u201e",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02c6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017d",145:"\u2018",146:"\u2019",147:"\u201c",148:"\u201d",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02dc",153:"\u2122",154:"\u0161",155:"\u203a",156:"\u0153",158:"\u017e",159:"\u0178"};ao={'"':"&quot;","'":"&#39;","<":"&lt;",">":"&gt;","&":"&amp;","`":"&#96;"},uo={"&lt;":"<","&gt;":">","&amp;":"&","&quot;":'"',"&apos;":"'"};var Ho=function(e,t){var n,r,o,i={};if(e){for(e=e.split(","),t=t||10,n=0;n<e.length;n+=2)r=String.fromCharCode(parseInt(e[n],t)),ao[r]||(o="&"+e[n+1]+";",i[r]=o,i[o]=r);return i}};io=Ho("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);var jo=function(e,t){return e.replace(t?Mo:Fo,function(e){return ao[e]||e})},qo=function(e,t){return e.replace(t?Mo:Fo,function(e){return 1<e.length?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":ao[e]||"&#"+e.charCodeAt(0)+";"})},$o=function(e,t,n){return n=n||io,e.replace(t?Mo:Fo,function(e){return ao[e]||n[e]||e})},Wo={encodeRaw:jo,encodeAllRaw:function(e){return(""+e).replace(zo,function(e){return ao[e]||e})},encodeNumeric:qo,encodeNamed:$o,getEncodeFunc:function(e,t){var n=Ho(t)||io,r=Io(e.replace(/\+/g,","));return r.named&&r.numeric?function(e,t){return e.replace(t?Mo:Fo,function(e){return ao[e]!==undefined?ao[e]:n[e]!==undefined?n[e]:1<e.length?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":"&#"+e.charCodeAt(0)+";"})}:r.named?t?function(e,t){return $o(e,t,n)}:$o:r.numeric?qo:jo},decode:function(e){return e.replace(Uo,function(e,t){return t?65535<(t="x"===t.charAt(0).toLowerCase()?parseInt(t.substr(1),16):parseInt(t,10))?(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t))):Vo[t]||String.fromCharCode(t):uo[e]||io[e]||(n=e,(r=or.fromTag("div").dom()).innerHTML=n,r.textContent||r.innerText||n);var n,r})}},Ko={},Xo={},Yo=Gt.makeMap,Go=Gt.each,Jo=Gt.extend,Qo=Gt.explode,Zo=Gt.inArray,ei=function(e,t){return(e=Gt.trim(e))?e.split(t||" "):[]},ti=function(e){var u,t,n,r,o,i,s={},a=function(e,t,n){var r,o,i,a=function(e,t){var n,r,o={};for(n=0,r=e.length;n<r;n++)o[e[n]]=t||{};return o};for(t=t||"","string"==typeof(n=n||[])&&(n=ei(n)),r=(e=ei(e)).length;r--;)i={attributes:a(o=ei([u,t].join(" "))),attributesOrder:o,children:a(n,Xo)},s[e[r]]=i},c=function(e,t){var n,r,o,i;for(n=(e=ei(e)).length,t=ei(t);n--;)for(r=s[e[n]],o=0,i=t.length;o<i;o++)r.attributes[t[o]]={},r.attributesOrder.push(t[o])};return Ko[e]?Ko[e]:(u="id accesskey class dir lang style tabindex title role",t="address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul",n="a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd label map noscript object q s samp script select small span strong sub sup textarea u var #text #comment","html4"!==e&&(u+=" contenteditable contextmenu draggable dropzone hidden spellcheck translate",t+=" article aside details dialog figure main header footer hgroup section nav",n+=" audio canvas command datalist mark meter output picture progress time wbr video ruby bdi keygen"),"html5-strict"!==e&&(u+=" xml:lang",n=[n,i="acronym applet basefont big font strike tt"].join(" "),Go(ei(i),function(e){a(e,"",n)}),t=[t,o="center dir isindex noframes"].join(" "),r=[t,n].join(" "),Go(ei(o),function(e){a(e,"",r)})),r=r||[t,n].join(" "),a("html","manifest","head body"),a("head","","base command link meta noscript script style title"),a("title hr noscript br"),a("base","href target"),a("link","href rel media hreflang type sizes hreflang"),a("meta","name http-equiv content charset"),a("style","media type scoped"),a("script","src async defer type charset"),a("body","onafterprint onbeforeprint onbeforeunload onblur onerror onfocus onhashchange onload onmessage onoffline ononline onpagehide onpageshow onpopstate onresize onscroll onstorage onunload",r),a("address dt dd div caption","",r),a("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn","",n),a("blockquote","cite",r),a("ol","reversed start type","li"),a("ul","","li"),a("li","value",r),a("dl","","dt dd"),a("a","href target rel media hreflang type",n),a("q","cite",n),a("ins del","cite datetime",r),a("img","src sizes srcset alt usemap ismap width height"),a("iframe","src name width height",r),a("embed","src type width height"),a("object","data type typemustmatch name usemap form width height",[r,"param"].join(" ")),a("param","name value"),a("map","name",[r,"area"].join(" ")),a("area","alt coords shape href target rel media hreflang type"),a("table","border","caption colgroup thead tfoot tbody tr"+("html4"===e?" col":"")),a("colgroup","span","col"),a("col","span"),a("tbody thead tfoot","","tr"),a("tr","","td th"),a("td","colspan rowspan headers",r),a("th","colspan rowspan headers scope abbr",r),a("form","accept-charset action autocomplete enctype method name novalidate target",r),a("fieldset","disabled form name",[r,"legend"].join(" ")),a("label","form for",n),a("input","accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"),a("button","disabled form formaction formenctype formmethod formnovalidate formtarget name type value","html4"===e?r:n),a("select","disabled form multiple name required size","option optgroup"),a("optgroup","disabled label","option"),a("option","disabled label selected value"),a("textarea","cols dirname disabled form maxlength name readonly required rows wrap"),a("menu","type label",[r,"li"].join(" ")),a("noscript","",r),"html4"!==e&&(a("wbr"),a("ruby","",[n,"rt rp"].join(" ")),a("figcaption","",r),a("mark rt rp summary bdi","",n),a("canvas","width height",r),a("video","src crossorigin poster preload autoplay mediagroup loop muted controls width height buffered",[r,"track source"].join(" ")),a("audio","src crossorigin preload autoplay mediagroup loop muted controls buffered volume",[r,"track source"].join(" ")),a("picture","","img source"),a("source","src srcset type media sizes"),a("track","kind src srclang label default"),a("datalist","",[n,"option"].join(" ")),a("article section nav aside main header footer","",r),a("hgroup","","h1 h2 h3 h4 h5 h6"),a("figure","",[r,"figcaption"].join(" ")),a("time","datetime",n),a("dialog","open",r),a("command","type label icon disabled checked radiogroup command"),a("output","for form name",n),a("progress","value max",n),a("meter","value min max low high optimum",n),a("details","open",[r,"summary"].join(" ")),a("keygen","autofocus challenge disabled form keytype name")),"html5-strict"!==e&&(c("script","language xml:space"),c("style","xml:space"),c("object","declare classid code codebase codetype archive standby align border hspace vspace"),c("embed","align name hspace vspace"),c("param","valuetype type"),c("a","charset name rev shape coords"),c("br","clear"),c("applet","codebase archive code object alt name width height align hspace vspace"),c("img","name longdesc align border hspace vspace"),c("iframe","longdesc frameborder marginwidth marginheight scrolling align"),c("font basefont","size color face"),c("input","usemap align"),c("select","onchange"),c("textarea"),c("h1 h2 h3 h4 h5 h6 div p legend caption","align"),c("ul","type compact"),c("li","type"),c("ol dl menu dir","compact"),c("pre","width xml:space"),c("hr","align noshade size width"),c("isindex","prompt"),c("table","summary width frame rules cellspacing cellpadding align bgcolor"),c("col","width align char charoff valign"),c("colgroup","width align char charoff valign"),c("thead","align char charoff valign"),c("tr","align char charoff valign bgcolor"),c("th","axis align char charoff valign nowrap bgcolor width height"),c("form","accept"),c("td","abbr axis scope align char charoff valign nowrap bgcolor width height"),c("tfoot","align char charoff valign"),c("tbody","align char charoff valign"),c("area","nohref"),c("body","background bgcolor text link vlink alink")),"html4"!==e&&(c("input button select textarea","autofocus"),c("input textarea","placeholder"),c("a","download"),c("link script img","crossorigin"),c("iframe","sandbox seamless allowfullscreen")),Go(ei("a form meter progress dfn"),function(e){s[e]&&delete s[e].children[e]}),delete s.caption.children.table,delete s.script,Ko[e]=s)},ni=function(e,n){var r;return e&&(r={},"string"==typeof e&&(e={"*":e}),Go(e,function(e,t){r[t]=r[t.toUpperCase()]="map"===n?Yo(e,/[, ]/):Qo(e,/[, ]/)})),r};function ri(i){var e,t,n,r,o,a,u,s,c,l,f,d,m,N={},g={},E=[],p={},h={},v=function(e,t,n){var r=i[e];return r?r=Yo(r,/[, ]/,Yo(r.toUpperCase(),/[, ]/)):(r=Ko[e])||(r=Yo(t," ",Yo(t.toUpperCase()," ")),r=Jo(r,n),Ko[e]=r),r};n=ti((i=i||{}).schema),!1===i.verify_html&&(i.valid_elements="*[*]"),e=ni(i.valid_styles),t=ni(i.invalid_styles,"map"),s=ni(i.valid_classes,"map"),r=v("whitespace_elements","pre script noscript style textarea video audio iframe object code"),o=v("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr"),a=v("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr track"),u=v("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls"),l=v("non_empty_elements","td th iframe video audio object script pre code",a),f=v("move_caret_before_on_enter_elements","table",l),d=v("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside main nav figure"),c=v("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex option datalist select optgroup figcaption details summary",d),m=v("text_inline_elements","span strong b em i font strike u var cite dfn code mark q sup sub samp"),Go((i.special||"script noscript noframes noembed title style textarea xmp").split(" "),function(e){h[e]=new RegExp("</"+e+"[^>]*>","gi")});var S=function(e){return new RegExp("^"+e.replace(/([?+*])/g,".$1")+"$")},b=function(e){var t,n,r,o,i,a,u,s,c,l,f,d,m,g,p,h,v,b,y,C=/^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,x=/^([!\-])?(\w+[\\:]:\w+|[^=:<]+)?(?:([=:<])(.*))?$/,w=/[*?+]/;if(e)for(e=ei(e,","),N["@"]&&(h=N["@"].attributes,v=N["@"].attributesOrder),t=0,n=e.length;t<n;t++)if(i=C.exec(e[t])){if(g=i[1],c=i[2],p=i[3],s=i[5],a={attributes:d={},attributesOrder:m=[]},"#"===g&&(a.paddEmpty=!0),"-"===g&&(a.removeEmpty=!0),"!"===i[4]&&(a.removeEmptyAttrs=!0),h){for(b in h)d[b]=h[b];m.push.apply(m,v)}if(s)for(r=0,o=(s=ei(s,"|")).length;r<o;r++)if(i=x.exec(s[r])){if(u={},f=i[1],l=i[2].replace(/[\\:]:/g,":"),g=i[3],y=i[4],"!"===f&&(a.attributesRequired=a.attributesRequired||[],a.attributesRequired.push(l),u.required=!0),"-"===f){delete d[l],m.splice(Zo(m,l),1);continue}g&&("="===g&&(a.attributesDefault=a.attributesDefault||[],a.attributesDefault.push({name:l,value:y}),u.defaultValue=y),":"===g&&(a.attributesForced=a.attributesForced||[],a.attributesForced.push({name:l,value:y}),u.forcedValue=y),"<"===g&&(u.validValues=Yo(y,"?"))),w.test(l)?(a.attributePatterns=a.attributePatterns||[],u.pattern=S(l),a.attributePatterns.push(u)):(d[l]||m.push(l),d[l]=u)}h||"@"!==c||(h=d,v=m),p&&(a.outputName=c,N[p]=a),w.test(c)?(a.pattern=S(c),E.push(a)):N[c]=a}},y=function(e){N={},E=[],b(e),Go(n,function(e,t){g[t]=e.children})},C=function(e){var a=/^(~)?(.+)$/;e&&(Ko.text_block_elements=Ko.block_elements=null,Go(ei(e,","),function(e){var t=a.exec(e),n="~"===t[1],r=n?"span":"div",o=t[2];if(g[o]=g[r],p[o]=r,n||(c[o.toUpperCase()]={},c[o]={}),!N[o]){var i=N[r];delete(i=Jo({},i)).removeEmptyAttrs,delete i.removeEmpty,N[o]=i}Go(g,function(e,t){e[r]&&(g[t]=e=Jo({},g[t]),e[o]=e[r])})}))},x=function(e){var o=/^([+\-]?)(\w+)\[([^\]]+)\]$/;Ko[i.schema]=null,e&&Go(ei(e,","),function(e){var t,n,r=o.exec(e);r&&(n=r[1],t=n?g[r[2]]:g[r[2]]={"#comment":{}},t=g[r[2]],Go(ei(r[3],"|"),function(e){"-"===n?delete t[e]:t[e]={}}))})},w=function(e){var t,n=N[e];if(n)return n;for(t=E.length;t--;)if((n=E[t]).pattern.test(e))return n};return i.valid_elements?y(i.valid_elements):(Go(n,function(e,t){N[t]={attributes:e.attributes,attributesOrder:e.attributesOrder},g[t]=e.children}),"html5"!==i.schema&&Go(ei("strong/b em/i"),function(e){e=ei(e,"/"),N[e[1]].outputName=e[0]}),Go(ei("ol ul sub sup blockquote span font a table tbody tr strong em b i"),function(e){N[e]&&(N[e].removeEmpty=!0)}),Go(ei("p h1 h2 h3 h4 h5 h6 th td pre div address caption li"),function(e){N[e].paddEmpty=!0}),Go(ei("span"),function(e){N[e].removeEmptyAttrs=!0})),C(i.custom_elements),x(i.valid_children),b(i.extended_valid_elements),x("+ol[ul|ol],+ul[ul|ol]"),Go({dd:"dl",dt:"dl",li:"ul ol",td:"tr",th:"tr",tr:"tbody thead tfoot",tbody:"table",thead:"table",tfoot:"table",legend:"fieldset",area:"map",param:"video audio object"},function(e,t){N[t]&&(N[t].parentsRequired=ei(e))}),i.invalid_elements&&Go(Qo(i.invalid_elements),function(e){N[e]&&delete N[e]}),w("span")||b("span[!data-mce-type|*]"),{children:g,elements:N,getValidStyles:function(){return e},getValidClasses:function(){return s},getBlockElements:function(){return c},getInvalidStyles:function(){return t},getShortEndedElements:function(){return a},getTextBlockElements:function(){return d},getTextInlineElements:function(){return m},getBoolAttrs:function(){return u},getElementRule:w,getSelfClosingElements:function(){return o},getNonEmptyElements:function(){return l},getMoveCaretBeforeOnEnterElements:function(){return f},getWhiteSpaceElements:function(){return r},getSpecialElements:function(){return h},isValidChild:function(e,t){var n=g[e.toLowerCase()];return!(!n||!n[t.toLowerCase()])},isValid:function(e,t){var n,r,o=w(e);if(o){if(!t)return!0;if(o.attributes[t])return!0;if(n=o.attributePatterns)for(r=n.length;r--;)if(n[r].pattern.test(e))return!0}return!1},getCustomElements:function(){return p},addValidElements:b,setValidElements:y,addCustomElements:C,addValidChildren:x}}var oi=function(e,t,n,r){var o=function(e){return 1<(e=parseInt(e,10).toString(16)).length?e:"0"+e};return"#"+o(t)+o(n)+o(r)};function ii(y,e){var C,t,c,l,x=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,w=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,N=/\s*([^:]+):\s*([^;]+);?/g,E=/\s+$/,S={},k="\ufeff";for(y=y||{},e&&(c=e.getValidStyles(),l=e.getInvalidStyles()),t=("\\\" \\' \\; \\: ; : "+k).split(" "),C=0;C<t.length;C++)S[t[C]]=k+C,S[k+C]=t[C];return{toHex:function(e){return e.replace(x,oi)},parse:function(e){var t,n,r,o,i,a,u,s,c={},l=y.url_converter,f=y.url_converter_scope||this,d=function(e,t,n){var r,o,i,a;if((r=c[e+"-top"+t])&&(o=c[e+"-right"+t])&&(i=c[e+"-bottom"+t])&&(a=c[e+"-left"+t])){var u=[r,o,i,a];for(C=u.length-1;C--&&u[C]===u[C+1];);-1<C&&n||(c[e+t]=-1===C?u[0]:u.join(" "),delete c[e+"-top"+t],delete c[e+"-right"+t],delete c[e+"-bottom"+t],delete c[e+"-left"+t])}},m=function(e){var t,n=c[e];if(n){for(t=(n=n.split(" ")).length;t--;)if(n[t]!==n[0])return!1;return c[e]=n[0],!0}},g=function(e){return o=!0,S[e]},p=function(e,t){return o&&(e=e.replace(/\uFEFF[0-9]/g,function(e){return S[e]})),t||(e=e.replace(/\\([\'\";:])/g,"$1")),e},h=function(e){return String.fromCharCode(parseInt(e.slice(1),16))},v=function(e){return e.replace(/\\[0-9a-f]+/gi,h)},b=function(e,t,n,r,o,i){if(o=o||i)return"'"+(o=p(o)).replace(/\'/g,"\\'")+"'";if(t=p(t||n||r),!y.allow_script_urls){var a=t.replace(/[\s\r\n]+/g,"");if(/(java|vb)script:/i.test(a))return"";if(!y.allow_svg_data_urls&&/^data:image\/svg/i.test(a))return""}return l&&(t=l.call(f,t,"style")),"url('"+t.replace(/\'/g,"\\'")+"')"};if(e){for(e=(e=e.replace(/[\u0000-\u001F]/g,"")).replace(/\\[\"\';:\uFEFF]/g,g).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(e){return e.replace(/[;:]/g,g)});t=N.exec(e);)if(N.lastIndex=t.index+t[0].length,n=t[1].replace(E,"").toLowerCase(),r=t[2].replace(E,""),n&&r){if(n=v(n),r=v(r),-1!==n.indexOf(k)||-1!==n.indexOf('"'))continue;if(!y.allow_script_urls&&("behavior"===n||/expression\s*\(|\/\*|\*\//.test(r)))continue;"font-weight"===n&&"700"===r?r="bold":"color"!==n&&"background-color"!==n||(r=r.toLowerCase()),r=(r=r.replace(x,oi)).replace(w,b),c[n]=o?p(r,!0):r}d("border","",!0),d("border","-width"),d("border","-color"),d("border","-style"),d("padding",""),d("margin",""),i="border",u="border-style",s="border-color",m(a="border-width")&&m(u)&&m(s)&&(c[i]=c[a]+" "+c[u]+" "+c[s],delete c[a],delete c[u],delete c[s]),"medium none"===c.border&&delete c.border,"none"===c["border-image"]&&delete c["border-image"]}return c},serialize:function(i,e){var t,n,r,o,a,u="",s=function(e){var t,n,r,o;if(t=c[e])for(n=0,r=t.length;n<r;n++)e=t[n],(o=i[e])&&(u+=(0<u.length?" ":"")+e+": "+o+";")};if(e&&c)s("*"),s(e);else for(t in i)!(n=i[t])||l&&(r=t,o=e,a=void 0,(a=l["*"])&&a[r]||(a=l[o])&&a[r])||(u+=(0<u.length?" ":"")+t+": "+n+";");return u}}}var ai,ui=Gt.each,si=Gt.grep,ci=me.ie,li=/^([a-z0-9],?)+$/i,fi=/^[ \t\r\n]*$/,di=function(n,r,o){var e={},i=r.keep_values,t={set:function(e,t,n){r.url_converter&&(t=r.url_converter.call(r.url_converter_scope||o(),t,n,e[0])),e.attr("data-mce-"+n,t).attr(n,t)},get:function(e,t){return e.attr("data-mce-"+t)||e.attr(t)}};return e={style:{set:function(e,t){null===t||"object"!=typeof t?(i&&e.attr("data-mce-style",t),e.attr("style",t)):e.css(t)},get:function(e){var t=e.attr("data-mce-style")||e.attr("style");return t=n.serialize(n.parse(t),e[0].nodeName)}}},i&&(e.href=e.src=t),e},mi=function(e,t){var n=t.attr("style"),r=e.serialize(e.parse(n),t[0].nodeName);r||(r=null),t.attr("data-mce-style",r)},gi=function(e,t){var n,r,o=0;if(e)for(n=e.nodeType,e=e.previousSibling;e;e=e.previousSibling)r=e.nodeType,(!t||3!==r||r!==n&&e.nodeValue.length)&&(o++,n=r);return o};function pi(a,u){var s,c=this;void 0===u&&(u={});var r={},i=H.window,o={},t=0,e=function(m,g){void 0===g&&(g={});var p,h=0,v={};p=g.maxLoadTime||5e3;var b=function(e){m.getElementsByTagName("head")[0].appendChild(e)},n=function(e,t,n){var o,r,i,a,u=function(){for(var e=a.passed,t=e.length;t--;)e[t]();a.status=2,a.passed=[],a.failed=[]},s=function(){for(var e=a.failed,t=e.length;t--;)e[t]();a.status=3,a.passed=[],a.failed=[]},c=function(e,t){e()||((new Date).getTime()-i<p?be.setTimeout(t):s())},l=function(){c(function(){for(var e,t,n=m.styleSheets,r=n.length;r--;)if((t=(e=n[r]).ownerNode?e.ownerNode:e.owningElement)&&t.id===o.id)return u(),!0},l)},f=function(){c(function(){try{var e=r.sheet.cssRules;return u(),!!e}catch(t){}},f)};if(e=Gt._addCacheSuffix(e),v[e]?a=v[e]:(a={passed:[],failed:[]},v[e]=a),t&&a.passed.push(t),n&&a.failed.push(n),1!==a.status)if(2!==a.status)if(3!==a.status){if(a.status=1,(o=m.createElement("link")).rel="stylesheet",o.type="text/css",o.id="u"+h++,o.async=!1,o.defer=!1,i=(new Date).getTime(),g.contentCssCors&&(o.crossOrigin="anonymous"),"onload"in o&&!((d=H.navigator.userAgent.match(/WebKit\/(\d*)/))&&parseInt(d[1],10)<536))o.onload=l,o.onerror=s;else{if(0<H.navigator.userAgent.indexOf("Firefox"))return(r=m.createElement("style")).textContent='@import "'+e+'"',f(),void b(r);l()}var d;b(o),o.href=e}else s();else u()},t=function(t){return Qr.nu(function(e){n(t,j(e,q(ro.value(t))),j(e,q(ro.error(t))))})},o=function(e){return e.fold($,$)};return{load:n,loadAll:function(e,n,r){eo(W(e,t)).get(function(e){var t=K(e,function(e){return e.isValue()});0<t.fail.length?r(t.fail.map(o)):n(t.pass.map(o))})}}}(a,{contentCssCors:u.contentCssCors}),l=[],f=u.schema?u.schema:ri({}),d=ii({url_converter:u.url_converter,url_converter_scope:u.url_converter_scope},u.schema),m=u.ownEvents?new Te(u.proxy):Te.Event,n=f.getBlockElements(),g=hn.overrideDefaults(function(){return{context:a,element:V.getRoot()}}),p=function(e){if(e&&a&&"string"==typeof e){var t=a.getElementById(e);return t&&t.id!==e?a.getElementsByName(e)[1]:t}return e},h=function(e){return"string"==typeof e&&(e=p(e)),g(e)},v=function(e,t,n){var r,o,i=h(e);return i.length&&(o=(r=s[t])&&r.get?r.get(i,t):i.attr(t)),void 0===o&&(o=n||""),o},b=function(e){var t=p(e);return t?t.attributes:[]},y=function(e,t,n){var r,o;""===n&&(n=null);var i=h(e);r=i.attr(t),i.length&&((o=s[t])&&o.set?o.set(i,n,t):i.attr(t,n),r!==n&&u.onSetAttrib&&u.onSetAttrib({attrElm:i,attrName:t,attrValue:n}))},C=function(){return u.root_element||a.body},x=function(e,t){return Xr.getPos(a.body,p(e),t)},w=function(e,t,n){var r=h(e);return n?r.css(t):("float"===(t=t.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}))&&(t=me.ie&&me.ie<12?"styleFloat":"cssFloat"),r[0]&&r[0].style?r[0].style[t]:undefined)},N=function(e){var t,n;return e=p(e),t=w(e,"width"),n=w(e,"height"),-1===t.indexOf("px")&&(t=0),-1===n.indexOf("px")&&(n=0),{w:parseInt(t,10)||e.offsetWidth||e.clientWidth,h:parseInt(n,10)||e.offsetHeight||e.clientHeight}},E=function(e,t){var n;if(!e)return!1;if(!Array.isArray(e)){if("*"===t)return 1===e.nodeType;if(li.test(t)){var r=t.toLowerCase().split(/,/),o=e.nodeName.toLowerCase();for(n=r.length-1;0<=n;n--)if(r[n]===o)return!0;return!1}if(e.nodeType&&1!==e.nodeType)return!1}var i=Array.isArray(e)?e:[e];return 0<Tt(t,i[0].ownerDocument||i[0],null,i).length},S=function(e,t,n,r){var o,i=[],a=p(e);for(r=r===undefined,n=n||("BODY"!==C().nodeName?C().parentNode:null),Gt.is(t,"string")&&(t="*"===(o=t)?function(e){return 1===e.nodeType}:function(e){return E(e,o)});a&&a!==n&&a.nodeType&&9!==a.nodeType;){if(!t||"function"==typeof t&&t(a)){if(!r)return[a];i.push(a)}a=a.parentNode}return r?i:null},k=function(e,t,n){var r=t;if(e)for("string"==typeof t&&(r=function(e){return E(e,t)}),e=e[n];e;e=e[n])if("function"==typeof r&&r(e))return e;return null},T=function(e,n,r){var o,t="string"==typeof e?p(e):e;if(!t)return!1;if(Gt.isArray(t)&&(t.length||0===t.length))return o=[],ui(t,function(e,t){e&&("string"==typeof e&&(e=p(e)),o.push(n.call(r,e,t)))}),o;var i=r||c;return n.call(i,t)},A=function(e,t){h(e).each(function(e,n){ui(t,function(e,t){y(n,t,e)})})},R=function(e,r){var t=h(e);ci?t.each(function(e,t){if(!1!==t.canHaveHTML){for(;t.firstChild;)t.removeChild(t.firstChild);try{t.innerHTML="<br>"+r,t.removeChild(t.firstChild)}catch(n){hn("<div></div>").html("<br>"+r).contents().slice(1).appendTo(t)}return r}}):t.html(r)},_=function(e,n,r,o,i){return T(e,function(e){var t="string"==typeof n?a.createElement(n):n;return A(t,r),o&&("string"!=typeof o&&o.nodeType?t.appendChild(o):"string"==typeof o&&R(t,o)),i?t:e.appendChild(t)})},D=function(e,t,n){return _(a.createElement(e),e,t,n,!0)},B=Wo.decode,O=Wo.encodeAllRaw,P=function(e,t){var n=h(e);return t?n.each(function(){for(var e;e=this.firstChild;)3===e.nodeType&&0===e.data.length?this.removeChild(e):this.parentNode.insertBefore(e,this)}).remove():n.remove(),1<n.length?n.toArray():n[0]},L=function(e,t,n){h(e).toggleClass(t,n).each(function(){""===this.className&&hn(this).attr("class",null)})},I=function(t,e,n){return T(e,function(e){return Gt.is(e,"array")&&(t=t.cloneNode(!0)),n&&ui(si(e.childNodes),function(e){t.appendChild(e)}),e.parentNode.replaceChild(t,e)})},M=function(){return a.createRange()},F=function(e,t,n,r){if(Gt.isArray(e)){for(var o=e.length;o--;)e[o]=F(e[o],t,n,r);return e}return!u.collect||e!==a&&e!==i||l.push([e,t,n,r]),m.bind(e,t,n,r||V)},z=function(e,t,n){var r;if(Gt.isArray(e)){for(r=e.length;r--;)e[r]=z(e[r],t,n);return e}if(l&&(e===a||e===i))for(r=l.length;r--;){var o=l[r];e!==o[0]||t&&t!==o[1]||n&&n!==o[2]||m.unbind(o[0],o[1],o[2])}return m.unbind(e,t,n)},U=function(e){if(e&&Bo.isElement(e)){var t=e.getAttribute("data-mce-contenteditable");return t&&"inherit"!==t?t:"inherit"!==e.contentEditable?e.contentEditable:null}return null},V={doc:a,settings:u,win:i,files:o,stdMode:!0,boxModel:!0,styleSheetLoader:e,boundEvents:l,styles:d,schema:f,events:m,isBlock:function(e){if("string"==typeof e)return!!n[e];if(e){var t=e.nodeType;if(t)return!(1!==t||!n[e.nodeName])}return!1},$:g,$$:h,root:null,clone:function(t,e){if(!ci||1!==t.nodeType||e)return t.cloneNode(e);if(!e){var n=a.createElement(t.nodeName);return ui(b(t),function(e){y(n,e.nodeName,v(t,e.nodeName))}),n}return null},getRoot:C,getViewPort:function(e){var t=e||i,n=t.document.documentElement;return{x:t.pageXOffset||n.scrollLeft,y:t.pageYOffset||n.scrollTop,w:t.innerWidth||n.clientWidth,h:t.innerHeight||n.clientHeight}},getRect:function(e){var t,n;return e=p(e),t=x(e),n=N(e),{x:t.x,y:t.y,w:n.w,h:n.h}},getSize:N,getParent:function(e,t,n){var r=S(e,t,n,!1);return r&&0<r.length?r[0]:null},getParents:S,get:p,getNext:function(e,t){return k(e,t,"nextSibling")},getPrev:function(e,t){return k(e,t,"previousSibling")},select:function(e,t){return Tt(e,p(t)||u.root_element||a,[])},is:E,add:_,create:D,createHTML:function(e,t,n){var r,o="";for(r in o+="<"+e,t)t.hasOwnProperty(r)&&null!==t[r]&&"undefined"!=typeof t[r]&&(o+=" "+r+'="'+O(t[r])+'"');return void 0!==n?o+">"+n+"</"+e+">":o+" />"},createFragment:function(e){var t,n=a.createElement("div"),r=a.createDocumentFragment();for(e&&(n.innerHTML=e);t=n.firstChild;)r.appendChild(t);return r},remove:P,setStyle:function(e,t,n){var r=h(e).css(t,n);u.update_styles&&mi(d,r)},getStyle:w,setStyles:function(e,t){var n=h(e).css(t);u.update_styles&&mi(d,n)},removeAllAttribs:function(e){return T(e,function(e){var t,n=e.attributes;for(t=n.length-1;0<=t;t--)e.removeAttributeNode(n.item(t))})},setAttrib:y,setAttribs:A,getAttrib:v,getPos:x,parseStyle:function(e){return d.parse(e)},serializeStyle:function(e,t){return d.serialize(e,t)},addStyle:function(e){var t,n;if(V!==pi.DOM&&a===H.document){if(r[e])return;r[e]=!0}(n=a.getElementById("mceDefaultStyles"))||((n=a.createElement("style")).id="mceDefaultStyles",n.type="text/css",(t=a.getElementsByTagName("head")[0]).firstChild?t.insertBefore(n,t.firstChild):t.appendChild(n)),n.styleSheet?n.styleSheet.cssText+=e:n.appendChild(a.createTextNode(e))},loadCSS:function(e){var n;V===pi.DOM||a!==H.document?(e||(e=""),n=a.getElementsByTagName("head")[0],ui(e.split(","),function(e){var t;e=Gt._addCacheSuffix(e),o[e]||(o[e]=!0,t=D("link",{rel:"stylesheet",href:e}),n.appendChild(t))})):pi.DOM.loadCSS(e)},addClass:function(e,t){h(e).addClass(t)},removeClass:function(e,t){L(e,t,!1)},hasClass:function(e,t){return h(e).hasClass(t)},toggleClass:L,show:function(e){h(e).show()},hide:function(e){h(e).hide()},isHidden:function(e){return"none"===h(e).css("display")},uniqueId:function(e){return(e||"mce_")+t++},setHTML:R,getOuterHTML:function(e){var t="string"==typeof e?p(e):e;return Bo.isElement(t)?t.outerHTML:hn("<div></div>").append(hn(t).clone()).html()},setOuterHTML:function(e,t){h(e).each(function(){try{if("outerHTML"in this)return void(this.outerHTML=t)}catch(e){}P(hn(this).html(t),!0)})},decode:B,encode:O,insertAfter:function(e,t){var r=p(t);return T(e,function(e){var t,n;return t=r.parentNode,(n=r.nextSibling)?t.insertBefore(e,n):t.appendChild(e),e})},replace:I,rename:function(t,e){var n;return t.nodeName!==e.toUpperCase()&&(n=D(e),ui(b(t),function(e){y(n,e.nodeName,v(t,e.nodeName))}),I(n,t,!0)),n||t},findCommonAncestor:function(e,t){for(var n,r=e;r;){for(n=t;n&&r!==n;)n=n.parentNode;if(r===n)break;r=r.parentNode}return!r&&e.ownerDocument?e.ownerDocument.documentElement:r},toHex:function(e){return d.toHex(Gt.trim(e))},run:T,getAttribs:b,isEmpty:function(e,t){var n,r,o,i,a,u,s=0;if(e=e.firstChild){a=new oo(e,e.parentNode),t=t||(f?f.getNonEmptyElements():null),i=f?f.getWhiteSpaceElements():{};do{if(o=e.nodeType,Bo.isElement(e)){var c=e.getAttribute("data-mce-bogus");if(c){e=a.next("all"===c);continue}if(u=e.nodeName.toLowerCase(),t&&t[u]){if("br"===u){s++,e=a.next();continue}return!1}for(n=(r=b(e)).length;n--;)if("name"===(u=r[n].nodeName)||"data-mce-bookmark"===u)return!1}if(8===o)return!1;if(3===o&&!fi.test(e.nodeValue))return!1;if(3===o&&e.parentNode&&i[e.parentNode.nodeName]&&fi.test(e.nodeValue))return!1;e=a.next()}while(e)}return s<=1},createRng:M,nodeIndex:gi,split:function(e,t,n){var r,o,i,a=M();if(e&&t)return a.setStart(e.parentNode,gi(e)),a.setEnd(t.parentNode,gi(t)),r=a.extractContents(),(a=M()).setStart(t.parentNode,gi(t)+1),a.setEnd(e.parentNode,gi(e)+1),o=a.extractContents(),(i=e.parentNode).insertBefore(Lo.trimNode(V,r),e),n?i.insertBefore(n,e):i.insertBefore(t,e),i.insertBefore(Lo.trimNode(V,o),e),P(e),n||t},bind:F,unbind:z,fire:function(e,t,n){return m.fire(e,t,n)},getContentEditable:U,getContentEditableParent:function(e){for(var t=C(),n=null;e&&e!==t&&null===(n=U(e));e=e.parentNode);return n},destroy:function(){if(l)for(var e=l.length;e--;){var t=l[e];m.unbind(t[0],t[1],t[2])}Tt.setDocument&&Tt.setDocument()},isChildOf:function(e,t){for(;e;){if(t===e)return!0;e=e.parentNode}return!1},dumpRng:function(e){return"startContainer: "+e.startContainer.nodeName+", startOffset: "+e.startOffset+", endContainer: "+e.endContainer.nodeName+", endOffset: "+e.endOffset}};return s=di(d,u,function(){return V}),V}(ai=pi||(pi={})).DOM=ai(H.document),ai.nodeIndex=gi;var hi=pi,vi=hi.DOM,bi=Gt.each,yi=Gt.grep,Ci=function(e){return"function"==typeof e},xi=function(){var l={},o=[],i={},a=[],f=0;this.isDone=function(e){return 2===l[e]},this.markDone=function(e){l[e]=2},this.add=this.load=function(e,t,n,r){l[e]===undefined&&(o.push(e),l[e]=0),t&&(i[e]||(i[e]=[]),i[e].push({success:t,failure:r,scope:n||this}))},this.remove=function(e){delete l[e],delete i[e]},this.loadQueue=function(e,t,n){this.loadScripts(o,e,t,n)},this.loadScripts=function(n,e,t,r){var u,s=[],c=function(t,e){bi(i[e],function(e){Ci(e[t])&&e[t].call(e.scope)}),i[e]=undefined};a.push({success:e,failure:r,scope:t||this}),(u=function(){var e=yi(n);if(n.length=0,bi(e,function(e){var t,n,r,o,i,a;2!==l[e]?3!==l[e]?1!==l[e]&&(l[e]=1,f++,t=e,n=function(){l[e]=2,f--,c("success",e),u()},r=function(){l[e]=3,f--,s.push(e),c("failure",e),u()},i=(a=vi).uniqueId(),(o=H.document.createElement("script")).id=i,o.type="text/javascript",o.src=Gt._addCacheSuffix(t),o.onload=function(){a.remove(i),o&&(o.onreadystatechange=o.onload=o=null),n()},o.onerror=function(){Ci(r)?r():"undefined"!=typeof console&&console.log&&console.log("Failed to load script: "+t)},(H.document.getElementsByTagName("head")[0]||H.document.body).appendChild(o)):c("failure",e):c("success",e)}),!f){var t=a.slice(0);a.length=0,bi(t,function(e){0===s.length?Ci(e.success)&&e.success.call(e.scope):Ci(e.failure)&&e.failure.call(e.scope,s)})}})()}};xi.ScriptLoader=new xi;var wi,Ni=Gt.each;function Ei(){var r=this,o=[],a={},u={},i=[],s=function(e){var t;return u[e]&&(t=u[e].dependencies),t||[]},c=function(e,t){return"object"==typeof t?t:"string"==typeof e?{prefix:"",resource:t,suffix:""}:{prefix:e.prefix,resource:t,suffix:e.suffix}},l=function(e,n,t,r){var o=s(e);Ni(o,function(e){var t=c(n,e);f(t.resource,t,undefined,undefined)}),t&&(r?t.call(r):t.call(xi))},f=function(e,t,n,r,o){if(!a[e]){var i="string"==typeof t?t:t.prefix+t.resource+t.suffix;0!==i.indexOf("/")&&-1===i.indexOf("://")&&(i=Ei.baseURL+"/"+i),a[e]=i.substring(0,i.lastIndexOf("/")),u[e]?l(e,t,n,r):xi.ScriptLoader.add(i,function(){return l(e,t,n,r)},r,o)}};return{items:o,urls:a,lookup:u,_listeners:i,get:function(e){return u[e]?u[e].instance:undefined},dependencies:s,requireLangPack:function(e,t){var n=Ei.language;if(n&&!1!==Ei.languageLoad){if(t)if(-1!==(t=","+t+",").indexOf(","+n.substr(0,2)+","))n=n.substr(0,2);else if(-1===t.indexOf(","+n+","))return;xi.ScriptLoader.add(a[e]+"/langs/"+n+".js")}},add:function(t,e,n){o.push(e),u[t]={instance:e,dependencies:n};var r=K(i,function(e){return e.name===t});return i=r.fail,Ni(r.pass,function(e){e.callback()}),e},remove:function(e){delete a[e],delete u[e]},createUrl:c,addComponents:function(e,t){var n=r.urls[e];Ni(t,function(e){xi.ScriptLoader.add(n+"/"+e)})},load:f,waitFor:function(e,t){u.hasOwnProperty(e)?t():i.push({name:e,callback:t})}}}(wi=Ei||(Ei={})).PluginManager=wi(),wi.ThemeManager=wi();var Si=function(t,n){Mr(t).each(function(e){e.dom().insertBefore(n.dom(),t.dom())})},ki=function(e,t){zr(e).fold(function(){Mr(e).each(function(e){Ai(e,t)})},function(e){Si(e,t)})},Ti=function(t,n){qr(t).fold(function(){Ai(t,n)},function(e){t.dom().insertBefore(n.dom(),e.dom())})},Ai=function(e,t){e.dom().appendChild(t.dom())},Ri=function(t,e){F(e,function(e){Ai(t,e)})},_i=function(e){e.dom().textContent="",F(Hr(e),function(e){Di(e)})},Di=function(e){var t=e.dom();null!==t.parentNode&&t.parentNode.removeChild(t)},Bi=function(e){var t,n=Hr(e);0<n.length&&(t=e,F(n,function(e){Si(t,e)})),Di(e)},Oi=function(n,r){var o=null;return{cancel:function(){null!==o&&(H.clearTimeout(o),o=null)},throttle:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];null===o&&(o=H.setTimeout(function(){n.apply(null,e),o=null},r))}}},Pi=function(e){var t=e,n=function(){return t};return{get:n,set:function(e){t=e},clone:function(){return Pi(n())}}},Li=function(e,t){var n=Cr(e,t);return n===undefined||""===n?[]:n.split(" ")},Ii=function(e){return e.dom().classList!==undefined},Mi=function(e,t){return o=t,i=Li(n=e,r="class").concat([o]),br(n,r,i.join(" ")),!0;var n,r,o,i},Fi=function(e,t){return o=t,0<(i=z(Li(n=e,r="class"),function(e){return e!==o})).length?br(n,r,i.join(" ")):xr(n,r),!1;var n,r,o,i},zi=function(e,t){Ii(e)?e.dom().classList.add(t):Mi(e,t)},Ui=function(e){0===(Ii(e)?e.dom().classList:Li(e,"class")).length&&xr(e,"class")},Vi=function(e,t){return Ii(e)&&e.dom().classList.contains(t)},Hi=function(e,t){var n=[];return F(Hr(e),function(e){t(e)&&(n=n.concat([e])),n=n.concat(Hi(e,t))}),n},ji=function(e,t){return n=t,o=(r=e)===undefined?H.document:r.dom(),Or(o)?[]:W(o.querySelectorAll(n),or.fromDom);var n,r,o};function qi(e,t,n,r,o){return e(n,r)?A.some(n):P(o)&&o(n)?A.none():t(n,r,o)}var $i,Wi=function(e,t,n){for(var r=e.dom(),o=P(n)?n:q(!1);r.parentNode;){r=r.parentNode;var i=or.fromDom(r);if(t(i))return A.some(i);if(o(i))break}return A.none()},Ki=function(e,t,n){return qi(function(e){return t(e)},Wi,e,t,n)},Xi=function(e,t,n){return Wi(e,function(e){return Br(e,t)},n)},Yi=function(e,t){return n=t,o=(r=e)===undefined?H.document:r.dom(),Or(o)?A.none():A.from(o.querySelector(n)).map(or.fromDom);var n,r,o},Gi=function(e,t,n){return qi(Br,Xi,e,t,n)},Ji=q("mce-annotation"),Qi=q("data-mce-annotation"),Zi=q("data-mce-annotation-uid"),ea=function(r,e){var t=r.selection.getRng(),n=or.fromDom(t.startContainer),o=or.fromDom(r.getBody()),i=e.fold(function(){return"."+Ji()},function(e){return"["+Qi()+'="'+e+'"]'}),a=jr(n,t.startOffset).getOr(n),u=Gi(a,i,function(e){return Pr(e,o)}),s=function(e,t){return n=t,(r=e.dom())&&r.hasAttribute&&r.hasAttribute(n)?A.some(Cr(e,t)):A.none();var n,r};return u.bind(function(e){return s(e,""+Zi()).bind(function(n){return s(e,""+Qi()).map(function(e){var t=ta(r,n);return{uid:n,name:e,elements:t}})})})},ta=function(e,t){var n=or.fromDom(e.getBody());return ji(n,"["+Zi()+'="'+t+'"]')},na=function(i,e){var n,r,o,a=Pi({}),c=function(e,t){u(e,function(e){return t(e),e})},u=function(e,t){var n=a.get(),r=t(n.hasOwnProperty(e)?n[e]:{listeners:[],previous:Pi(A.none())});n[e]=r,a.set(n)},t=(n=function(){var e,t,n,r=a.get(),o=(e=dr(r),(n=Z.call(e,0)).sort(t),n);F(o,function(e){u(e,function(u){var s=u.previous.get();return ea(i,A.some(e)).fold(function(){var t;s.isSome()&&(c(t=e,function(e){F(e.listeners,function(e){return e(!1,t)})}),u.previous.set(A.none()))},function(e){var t,n,r,o=e.uid,i=e.name,a=e.elements;s.is(o)||(n=o,r=a,c(t=i,function(e){F(e.listeners,function(e){return e(!0,t,{uid:n,nodes:W(r,function(e){return e.dom()})})})}),u.previous.set(A.some(o)))}),{previous:u.previous,listeners:u.listeners}})})},r=30,o=null,{cancel:function(){null!==o&&(H.clearTimeout(o),o=null)},throttle:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];null!==o&&H.clearTimeout(o),o=H.setTimeout(function(){n.apply(null,e),o=null},r)}});return i.on("remove",function(){t.cancel()}),i.on("nodeChange",function(){t.throttle()}),{addListener:function(e,t){u(e,function(e){return{previous:e.previous,listeners:e.listeners.concat([t])}})}}},ra=function(e,n){e.on("init",function(){e.serializer.addNodeFilter("span",function(e){F(e,function(t){var e;(e=t,A.from(e.attributes.map[Qi()]).bind(n.lookup)).each(function(e){!1===e.persistent&&t.unwrap()})})})})},oa=0,ia=function(e,t){return or.fromDom(e.dom().cloneNode(t))},aa=function(e){return ia(e,!1)},ua=function(e){return ia(e,!0)},sa=function(e,t){var n,r,o=Ir(e).dom(),i=or.fromDom(o.createDocumentFragment()),a=(n=t,(r=(o||H.document).createElement("div")).innerHTML=n,Hr(or.fromDom(r)));Ri(i,a),_i(e),Ai(e,i)},ca="\ufeff",la=function(e){return e===ca},fa=ca,da=function(e){return e.replace(new RegExp(ca,"g"),"")},ma=Bo.isElement,ga=Bo.isText,pa=function(e){return ga(e)&&(e=e.parentNode),ma(e)&&e.hasAttribute("data-mce-caret")},ha=function(e){return ga(e)&&la(e.data)},va=function(e){return pa(e)||ha(e)},ba=function(e){return e.firstChild!==e.lastChild||!Bo.isBr(e.firstChild)},ya=function(e){var t=e.container();return!(!e||!Bo.isText(t))&&(t.data.charAt(e.offset())===fa||e.isAtStart()&&ha(t.previousSibling))},Ca=function(e){var t=e.container();return!(!e||!Bo.isText(t))&&(t.data.charAt(e.offset()-1)===fa||e.isAtEnd()&&ha(t.nextSibling))},xa=function(e,t,n){var r,o,i;return(r=t.ownerDocument.createElement(e)).setAttribute("data-mce-caret",n?"before":"after"),r.setAttribute("data-mce-bogus","all"),r.appendChild(((i=H.document.createElement("br")).setAttribute("data-mce-bogus","1"),i)),o=t.parentNode,n?o.insertBefore(r,t):t.nextSibling?o.insertBefore(r,t.nextSibling):o.appendChild(r),r},wa=function(e){return ga(e)&&e.data[0]===fa},Na=function(e){return ga(e)&&e.data[e.data.length-1]===fa},Ea=function(e){return e&&e.hasAttribute("data-mce-caret")?(t=e.getElementsByTagName("br"),n=t[t.length-1],Bo.isBogus(n)&&n.parentNode.removeChild(n),e.removeAttribute("data-mce-caret"),e.removeAttribute("data-mce-bogus"),e.removeAttribute("style"),e.removeAttribute("_moz_abspos"),e):null;var t,n},Sa=Bo.isContentEditableTrue,ka=Bo.isContentEditableFalse,Ta=Bo.isBr,Aa=Bo.isText,Ra=Bo.matchNodeNames("script style textarea"),_a=Bo.matchNodeNames("img input textarea hr iframe video audio object"),Da=Bo.matchNodeNames("table"),Ba=va,Oa=function(e){return!Ba(e)&&(Aa(e)?!Ra(e.parentNode):_a(e)||Ta(e)||Da(e)||Pa(e))},Pa=function(e){return!1===(t=e,Bo.isElement(t)&&"true"===t.getAttribute("unselectable"))&&ka(e);var t},La=function(e,t){return Oa(e)&&function(e,t){for(e=e.parentNode;e&&e!==t;e=e.parentNode){if(Pa(e))return!1;if(Sa(e))return!0}return!0}(e,t)},Ia=Math.round,Ma=function(e){return e?{left:Ia(e.left),top:Ia(e.top),bottom:Ia(e.bottom),right:Ia(e.right),width:Ia(e.width),height:Ia(e.height)}:{left:0,top:0,bottom:0,right:0,width:0,height:0}},Fa=function(e,t){return e=Ma(e),t||(e.left=e.left+e.width),e.right=e.left,e.width=0,e},za=function(e,t,n){return 0<=e&&e<=Math.min(t.height,n.height)/2},Ua=function(e,t){return e.bottom-e.height/2<t.top||!(e.top>t.bottom)&&za(t.top-e.bottom,e,t)},Va=function(e,t){return e.top>t.bottom||!(e.bottom<t.top)&&za(t.bottom-e.top,e,t)},Ha=function(e){var t=e.startContainer,n=e.startOffset;return t.hasChildNodes()&&e.endOffset===n+1?t.childNodes[n]:null},ja=function(e,t){return 1===e.nodeType&&e.hasChildNodes()&&(t>=e.childNodes.length&&(t=e.childNodes.length-1),e=e.childNodes[t]),e},qa=new RegExp("[\u0300-\u036f\u0483-\u0487\u0488-\u0489\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e3-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2-\u09e3\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62-\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c00\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0c81\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc-\u0ccd\u0cd5-\u0cd6\u0ce2-\u0ce3\u0d01\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62-\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1ab0-\u1abd\u1abe\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab-\u1bad\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1cf8-\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u200c-\u200d\u20d0-\u20dc\u20dd-\u20e0\u20e1\u20e2-\u20e4\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u302e-\u302f\u3099-\u309a\ua66f\ua670-\ua672\ua674-\ua67d\ua69e-\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\ua9e5\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaa7c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\uff9e-\uff9f]"),$a=function(e){return"string"==typeof e&&768<=e.charCodeAt(0)&&qa.test(e)},Wa=function(e,t){for(var n=[],r=0;r<e.length;r++){var o=e[r];if(!o.isSome())return A.none();n.push(o.getOrDie())}return A.some(t.apply(null,n))},Ka=[].slice,Xa=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=Ka.call(arguments);return function(e){for(var t=0;t<n.length;t++)if(!n[t](e))return!1;return!0}},Ya=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=Ka.call(arguments);return function(e){for(var t=0;t<n.length;t++)if(n[t](e))return!0;return!1}},Ga=Bo.isElement,Ja=Oa,Qa=Bo.matchStyleValues("display","block table"),Za=Bo.matchStyleValues("float","left right"),eu=Xa(Ga,Ja,y(Za)),tu=y(Bo.matchStyleValues("white-space","pre pre-line pre-wrap")),nu=Bo.isText,ru=Bo.isBr,ou=hi.nodeIndex,iu=ja,au=function(e){return"createRange"in e?e.createRange():hi.DOM.createRng()},uu=function(e){return e&&/[\r\n\t ]/.test(e)},su=function(e){return!!e.setStart&&!!e.setEnd},cu=function(e){var t,n=e.startContainer,r=e.startOffset;return!!(uu(e.toString())&&tu(n.parentNode)&&Bo.isText(n)&&(t=n.data,uu(t[r-1])||uu(t[r+1])))},lu=function(e){return 0===e.left&&0===e.right&&0===e.top&&0===e.bottom},fu=function(e){var t,n,r,o,i,a,u,s;return t=0<(n=e.getClientRects()).length?Ma(n[0]):Ma(e.getBoundingClientRect()),!su(e)&&ru(e)&&lu(t)?(i=(r=e).ownerDocument,a=au(i),u=i.createTextNode("\xa0"),(s=r.parentNode).insertBefore(u,r),a.setStart(u,0),a.setEnd(u,1),o=Ma(a.getBoundingClientRect()),s.removeChild(u),o):lu(t)&&su(e)?function(e){var t=e.startContainer,n=e.endContainer,r=e.startOffset,o=e.endOffset;if(t===n&&Bo.isText(n)&&0===r&&1===o){var i=e.cloneRange();return i.setEndAfter(n),fu(i)}return null}(e):t},du=function(e,t){var n=Fa(e,t);return n.width=1,n.right=n.left+1,n},mu=function(e){var t,n,r=[],o=function(e){var t,n;0!==e.height&&(0<r.length&&(t=e,n=r[r.length-1],t.left===n.left&&t.top===n.top&&t.bottom===n.bottom&&t.right===n.right)||r.push(e))},i=function(e,t){var n=au(e.ownerDocument);if(t<e.data.length){if($a(e.data[t]))return r;if($a(e.data[t-1])&&(n.setStart(e,t),n.setEnd(e,t+1),!cu(n)))return o(du(fu(n),!1)),r}0<t&&(n.setStart(e,t-1),n.setEnd(e,t),cu(n)||o(du(fu(n),!1))),t<e.data.length&&(n.setStart(e,t),n.setEnd(e,t+1),cu(n)||o(du(fu(n),!0)))};if(nu(e.container()))return i(e.container(),e.offset()),r;if(Ga(e.container()))if(e.isAtEnd())n=iu(e.container(),e.offset()),nu(n)&&i(n,n.data.length),eu(n)&&!ru(n)&&o(du(fu(n),!1));else{if(n=iu(e.container(),e.offset()),nu(n)&&i(n,0),eu(n)&&e.isAtEnd())return o(du(fu(n),!1)),r;t=iu(e.container(),e.offset()-1),eu(t)&&!ru(t)&&(Qa(t)||Qa(n)||!eu(n))&&o(du(fu(t),!1)),eu(n)&&o(du(fu(n),!0))}return r};function gu(t,n,e){var r=function(){return e||(e=mu(gu(t,n))),e};return{container:q(t),offset:q(n),toRange:function(){var e;return(e=au(t.ownerDocument)).setStart(t,n),e.setEnd(t,n),e},getClientRects:r,isVisible:function(){return 0<r().length},isAtStart:function(){return nu(t),0===n},isAtEnd:function(){return nu(t)?n>=t.data.length:n>=t.childNodes.length},isEqual:function(e){return e&&t===e.container()&&n===e.offset()},getNode:function(e){return iu(t,e?n-1:n)}}}($i=gu||(gu={})).fromRangeStart=function(e){return $i(e.startContainer,e.startOffset)},$i.fromRangeEnd=function(e){return $i(e.endContainer,e.endOffset)},$i.after=function(e){return $i(e.parentNode,ou(e)+1)},$i.before=function(e){return $i(e.parentNode,ou(e))},$i.isAbove=function(e,t){return Wa([te(t.getClientRects()),ne(e.getClientRects())],Ua).getOr(!1)},$i.isBelow=function(e,t){return Wa([ne(t.getClientRects()),te(e.getClientRects())],Va).getOr(!1)},$i.isAtStart=function(e){return!!e&&e.isAtStart()},$i.isAtEnd=function(e){return!!e&&e.isAtEnd()},$i.isTextPosition=function(e){return!!e&&Bo.isText(e.container())},$i.isElementPosition=function(e){return!1===$i.isTextPosition(e)};var pu,hu,vu=gu,bu=Bo.isText,yu=Bo.isBogus,Cu=hi.nodeIndex,xu=function(e){var t=e.parentNode;return yu(t)?xu(t):t},wu=function(e){return e?$t.reduce(e.childNodes,function(e,t){return yu(t)&&"BR"!==t.nodeName?e=e.concat(wu(t)):e.push(t),e},[]):[]},Nu=function(t){return function(e){return t===e}},Eu=function(e){var t,r,n,o;return(bu(e)?"text()":e.nodeName.toLowerCase())+"["+(r=wu(xu(t=e)),n=$t.findIndex(r,Nu(t),t),r=r.slice(0,n+1),o=$t.reduce(r,function(e,t,n){return bu(t)&&bu(r[n-1])&&e++,e},0),r=$t.filter(r,Bo.matchNodeNames(t.nodeName)),(n=$t.findIndex(r,Nu(t),t))-o)+"]"},Su=function(e,t){var n,r,o,i,a,u=[];return n=t.container(),r=t.offset(),bu(n)?o=function(e,t){for(;(e=e.previousSibling)&&bu(e);)t+=e.data.length;return t}(n,r):(r>=(i=n.childNodes).length?(o="after",r=i.length-1):o="before",n=i[r]),u.push(Eu(n)),a=function(e,t,n){var r=[];for(t=t.parentNode;!(t===e||n&&n(t));t=t.parentNode)r.push(t);return r}(e,n),a=$t.filter(a,y(Bo.isBogus)),(u=u.concat($t.map(a,function(e){return Eu(e)}))).reverse().join("/")+","+o},ku=function(e,t){var n,r,o;return t?(t=(n=t.split(","))[0].split("/"),o=1<n.length?n[1]:"before",(r=$t.reduce(t,function(e,t){return(t=/([\w\-\(\)]+)\[([0-9]+)\]/.exec(t))?("text()"===t[1]&&(t[1]="#text"),n=e,r=t[1],o=parseInt(t[2],10),i=wu(n),i=$t.filter(i,function(e,t){return!bu(e)||!bu(i[t-1])}),(i=$t.filter(i,Bo.matchNodeNames(r)))[o]):null;var n,r,o,i},e))?bu(r)?function(e,t){for(var n,r=e,o=0;bu(r);){if(n=r.data.length,o<=t&&t<=o+n){e=r,t-=o;break}if(!bu(r.nextSibling)){e=r,t=n;break}o+=n,r=r.nextSibling}return bu(e)&&t>e.data.length&&(t=e.data.length),vu(e,t)}(r,parseInt(o,10)):(o="after"===o?Cu(r)+1:Cu(r),vu(r.parentNode,o)):null):null},Tu=function(e,t){Bo.isText(t)&&0===t.data.length&&e.remove(t)},Au=function(e,t,n){var r,o,i,a,u,s,c;Bo.isDocumentFragment(n)?(i=e,a=t,u=n,s=A.from(u.firstChild),c=A.from(u.lastChild),a.insertNode(u),s.each(function(e){return Tu(i,e.previousSibling)}),c.each(function(e){return Tu(i,e.nextSibling)})):(r=e,o=n,t.insertNode(o),Tu(r,o.previousSibling),Tu(r,o.nextSibling))},Ru=Bo.isContentEditableFalse,_u=function(e,t,n,r,o){var i,a=r[o?"startContainer":"endContainer"],u=r[o?"startOffset":"endOffset"],s=[],c=0,l=e.getRoot();for(Bo.isText(a)?s.push(n?function(e,t,n){var r,o;for(o=e(t.data.slice(0,n)).length,r=t.previousSibling;r&&Bo.isText(r);r=r.previousSibling)o+=e(r.data).length;return o}(t,a,u):u):(u>=(i=a.childNodes).length&&i.length&&(c=1,u=Math.max(0,i.length-1)),s.push(e.nodeIndex(i[u],n)+c));a&&a!==l;a=a.parentNode)s.push(e.nodeIndex(a,n));return s},Du=function(e,t,n){var r=0;return Gt.each(e.select(t),function(e){if("all"!==e.getAttribute("data-mce-bogus"))return e!==n&&void r++}),r},Bu=function(e,t){var n,r,o,i=t?"start":"end";n=e[i+"Container"],r=e[i+"Offset"],Bo.isElement(n)&&"TR"===n.nodeName&&(n=(o=n.childNodes)[Math.min(t?r:r-1,o.length-1)])&&(r=t?0:n.childNodes.length,e["set"+(t?"Start":"End")](n,r))},Ou=function(e){return Bu(e,!0),Bu(e,!1),e},Pu=function(e,t){var n;if(Bo.isElement(e)&&(e=ja(e,t),Ru(e)))return e;if(va(e)){if(Bo.isText(e)&&pa(e)&&(e=e.parentNode),n=e.previousSibling,Ru(n))return n;if(n=e.nextSibling,Ru(n))return n}},Lu=function(e,t,n){var r=n.getNode(),o=r?r.nodeName:null,i=n.getRng();if(Ru(r)||"IMG"===o)return{name:o,index:Du(n.dom,o,r)};var a,u,s,c,l,f,d,m=Pu((a=i).startContainer,a.startOffset)||Pu(a.endContainer,a.endOffset);return m?{name:o=m.tagName,index:Du(n.dom,o,m)}:(u=e,c=t,l=i,f=(s=n).dom,(d={}).start=_u(f,u,c,l,!0),s.isCollapsed()||(d.end=_u(f,u,c,l,!1)),d)},Iu=function(e,t,n){var r={"data-mce-type":"bookmark",id:t,style:"overflow:hidden;line-height:0px"};return n?e.create("span",r,"&#xFEFF;"):e.create("span",r)},Mu=function(e,t){var n=e.dom,r=e.getRng(),o=n.uniqueId(),i=e.isCollapsed(),a=e.getNode(),u=a.nodeName;if("IMG"===u)return{name:u,index:Du(n,u,a)};var s=Ou(r.cloneRange());if(!i){s.collapse(!1);var c=Iu(n,o+"_end",t);Au(n,s,c)}(r=Ou(r)).collapse(!0);var l=Iu(n,o+"_start",t);return Au(n,r,l),e.moveToBookmark({id:o,keep:1}),{id:o}},Fu={getBookmark:function(e,t,n){return 2===t?Lu(da,n,e):3===t?(o=(r=e).getRng(),{start:Su(r.dom.getRoot(),vu.fromRangeStart(o)),end:Su(r.dom.getRoot(),vu.fromRangeEnd(o))}):t?{rng:e.getRng()}:Mu(e,!1);var r,o},getUndoBookmark:d(Lu,$,!0),getPersistentBookmark:Mu},zu="_mce_caret",Uu=function(e){return Bo.isElement(e)&&e.id===zu},Vu=function(e,t){for(;t&&t!==e;){if(t.id===zu)return t;t=t.parentNode}return null},Hu=Bo.isElement,ju=Bo.isText,qu=function(e){var t=e.parentNode;t&&t.removeChild(e)},$u=function(e,t){0===t.length?qu(e):e.nodeValue=t},Wu=function(e){var t=da(e);return{count:e.length-t.length,text:t}},Ku=function(e,t){return Gu(e),t},Xu=function(e,t){var n,r,o,i=t.container(),a=(n=re(i.childNodes),r=e,o=I(n,r),-1===o?A.none():A.some(o)).map(function(e){return e<t.offset()?vu(i,t.offset()-1):t}).getOr(t);return Gu(e),a},Yu=function(e,t){return ju(e)&&t.container()===e?(r=t,o=Wu((n=e).data.substr(0,r.offset())),i=Wu(n.data.substr(r.offset())),0<(a=o.text+i.text).length?($u(n,a),vu(n,r.offset()-o.count)):r):Ku(e,t);var n,r,o,i,a},Gu=function(e){if(Hu(e)&&va(e)&&(ba(e)?e.removeAttribute("data-mce-caret"):qu(e)),ju(e)){var t=da(function(e){try{return e.nodeValue}catch(t){return""}}(e));$u(e,t)}},Ju={removeAndReposition:function(e,t){return vu.isTextPosition(t)?Yu(e,t):(n=e,(r=t).container()===n.parentNode?Xu(n,r):Ku(n,r));var n,r},remove:Gu},Qu=nr.detect().browser,Zu=Bo.isContentEditableFalse,es=function(e,t,n){var r,o,i,a,u,s=Fa(t.getBoundingClientRect(),n);return"BODY"===e.tagName?(r=e.ownerDocument.documentElement,o=e.scrollLeft||r.scrollLeft,i=e.scrollTop||r.scrollTop):(u=e.getBoundingClientRect(),o=e.scrollLeft-u.left,i=e.scrollTop-u.top),s.left+=o,s.right+=o,s.top+=i,s.bottom+=i,s.width=1,0<(a=t.offsetWidth-t.clientWidth)&&(n&&(a*=-1),s.left+=a,s.right+=a),s},ts=function(a,u,e){var t,s,c=Pi(A.none()),l=function(){!function(e){var t,n,r,o,i;for(t=hn("*[contentEditable=false]",e),o=0;o<t.length;o++)r=(n=t[o]).previousSibling,Na(r)&&(1===(i=r.data).length?r.parentNode.removeChild(r):r.deleteData(i.length-1,1)),r=n.nextSibling,wa(r)&&(1===(i=r.data).length?r.parentNode.removeChild(r):r.deleteData(0,1))}(a),s&&(Ju.remove(s),s=null),c.get().each(function(e){hn(e.caret).remove(),c.set(A.none())}),clearInterval(t)},f=function(){t=be.setInterval(function(){e()?hn("div.mce-visual-caret",a).toggleClass("mce-visual-caret-hidden"):hn("div.mce-visual-caret",a).addClass("mce-visual-caret-hidden")},500)};return{show:function(t,e){var n,r,o;if(l(),o=e,Bo.isElement(o)&&/^(TD|TH)$/i.test(o.tagName))return null;if(!u(e))return s=function(e,t){var n,r,o;if(r=e.ownerDocument.createTextNode(fa),o=e.parentNode,t){if(n=e.previousSibling,ga(n)){if(va(n))return n;if(Na(n))return n.splitText(n.data.length-1)}o.insertBefore(r,e)}else{if(n=e.nextSibling,ga(n)){if(va(n))return n;if(wa(n))return n.splitText(1),n}e.nextSibling?o.insertBefore(r,e.nextSibling):o.appendChild(r)}return r}(e,t),r=e.ownerDocument.createRange(),Zu(s.nextSibling)?(r.setStart(s,0),r.setEnd(s,0)):(r.setStart(s,1),r.setEnd(s,1)),r;s=xa("p",e,t),n=es(a,e,t),hn(s).css("top",n.top);var i=hn('<div class="mce-visual-caret" data-mce-bogus="all"></div>').css(n).appendTo(a)[0];return c.set(A.some({caret:i,element:e,before:t})),c.get().each(function(e){t&&hn(e.caret).addClass("mce-visual-caret-before")}),f(),(r=e.ownerDocument.createRange()).setStart(s,0),r.setEnd(s,0),r},hide:l,getCss:function(){return".mce-visual-caret {position: absolute;background-color: black;background-color: currentcolor;}.mce-visual-caret-hidden {display: none;}*[data-mce-caret] {position: absolute;left: -1000px;right: auto;top: 0;margin: 0;padding: 0;}"},reposition:function(){c.get().each(function(e){var t=es(a,e.element,e.before);hn(e.caret).css(t)})},destroy:function(){return be.clearInterval(t)}}},ns=function(){return Qu.isIE()||Qu.isEdge()||Qu.isFirefox()},rs=function(e){return Zu(e)||Bo.isTable(e)&&ns()},os=Bo.isContentEditableFalse,is=Bo.matchStyleValues("display","block table table-cell table-caption list-item"),as=va,us=pa,ss=Bo.isElement,cs=Oa,ls=function(e){return 0<e},fs=function(e){return e<0},ds=function(e,t){for(var n;n=e(t);)if(!us(n))return n;return null},ms=function(e,t,n,r,o){var i=new oo(e,r);if(fs(t)){if((os(e)||us(e))&&n(e=ds(i.prev,!0)))return e;for(;e=ds(i.prev,o);)if(n(e))return e}if(ls(t)){if((os(e)||us(e))&&n(e=ds(i.next,!0)))return e;for(;e=ds(i.next,o);)if(n(e))return e}return null},gs=function(e,t){for(;e&&e!==t;){if(is(e))return e;e=e.parentNode}return null},ps=function(e,t,n){return gs(e.container(),n)===gs(t.container(),n)},hs=function(e,t){var n,r;return t?(n=t.container(),r=t.offset(),ss(n)?n.childNodes[r+e]:null):null},vs=function(e,t){var n=t.ownerDocument.createRange();return e?(n.setStartBefore(t),n.setEndBefore(t)):(n.setStartAfter(t),n.setEndAfter(t)),n},bs=function(e,t,n){var r,o,i,a;for(o=e?"previousSibling":"nextSibling";n&&n!==t;){if(r=n[o],as(r)&&(r=r[o]),os(r)){if(a=n,gs(r,i=t)===gs(a,i))return r;break}if(cs(r))break;n=n.parentNode}return null},ys=d(vs,!0),Cs=d(vs,!1),xs=function(e,t,n){var r,o,i,a,u=d(bs,!0,t),s=d(bs,!1,t);if(o=n.startContainer,i=n.startOffset,pa(o)){if(ss(o)||(o=o.parentNode),"before"===(a=o.getAttribute("data-mce-caret"))&&(r=o.nextSibling,rs(r)))return ys(r);if("after"===a&&(r=o.previousSibling,rs(r)))return Cs(r)}if(!n.collapsed)return n;if(Bo.isText(o)){if(as(o)){if(1===e){if(r=s(o))return ys(r);if(r=u(o))return Cs(r)}if(-1===e){if(r=u(o))return Cs(r);if(r=s(o))return ys(r)}return n}if(Na(o)&&i>=o.data.length-1)return 1===e&&(r=s(o))?ys(r):n;if(wa(o)&&i<=1)return-1===e&&(r=u(o))?Cs(r):n;if(i===o.data.length)return(r=s(o))?ys(r):n;if(0===i)return(r=u(o))?Cs(r):n}return n},ws=function(e,t){return A.from(hs(e?0:-1,t)).filter(os)},Ns=function(e,t,n){var r=xs(e,t,n);return-1===e?gu.fromRangeStart(r):gu.fromRangeEnd(r)},Es=function(e){return A.from(e.getNode()).map(or.fromDom)},Ss=function(e,t){for(;t=e(t);)if(t.isVisible())return t;return t},ks=function(e,t){var n=ps(e,t);return!(n||!Bo.isBr(e.getNode()))||n};(hu=pu||(pu={}))[hu.Backwards=-1]="Backwards",hu[hu.Forwards=1]="Forwards";var Ts,As,Rs,_s,Ds,Bs=Bo.isContentEditableFalse,Os=Bo.isText,Ps=Bo.isElement,Ls=Bo.isBr,Is=Oa,Ms=function(e){return _a(e)||!!Pa(t=e)&&!0!==U(re(t.getElementsByTagName("*")),function(e,t){return e||Sa(t)},!1);var t},Fs=La,zs=function(e,t){return e.hasChildNodes()&&t<e.childNodes.length?e.childNodes[t]:null},Us=function(e,t){if(ls(e)){if(Is(t.previousSibling)&&!Os(t.previousSibling))return vu.before(t);if(Os(t))return vu(t,0)}if(fs(e)){if(Is(t.nextSibling)&&!Os(t.nextSibling))return vu.after(t);if(Os(t))return vu(t,t.data.length)}return fs(e)?Ls(t)?vu.before(t):vu.after(t):vu.before(t)},Vs=function(e,t,n){var r,o,i,a,u;if(!Ps(n)||!t)return null;if(t.isEqual(vu.after(n))&&n.lastChild){if(u=vu.after(n.lastChild),fs(e)&&Is(n.lastChild)&&Ps(n.lastChild))return Ls(n.lastChild)?vu.before(n.lastChild):u}else u=t;var s,c,l,f=u.container(),d=u.offset();if(Os(f)){if(fs(e)&&0<d)return vu(f,--d);if(ls(e)&&d<f.length)return vu(f,++d);r=f}else{if(fs(e)&&0<d&&(o=zs(f,d-1),Is(o)))return!Ms(o)&&(i=ms(o,e,Fs,o))?Os(i)?vu(i,i.data.length):vu.after(i):Os(o)?vu(o,o.data.length):vu.before(o);if(ls(e)&&d<f.childNodes.length&&(o=zs(f,d),Is(o)))return Ls(o)?(s=n,(l=(c=o).nextSibling)&&Is(l)?Os(l)?vu(l,0):vu.before(l):Vs(pu.Forwards,vu.after(c),s)):!Ms(o)&&(i=ms(o,e,Fs,o))?Os(i)?vu(i,0):vu.before(i):Os(o)?vu(o,0):vu.after(o);r=o||u.getNode()}return(ls(e)&&u.isAtEnd()||fs(e)&&u.isAtStart())&&(r=ms(r,e,q(!0),n,!0),Fs(r,n))?Us(e,r):(o=ms(r,e,Fs,n),!(a=$t.last(z(function(e,t){for(var n=[];e&&e!==t;)n.push(e),e=e.parentNode;return n}(f,n),Bs)))||o&&a.contains(o)?o?Us(e,o):null:u=ls(e)?vu.after(a):vu.before(a))},Hs=function(t){return{next:function(e){return Vs(pu.Forwards,e,t)},prev:function(e){return Vs(pu.Backwards,e,t)}}},js=function(e){return vu.isTextPosition(e)?0===e.offset():Oa(e.getNode())},qs=function(e){if(vu.isTextPosition(e)){var t=e.container();return e.offset()===t.data.length}return Oa(e.getNode(!0))},$s=function(e,t){return!vu.isTextPosition(e)&&!vu.isTextPosition(t)&&e.getNode()===t.getNode(!0)},Ws=function(e,t,n){return e?!$s(t,n)&&(r=t,!(!vu.isTextPosition(r)&&Bo.isBr(r.getNode())))&&qs(t)&&js(n):!$s(n,t)&&js(t)&&qs(n);var r},Ks=function(e,t,n){var r=Hs(t);return A.from(e?r.next(n):r.prev(n))},Xs=function(t,n,r){return Ks(t,n,r).bind(function(e){return ps(r,e,n)&&Ws(t,r,e)?Ks(t,n,e):A.some(e)})},Ys=function(t,n,e,r){return Xs(t,n,e).bind(function(e){return r(e)?Ys(t,n,e,r):A.some(e)})},Gs=function(e,t){var n,r,o,i,a,u=e?t.firstChild:t.lastChild;return Bo.isText(u)?A.some(vu(u,e?0:u.data.length)):u?Oa(u)?A.some(e?vu.before(u):(a=u,Bo.isBr(a)?vu.before(a):vu.after(a))):(r=t,o=u,i=(n=e)?vu.before(o):vu.after(o),Ks(n,r,i)):A.none()},Js=d(Ks,!0),Qs=d(Ks,!1),Zs={fromPosition:Ks,nextPosition:Js,prevPosition:Qs,navigate:Xs,navigateIgnore:Ys,positionIn:Gs,firstPositionIn:d(Gs,!0),lastPositionIn:d(Gs,!1)},ec=function(e,t){return!e.isBlock(t)||t.innerHTML||me.ie||(t.innerHTML='<br data-mce-bogus="1" />'),t},tc=function(e,t){return Zs.lastPositionIn(e).fold(function(){return!1},function(e){return t.setStart(e.container(),e.offset()),t.setEnd(e.container(),e.offset()),!0})},nc=function(e,t,n){return!(!1!==t.hasChildNodes()||!Vu(e,t)||(o=n,i=(r=t).ownerDocument.createTextNode(fa),r.appendChild(i),o.setStart(i,0),o.setEnd(i,0),0));var r,o,i},rc=function(e,t,n,r){var o,i,a,u,s=n[t?"start":"end"],c=e.getRoot();if(s){for(a=s[0],i=c,o=s.length-1;1<=o;o--){if(u=i.childNodes,nc(c,i,r))return!0;if(s[o]>u.length-1)return!!nc(c,i,r)||tc(i,r);i=u[s[o]]}3===i.nodeType&&(a=Math.min(s[0],i.nodeValue.length)),1===i.nodeType&&(a=Math.min(s[0],i.childNodes.length)),t?r.setStart(i,a):r.setEnd(i,a)}return!0},oc=function(e){return Bo.isText(e)&&0<e.data.length},ic=function(e,t,n){var r,o,i,a,u,s,c=e.get(n.id+"_"+t),l=n.keep;if(c){if(r=c.parentNode,"start"===t?l?c.hasChildNodes()?(r=c.firstChild,o=1):oc(c.nextSibling)?(r=c.nextSibling,o=0):oc(c.previousSibling)?(r=c.previousSibling,o=c.previousSibling.data.length):(r=c.parentNode,o=e.nodeIndex(c)+1):o=e.nodeIndex(c):l?c.hasChildNodes()?(r=c.firstChild,o=1):oc(c.previousSibling)?(r=c.previousSibling,o=c.previousSibling.data.length):(r=c.parentNode,o=e.nodeIndex(c)):o=e.nodeIndex(c),u=r,s=o,!l){for(a=c.previousSibling,i=c.nextSibling,Gt.each(Gt.grep(c.childNodes),function(e){Bo.isText(e)&&(e.nodeValue=e.nodeValue.replace(/\uFEFF/g,""))});c=e.get(n.id+"_"+t);)e.remove(c,!0);a&&i&&a.nodeType===i.nodeType&&Bo.isText(a)&&!me.opera&&(o=a.nodeValue.length,a.appendData(i.nodeValue),e.remove(i),u=a,s=o)}return A.some(vu(u,s))}return A.none()},ac=function(e,t){var n,r,o,i,a,u,s,c,l,f,d,m,g,p,h,v,b=e.dom;if(t){if(v=t,Gt.isArray(v.start))return p=t,h=(g=b).createRng(),rc(g,!0,p,h)&&rc(g,!1,p,h)?A.some(h):A.none();if("string"==typeof t.start)return A.some((f=t,d=(l=b).createRng(),m=ku(l.getRoot(),f.start),d.setStart(m.container(),m.offset()),m=ku(l.getRoot(),f.end),d.setEnd(m.container(),m.offset()),d));if(t.hasOwnProperty("id"))return s=ic(o=b,"start",i=t),c=ic(o,"end",i),Wa([s,(a=c,u=s,a.isSome()?a:u)],function(e,t){var n=o.createRng();return n.setStart(ec(o,e.container()),e.offset()),n.setEnd(ec(o,t.container()),t.offset()),n});if(t.hasOwnProperty("name"))return n=b,r=t,A.from(n.select(r.name)[r.index]).map(function(e){var t=n.createRng();return t.selectNode(e),t});if(t.hasOwnProperty("rng"))return A.some(t.rng)}return A.none()},uc=function(e,t,n){return Fu.getBookmark(e,t,n)},sc=function(t,e){ac(t,e).each(function(e){t.setRng(e)})},cc=function(e){return Bo.isElement(e)&&"SPAN"===e.tagName&&"bookmark"===e.getAttribute("data-mce-type")},lc=function(e){return e&&/^(IMG)$/.test(e.nodeName)},fc=function(e){return e&&3===e.nodeType&&/^([\t \r\n]+|)$/.test(e.nodeValue)},dc=function(e,t,n){return"color"!==n&&"backgroundColor"!==n||(t=e.toHex(t)),"fontWeight"===n&&700===t&&(t="bold"),"fontFamily"===n&&(t=t.replace(/[\'\"]/g,"").replace(/,\s+/g,",")),""+t},mc={isInlineBlock:lc,moveStart:function(e,t,n){var r,o,i,a=n.startOffset,u=n.startContainer;if((n.startContainer!==n.endContainer||!lc(n.startContainer.childNodes[n.startOffset]))&&1===u.nodeType)for(a<(i=u.childNodes).length?r=new oo(u=i[a],e.getParent(u,e.isBlock)):(r=new oo(u=i[i.length-1],e.getParent(u,e.isBlock))).next(!0),o=r.current();o;o=r.next())if(3===o.nodeType&&!fc(o))return n.setStart(o,0),void t.setRng(n)},getNonWhiteSpaceSibling:function(e,t,n){if(e)for(t=t?"nextSibling":"previousSibling",e=n?e:e[t];e;e=e[t])if(1===e.nodeType||!fc(e))return e},isTextBlock:function(e,t){return t.nodeType&&(t=t.nodeName),!!e.schema.getTextBlockElements()[t.toLowerCase()]},isValid:function(e,t,n){return e.schema.isValidChild(t,n)},isWhiteSpaceNode:fc,replaceVars:function(e,n){return"string"!=typeof e?e=e(n):n&&(e=e.replace(/%(\w+)/g,function(e,t){return n[t]||e})),e},isEq:function(e,t){return t=t||"",e=""+((e=e||"").nodeName||e),t=""+(t.nodeName||t),e.toLowerCase()===t.toLowerCase()},normalizeStyleValue:dc,getStyle:function(e,t,n){return dc(e,e.getStyle(t,n),n)},getTextDecoration:function(t,e){var n;return t.getParent(e,function(e){return(n=t.getStyle(e,"text-decoration"))&&"none"!==n}),n},getParents:function(e,t,n){return e.getParents(t,n,e.getRoot())}},gc=cc,pc=mc.getParents,hc=mc.isWhiteSpaceNode,vc=mc.isTextBlock,bc=function(e,t){for(void 0===t&&(t=3===e.nodeType?e.length:e.childNodes.length);e&&e.hasChildNodes();)(e=e.childNodes[t])&&(t=3===e.nodeType?e.length:e.childNodes.length);return{node:e,offset:t}},yc=function(e,t){for(var n=t;n;){if(1===n.nodeType&&e.getContentEditable(n))return"false"===e.getContentEditable(n)?n:t;n=n.parentNode}return t},Cc=function(e,t,n,r){var o,i,a=n.nodeValue;return void 0===r&&(r=e?a.length:0),e?(o=a.lastIndexOf(" ",r),-1!==(o=(i=a.lastIndexOf("\xa0",r))<o?o:i)&&!t&&(o<r||!e)&&o<=a.length&&o++):(o=a.indexOf(" ",r),i=a.indexOf("\xa0",r),o=-1!==o&&(-1===i||o<i)?o:i),o},xc=function(e,t,n,r,o,i){var a,u,s,c;if(3===n.nodeType){if(-1!==(s=Cc(o,i,n,r)))return{container:n,offset:s};c=n}for(a=new oo(n,e.getParent(n,e.isBlock)||t);u=a[o?"prev":"next"]();)if(3!==u.nodeType||gc(u.parentNode)){if(e.isBlock(u)||mc.isEq(u,"BR"))break}else if(-1!==(s=Cc(o,i,c=u)))return{container:u,offset:s};if(c)return{container:c,offset:r=o?0:c.length}},wc=function(e,t,n,r,o){var i,a,u,s;for(3===r.nodeType&&0===r.nodeValue.length&&r[o]&&(r=r[o]),i=pc(e,r),a=0;a<i.length;a++)for(u=0;u<t.length;u++)if(!("collapsed"in(s=t[u])&&s.collapsed!==n.collapsed)&&e.is(i[a],s.selector))return i[a];return r},Nc=function(t,e,n,r){var o,i=t.dom,a=i.getRoot();if(e[0].wrapper||(o=i.getParent(n,e[0].block,a)),!o){var u=i.getParent(n,"LI,TD,TH");o=i.getParent(3===n.nodeType?n.parentNode:n,function(e){return e!==a&&vc(t,e)},u)}if(o&&e[0].wrapper&&(o=pc(i,o,"ul,ol").reverse()[0]||o),!o)for(o=n;o[r]&&!i.isBlock(o[r])&&(o=o[r],!mc.isEq(o,"br")););return o||n},Ec=function(e,t,n,r,o,i,a){var u,s,c,l,f,d;if(u=s=a?n:o,l=a?"previousSibling":"nextSibling",f=e.getRoot(),3===u.nodeType&&!hc(u)&&(a?0<r:i<u.nodeValue.length))return u;for(;;){if(!t[0].block_expand&&e.isBlock(s))return s;for(c=s[l];c;c=c[l])if(!gc(c)&&!hc(c)&&("BR"!==(d=c).nodeName||!d.getAttribute("data-mce-bogus")||d.nextSibling))return s;if(s===f||s.parentNode===f){u=s;break}s=s.parentNode}return u},Sc=function(e,t,n,r){var o,i=t.startContainer,a=t.startOffset,u=t.endContainer,s=t.endOffset,c=e.dom;return 1===i.nodeType&&i.hasChildNodes()&&3===(i=ja(i,a)).nodeType&&(a=0),1===u.nodeType&&u.hasChildNodes()&&3===(u=ja(u,t.collapsed?s:s-1)).nodeType&&(s=u.nodeValue.length),i=yc(c,i),u=yc(c,u),(gc(i.parentNode)||gc(i))&&(i=gc(i)?i:i.parentNode,3===(i=t.collapsed?i.previousSibling||i:i.nextSibling||i).nodeType&&(a=t.collapsed?i.length:0)),(gc(u.parentNode)||gc(u))&&(u=gc(u)?u:u.parentNode,3===(u=t.collapsed?u.nextSibling||u:u.previousSibling||u).nodeType&&(s=t.collapsed?0:u.length)),t.collapsed&&((o=xc(c,e.getBody(),i,a,!0,r))&&(i=o.container,a=o.offset),(o=xc(c,e.getBody(),u,s,!1,r))&&(u=o.container,s=o.offset)),n[0].inline&&(u=r?u:function(e,t){var n=bc(e,t);if(n.node){for(;n.node&&0===n.offset&&n.node.previousSibling;)n=bc(n.node.previousSibling);n.node&&0<n.offset&&3===n.node.nodeType&&" "===n.node.nodeValue.charAt(n.offset-1)&&1<n.offset&&(e=n.node).splitText(n.offset-1)}return e}(u,s)),(n[0].inline||n[0].block_expand)&&(n[0].inline&&3===i.nodeType&&0!==a||(i=Ec(c,n,i,a,u,s,!0)),n[0].inline&&3===u.nodeType&&s!==u.nodeValue.length||(u=Ec(c,n,i,a,u,s,!1))),n[0].selector&&!1!==n[0].expand&&!n[0].inline&&(i=wc(c,n,t,i,"previousSibling"),u=wc(c,n,t,u,"nextSibling")),(n[0].block||n[0].selector)&&(i=Nc(e,n,i,"previousSibling"),u=Nc(e,n,u,"nextSibling"),n[0].block&&(c.isBlock(i)||(i=Ec(c,n,i,a,u,s,!0)),c.isBlock(u)||(u=Ec(c,n,i,a,u,s,!1)))),1===i.nodeType&&(a=c.nodeIndex(i),i=i.parentNode),1===u.nodeType&&(s=c.nodeIndex(u)+1,u=u.parentNode),{startContainer:i,startOffset:a,endContainer:u,endOffset:s}},kc=Gt.each,Tc=function(e,t,o){var n,r,i,a,u,s,c,l=t.startContainer,f=t.startOffset,d=t.endContainer,m=t.endOffset;if(0<(c=e.select("td[data-mce-selected],th[data-mce-selected]")).length)kc(c,function(e){o([e])});else{var g,p,h,v=function(e){var t;return 3===(t=e[0]).nodeType&&t===l&&f>=t.nodeValue.length&&e.splice(0,1),t=e[e.length-1],0===m&&0<e.length&&t===d&&3===t.nodeType&&e.splice(e.length-1,1),e},b=function(e,t,n){for(var r=[];e&&e!==n;e=e[t])r.push(e);return r},y=function(e,t){do{if(e.parentNode===t)return e;e=e.parentNode}while(e)},C=function(e,t,n){var r=n?"nextSibling":"previousSibling";for(u=(a=e).parentNode;a&&a!==t;a=u)u=a.parentNode,(s=b(a===e?a:a[r],r)).length&&(n||s.reverse(),o(v(s)))};if(1===l.nodeType&&l.hasChildNodes()&&(l=l.childNodes[f]),1===d.nodeType&&d.hasChildNodes()&&(p=m,h=(g=d).childNodes,--p>h.length-1?p=h.length-1:p<0&&(p=0),d=h[p]||g),l===d)return o(v([l]));for(n=e.findCommonAncestor(l,d),a=l;a;a=a.parentNode){if(a===d)return C(l,n,!0);if(a===n)break}for(a=d;a;a=a.parentNode){if(a===l)return C(d,n);if(a===n)break}r=y(l,n)||l,i=y(d,n)||d,C(l,r,!0),(s=b(r===l?r:r.nextSibling,"nextSibling",i===d?i.nextSibling:i)).length&&o(v(s)),C(d,i)}},Ac=(Ts=fr,As="text",Rs=function(e){return Ts(e)?A.from(e.dom().nodeValue):A.none()},_s=nr.detect().browser,{get:function(e){if(!Ts(e))throw new Error("Can only get "+As+" value of a "+As+" node");return Ds(e).getOr("")},getOption:Ds=_s.isIE()&&10===_s.version.major?function(e){try{return Rs(e)}catch(jN){return A.none()}}:Rs,set:function(e,t){if(!Ts(e))throw new Error("Can only set raw "+As+" value of a "+As+" node");e.dom().nodeValue=t}}),Rc=function(e){return Ac.get(e)},_c=function(r,o,i,a){return Mr(o).fold(function(){return"skipping"},function(e){return"br"===a||fr(n=o)&&"\ufeff"===Rc(n)?"valid":lr(t=o)&&Vi(t,Ji())?"existing":Uu(o)?"caret":mc.isValid(r,i,a)&&mc.isValid(r,sr(e),i)?"valid":"invalid-child";var t,n})},Dc=function(e,t,n,r){var o,i,a=t.uid,u=void 0===a?(o="mce-annotation",i=(new Date).getTime(),o+"_"+Math.floor(1e9*Math.random())+ ++oa+String(i)):a,s=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n}(t,["uid"]),c=or.fromTag("span",e);zi(c,Ji()),br(c,""+Zi(),u),br(c,""+Qi(),n);var l,f=r(u,s),d=f.attributes,m=void 0===d?{}:d,g=f.classes,p=void 0===g?[]:g;return yr(c,m),l=c,F(p,function(e){zi(l,e)}),c},Bc=function(i,e,t,n,r){var a=[],u=Dc(i.getDoc(),r,t,n),s=Pi(A.none()),c=function(){s.set(A.none())},l=function(e){F(e,o)},o=function(e){var t,n;switch(_c(i,e,"span",sr(e))){case"invalid-child":c();var r=Hr(e);l(r),c();break;case"valid":var o=s.get().getOrThunk(function(){var e=aa(u);return a.push(e),s.set(A.some(e)),e});Si(t=e,n=o),Ai(n,t)}};return Tc(i.dom,e,function(e){var t;c(),t=W(e,or.fromDom),l(t)}),a},Oc=function(s,c,l,f){s.undoManager.transact(function(){var e,t,n,r,o=s.selection.getRng();if(o.collapsed&&(r=Sc(e=s,t=o,[{inline:!0}],3===(n=t).startContainer.nodeType&&n.startContainer.nodeValue.length>=n.startOffset&&"\xa0"===n.startContainer.nodeValue[n.startOffset]),t.setStart(r.startContainer,r.startOffset),t.setEnd(r.endContainer,r.endOffset),e.selection.setRng(t)),s.selection.getRng().collapsed){var i=Dc(s.getDoc(),f,c,l.decorate);sa(i,"\xa0"),s.selection.getRng().insertNode(i.dom()),s.selection.select(i.dom())}else{var a=Fu.getPersistentBookmark(s.selection,!1),u=s.selection.getRng();Bc(s,u,c,l.decorate,f),s.selection.moveToBookmark(a)}})};function Pc(s){var n,r=(n={},{register:function(e,t){n[e]={name:e,settings:t}},lookup:function(e){return n.hasOwnProperty(e)?A.from(n[e]).map(function(e){return e.settings}):A.none()}});ra(s,r);var o=na(s);return{register:function(e,t){r.register(e,t)},annotate:function(t,n){r.lookup(t).each(function(e){Oc(s,t,e,n)})},annotationChanged:function(e,t){o.addListener(e,t)},remove:function(e){ea(s,A.some(e)).each(function(e){var t=e.elements;F(t,Bi)})},getAll:function(e){var t,n,r,o,i,a,u=(t=s,n=e,r=or.fromDom(t.getBody()),o=ji(r,"["+Qi()+'="'+n+'"]'),i={},F(o,function(e){var t=Cr(e,Zi()),n=i.hasOwnProperty(t)?i[t]:[];i[t]=n.concat([e])}),i);return a=function(e){return W(e,function(e){return e.dom()})},pr(u,function(e,t,n){return{k:t,v:a(e,t,n)}})}}}var Lc=function(e){return Gt.grep(e.childNodes,function(e){return"LI"===e.nodeName})},Ic=function(e){return e&&e.firstChild&&e.firstChild===e.lastChild&&("\xa0"===(t=e.firstChild).data||Bo.isBr(t));var t},Mc=function(e){return 0<e.length&&(!(t=e[e.length-1]).firstChild||Ic(t))?e.slice(0,-1):e;var t},Fc=function(e,t){var n=e.getParent(t,e.isBlock);return n&&"LI"===n.nodeName?n:null},zc=function(e,t){var n=vu.after(e),r=Hs(t).prev(n);return r?r.toRange():null},Uc=function(t,e,n){var r,o,i,a,u=t.parentNode;return Gt.each(e,function(e){u.insertBefore(e,t)}),r=t,o=n,i=vu.before(r),(a=Hs(o).next(i))?a.toRange():null},Vc=function(e,t){var n,r,o,i,a,u,s=t.firstChild,c=t.lastChild;return s&&"meta"===s.name&&(s=s.next),c&&"mce_marker"===c.attr("id")&&(c=c.prev),r=c,u=(n=e).getNonEmptyElements(),r&&(r.isEmpty(u)||(o=r,n.getBlockElements()[o.name]&&(a=o).firstChild&&a.firstChild===a.lastChild&&("br"===(i=o.firstChild).name||"\xa0"===i.value)))&&(c=c.prev),!(!s||s!==c||"ul"!==s.name&&"ol"!==s.name)},Hc=function(e,o,i,t){var n,r,a,u,s,c,l,f,d,m,g,p,h,v,b,y,C,x,w,N=(n=o,r=t,c=e.serialize(r),l=n.createFragment(c),u=(a=l).firstChild,s=a.lastChild,u&&"META"===u.nodeName&&u.parentNode.removeChild(u),s&&"mce_marker"===s.id&&s.parentNode.removeChild(s),a),E=Fc(o,i.startContainer),S=Mc(Lc(N.firstChild)),k=o.getRoot(),T=function(e){var t=vu.fromRangeStart(i),n=Hs(o.getRoot()),r=1===e?n.prev(t):n.next(t);return!r||Fc(o,r.getNode())!==E};return T(1)?Uc(E,S,k):T(2)?(f=E,d=S,m=k,o.insertAfter(d.reverse(),f),zc(d[0],m)):(p=S,h=k,v=g=E,y=(b=i).cloneRange(),C=b.cloneRange(),y.setStartBefore(v),C.setEndAfter(v),x=[y.cloneContents(),C.cloneContents()],(w=g.parentNode).insertBefore(x[0],g),Gt.each(p,function(e){w.insertBefore(e,g)}),w.insertBefore(x[1],g),w.removeChild(g),zc(p[p.length-1],h))},jc=function(e,t){return!!Fc(e,t)},qc=Gt.each,$c=function(o){this.compare=function(e,t){if(e.nodeName!==t.nodeName)return!1;var n=function(n){var r={};return qc(o.getAttribs(n),function(e){var t=e.nodeName.toLowerCase();0!==t.indexOf("_")&&"style"!==t&&0!==t.indexOf("data-")&&(r[t]=o.getAttrib(n,t))}),r},r=function(e,t){var n,r;for(r in e)if(e.hasOwnProperty(r)){if(void 0===(n=t[r]))return!1;if(e[r]!==n)return!1;delete t[r]}for(r in t)if(t.hasOwnProperty(r))return!1;return!0};return!(!r(n(e),n(t))||!r(o.parseStyle(o.getAttrib(e,"style")),o.parseStyle(o.getAttrib(t,"style")))||cc(e)||cc(t))}},Wc=function(e){var t=ji(e,"br"),n=z(function(e){for(var t=[],n=e.dom();n;)t.push(or.fromDom(n)),n=n.lastChild;return t}(e).slice(-1),mo);t.length===n.length&&F(n,Di)},Kc=function(e){_i(e),Ai(e,or.fromHtml('<br data-mce-bogus="1">'))},Xc=function(n){$r(n).each(function(t){Fr(t).each(function(e){lo(n)&&mo(t)&&lo(e)&&Di(t)})})},Yc=Gt.makeMap;function Gc(e){var u,s,c,l,f,d=[];return u=(e=e||{}).indent,s=Yc(e.indent_before||""),c=Yc(e.indent_after||""),l=Wo.getEncodeFunc(e.entity_encoding||"raw",e.entities),f="html"===e.element_format,{start:function(e,t,n){var r,o,i,a;if(u&&s[e]&&0<d.length&&0<(a=d[d.length-1]).length&&"\n"!==a&&d.push("\n"),d.push("<",e),t)for(r=0,o=t.length;r<o;r++)i=t[r],d.push(" ",i.name,'="',l(i.value,!0),'"');d[d.length]=!n||f?">":" />",n&&u&&c[e]&&0<d.length&&0<(a=d[d.length-1]).length&&"\n"!==a&&d.push("\n")},end:function(e){var t;d.push("</",e,">"),u&&c[e]&&0<d.length&&0<(t=d[d.length-1]).length&&"\n"!==t&&d.push("\n")},text:function(e,t){0<e.length&&(d[d.length]=t?e:l(e))},cdata:function(e){d.push("<![CDATA[",e,"]]>")},comment:function(e){d.push("\x3c!--",e,"--\x3e")},pi:function(e,t){t?d.push("<?",e," ",l(t),"?>"):d.push("<?",e,"?>"),u&&d.push("\n")},doctype:function(e){d.push("<!DOCTYPE",e,">",u?"\n":"")},reset:function(){d.length=0},getContent:function(){return d.join("").replace(/\n$/,"")}}}function Jc(t,g){void 0===g&&(g=ri());var p=Gc(t);return(t=t||{}).validate=!("validate"in t)||t.validate,{serialize:function(e){var f,d;d=t.validate,f={3:function(e){p.text(e.value,e.raw)},8:function(e){p.comment(e.value)},7:function(e){p.pi(e.name,e.value)},10:function(e){p.doctype(e.value)},4:function(e){p.cdata(e.value)},11:function(e){if(e=e.firstChild)for(;m(e),e=e.next;);}},p.reset();var m=function(e){var t,n,r,o,i,a,u,s,c,l=f[e.type];if(l)l(e);else{if(t=e.name,n=e.shortEnded,r=e.attributes,d&&r&&1<r.length&&((a=[]).map={},c=g.getElementRule(e.name))){for(u=0,s=c.attributesOrder.length;u<s;u++)(o=c.attributesOrder[u])in r.map&&(i=r.map[o],a.map[o]=i,a.push({name:o,value:i}));for(u=0,s=r.length;u<s;u++)(o=r[u].name)in a.map||(i=r.map[o],a.map[o]=i,a.push({name:o,value:i}));r=a}if(p.start(e.name,r,n),!n){if(e=e.firstChild)for(;m(e),e=e.next;);p.end(t)}}};return 1!==e.type||t.inner?f[11](e):m(e),p.getContent()}}}var Qc,Zc=function(a){var u=vu.fromRangeStart(a),s=vu.fromRangeEnd(a),c=a.commonAncestorContainer;return Zs.fromPosition(!1,c,s).map(function(e){return!ps(u,s,c)&&ps(u,e,c)?(t=u.container(),n=u.offset(),r=e.container(),o=e.offset(),(i=H.document.createRange()).setStart(t,n),i.setEnd(r,o),i):a;var t,n,r,o,i}).getOr(a)},el=function(e){return e.collapsed?e:Zc(e)},tl=Bo.matchNodeNames("td th"),nl=function(e,t){var n,r,o=e.selection.getRng(),i=o.startContainer,a=o.startOffset;o.collapsed&&(n=i,r=a,Bo.isText(n)&&"\xa0"===n.nodeValue[r-1])&&Bo.isText(i)&&(i.insertData(a-1," "),i.deleteData(a,1),o.setStart(i,a),o.setEnd(i,a),e.selection.setRng(o)),e.selection.setContent(t)},rl=function(e,t,n){var r,o,i,a,u,s,c,l,f,d,m,g=e.selection,p=e.dom;if(/^ | $/.test(t)&&(t=function(e,t){var n,r;n=e.startContainer,r=e.startOffset;var o=function(e){return n[e]&&3===n[e].nodeType};return 3===n.nodeType&&(0<r?t=t.replace(/^&nbsp;/," "):o("previousSibling")||(t=t.replace(/^ /,"&nbsp;")),r<n.length?t=t.replace(/&nbsp;(<br>|)$/," "):o("nextSibling")||(t=t.replace(/(&nbsp;| )(<br>|)$/,"&nbsp;"))),t}(g.getRng(),t)),r=e.parser,m=n.merge,o=Jc({validate:e.settings.validate},e.schema),d='<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#x200B;</span>',s={content:t,format:"html",selection:!0,paste:n.paste},(s=e.fire("BeforeSetContent",s)).isDefaultPrevented())e.fire("SetContent",{content:s.content,format:"html",selection:!0,paste:n.paste});else{-1===(t=s.content).indexOf("{$caret}")&&(t+="{$caret}"),t=t.replace(/\{\$caret\}/,d);var h,v,b,y,C,x,w=(l=g.getRng()).startContainer||(l.parentElement?l.parentElement():null),N=e.getBody();w===N&&g.isCollapsed()&&p.isBlock(N.firstChild)&&(h=e,(v=N.firstChild)&&!h.schema.getShortEndedElements()[v.nodeName])&&p.isEmpty(N.firstChild)&&((l=p.createRng()).setStart(N.firstChild,0),l.setEnd(N.firstChild,0),g.setRng(l)),g.isCollapsed()||(e.selection.setRng(el(e.selection.getRng())),e.getDoc().execCommand("Delete",!1,null),b=e.selection.getRng(),y=t,C=b.startContainer,x=b.startOffset,3===C.nodeType&&b.collapsed&&("\xa0"===C.data[x]?(C.deleteData(x,1),/[\u00a0| ]$/.test(y)||(y+=" ")):"\xa0"===C.data[x-1]&&(C.deleteData(x-1,1),/[\u00a0| ]$/.test(y)||(y=" "+y))),t=y);var E,S,k,T={context:(i=g.getNode()).nodeName.toLowerCase(),data:n.data,insert:!0};if(u=r.parse(t,T),!0===n.paste&&Vc(e.schema,u)&&jc(p,i))return l=Hc(o,p,e.selection.getRng(),u),e.selection.setRng(l),void e.fire("SetContent",s);if(function(e){for(var t=e;t=t.walk();)1===t.type&&t.attr("data-mce-fragment","1")}(u),"mce_marker"===(f=u.lastChild).attr("id"))for(f=(c=f).prev;f;f=f.walk(!0))if(3===f.type||!p.isBlock(f.name)){e.schema.isValidChild(f.parent.name,"span")&&f.parent.insert(c,f,"br"===f.name);break}if(e._selectionOverrides.showBlockCaretContainer(i),T.invalid){for(nl(e,d),i=g.getNode(),a=e.getBody(),9===i.nodeType?i=f=a:f=i;f!==a;)f=(i=f).parentNode;t=i===a?a.innerHTML:p.getOuterHTML(i),t=o.serialize(r.parse(t.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i,function(){return o.serialize(u)}))),i===a?p.setHTML(a,t):p.setOuterHTML(i,t)}else!function(e,t,n){if("all"===n.getAttribute("data-mce-bogus"))n.parentNode.insertBefore(e.dom.createFragment(t),n);else{var r=n.firstChild,o=n.lastChild;!r||r===o&&"BR"===r.nodeName?e.dom.setHTML(n,t):nl(e,t)}}(e,t=o.serialize(u),i);!function(e,t){var n=e.schema.getTextInlineElements(),r=e.dom;if(t){var o=e.getBody(),i=new $c(r);Gt.each(r.select("*[data-mce-fragment]"),function(e){for(var t=e.parentNode;t&&t!==o;t=t.parentNode)n[e.nodeName.toLowerCase()]&&i.compare(t,e)&&r.remove(e,!0)})}}(e,m),function(n,e){var t,r,o,i,a,u=n.dom,s=n.selection;if(e){if(n.selection.scrollIntoView(e),t=function(e){for(var t=n.getBody();e&&e!==t;e=e.parentNode)if("false"===n.dom.getContentEditable(e))return e;return null}(e))return u.remove(e),s.select(t);var c=u.createRng();(i=e.previousSibling)&&3===i.nodeType?(c.setStart(i,i.nodeValue.length),me.ie||(a=e.nextSibling)&&3===a.nodeType&&(i.appendData(a.data),a.parentNode.removeChild(a))):(c.setStartBefore(e),c.setEndBefore(e)),r=u.getParent(e,u.isBlock),u.remove(e),r&&u.isEmpty(r)&&(n.$(r).empty(),c.setStart(r,0),c.setEnd(r,0),tl(r)||r.getAttribute("data-mce-fragment")||!(o=function(e){var t=vu.fromRangeStart(e);if(t=Hs(n.getBody()).next(t))return t.toRange()}(c))?u.add(r,u.create("br",{"data-mce-bogus":"1"})):(c=o,u.remove(r))),s.setRng(c)}}(e,p.get("mce_marker")),E=e.getBody(),Gt.each(E.getElementsByTagName("*"),function(e){e.removeAttribute("data-mce-fragment")}),S=e.dom,k=e.selection.getStart(),A.from(S.getParent(k,"td,th")).map(or.fromDom).each(Xc),e.fire("SetContent",s),e.addVisual()}},ol=function(e,t){var n,r,o="string"!=typeof(n=t)?(r=Gt.extend({paste:n.paste,data:{paste:n.paste}},n),{content:n.content,details:r}):{content:n,details:{}};rl(e,o.content,o.details)},il=Sr("sections","settings"),al=nr.detect().deviceType.isTouch(),ul=["lists","autolink","autosave"],sl={theme:"mobile"},cl=function(e){var t=D(e)?e.join(" "):e,n=W(R(t)?t.split(" "):[],Xn);return z(n,function(e){return 0<e.length})},ll=function(n,e){var r,o,i,t=(r=function(e,t){return M(n,t)},o={},i={},gr(e,function(e,t){(r(e,t)?o:i)[t]=e}),{t:o,f:i});return il(t.t,t.f)},fl=function(e,t){return e.sections().hasOwnProperty(t)},dl=function(e,t,n,r){var o,i=cl(n.forced_plugins),a=cl(r.plugins),u=e&&fl(t,"mobile")?z(a,d(M,ul)):a,s=(o=u,[].concat(cl(i)).concat(cl(o)));return Gt.extend(r,{plugins:s.join(" ")})},ml=function(e,t,n,r){var o,i,a,u,s,c,l,f,d,m,g=ll(["mobile"],r),p=Gt.extend(t,n,g.settings(),(f=e,m=(d=g).settings().inline,f&&fl(d,"mobile")&&!m?(u="mobile",s=sl,c=g.sections(),l=c.hasOwnProperty(u)?c[u]:{},Gt.extend({},s,l)):{}),{validate:!0,content_editable:g.settings().inline,external_plugins:(o=n,i=g.settings(),a=i.external_plugins?i.external_plugins:{},o&&o.external_plugins?Gt.extend({},o.external_plugins,a):a)});return dl(e,g,n,p)},gl=function(e,t,n){return A.from(t.settings[n]).filter(e)},pl=d(gl,R),hl=function(e,t,n,r){var o,i,a,u=t in e.settings?e.settings[t]:n;return"hash"===r?(a={},"string"==typeof(i=u)?F(0<i.indexOf("=")?i.split(/[;,](?![^=;,]*(?:[;,]|$))/):i.split(","),function(e){var t=e.split("=");1<t.length?a[Gt.trim(t[0])]=Gt.trim(t[1]):a[Gt.trim(t[0])]=Gt.trim(t)}):a=i,a):"string"===r?gl(R,e,t).getOr(n):"number"===r?gl(L,e,t).getOr(n):"boolean"===r?gl(O,e,t).getOr(n):"object"===r?gl(_,e,t).getOr(n):"array"===r?gl(D,e,t).getOr(n):"string[]"===r?gl((o=R,function(e){return D(e)&&Q(e,o)}),e,t).getOr(n):"function"===r?gl(P,e,t).getOr(n):u},vl=/[\u0591-\u07FF\uFB1D-\uFDFF\uFE70-\uFEFC]/,bl=function(e,t){if(!t)return t;var n=t.container(),r=t.offset();return e?ha(n)?Bo.isText(n.nextSibling)?vu(n.nextSibling,0):vu.after(n):ya(t)?vu(n,r+1):t:ha(n)?Bo.isText(n.previousSibling)?vu(n.previousSibling,n.previousSibling.data.length):vu.before(n):Ca(t)?vu(n,r-1):t},yl={isInlineTarget:function(e,t){var n=pl(e,"inline_boundaries_selector").getOr("a[href],code");return Br(or.fromDom(t),n)},findRootInline:function(e,t,n){var r,o,i,a=(r=e,o=t,i=n,z(hi.DOM.getParents(i.container(),"*",o),r));return A.from(a[a.length-1])},isRtl:function(e){return"rtl"===hi.DOM.getStyle(e,"direction",!0)||(t=e.textContent,vl.test(t));var t},isAtZwsp:function(e){return ya(e)||Ca(e)},normalizePosition:bl,normalizeForwards:d(bl,!0),normalizeBackwards:d(bl,!1),hasSameParentBlock:function(e,t,n){var r=gs(t,e),o=gs(n,e);return r&&r===o}},Cl=function(e,t){return Lr(e,t)?Ki(t,function(e){return go(e)||ho(e)},(n=e,function(e){return Pr(n,or.fromDom(e.dom().parentNode))})):A.none();var n},xl=function(e){var t,n,r;e.dom.isEmpty(e.getBody())&&(e.setContent(""),n=(t=e).getBody(),r=n.firstChild&&t.dom.isBlock(n.firstChild)?n.firstChild:n,t.selection.setCursorLocation(r,0))},wl=function(i,a,u){return Wa([Zs.firstPositionIn(u),Zs.lastPositionIn(u)],function(e,t){var n=yl.normalizePosition(!0,e),r=yl.normalizePosition(!1,t),o=yl.normalizePosition(!1,a);return i?Zs.nextPosition(u,o).map(function(e){return e.isEqual(r)&&a.isEqual(n)}).getOr(!1):Zs.prevPosition(u,o).map(function(e){return e.isEqual(n)&&a.isEqual(r)}).getOr(!1)}).getOr(!0)},Nl=function(e,t){var n,r,o,i=or.fromDom(e),a=or.fromDom(t);return n=a,r="pre,code",o=d(Pr,i),Xi(n,r,o).isSome()},El=function(e,t){return Oa(t)&&!1===(r=e,o=t,Bo.isText(o)&&/^[ \t\r\n]*$/.test(o.data)&&!1===Nl(r,o))||(n=t,Bo.isElement(n)&&"A"===n.nodeName&&n.hasAttribute("name"))||Sl(t);var n,r,o},Sl=Bo.hasAttribute("data-mce-bookmark"),kl=Bo.hasAttribute("data-mce-bogus"),Tl=Bo.hasAttributeValue("data-mce-bogus","all"),Al=function(e){return function(e){var t,n,r=0;if(El(e,e))return!1;if(!(n=e.firstChild))return!0;t=new oo(n,e);do{if(Tl(n))n=t.next(!0);else if(kl(n))n=t.next();else if(Bo.isBr(n))r++,n=t.next();else{if(El(e,n))return!1;n=t.next()}}while(n);return r<=1}(e.dom())},Rl=Sr("block","position"),_l=Sr("from","to"),Dl=function(e,t){var n=or.fromDom(e),r=or.fromDom(t.container());return Cl(n,r).map(function(e){return Rl(e,t)})},Bl=function(o,i,e){var t=Dl(o,vu.fromRangeStart(e)),n=t.bind(function(e){return Zs.fromPosition(i,o,e.position()).bind(function(e){return Dl(o,e).map(function(e){return t=o,n=i,r=e,Bo.isBr(r.position().getNode())&&!1===Al(r.block())?Zs.positionIn(!1,r.block().dom()).bind(function(e){return e.isEqual(r.position())?Zs.fromPosition(n,t,e).bind(function(e){return Dl(t,e)}):A.some(r)}).getOr(r):r;var t,n,r})})});return Wa([t,n],_l).filter(function(e){return!1===Pr((r=e).from().block(),r.to().block())&&Mr((n=e).from().block()).bind(function(t){return Mr(n.to().block()).filter(function(e){return Pr(t,e)})}).isSome()&&(t=e,!1===Bo.isContentEditableFalse(t.from().block())&&!1===Bo.isContentEditableFalse(t.to().block()));var t,n,r})},Ol=function(e,t,n){return n.collapsed?Bl(e,t,n):A.none()},Pl=function(e,t,n){return Lr(t,e)?function(e,t){for(var n=P(t)?t:q(!1),r=e.dom(),o=[];null!==r.parentNode&&r.parentNode!==undefined;){var i=r.parentNode,a=or.fromDom(i);if(o.push(a),!0===n(a))break;r=i}return o}(e,function(e){return n(e)||Pr(e,t)}).slice(0,-1):[]},Ll=function(e,t){return Pl(e,t,q(!1))},Il=Ll,Ml=function(e,t){return[e].concat(Ll(e,t))},Fl=function(e){var t,n=(t=Hr(e),X(t,lo).fold(function(){return t},function(e){return t.slice(0,e)}));return F(n,Di),n},zl=function(e,t){var n=Ml(t,e);return V(n.reverse(),Al).each(Di)},Ul=function(e,t,n,r){if(Al(n))return Kc(n),Zs.firstPositionIn(n.dom());0===z(Ur(r),function(e){return!Al(e)}).length&&Al(t)&&Si(r,or.fromTag("br"));var o=Zs.prevPosition(n.dom(),vu.before(r.dom()));return F(Fl(t),function(e){Si(r,e)}),zl(e,t),o},Vl=function(e,t,n){if(Al(n))return Di(n),Al(t)&&Kc(t),Zs.firstPositionIn(t.dom());var r=Zs.lastPositionIn(n.dom());return F(Fl(t),function(e){Ai(n,e)}),zl(e,t),r},Hl=function(e,t){return Lr(t,e)?(n=Ml(e,t),A.from(n[n.length-1])):A.none();var n},jl=function(e,t){Zs.positionIn(e,t.dom()).map(function(e){return e.getNode()}).map(or.fromDom).filter(mo).each(Di)},ql=function(e,t,n){return jl(!0,t),jl(!1,n),Hl(t,n).fold(d(Vl,e,t,n),d(Ul,e,t,n))},$l=function(e,t,n,r){return t?ql(e,r,n):ql(e,n,r)},Wl=function(t,n){var e,r=or.fromDom(t.getBody());return(e=Ol(r.dom(),n,t.selection.getRng()).bind(function(e){return $l(r,n,e.from().block(),e.to().block())})).each(function(e){t.selection.setRng(e.toRange())}),e.isSome()},Kl=function(e,t){var n=or.fromDom(t),r=d(Pr,e);return Wi(n,yo,r).isSome()},Xl=function(e,t){var n,r,o=Zs.prevPosition(e.dom(),vu.fromRangeStart(t)).isNone(),i=Zs.nextPosition(e.dom(),vu.fromRangeEnd(t)).isNone();return!(Kl(n=e,(r=t).startContainer)||Kl(n,r.endContainer))&&o&&i},Yl=function(e){var n,r,o,t,i=or.fromDom(e.getBody()),a=e.selection.getRng();return Xl(i,a)?((t=e).setContent(""),t.selection.setCursorLocation(),!0):(n=i,r=e.selection,o=r.getRng(),Wa([Cl(n,or.fromDom(o.startContainer)),Cl(n,or.fromDom(o.endContainer))],function(e,t){return!1===Pr(e,t)&&(o.deleteContents(),$l(n,!0,e,t).each(function(e){r.setRng(e.toRange())}),!0)}).getOr(!1))},Gl=function(e,t){return!e.selection.isCollapsed()&&Yl(e)},Jl=function(a){if(!D(a))throw new Error("cases must be an array");if(0===a.length)throw new Error("there must be at least one case");var u=[],n={};return F(a,function(e,r){var t=dr(e);if(1!==t.length)throw new Error("one and only one name per case");var o=t[0],i=e[o];if(n[o]!==undefined)throw new Error("duplicate key detected:"+o);if("cata"===o)throw new Error("cannot have a case named cata (sorry)");if(!D(i))throw new Error("case arguments must be an array");u.push(o),n[o]=function(){var e=arguments.length;if(e!==i.length)throw new Error("Wrong number of arguments to case "+o+". Expected "+i.length+" ("+i+"), got "+e);for(var n=new Array(e),t=0;t<n.length;t++)n[t]=arguments[t];return{fold:function(){if(arguments.length!==a.length)throw new Error("Wrong number of arguments to fold. Expected "+a.length+", got "+arguments.length);return arguments[r].apply(null,n)},match:function(e){var t=dr(e);if(u.length!==t.length)throw new Error("Wrong number of arguments to match. Expected: "+u.join(",")+"\nActual: "+t.join(","));if(!Q(u,function(e){return M(t,e)}))throw new Error("Not all branches were specified when using match. Specified: "+t.join(", ")+"\nRequired: "+u.join(", "));return e[o].apply(null,n)},log:function(e){H.console.log(e,{constructors:u,constructor:o,params:n})}}}}),n},Ql=function(e){return Es(e).exists(mo)},Zl=function(e,t,n){var r=z(Ml(or.fromDom(n.container()),t),lo),o=te(r).getOr(t);return Zs.fromPosition(e,o.dom(),n).filter(Ql)},ef=function(e,t){return Es(t).exists(mo)||Zl(!0,e,t).isSome()},tf=function(e,t){return(n=t,A.from(n.getNode(!0)).map(or.fromDom)).exists(mo)||Zl(!1,e,t).isSome();var n},nf=d(Zl,!1),rf=d(Zl,!0),of=(Qc="\xa0",function(e){return Qc===e}),af=function(e){return/^[\r\n\t ]$/.test(e)},uf=function(e){return!af(e)&&!of(e)},sf=function(n,r,o){return A.from(o.container()).filter(Bo.isText).exists(function(e){var t=n?0:-1;return r(e.data.charAt(o.offset()+t))})},cf=d(sf,!0,af),lf=d(sf,!1,af),ff=function(e){var t=e.container();return Bo.isText(t)&&0===t.data.length},df=function(e,t){var n=hs(e,t);return Bo.isContentEditableFalse(n)&&!Bo.isBogusAll(n)},mf=d(df,0),gf=d(df,-1),pf=function(e,t){return Bo.isTable(hs(e,t))},hf=d(pf,0),vf=d(pf,-1),bf=Jl([{remove:["element"]},{moveToElement:["element"]},{moveToPosition:["position"]}]),yf=function(e,t,n,r){var o=r.getNode(!1===t);return Cl(or.fromDom(e),or.fromDom(n.getNode())).map(function(e){return Al(e)?bf.remove(e.dom()):bf.moveToElement(o)}).orThunk(function(){return A.some(bf.moveToElement(o))})},Cf=function(u,s,c){return Zs.fromPosition(s,u,c).bind(function(e){return a=e.getNode(),yo(or.fromDom(a))||ho(or.fromDom(a))?A.none():(t=u,o=e,i=function(e){return fo(or.fromDom(e))&&!ps(r,o,t)},ws(!(n=s),r=c).fold(function(){return ws(n,o).fold(q(!1),i)},i)?A.none():s&&Bo.isContentEditableFalse(e.getNode())?yf(u,s,c,e):!1===s&&Bo.isContentEditableFalse(e.getNode(!0))?yf(u,s,c,e):s&&gf(c)?A.some(bf.moveToPosition(e)):!1===s&&mf(c)?A.some(bf.moveToPosition(e)):A.none());var t,n,r,o,i,a})},xf=function(r,e,o){return i=e,a=o.getNode(!1===i),u=i?"after":"before",Bo.isElement(a)&&a.getAttribute("data-mce-caret")===u?(t=e,n=o.getNode(!1===e),t&&Bo.isContentEditableFalse(n.nextSibling)?A.some(bf.moveToElement(n.nextSibling)):!1===t&&Bo.isContentEditableFalse(n.previousSibling)?A.some(bf.moveToElement(n.previousSibling)):A.none()).fold(function(){return Cf(r,e,o)},A.some):Cf(r,e,o).bind(function(e){return t=r,n=o,e.fold(function(e){return A.some(bf.remove(e))},function(e){return A.some(bf.moveToElement(e))},function(e){return ps(n,e,t)?A.none():A.some(bf.moveToPosition(e))});var t,n});var t,n,i,a,u},wf=function(e,t,n){if(0!==n){var r,o,i,a=e.data.slice(t,t+n),u=t+n>=e.data.length,s=0===t;e.replaceData(t,n,(o=s,i=u,U((r=a).split(""),function(e,t){return-1!==" \f\n\r\t\x0B".indexOf(t)||"\xa0"===t?e.previousCharIsSpace||""===e.str&&o||e.str.length===r.length-1&&i?{previousCharIsSpace:!1,str:e.str+"\xa0"}:{previousCharIsSpace:!0,str:e.str+" "}:{previousCharIsSpace:!1,str:e.str+t}},{previousCharIsSpace:!1,str:""}).str))}},Nf=function(e,t){var n,r=e.data.slice(t),o=r.length-(n=r,n.replace(/^\s+/g,"")).length;return wf(e,t,o)},Ef=function(e,t){return r=e,o=(n=t).container(),i=n.offset(),!1===vu.isTextPosition(n)&&o===r.parentNode&&i>vu.before(r).offset()?vu(t.container(),t.offset()-1):t;var n,r,o,i},Sf=function(e){return Oa(e.previousSibling)?A.some((t=e.previousSibling,Bo.isText(t)?vu(t,t.data.length):vu.after(t))):e.previousSibling?Zs.lastPositionIn(e.previousSibling):A.none();var t},kf=function(e){return Oa(e.nextSibling)?A.some((t=e.nextSibling,Bo.isText(t)?vu(t,0):vu.before(t))):e.nextSibling?Zs.firstPositionIn(e.nextSibling):A.none();var t},Tf=function(r,o){return Sf(o).orThunk(function(){return kf(o)}).orThunk(function(){return e=r,t=o,n=vu.before(t.previousSibling?t.previousSibling:t.parentNode),Zs.prevPosition(e,n).fold(function(){return Zs.nextPosition(e,vu.after(t))},A.some);var e,t,n})},Af=function(n,r){return kf(r).orThunk(function(){return Sf(r)}).orThunk(function(){return e=n,t=r,Zs.nextPosition(e,vu.after(t)).fold(function(){return Zs.prevPosition(e,vu.before(t))},A.some);var e,t})},Rf=function(e,t,n){return(r=e,o=t,i=n,r?Af(o,i):Tf(o,i)).map(d(Ef,n));var r,o,i},_f=function(t,n,e){e.fold(function(){t.focus()},function(e){t.selection.setRng(e.toRange(),n)})},Df=function(e,t){return t&&e.schema.getBlockElements().hasOwnProperty(sr(t))},Bf=function(e){if(Al(e)){var t=or.fromHtml('<br data-mce-bogus="1">');return _i(e),Ai(e,t),A.some(vu.before(t.dom()))}return A.none()},Of=function(e,t,l){var n=Fr(e).filter(function(e){return Bo.isText(e.dom())}),r=zr(e).filter(function(e){return Bo.isText(e.dom())});return Di(e),Wa([n,r,t],function(e,t,n){var r,o,i,a,u=e.dom(),s=t.dom(),c=u.data.length;return o=s,i=l,a=Yn((r=u).data).length,r.appendData(o.data),Di(or.fromDom(o)),i&&Nf(r,a),n.container()===s?vu(u,c):n}).orThunk(function(){return l&&(n.each(function(e){return t=e.dom(),n=e.dom().length,r=t.data.slice(0,n),o=r.length-Yn(r).length,wf(t,n-o,o);var t,n,r,o}),r.each(function(e){return Nf(e.dom(),0)})),t})},Pf=function(e,t){return n=e.schema.getTextInlineElements(),r=sr(t),mr.call(n,r);var n,r},Lf=function(t,n,e,r){void 0===r&&(r=!0);var o,i=Rf(n,t.getBody(),e.dom()),a=Wi(e,d(Df,t),(o=t.getBody(),function(e){return e.dom()===o})),u=Of(e,i,Pf(t,e));t.dom.isEmpty(t.getBody())?(t.setContent(""),t.selection.setCursorLocation()):a.bind(Bf).fold(function(){r&&_f(t,n,u)},function(e){r&&_f(t,n,A.some(e))})},If=function(a,u){var e,t,n,r,o,i;return(e=a.getBody(),t=u,n=a.selection.getRng(),r=xs(t?1:-1,e,n),o=vu.fromRangeStart(r),i=or.fromDom(e),!1===t&&gf(o)?A.some(bf.remove(o.getNode(!0))):t&&mf(o)?A.some(bf.remove(o.getNode())):!1===t&&mf(o)&&tf(i,o)?nf(i,o).map(function(e){return bf.remove(e.getNode())}):t&&gf(o)&&ef(i,o)?rf(i,o).map(function(e){return bf.remove(e.getNode())}):xf(e,t,o)).map(function(e){return e.fold((o=a,i=u,function(e){return o._selectionOverrides.hideFakeCaret(),Lf(o,i,or.fromDom(e)),!0}),(n=a,r=u,function(e){var t=r?vu.before(e):vu.after(e);return n.selection.setRng(t.toRange()),!0}),(t=a,function(e){return t.selection.setRng(e.toRange()),!0}));var t,n,r,o,i}).getOr(!1)},Mf=function(e,t){var n,r=e.selection.getNode();return!!Bo.isContentEditableFalse(r)&&(n=or.fromDom(e.getBody()),F(ji(n,".mce-offscreen-selection"),Di),Lf(e,t,or.fromDom(e.selection.getNode())),xl(e),!0)},Ff=function(e,t){return e.selection.isCollapsed()?If(e,t):Mf(e,t)},zf=function(e){var t,n=function(e,t){for(;t&&t!==e;){if(Bo.isContentEditableTrue(t)||Bo.isContentEditableFalse(t))return t;t=t.parentNode}return null}(e.getBody(),e.selection.getNode());return Bo.isContentEditableTrue(n)&&e.dom.isBlock(n)&&e.dom.isEmpty(n)&&(t=e.dom.create("br",{"data-mce-bogus":"1"}),e.dom.setHTML(n,""),n.appendChild(t),e.selection.setRng(vu.before(t).toRange())),!0},Uf=Bo.isText,Vf=function(e){return Uf(e)&&e.data[0]===fa},Hf=function(e){return Uf(e)&&e.data[e.data.length-1]===fa},jf=function(e){return e.ownerDocument.createTextNode(fa)},qf=function(e,t){return e?function(e){if(Uf(e.previousSibling))return Hf(e.previousSibling)||e.previousSibling.appendData(fa),e.previousSibling;if(Uf(e))return Vf(e)||e.insertData(0,fa),e;var t=jf(e);return e.parentNode.insertBefore(t,e),t}(t):function(e){if(Uf(e.nextSibling))return Vf(e.nextSibling)||e.nextSibling.insertData(0,fa),e.nextSibling;if(Uf(e))return Hf(e)||e.appendData(fa),e;var t=jf(e);return e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t),t}(t)},$f=d(qf,!0),Wf=d(qf,!1),Kf=function(e,t){return Bo.isText(e.container())?qf(t,e.container()):qf(t,e.getNode())},Xf=function(e,t){var n=t.get();return n&&e.container()===n&&ha(n)},Yf=function(n,e){return e.fold(function(e){Ju.remove(n.get());var t=$f(e);return n.set(t),A.some(vu(t,t.length-1))},function(e){return Zs.firstPositionIn(e).map(function(e){if(Xf(e,n))return vu(n.get(),1);Ju.remove(n.get());var t=Kf(e,!0);return n.set(t),vu(t,1)})},function(e){return Zs.lastPositionIn(e).map(function(e){if(Xf(e,n))return vu(n.get(),n.get().length-1);Ju.remove(n.get());var t=Kf(e,!1);return n.set(t),vu(t,t.length-1)})},function(e){Ju.remove(n.get());var t=Wf(e);return n.set(t),A.some(vu(t,1))})},Gf=function(e,t){for(var n=0;n<e.length;n++){var r=e[n].apply(null,t);if(r.isSome())return r}return A.none()},Jf=Jl([{before:["element"]},{start:["element"]},{end:["element"]},{after:["element"]}]),Qf=function(e,t){var n=gs(t,e);return n||e},Zf=function(e,t,n){var r=yl.normalizeForwards(n),o=Qf(t,r.container());return yl.findRootInline(e,o,r).fold(function(){return Zs.nextPosition(o,r).bind(d(yl.findRootInline,e,o)).map(function(e){return Jf.before(e)})},A.none)},ed=function(e,t){return null===Vu(e,t)},td=function(e,t,n){return yl.findRootInline(e,t,n).filter(d(ed,t))},nd=function(e,t,n){var r=yl.normalizeBackwards(n);return td(e,t,r).bind(function(e){return Zs.prevPosition(e,r).isNone()?A.some(Jf.start(e)):A.none()})},rd=function(e,t,n){var r=yl.normalizeForwards(n);return td(e,t,r).bind(function(e){return Zs.nextPosition(e,r).isNone()?A.some(Jf.end(e)):A.none()})},od=function(e,t,n){var r=yl.normalizeBackwards(n),o=Qf(t,r.container());return yl.findRootInline(e,o,r).fold(function(){return Zs.prevPosition(o,r).bind(d(yl.findRootInline,e,o)).map(function(e){return Jf.after(e)})},A.none)},id=function(e){return!1===yl.isRtl(ud(e))},ad=function(e,t,n){return Gf([Zf,nd,rd,od],[e,t,n]).filter(id)},ud=function(e){return e.fold($,$,$,$)},sd=function(e){return e.fold(q("before"),q("start"),q("end"),q("after"))},cd=function(e){return e.fold(Jf.before,Jf.before,Jf.after,Jf.after)},ld=function(n,e,r,t,o,i){return Wa([yl.findRootInline(e,r,t),yl.findRootInline(e,r,o)],function(e,t){return e!==t&&yl.hasSameParentBlock(r,e,t)?Jf.after(n?e:t):i}).getOr(i)},fd=function(e,r){return e.fold(q(!0),function(e){return n=r,!(sd(t=e)===sd(n)&&ud(t)===ud(n));var t,n})},dd=function(e,t){return e?t.fold(j(A.some,Jf.start),A.none,j(A.some,Jf.after),A.none):t.fold(A.none,j(A.some,Jf.before),A.none,j(A.some,Jf.end))},md=function(a,u,s,c){var e=yl.normalizePosition(a,c),l=ad(u,s,e);return ad(u,s,e).bind(d(dd,a)).orThunk(function(){return t=a,n=u,r=s,o=l,e=c,i=yl.normalizePosition(t,e),Zs.fromPosition(t,r,i).map(d(yl.normalizePosition,t)).fold(function(){return o.map(cd)},function(e){return ad(n,r,e).map(d(ld,t,n,r,i,e)).filter(d(fd,o))}).filter(id);var t,n,r,o,e,i})},gd=ad,pd=md,hd=(d(md,!1),d(md,!0),cd),vd=function(e){return e.fold(Jf.start,Jf.start,Jf.end,Jf.end)},bd=function(e){return P(e.selection.getSel().modify)},yd=function(e,t,n){var r=e?1:-1;return t.setRng(vu(n.container(),n.offset()+r).toRange()),t.getSel().modify("move",e?"forward":"backward","word"),!0},Cd=function(e,t){var n=t.selection.getRng(),r=e?vu.fromRangeEnd(n):vu.fromRangeStart(n);return!!bd(t)&&(e&&ya(r)?yd(!0,t.selection,r):!(e||!Ca(r))&&yd(!1,t.selection,r))},xd=function(e,t){var n=e.dom.createRng();n.setStart(t.container(),t.offset()),n.setEnd(t.container(),t.offset()),e.selection.setRng(n)},wd=function(e){return!1!==e.settings.inline_boundaries},Nd=function(e,t){e?t.setAttribute("data-mce-selected","inline-boundary"):t.removeAttribute("data-mce-selected")},Ed=function(t,e,n){return Yf(e,n).map(function(e){return xd(t,e),n})},Sd=function(e,t,n){return function(){return!!wd(t)&&Cd(e,t)}},kd={move:function(a,u,s){return function(){return!!wd(a)&&(t=a,n=u,e=s,r=t.getBody(),o=vu.fromRangeStart(t.selection.getRng()),i=d(yl.isInlineTarget,t),pd(e,i,r,o).bind(function(e){return Ed(t,n,e)})).isSome();var t,n,e,r,o,i}},moveNextWord:d(Sd,!0),movePrevWord:d(Sd,!1),setupSelectedState:function(a){var u=Pi(null),s=d(yl.isInlineTarget,a);return a.on("NodeChange",function(e){var t,n,r,o,i;wd(a)&&(t=s,n=a.dom,r=e.parents,o=z(n.select('*[data-mce-selected="inline-boundary"]'),t),i=z(r,t),F(ee(o,i),d(Nd,!1)),F(ee(i,o),d(Nd,!0)),function(e,t){if(e.selection.isCollapsed()&&!0!==e.composing&&t.get()){var n=vu.fromRangeStart(e.selection.getRng());vu.isTextPosition(n)&&!1===yl.isAtZwsp(n)&&(xd(e,Ju.removeAndReposition(t.get(),n)),t.set(null))}}(a,u),function(n,r,o,e){if(r.selection.isCollapsed()){var t=z(e,n);F(t,function(e){var t=vu.fromRangeStart(r.selection.getRng());gd(n,r.getBody(),t).bind(function(e){return Ed(r,o,e)})})}}(s,a,u,e.parents))}),u},setCaretPosition:xd},Td=function(t,n){return function(e){return Yf(n,e).map(function(e){return kd.setCaretPosition(t,e),!0}).getOr(!1)}},Ad=function(r,o,i,a){var u=r.getBody(),s=d(yl.isInlineTarget,r);r.undoManager.ignore(function(){var e,t,n;r.selection.setRng((e=i,t=a,(n=H.document.createRange()).setStart(e.container(),e.offset()),n.setEnd(t.container(),t.offset()),n)),r.execCommand("Delete"),gd(s,u,vu.fromRangeStart(r.selection.getRng())).map(vd).map(Td(r,o))}),r.nodeChanged()},Rd=function(n,r,i,o){var e,t,a=(e=n.getBody(),t=o.container(),gs(t,e)||e),u=d(yl.isInlineTarget,n),s=gd(u,a,o);return s.bind(function(e){return i?e.fold(q(A.some(vd(e))),A.none,q(A.some(hd(e))),A.none):e.fold(A.none,q(A.some(hd(e))),A.none,q(A.some(vd(e))))}).map(Td(n,r)).getOrThunk(function(){var t=Zs.navigate(i,a,o),e=t.bind(function(e){return gd(u,a,e)});return s.isSome()&&e.isSome()?yl.findRootInline(u,a,o).map(function(e){return o=e,!!Wa([Zs.firstPositionIn(o),Zs.lastPositionIn(o)],function(e,t){var n=yl.normalizePosition(!0,e),r=yl.normalizePosition(!1,t);return Zs.nextPosition(o,n).map(function(e){return e.isEqual(r)}).getOr(!0)}).getOr(!0)&&(Lf(n,i,or.fromDom(e)),!0);var o}).getOr(!1):e.bind(function(e){return t.map(function(e){return i?Ad(n,r,o,e):Ad(n,r,e,o),!0})}).getOr(!1)})},_d=function(e,t,n){if(e.selection.isCollapsed()&&!1!==e.settings.inline_boundaries){var r=vu.fromRangeStart(e.selection.getRng());return Rd(e,t,n,r)}return!1},Dd=Sr("start","end"),Bd=Sr("rng","table","cells"),Od=Jl([{removeTable:["element"]},{emptyCells:["cells"]}]),Pd=function(e,t){return Gi(or.fromDom(e),"td,th",t)},Ld=function(e,t){return Xi(e,"table",t)},Id=function(e){return!1===Pr(e.start(),e.end())},Md=function(e,n){return Ld(e.start(),n).bind(function(t){return Ld(e.end(),n).bind(function(e){return Pr(t,e)?A.some(t):A.none()})})},Fd=function(e){return ji(e,"td,th")},zd=function(r,e){var t=Pd(e.startContainer,r),n=Pd(e.endContainer,r);return e.collapsed?A.none():Wa([t,n],Dd).fold(function(){return t.fold(function(){return n.bind(function(t){return Ld(t,r).bind(function(e){return te(Fd(e)).map(function(e){return Dd(e,t)})})})},function(t){return Ld(t,r).bind(function(e){return ne(Fd(e)).map(function(e){return Dd(t,e)})})})},function(e){return Ud(r,e)?A.none():(n=r,Ld((t=e).start(),n).bind(function(e){return ne(Fd(e)).map(function(e){return Dd(t.start(),e)})}));var t,n})},Ud=function(e,t){return Md(t,e).isSome()},Vd=function(e,t){var n,r,o,i,a=d(Pr,e);return(n=t,r=a,o=Pd(n.startContainer,r),i=Pd(n.endContainer,r),Wa([o,i],Dd).filter(Id).filter(function(e){return Ud(r,e)}).orThunk(function(){return zd(r,n)})).bind(function(e){return Md(t=e,a).map(function(e){return Bd(t,e,Fd(e))});var t})},Hd=function(e,t){return X(e,function(e){return Pr(e,t)})},jd=function(n){return(r=n,Wa([Hd(r.cells(),r.rng().start()),Hd(r.cells(),r.rng().end())],function(e,t){return r.cells().slice(e,t+1)})).map(function(e){var t=n.cells();return e.length===t.length?Od.removeTable(n.table()):Od.emptyCells(e)});var r},qd=function(e,t){return Vd(e,t).bind(jd)},$d=function(e){var t=[];if(e)for(var n=0;n<e.rangeCount;n++)t.push(e.getRangeAt(n));return t},Wd=$d,Kd=function(e){return J(e,function(e){var t=Ha(e);return t?[or.fromDom(t)]:[]})},Xd=function(e){return 1<$d(e).length},Yd=function(e){return z(Kd(e),yo)},Gd=function(e){return ji(e,"td[data-mce-selected],th[data-mce-selected]")},Jd=function(e,t){var n=Gd(t),r=Yd(e);return 0<n.length?n:r},Qd=Jd,Zd=function(e){return Jd(Wd(e.selection.getSel()),or.fromDom(e.getBody()))},em=function(e,t){return F(t,Kc),e.selection.setCursorLocation(t[0].dom(),0),!0},tm=function(e,t){return Lf(e,!1,t),!0},nm=function(n,e,r,t){return om(e,t).fold(function(){return t=n,qd(e,r).map(function(e){return e.fold(d(tm,t),d(em,t))});var t},function(e){return im(n,e)}).getOr(!1)},rm=function(e,t){return V(Ml(t,e),yo)},om=function(e,t){return V(Ml(t,e),function(e){return"caption"===sr(e)})},im=function(e,t){return Kc(t),e.selection.setCursorLocation(t.dom(),0),A.some(!0)},am=function(u,s,c,l,f){return Zs.navigate(c,u.getBody(),f).bind(function(e){return r=l,o=c,i=f,a=e,Zs.firstPositionIn(r.dom()).bind(function(t){return Zs.lastPositionIn(r.dom()).map(function(e){return o?i.isEqual(t)&&a.isEqual(e):i.isEqual(e)&&a.isEqual(t)})}).getOr(!0)?im(u,l):(t=l,n=e,om(s,or.fromDom(n.getNode())).map(function(e){return!1===Pr(e,t)}));var t,n,r,o,i,a}).or(A.some(!0))},um=function(a,u,s,e){var c=vu.fromRangeStart(a.selection.getRng());return rm(s,e).bind(function(e){return Al(e)?im(a,e):(t=a,n=s,r=u,o=e,i=c,Zs.navigate(r,t.getBody(),i).bind(function(e){return rm(n,or.fromDom(e.getNode())).map(function(e){return!1===Pr(e,o)})}));var t,n,r,o,i})},sm=function(a,u,e){var s=or.fromDom(a.getBody());return om(s,e).fold(function(){return um(a,u,s,e)},function(e){return t=a,n=u,r=s,o=e,i=vu.fromRangeStart(t.selection.getRng()),Al(o)?im(t,o):am(t,r,n,o,i);var t,n,r,o,i}).getOr(!1)},cm=function(e,t){var n,r,o,i,a,u=or.fromDom(e.selection.getStart(!0)),s=Zd(e);return e.selection.isCollapsed()&&0===s.length?sm(e,t,u):(n=e,r=u,o=or.fromDom(n.getBody()),i=n.selection.getRng(),0!==(a=Zd(n)).length?em(n,a):nm(n,o,i,r))},lm=mc.isEq,fm=function(e,t,n){var r=e.formatter.get(n);if(r)for(var o=0;o<r.length;o++)if(!1===r[o].inherit&&e.dom.is(t,r[o].selector))return!0;return!1},dm=function(t,e,n,r){var o=t.dom.getRoot();return e!==o&&(e=t.dom.getParent(e,function(e){return!!fm(t,e,n)||e.parentNode===o||!!pm(t,e,n,r,!0)}),pm(t,e,n,r))},mm=function(e,t,n){return!!lm(t,n.inline)||!!lm(t,n.block)||(n.selector?1===t.nodeType&&e.is(t,n.selector):void 0)},gm=function(e,t,n,r,o,i){var a,u,s,c=n[r];if(n.onmatch)return n.onmatch(t,n,r);if(c)if("undefined"==typeof c.length){for(a in c)if(c.hasOwnProperty(a)){if(u="attributes"===r?e.getAttrib(t,a):mc.getStyle(e,t,a),o&&!u&&!n.exact)return;if((!o||n.exact)&&!lm(u,mc.normalizeStyleValue(e,mc.replaceVars(c[a],i),a)))return}}else for(s=0;s<c.length;s++)if("attributes"===r?e.getAttrib(t,c[s]):mc.getStyle(e,t,c[s]))return n;return n},pm=function(e,t,n,r,o){var i,a,u,s,c=e.formatter.get(n),l=e.dom;if(c&&t)for(a=0;a<c.length;a++)if(i=c[a],mm(e.dom,t,i)&&gm(l,t,i,"attributes",o,r)&&gm(l,t,i,"styles",o,r)){if(s=i.classes)for(u=0;u<s.length;u++)if(!e.dom.hasClass(t,s[u]))return;return i}},hm={matchNode:pm,matchName:mm,match:function(e,t,n,r){var o;return r?dm(e,r,t,n):(r=e.selection.getNode(),!!dm(e,r,t,n)||!((o=e.selection.getStart())===r||!dm(e,o,t,n)))},matchAll:function(r,o,i){var e,a=[],u={};return e=r.selection.getStart(),r.dom.getParent(e,function(e){var t,n;for(t=0;t<o.length;t++)n=o[t],!u[n]&&pm(r,e,n,i)&&(u[n]=!0,a.push(n))},r.dom.getRoot()),a},canApply:function(e,t){var n,r,o,i,a,u=e.formatter.get(t),s=e.dom;if(u)for(n=e.selection.getStart(),r=mc.getParents(s,n),i=u.length-1;0<=i;i--){if(!(a=u[i].selector)||u[i].defaultBlock)return!0;for(o=r.length-1;0<=o;o--)if(s.is(r[o],a))return!0}return!1},matchesUnInheritedFormatSelector:fm},vm=function(e,t){return e.splitText(t)},bm=function(e){var t=e.startContainer,n=e.startOffset,r=e.endContainer,o=e.endOffset;return t===r&&Bo.isText(t)?0<n&&n<t.nodeValue.length&&(t=(r=vm(t,n)).previousSibling,n<o?(t=r=vm(r,o-=n).previousSibling,o=r.nodeValue.length,n=0):o=0):(Bo.isText(t)&&0<n&&n<t.nodeValue.length&&(t=vm(t,n),n=0),Bo.isText(r)&&0<o&&o<r.nodeValue.length&&(o=(r=vm(r,o).previousSibling).nodeValue.length)),{startContainer:t,startOffset:n,endContainer:r,endOffset:o}},ym=fa,Cm="_mce_caret",xm=function(e){return 0<function(e){for(var t=[];e;){if(3===e.nodeType&&e.nodeValue!==ym||1<e.childNodes.length)return[];1===e.nodeType&&t.push(e),e=e.firstChild}return t}(e).length},wm=function(e){var t;if(e)for(e=(t=new oo(e,e)).current();e;e=t.next())if(3===e.nodeType)return e;return null},Nm=function(e){var t=or.fromTag("span");return yr(t,{id:Cm,"data-mce-bogus":"1","data-mce-type":"format-caret"}),e&&Ai(t,or.fromText(ym)),t},Em=function(e,t,n){void 0===n&&(n=!0);var r,o=e.dom,i=e.selection;if(xm(t))Lf(e,!1,or.fromDom(t),n);else{var a=i.getRng(),u=o.getParent(t,o.isBlock),s=((r=wm(t))&&r.nodeValue.charAt(0)===ym&&r.deleteData(0,1),r);a.startContainer===s&&0<a.startOffset&&a.setStart(s,a.startOffset-1),a.endContainer===s&&0<a.endOffset&&a.setEnd(s,a.endOffset-1),o.remove(t,!0),u&&o.isEmpty(u)&&Kc(or.fromDom(u)),i.setRng(a)}},Sm=function(e,t,n){void 0===n&&(n=!0);var r=e.dom,o=e.selection;if(t)Em(e,t,n);else if(!(t=Vu(e.getBody(),o.getStart())))for(;t=r.get(Cm);)Em(e,t,!1)},km=function(e,t,n){var r=e.dom,o=r.getParent(n,d(mc.isTextBlock,e));o&&r.isEmpty(o)?n.parentNode.replaceChild(t,n):(Wc(or.fromDom(n)),r.isEmpty(n)?n.parentNode.replaceChild(t,n):r.insertAfter(t,n))},Tm=function(e,t){return e.appendChild(t),t},Am=function(e,t){var n,r,o=(n=function(e,t){return Tm(e,t.cloneNode(!1))},r=t,function(e,t){for(var n=e.length-1;0<=n;n--)t(e[n],n,e)}(e,function(e){r=n(r,e)}),r);return Tm(o,o.ownerDocument.createTextNode(ym))},Rm=function(i){i.on("mouseup keydown",function(e){var t,n,r,o;t=i,n=e.keyCode,r=t.selection,o=t.getBody(),Sm(t,null,!1),8!==n&&46!==n||!r.isCollapsed()||r.getStart().innerHTML!==ym||Sm(t,Vu(o,r.getStart())),37!==n&&39!==n||Sm(t,Vu(o,r.getStart()))})},_m=function(e,t){return e.schema.getTextInlineElements().hasOwnProperty(sr(t))&&!Uu(t.dom())&&!Bo.isBogus(t.dom())},Dm=function(e){return 1===Hr(e).length},Bm=function(e,t,n,r){var o,i,a,u,s=d(_m,t),c=W(z(r,s),function(e){return e.dom()});if(0===c.length)Lf(t,e,n);else{var l=(o=n.dom(),i=c,a=Nm(!1),u=Am(i,a.dom()),Si(or.fromDom(o),a),Di(or.fromDom(o)),vu(u,0));t.selection.setRng(l.toRange())}},Om=function(r,o){var t,e=or.fromDom(r.getBody()),n=or.fromDom(r.selection.getStart()),i=z((t=Ml(n,e),X(t,lo).fold(q(t),function(e){return t.slice(0,e)})),Dm);return ne(i).map(function(e){var t,n=vu.fromRangeStart(r.selection.getRng());return!(!wl(o,n,e.dom())||Uu((t=e).dom())&&xm(t.dom())||(Bm(o,r,e,i),0))}).getOr(!1)},Pm=function(e,t){return!!e.selection.isCollapsed()&&Om(e,t)},Lm=Bo.isContentEditableTrue,Im=Bo.isContentEditableFalse,Mm=function(e,t,n,r,o){return t._selectionOverrides.showCaret(e,n,r,o)},Fm=function(e,t){var n,r;return e.fire("BeforeObjectSelected",{target:t}).isDefaultPrevented()?null:((r=(n=t).ownerDocument.createRange()).selectNode(n),r)},zm=function(e,t,n){var r=xs(1,e.getBody(),t),o=vu.fromRangeStart(r),i=o.getNode();if(Im(i))return Mm(1,e,i,!o.isAtEnd(),!1);var a=o.getNode(!0);if(Im(a))return Mm(1,e,a,!1,!1);var u=e.dom.getParent(o.getNode(),function(e){return Im(e)||Lm(e)});return Im(u)?Mm(1,e,u,!1,n):null},Um=function(e,t,n){if(!t||!t.collapsed)return t;var r=zm(e,t,n);return r||t},Vm=function(e,t,n,r,o,i){var a,u,s=Mm(r,e,i.getNode(!o),o,!0);if(t.collapsed){var c=t.cloneRange();o?c.setEnd(s.startContainer,s.startOffset):c.setStart(s.endContainer,s.endOffset),c.deleteContents()}else t.deleteContents();return e.selection.setRng(s),a=e.dom,u=n,Bo.isText(u)&&0===u.data.length&&a.remove(u),!0},Hm=function(e,t){return function(e,t){var n=e.selection.getRng();if(!Bo.isText(n.commonAncestorContainer))return!1;var r=t?pu.Forwards:pu.Backwards,o=Hs(e.getBody()),i=d(Ss,o.next),a=d(Ss,o.prev),u=t?i:a,s=t?mf:gf,c=Ns(r,e.getBody(),n),l=yl.normalizePosition(t,u(c));if(!l)return!1;if(s(l))return Vm(e,n,c.getNode(),r,t,l);var f=u(l);return!!(f&&s(f)&&ks(l,f))&&Vm(e,n,c.getNode(),r,t,f)}(e,t)},jm=function(e,t){e.getDoc().execCommand(t,!1,null)},qm=function(e){Ff(e,!1)||Hm(e,!1)||_d(e,!1)||Wl(e,!1)||cm(e)||Gl(e,!1)||Pm(e,!1)||(jm(e,"Delete"),xl(e))},$m=function(e){Ff(e,!0)||Hm(e,!0)||_d(e,!0)||Wl(e,!0)||cm(e)||Gl(e,!0)||Pm(e,!0)||jm(e,"ForwardDelete")},Wm=function(e,t,n){var r=e.getParam(t,n);if(-1!==r.indexOf("=")){var o=e.getParam(t,"","hash");return o.hasOwnProperty(e.id)?o[e.id]:n}return r},Km=function(e){return e.getParam("iframe_attrs",{})},Xm=function(e){return e.getParam("doctype","<!DOCTYPE html>")},Ym=function(e){return e.getParam("document_base_url","")},Gm=function(e){return Wm(e,"body_id","tinymce")},Jm=function(e){return Wm(e,"body_class","")},Qm=function(e){return e.getParam("content_security_policy","")},Zm=function(e){return e.getParam("br_in_pre",!0)},eg=function(e){if(e.getParam("force_p_newlines",!1))return"p";var t=e.getParam("forced_root_block","p");return!1===t?"":t},tg=function(e){return e.getParam("forced_root_block_attrs",{})},ng=function(e){return e.getParam("br_newline_selector",".mce-toc h2,figcaption,caption")},rg=function(e){return e.getParam("no_newline_selector","")},og=function(e){return e.getParam("keep_styles",!0)},ig=function(e){return e.getParam("end_container_on_empty_block",!1)},ag=function(e){return Gt.explode(e.getParam("font_size_style_values",""))},ug=function(e){return Gt.explode(e.getParam("font_size_classes",""))},sg=function(e){return e.getParam("images_dataimg_filter",q(!0),"function")},cg=function(e){return e.getParam("automatic_uploads",!0,"boolean")},lg=function(e){return e.getParam("images_reuse_filename",!1,"boolean")},fg=function(e){return e.getParam("images_replace_blob_uris",!0,"boolean")},dg=function(e){return e.getParam("images_upload_url","","string")},mg=function(e){return e.getParam("images_upload_base_path","","string")},gg=function(e){return e.getParam("images_upload_credentials",!1,"boolean")},pg=function(e){return e.getParam("images_upload_handler",null,"function")},hg=function(e){return e.getParam("content_css_cors",!1,"boolean")},vg=function(o,t,e){var n=function(e){return t=o,n=e.dom(),r=Er(n,t),A.from(r).filter(function(e){return 0<e.length});var t,n,r};return Ki(or.fromDom(e),function(e){return n(e).isSome()},function(e){return Pr(or.fromDom(t),e)}).bind(n)},bg=function(o){return function(r,e){return A.from(e).map(or.fromDom).filter(lr).bind(function(e){return vg(o,r,e.dom()).or((t=o,n=e.dom(),A.from(hi.DOM.getStyle(n,t,!0))));var t,n}).getOr("")}},yg={getFontSize:bg("font-size"),getFontFamily:j(function(e){return e.replace(/[\'\"\\]/g,"").replace(/,\s+/g,",")},bg("font-family")),toPt:function(e,t){return/[0-9.]+px$/.test(e)?(n=72*parseInt(e,10)/96,r=t||0,o=Math.pow(10,r),Math.round(n*o)/o+"pt"):e;var n,r,o}},Cg=function(e){return Zs.firstPositionIn(e.getBody()).map(function(e){var t=e.container();return Bo.isText(t)?t.parentNode:t})},xg=function(o){return A.from(o.selection.getRng()).bind(function(e){var t,n,r=o.getBody();return n=r,(t=e).startContainer===n&&0===t.startOffset?A.none():A.from(o.selection.getStart(!0))})},wg=function(e,t){if(/^[0-9\.]+$/.test(t)){var n=parseInt(t,10);if(1<=n&&n<=7){var r=ag(e),o=ug(e);return o?o[n-1]||t:r[n-1]||t}return t}return t},Ng=function(e,t){return e&&t&&e.startContainer===t.startContainer&&e.startOffset===t.startOffset&&e.endContainer===t.endContainer&&e.endOffset===t.endOffset},Eg=function(e,t,n){return null!==function(e,t,n){for(;e&&e!==t;){if(n(e))return e;e=e.parentNode}return null}(e,t,n)},Sg=function(e,t,n){return Eg(e,t,function(e){return e.nodeName===n})},kg=function(e){return e&&"TABLE"===e.nodeName},Tg=function(e,t,n){for(var r=new oo(t,e.getParent(t.parentNode,e.isBlock)||e.getRoot());t=r[n?"prev":"next"]();)if(Bo.isBr(t))return!0},Ag=function(e,t,n,r,o){var i,a,u,s,c,l,f=e.getRoot(),d=e.schema.getNonEmptyElements();if(u=e.getParent(o.parentNode,e.isBlock)||f,r&&Bo.isBr(o)&&t&&e.isEmpty(u))return A.some(gu(o.parentNode,e.nodeIndex(o)));for(i=new oo(o,u);s=i[r?"prev":"next"]();){if("false"===e.getContentEditableParent(s)||(l=f,va(c=s)&&!1===Eg(c,l,Uu)))return A.none();if(Bo.isText(s)&&0<s.nodeValue.length)return!1===Sg(s,f,"A")?A.some(gu(s,r?s.nodeValue.length:0)):A.none();if(e.isBlock(s)||d[s.nodeName.toLowerCase()])return A.none();a=s}return n&&a?A.some(gu(a,0)):A.none()},Rg=function(e,t,n,r){var o,i,a,u,s,c,l,f,d,m,g=e.getRoot(),p=!1;if(o=r[(n?"start":"end")+"Container"],i=r[(n?"start":"end")+"Offset"],l=Bo.isElement(o)&&i===o.childNodes.length,s=e.schema.getNonEmptyElements(),c=n,va(o))return A.none();if(Bo.isElement(o)&&i>o.childNodes.length-1&&(c=!1),Bo.isDocument(o)&&(o=g,i=0),o===g){if(c&&(u=o.childNodes[0<i?i-1:0])){if(va(u))return A.none();if(s[u.nodeName]||kg(u))return A.none()}if(o.hasChildNodes()){if(i=Math.min(!c&&0<i?i-1:i,o.childNodes.length-1),o=o.childNodes[i],i=Bo.isText(o)&&l?o.data.length:0,!t&&o===g.lastChild&&kg(o))return A.none();if(function(e,t){for(;t&&t!==e;){if(Bo.isContentEditableFalse(t))return!0;t=t.parentNode}return!1}(g,o)||va(o))return A.none();if(o.hasChildNodes()&&!1===kg(o)){a=new oo(u=o,g);do{if(Bo.isContentEditableFalse(u)||va(u)){p=!1;break}if(Bo.isText(u)&&0<u.nodeValue.length){i=c?0:u.nodeValue.length,o=u,p=!0;break}if(s[u.nodeName.toLowerCase()]&&(!(f=u)||!/^(TD|TH|CAPTION)$/.test(f.nodeName))){i=e.nodeIndex(u),o=u.parentNode,c||i++,p=!0;break}}while(u=c?a.next():a.prev())}}}return t&&(Bo.isText(o)&&0===i&&Ag(e,l,t,!0,o).each(function(e){o=e.container(),i=e.offset(),p=!0}),Bo.isElement(o)&&((u=o.childNodes[i])||(u=o.childNodes[i-1]),!u||!Bo.isBr(u)||(m="A",(d=u).previousSibling&&d.previousSibling.nodeName===m)||Tg(e,u,!1)||Tg(e,u,!0)||Ag(e,l,t,!0,u).each(function(e){o=e.container(),i=e.offset(),p=!0}))),c&&!t&&Bo.isText(o)&&i===o.nodeValue.length&&Ag(e,l,t,!1,o).each(function(e){o=e.container(),i=e.offset(),p=!0}),p?A.some(gu(o,i)):A.none()},_g=function(e,t){var n=t.collapsed,r=t.cloneRange(),o=gu.fromRangeStart(t);return Rg(e,n,!0,r).each(function(e){n&&gu.isAbove(o,e)||r.setStart(e.container(),e.offset())}),n||Rg(e,n,!1,r).each(function(e){r.setEnd(e.container(),e.offset())}),n&&r.collapse(!0),Ng(t,r)?A.none():A.some(r)},Dg=function(e,t,n){var r=e.create("span",{},"&nbsp;");n.parentNode.insertBefore(r,n),t.scrollIntoView(r),e.remove(r)},Bg=function(e,t,n,r){var o=e.createRng();r?(o.setStartBefore(n),o.setEndBefore(n)):(o.setStartAfter(n),o.setEndAfter(n)),t.setRng(o)},Og=function(e,t){var n,r,o=e.selection,i=e.dom,a=o.getRng();_g(i,a).each(function(e){a.setStart(e.startContainer,e.startOffset),a.setEnd(e.endContainer,e.endOffset)});var u=a.startOffset,s=a.startContainer;if(1===s.nodeType&&s.hasChildNodes()){var c=u>s.childNodes.length-1;s=s.childNodes[Math.min(u,s.childNodes.length-1)]||s,u=c&&3===s.nodeType?s.nodeValue.length:0}var l=i.getParent(s,i.isBlock),f=l?i.getParent(l.parentNode,i.isBlock):null,d=f?f.nodeName.toUpperCase():"",m=t&&t.ctrlKey;"LI"!==d||m||(l=f),s&&3===s.nodeType&&u>=s.nodeValue.length&&(function(e,t,n){for(var r,o=new oo(t,n),i=e.getNonEmptyElements();r=o.next();)if(i[r.nodeName.toLowerCase()]||0<r.length)return!0}(e.schema,s,l)||(n=i.create("br"),a.insertNode(n),a.setStartAfter(n),a.setEndAfter(n),r=!0)),n=i.create("br"),Au(i,a,n),Dg(i,o,n),Bg(i,o,n,r),e.undoManager.add()},Pg=function(e,t){var n=or.fromTag("br");Si(or.fromDom(t),n),e.undoManager.add()},Lg=function(e,t){Ig(e.getBody(),t)||ki(or.fromDom(t),or.fromTag("br"));var n=or.fromTag("br");ki(or.fromDom(t),n),Dg(e.dom,e.selection,n.dom()),Bg(e.dom,e.selection,n.dom(),!1),e.undoManager.add()},Ig=function(e,t){return n=vu.after(t),!!Bo.isBr(n.getNode())||Zs.nextPosition(e,vu.after(t)).map(function(e){return Bo.isBr(e.getNode())}).getOr(!1);var n},Mg=function(e){return e&&"A"===e.nodeName&&"href"in e},Fg=function(e){return e.fold(q(!1),Mg,Mg,q(!1))},zg=function(e,t){t.fold(o,d(Pg,e),d(Lg,e),o)},Ug=function(e,t){var n,r,o,i=(n=e,r=d(yl.isInlineTarget,n),o=vu.fromRangeStart(n.selection.getRng()),gd(r,n.getBody(),o).filter(Fg));i.isSome()?i.each(d(zg,e)):Og(e,t)},Vg=(Jl([{before:["element"]},{on:["element","offset"]},{after:["element"]}]),Jl([{domRange:["rng"]},{relative:["startSitu","finishSitu"]},{exact:["start","soffset","finish","foffset"]}]),Sr("start","soffset","finish","foffset")),Hg=nr.detect().browser,jg=function(e,t){var n=fr(t)?Rc(t).length:Hr(t).length+1;return n<e?n:e<0?0:e},qg=function(e){return Vg(e.start(),jg(e.soffset(),e.start()),e.finish(),jg(e.foffset(),e.finish()))},$g=function(e,t){return Lr(e,t)||Pr(e,t)},Wg=function(t){return function(e){return $g(t,e.start())&&$g(t,e.finish())}},Kg=function(e){return!0===e.inline||Hg.isIE()},Xg=function(e){return Vg(or.fromDom(e.startContainer),e.startOffset,or.fromDom(e.endContainer),e.endOffset)},Yg=function(e){var t=e.getSelection();return(t&&0!==t.rangeCount?A.from(t.getRangeAt(0)):A.none()).map(Xg)},Gg=function(e){var t,n=(t=e.dom().ownerDocument.defaultView,or.fromDom(t));return Yg(n.dom()).filter(Wg(e))},Jg=function(e,t){return A.from(t).filter(Wg(e)).map(qg)},Qg=function(e){var t=H.document.createRange();try{return t.setStart(e.start().dom(),e.soffset()),t.setEnd(e.finish().dom(),e.foffset()),A.some(t)}catch(n){return A.none()}},Zg=function(e){return(e.bookmark?e.bookmark:A.none()).bind(d(Jg,or.fromDom(e.getBody()))).bind(Qg)},ep=function(e){var t=Kg(e)?Gg(or.fromDom(e.getBody())):A.none();e.bookmark=t.isSome()?t:e.bookmark},tp=function(t){Zg(t).each(function(e){t.selection.setRng(e)})},np=Zg,rp=function(e){return po(e)||ho(e)},op=function(e){return z(W(e.selection.getSelectedBlocks(),or.fromDom),function(e){return!rp(e)&&!Mr(e).map(rp).getOr(!1)})},ip=function(e,t){var n=e.settings,r=e.dom,o=e.selection,i=e.formatter,a=/[a-z%]+$/i.exec(n.indentation)[0],u=parseInt(n.indentation,10),s=e.getParam("indent_use_margin",!1);e.queryCommandState("InsertUnorderedList")||e.queryCommandState("InsertOrderedList")||n.forced_root_block||r.getParent(o.getNode(),r.isBlock)||i.apply("div"),F(op(e),function(e){!function(e,t,n,r,o,i){if("false"!==e.getContentEditable(i)){var a=n?"margin":"padding";if(a="TABLE"===i.nodeName?"margin":a,a+="rtl"===e.getStyle(i,"direction",!0)?"Right":"Left","outdent"===t){var u=Math.max(0,parseInt(i.style[a]||0,10)-r);e.setStyle(i,a,u?u+o:"")}else u=parseInt(i.style[a]||0,10)+r+o,e.setStyle(i,a,u)}}(r,t,s,u,a,e.dom())})},ap=Gt.each,up=Gt.extend,sp=Gt.map,cp=Gt.inArray;function lp(s){var o,i,a,t,c={state:{},exec:{},value:{}},n=s.settings;s.on("PreInit",function(){o=s.dom,i=s.selection,n=s.settings,a=s.formatter});var r=function(e){var t;if(!s.quirks.isHidden()&&!s.removed){if(e=e.toLowerCase(),t=c.state[e])return t(e);try{return s.getDoc().queryCommandState(e)}catch(n){}return!1}},e=function(e,n){n=n||"exec",ap(e,function(t,e){ap(e.toLowerCase().split(","),function(e){c[n][e]=t})})},u=function(e,t,n){e=e.toLowerCase(),c.value[e]=function(){return t.call(n||s)}};up(this,{execCommand:function(t,n,r,e){var o,i,a=!1;if(!s.removed){if(/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(t)||e&&e.skip_focus?tp(s):s.focus(),(e=s.fire("BeforeExecCommand",{command:t,ui:n,value:r})).isDefaultPrevented())return!1;if(i=t.toLowerCase(),o=c.exec[i])return o(i,n,r),s.fire("ExecCommand",{command:t,ui:n,value:r}),!0;if(ap(s.plugins,function(e){if(e.execCommand&&e.execCommand(t,n,r))return s.fire("ExecCommand",{command:t,ui:n,value:r}),!(a=!0)}),a)return a;if(s.theme&&s.theme.execCommand&&s.theme.execCommand(t,n,r))return s.fire("ExecCommand",{command:t,ui:n,value:r}),!0;try{a=s.getDoc().execCommand(t,n,r)}catch(u){}return!!a&&(s.fire("ExecCommand",{command:t,ui:n,value:r}),!0)}},queryCommandState:r,queryCommandValue:function(e){var t;if(!s.quirks.isHidden()&&!s.removed){if(e=e.toLowerCase(),t=c.value[e])return t(e);try{return s.getDoc().queryCommandValue(e)}catch(n){}}},queryCommandSupported:function(e){if(e=e.toLowerCase(),c.exec[e])return!0;try{return s.getDoc().queryCommandSupported(e)}catch(t){}return!1},addCommands:e,addCommand:function(e,o,i){e=e.toLowerCase(),c.exec[e]=function(e,t,n,r){return o.call(i||s,t,n,r)}},addQueryStateHandler:function(e,t,n){e=e.toLowerCase(),c.state[e]=function(){return t.call(n||s)}},addQueryValueHandler:u,hasCustomCommand:function(e){return e=e.toLowerCase(),!!c.exec[e]}});var l=function(e,t,n){return t===undefined&&(t=!1),n===undefined&&(n=null),s.getDoc().execCommand(e,t,n)},f=function(e){return a.match(e)},d=function(e,t){a.toggle(e,t?{value:t}:undefined),s.nodeChanged()},m=function(e){t=i.getBookmark(e)},g=function(){i.moveToBookmark(t)};e({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){s.undoManager.add()},"Cut,Copy,Paste":function(e){var t,n=s.getDoc();try{l(e)}catch(o){t=!0}if("paste"!==e||n.queryCommandEnabled(e)||(t=!0),t||!n.queryCommandSupported(e)){var r=s.translate("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.");me.mac&&(r=r.replace(/Ctrl\+/g,"\u2318+")),s.notificationManager.open({text:r,type:"error"})}},unlink:function(){if(i.isCollapsed()){var e=s.dom.getParent(s.selection.getStart(),"a");e&&s.dom.remove(e,!0)}else a.remove("link")},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,JustifyNone":function(e){var t=e.substring(7);"full"===t&&(t="justify"),ap("left,center,right,justify".split(","),function(e){t!==e&&a.remove("align"+e)}),"none"!==t&&d("align"+t)},"InsertUnorderedList,InsertOrderedList":function(e){var t,n;l(e),(t=o.getParent(i.getNode(),"ol,ul"))&&(n=t.parentNode,/^(H[1-6]|P|ADDRESS|PRE)$/.test(n.nodeName)&&(m(),o.split(n,t),g()))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){d(e)},"ForeColor,HiliteColor":function(e,t,n){d(e,n)},FontName:function(e,t,n){var r,o;o=n,(r=s).formatter.toggle("fontname",{value:wg(r,o)}),r.nodeChanged()},FontSize:function(e,t,n){var r,o;o=n,(r=s).formatter.toggle("fontsize",{value:wg(r,o)}),r.nodeChanged()},RemoveFormat:function(e){a.remove(e)},mceBlockQuote:function(){d("blockquote")},FormatBlock:function(e,t,n){return d(n||"p")},mceCleanup:function(){var e=i.getBookmark();s.setContent(s.getContent()),i.moveToBookmark(e)},mceRemoveNode:function(e,t,n){var r=n||i.getNode();r!==s.getBody()&&(m(),s.dom.remove(r,!0),g())},mceSelectNodeDepth:function(e,t,n){var r=0;o.getParent(i.getNode(),function(e){if(1===e.nodeType&&r++===n)return i.select(e),!1},s.getBody())},mceSelectNode:function(e,t,n){i.select(n)},mceInsertContent:function(e,t,n){ol(s,n)},mceInsertRawHTML:function(e,t,n){i.setContent("tiny_mce_marker");var r=s.getContent();s.setContent(r.replace(/tiny_mce_marker/g,function(){return n}))},mceToggleFormat:function(e,t,n){d(n)},mceSetContent:function(e,t,n){s.setContent(n)},"Indent,Outdent":function(e){ip(s,e)},mceRepaint:function(){},InsertHorizontalRule:function(){s.execCommand("mceInsertContent",!1,"<hr />")},mceToggleVisualAid:function(){s.hasVisual=!s.hasVisual,s.addVisual()},mceReplaceContent:function(e,t,n){s.execCommand("mceInsertContent",!1,n.replace(/\{\$selection\}/g,i.getContent({format:"text"})))},mceInsertLink:function(e,t,n){var r;"string"==typeof n&&(n={href:n}),r=o.getParent(i.getNode(),"a"),n.href=n.href.replace(" ","%20"),r&&n.href||a.remove("link"),n.href&&a.apply("link",n,r)},selectAll:function(){var e=o.getParent(i.getStart(),Bo.isContentEditableTrue);if(e){var t=o.createRng();t.selectNodeContents(e),i.setRng(t)}},"delete":function(){qm(s)},forwardDelete:function(){$m(s)},mceNewDocument:function(){s.setContent("")},InsertLineBreak:function(e,t,n){return Ug(s,n),!0}});var p=function(n){return function(){var e=i.isCollapsed()?[o.getParent(i.getNode(),o.isBlock)]:i.getSelectedBlocks(),t=sp(e,function(e){return!!a.matchNode(e,n)});return-1!==cp(t,!0)}};e({JustifyLeft:p("alignleft"),JustifyCenter:p("aligncenter"),JustifyRight:p("alignright"),JustifyFull:p("alignjustify"),"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){return f(e)},mceBlockQuote:function(){return f("blockquote")},Outdent:function(){var e;if(n.inline_styles){if((e=o.getParent(i.getStart(),o.isBlock))&&0<parseInt(e.style.paddingLeft,10))return!0;if((e=o.getParent(i.getEnd(),o.isBlock))&&0<parseInt(e.style.paddingLeft,10))return!0}return r("InsertUnorderedList")||r("InsertOrderedList")||!n.inline_styles&&!!o.getParent(i.getNode(),"BLOCKQUOTE")},"InsertUnorderedList,InsertOrderedList":function(e){var t=o.getParent(i.getNode(),"ul,ol");return t&&("insertunorderedlist"===e&&"UL"===t.tagName||"insertorderedlist"===e&&"OL"===t.tagName)}},"state"),e({Undo:function(){s.undoManager.undo()},Redo:function(){s.undoManager.redo()}}),u("FontName",function(){return xg(t=s).fold(function(){return Cg(t).map(function(e){return yg.getFontFamily(t.getBody(),e)}).getOr("")},function(e){return yg.getFontFamily(t.getBody(),e)});var t},this),u("FontSize",function(){return xg(t=s).fold(function(){return Cg(t).map(function(e){return yg.getFontSize(t.getBody(),e)}).getOr("")},function(e){return yg.getFontSize(t.getBody(),e)});var t},this)}var fp=Gt.makeMap("focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover draggesture dragdrop drop drag submit compositionstart compositionend compositionupdate touchstart touchmove touchend"," "),dp=function(a){var u,s,c=this,l={},f=function(){return!1},d=function(){return!0};u=(a=a||{}).scope||c,s=a.toggleEvent||f;var r=function(e,t,n,r){var o,i,a;if(!1===t&&(t=f),t)for(t={func:t},r&&Gt.extend(t,r),a=(i=e.toLowerCase().split(" ")).length;a--;)e=i[a],(o=l[e])||(o=l[e]=[],s(e,!0)),n?o.unshift(t):o.push(t);return c},m=function(e,t){var n,r,o,i,a;if(e)for(n=(i=e.toLowerCase().split(" ")).length;n--;){if(e=i[n],r=l[e],!e){for(o in l)s(o,!1),delete l[o];return c}if(r){if(t)for(a=r.length;a--;)r[a].func===t&&(r=r.slice(0,a).concat(r.slice(a+1)),l[e]=r);else r.length=0;r.length||(s(e,!1),delete l[e])}}else{for(e in l)s(e,!1);l={}}return c};c.fire=function(e,t){var n,r,o,i;if(e=e.toLowerCase(),(t=t||{}).type=e,t.target||(t.target=u),t.preventDefault||(t.preventDefault=function(){t.isDefaultPrevented=d},t.stopPropagation=function(){t.isPropagationStopped=d},t.stopImmediatePropagation=function(){t.isImmediatePropagationStopped=d},t.isDefaultPrevented=f,t.isPropagationStopped=f,t.isImmediatePropagationStopped=f),a.beforeFire&&a.beforeFire(t),n=l[e])for(r=0,o=n.length;r<o;r++){if((i=n[r]).once&&m(e,i.func),t.isImmediatePropagationStopped())return t.stopPropagation(),t;if(!1===i.func.call(u,t))return t.preventDefault(),t}return t},c.on=r,c.off=m,c.once=function(e,t,n){return r(e,t,n,{once:!0})},c.has=function(e){return e=e.toLowerCase(),!(!l[e]||0===l[e].length)}};dp.isNative=function(e){return!!fp[e.toLowerCase()]};var mp,gp=function(n){return n._eventDispatcher||(n._eventDispatcher=new dp({scope:n,toggleEvent:function(e,t){dp.isNative(e)&&n.toggleNativeEvent&&n.toggleNativeEvent(e,t)}})),n._eventDispatcher},pp={fire:function(e,t,n){if(this.removed&&"remove"!==e&&"detach"!==e)return t;if(t=gp(this).fire(e,t,n),!1!==n&&this.parent)for(var r=this.parent();r&&!t.isPropagationStopped();)r.fire(e,t,!1),r=r.parent();return t},on:function(e,t,n){return gp(this).on(e,t,n)},off:function(e,t){return gp(this).off(e,t)},once:function(e,t){return gp(this).once(e,t)},hasEventListeners:function(e){return gp(this).has(e)}},hp=function(e,t){return e.fire("PreProcess",t)},vp=function(e,t){return e.fire("PostProcess",t)},bp=function(e){return e.fire("remove")},yp=function(e){return e.fire("detach")},Cp=function(e,t){return e.fire("SwitchMode",{mode:t})},xp=function(e,t,n,r){e.fire("ObjectResizeStart",{target:t,width:n,height:r})},wp=function(e,t,n,r){e.fire("ObjectResized",{target:t,width:n,height:r})},Np=function(e,t,n){try{e.getDoc().execCommand(t,!1,n)}catch(r){}},Ep=function(e,t,n){var r,o;Vi(e,t)&&!1===n?(o=t,Ii(r=e)?r.dom().classList.remove(o):Fi(r,o),Ui(r)):n&&zi(e,t)},Sp=function(e,t){Ep(or.fromDom(e.getBody()),"mce-content-readonly",t),t?(e.selection.controlSelection.hideResizeRect(),e.readonly=!0,e.getBody().contentEditable="false"):(e.readonly=!1,e.getBody().contentEditable="true",Np(e,"StyleWithCSS",!1),Np(e,"enableInlineTableEditing",!1),Np(e,"enableObjectResizing",!1),e.focus(),e.nodeChanged())},kp=function(e){return e.readonly?"readonly":"design"},Tp=hi.DOM,Ap=function(e,t){return"selectionchange"===t?e.getDoc():!e.inline&&/^mouse|touch|click|contextmenu|drop|dragover|dragend/.test(t)?e.getDoc().documentElement:e.settings.event_root?(e.eventRoot||(e.eventRoot=Tp.select(e.settings.event_root)[0]),e.eventRoot):e.getBody()},Rp=function(e,t,n){var r;(r=e).hidden||r.readonly?!0===e.readonly&&n.preventDefault():e.fire(t,n)},_p=function(i,a){var e,t;if(i.delegates||(i.delegates={}),!i.delegates[a]&&!i.removed)if(e=Ap(i,a),i.settings.event_root){if(mp||(mp={},i.editorManager.on("removeEditor",function(){var e;if(!i.editorManager.activeEditor&&mp){for(e in mp)i.dom.unbind(Ap(i,e));mp=null}})),mp[a])return;t=function(e){for(var t=e.target,n=i.editorManager.get(),r=n.length;r--;){var o=n[r].getBody();(o===t||Tp.isChildOf(t,o))&&Rp(n[r],a,e)}},mp[a]=t,Tp.bind(e,a,t)}else t=function(e){Rp(i,a,e)},Tp.bind(e,a,t),i.delegates[a]=t},Dp={bindPendingEventDelegates:function(){var t=this;Gt.each(t._pendingNativeEvents,function(e){_p(t,e)})},toggleNativeEvent:function(e,t){var n=this;"focus"!==e&&"blur"!==e&&(t?n.initialized?_p(n,e):n._pendingNativeEvents?n._pendingNativeEvents.push(e):n._pendingNativeEvents=[e]:n.initialized&&(n.dom.unbind(Ap(n,e),e,n.delegates[e]),delete n.delegates[e]))},unbindAllNativeEvents:function(){var e,t=this,n=t.getBody(),r=t.dom;if(t.delegates){for(e in t.delegates)t.dom.unbind(Ap(t,e),e,t.delegates[e]);delete t.delegates}!t.inline&&n&&r&&(n.onload=null,r.unbind(t.getWin()),r.unbind(t.getDoc())),r&&(r.unbind(n),r.unbind(t.getContainer()))}},Bp=Dp=Gt.extend({},pp,Dp),Op=Gt.each,Pp=Gt.explode,Lp={f1:112,f2:113,f3:114,f4:115,f5:116,f6:117,f7:118,f8:119,f9:120,f10:121,f11:122,f12:123},Ip=Gt.makeMap("alt,ctrl,shift,meta,access");function Mp(i){var a={},r=[],u=function(e){var t,n,r={};for(n in Op(Pp(e,"+"),function(e){e in Ip?r[e]=!0:/^[0-9]{2,}$/.test(e)?r.keyCode=parseInt(e,10):(r.charCode=e.charCodeAt(0),r.keyCode=Lp[e]||e.toUpperCase().charCodeAt(0))}),t=[r.keyCode],Ip)r[n]?t.push(n):r[n]=!1;return r.id=t.join(","),r.access&&(r.alt=!0,me.mac?r.ctrl=!0:r.shift=!0),r.meta&&(me.mac?r.meta=!0:(r.ctrl=!0,r.meta=!1)),r},s=function(e,t,n,r){var o;return(o=Gt.map(Pp(e,">"),u))[o.length-1]=Gt.extend(o[o.length-1],{func:n,scope:r||i}),Gt.extend(o[0],{desc:i.translate(t),subpatterns:o.slice(1)})},o=function(e,t){return!!t&&t.ctrl===e.ctrlKey&&t.meta===e.metaKey&&t.alt===e.altKey&&t.shift===e.shiftKey&&!!(e.keyCode===t.keyCode||e.charCode&&e.charCode===t.charCode)&&(e.preventDefault(),!0)},c=function(e){return e.func?e.func.call(e.scope):null};i.on("keyup keypress keydown",function(t){var e,n;((n=t).altKey||n.ctrlKey||n.metaKey||"keydown"===(e=t).type&&112<=e.keyCode&&e.keyCode<=123)&&!t.isDefaultPrevented()&&(Op(a,function(e){if(o(t,e))return r=e.subpatterns.slice(0),"keydown"===t.type&&c(e),!0}),o(t,r[0])&&(1===r.length&&"keydown"===t.type&&c(r[0]),r.shift()))}),this.add=function(e,n,r,o){var t;return"string"==typeof(t=r)?r=function(){i.execCommand(t,!1,null)}:Gt.isArray(t)&&(r=function(){i.execCommand(t[0],t[1],t[2])}),Op(Pp(Gt.trim(e.toLowerCase())),function(e){var t=s(e,n,r,o);a[t.id]=t}),!0},this.remove=function(e){var t=s(e);return!!a[t.id]&&(delete a[t.id],!0)}}var Fp=function(e){var t=Ir(e).dom();return e.dom()===t.activeElement},zp=function(t){return(e=Ir(t),n=e!==undefined?e.dom():H.document,A.from(n.activeElement).map(or.fromDom)).filter(function(e){return t.dom().contains(e.dom())});var e,n},Up=function(t,e){return(n=e,n.collapsed?A.from(ja(n.startContainer,n.startOffset)).map(or.fromDom):A.none()).bind(function(e){return bo(e)?A.some(e):!1===Lr(t,e)?A.some(t):A.none()});var n},Vp=function(t,e){Up(or.fromDom(t.getBody()),e).bind(function(e){return Zs.firstPositionIn(e.dom())}).fold(function(){t.selection.normalize()},function(e){return t.selection.setRng(e.toRange())})},Hp=function(e){if(e.setActive)try{e.setActive()}catch(t){e.focus()}else e.focus()},jp=function(e){var t,n=e.getBody();return n&&(t=or.fromDom(n),Fp(t)||zp(t).isSome())},qp=function(e){return e.inline?jp(e):(t=e).iframeElement&&Fp(or.fromDom(t.iframeElement));var t},$p=function(e){return e.editorManager.setActive(e)},Wp=function(e,t){e.removed||(t?$p(e):function(t){var e=t.selection,n=t.settings.content_editable,r=t.getBody(),o=e.getRng();t.quirks.refreshContentEditable();var i,a,u=(i=t,a=e.getNode(),i.dom.getParent(a,function(e){return"true"===i.dom.getContentEditable(e)}));if(t.$.contains(r,u))return Hp(u),Vp(t,o),$p(t);t.bookmark!==undefined&&!1===qp(t)&&np(t).each(function(e){t.selection.setRng(e),o=e}),n||(me.opera||Hp(r),t.getWin().focus()),(me.gecko||n)&&(Hp(r),Vp(t,o)),$p(t)}(e))},Kp=qp,Xp=function(e,t){return t.dom()[e]},Yp=function(e,t){return parseInt(Nr(t,e),10)},Gp=d(Xp,"clientWidth"),Jp=d(Xp,"clientHeight"),Qp=d(Yp,"margin-top"),Zp=d(Yp,"margin-left"),eh=function(e,t,n){var r,o,i,a,u,s,c,l,f,d,m,g=or.fromDom(e.getBody()),p=e.inline?g:(r=g,or.fromDom(r.dom().ownerDocument.documentElement)),h=(o=e.inline,a=t,u=n,s=(i=p).dom().getBoundingClientRect(),{x:a-(o?s.left+i.dom().clientLeft+Zp(i):0),y:u-(o?s.top+i.dom().clientTop+Qp(i):0)});return l=h.x,f=h.y,d=Gp(c=p),m=Jp(c),0<=l&&0<=f&&l<=d&&f<=m},th=function(e){var t,n=e.inline?e.getBody():e.getContentAreaContainer();return(t=n,A.from(t).map(or.fromDom)).map(function(e){return Lr(Ir(e),e)}).getOr(!1)};function nh(n){var t,o=[],i=function(){var e,t=n.theme;return t&&t.getNotificationManagerImpl?t.getNotificationManagerImpl():{open:e=function(){throw new Error("Theme did not provide a NotificationManager implementation.")},close:e,reposition:e,getArgs:e}},a=function(){0<o.length&&i().reposition(o)},u=function(t){X(o,function(e){return e===t}).each(function(e){o.splice(e,1)})},r=function(r){if(!n.removed&&th(n))return V(o,function(e){return t=i().getArgs(e),n=r,!(t.type!==n.type||t.text!==n.text||t.progressBar||t.timeout||n.progressBar||n.timeout);var t,n}).getOrThunk(function(){n.editorManager.setActive(n);var e,t=i().open(r,function(){u(t),a()});return e=t,o.push(e),a(),t})};return(t=n).on("SkinLoaded",function(){var e=t.settings.service_message;e&&r({text:e,type:"warning",timeout:0,icon:""})}),t.on("ResizeEditor ResizeWindow",function(){be.requestAnimationFrame(a)}),t.on("remove",function(){F(o.slice(),function(e){i().close(e)})}),{open:r,close:function(){A.from(o[0]).each(function(e){i().close(e),u(e),a()})},getNotifications:function(){return o}}}function rh(r){var o=[],i=function(){var e,t=r.theme;return t&&t.getWindowManagerImpl?t.getWindowManagerImpl():{open:e=function(){throw new Error("Theme did not provide a WindowManager implementation.")},alert:e,confirm:e,close:e,getParams:e,setParams:e}},a=function(e,t){return function(){return t?t.apply(e,arguments):undefined}},u=function(e){var t;o.push(e),t=e,r.fire("OpenWindow",{win:t})},s=function(n){X(o,function(e){return e===n}).each(function(e){var t;o.splice(e,1),t=n,r.fire("CloseWindow",{win:t}),0===o.length&&r.focus()})},e=function(){return A.from(o[o.length-1])};return r.on("remove",function(){F(o.slice(0),function(e){i().close(e)})}),{windows:o,open:function(e,t){r.editorManager.setActive(r),ep(r);var n=i().open(e,t,s);return u(n),n},alert:function(e,t,n){var r=i().alert(e,a(n||this,t),s);u(r)},confirm:function(e,t,n){var r=i().confirm(e,a(n||this,t),s);u(r)},close:function(){e().each(function(e){i().close(e),s(e)})},getParams:function(){return e().map(i().getParams).getOr(null)},setParams:function(t){e().each(function(e){i().setParams(e,t)})},getWindows:function(){return o}}}var oh={},ih="en",ah={setCode:function(e){e&&(ih=e,this.rtl=!!this.data[e]&&"rtl"===this.data[e]._dir)},getCode:function(){return ih},rtl:!1,add:function(e,t){var n=oh[e];for(var r in n||(oh[e]=n={}),t)n[r]=t[r];this.setCode(e)},translate:function(e){var t=oh[ih]||{},n=function(e){return Gt.is(e,"function")?Object.prototype.toString.call(e):r(e)?"":""+e},r=function(e){return""===e||null===e||Gt.is(e,"undefined")},o=function(e){return e=n(e),Gt.hasOwn(t,e)?n(t[e]):e};if(r(e))return"";if(Gt.is(e,"object")&&Gt.hasOwn(e,"raw"))return n(e.raw);if(Gt.is(e,"array")){var i=e.slice(1);e=o(e[0]).replace(/\{([0-9]+)\}/g,function(e,t){return Gt.hasOwn(i,t)?n(i[t]):e})}return o(e).replace(/{context:\w+}$/,"")},data:oh},uh=Ei.PluginManager,sh=function(e,t){var n=function(e,t){for(var n in uh.urls)if(uh.urls[n]+"/plugin"+t+".js"===e)return n;return null}(t,e.suffix);return n?ah.translate(["Failed to load plugin: {0} from url {1}",n,t]):ah.translate(["Failed to load plugin url: {0}",t])},ch=function(e,t){e.notificationManager.open({type:"error",text:t})},lh=function(e,t){e._skinLoaded?ch(e,t):e.on("SkinLoaded",function(){ch(e,t)})},fh=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r=H.window.console;r&&(r.error?r.error.apply(r,arguments):r.log.apply(r,arguments))},dh={pluginLoadError:function(e,t){lh(e,sh(e,t))},pluginInitError:function(e,t,n){var r=ah.translate(["Failed to initialize plugin: {0}",t]);fh(r,n),lh(e,r)},uploadError:function(e,t){lh(e,ah.translate(["Failed to upload image: {0}",t]))},displayError:lh,initError:fh},mh=Ei.PluginManager,gh=Ei.ThemeManager;function ph(){return new(ae.getOrDie("XMLHttpRequest"))}function hh(u,s){var r={},n=function(e,r,o,t){var i,n;(i=ph()).open("POST",s.url),i.withCredentials=s.credentials,i.upload.onprogress=function(e){t(e.loaded/e.total*100)},i.onerror=function(){o("Image upload failed due to a XHR Transport error. Code: "+i.status)},i.onload=function(){var e,t,n;i.status<200||300<=i.status?o("HTTP Error: "+i.status):(e=JSON.parse(i.responseText))&&"string"==typeof e.location?r((t=s.basePath,n=e.location,t?t.replace(/\/$/,"")+"/"+n.replace(/^\//,""):n)):o("Invalid JSON: "+i.responseText)},(n=new H.FormData).append("file",e.blob(),e.filename()),i.send(n)},c=function(e,t){return{url:t,blobInfo:e,status:!0}},l=function(e,t){return{url:"",blobInfo:e,status:!1,error:t}},f=function(e,t){Gt.each(r[e],function(e){e(t)}),delete r[e]},o=function(e,n){return e=Gt.grep(e,function(e){return!u.isUploaded(e.blobUri())}),ge.all(Gt.map(e,function(e){return u.isPending(e.blobUri())?(t=e.blobUri(),new ge(function(e){r[t]=r[t]||[],r[t].push(e)})):(o=e,i=s.handler,a=n,u.markPending(o.blobUri()),new ge(function(t){var n;try{var r=function(){n&&n.close()};i(o,function(e){r(),u.markUploaded(o.blobUri(),e),f(o.blobUri(),c(o,e)),t(c(o,e))},function(e){r(),u.removeFailed(o.blobUri()),f(o.blobUri(),l(o,e)),t(l(o,e))},function(e){e<0||100<e||(n||(n=a()),n.progressBar.value(e))})}catch(e){t(l(o,e.message))}}));var o,i,a,t}))};return!1===P(s.handler)&&(s.handler=n),{upload:function(e,t){return s.url||s.handler!==n?o(e,t):new ge(function(e){e([])})}}}var vh=function(e){return ae.getOrDie("atob")(e)},bh=function(e){var t,n,r=decodeURIComponent(e).split(",");return(n=/data:([^;]+)/.exec(r[0]))&&(t=n[1]),{type:t,data:r[1]}},yh=function(a){return new ge(function(e){var t,n,r,o,i=bh(a);try{t=vh(i.data)}catch(jN){return void e(new H.Blob([]))}for(o=t.length,n=new(ae.getOrDie("Uint8Array"))(o),r=0;r<n.length;r++)n[r]=t.charCodeAt(r);e(new H.Blob([n],{type:i.type}))})},Ch=function(e){return 0===e.indexOf("blob:")?(i=e,new ge(function(e,t){var n=function(){t("Cannot convert "+i+" to Blob. Resource might not exist or is inaccessible.")};try{var r=ph();r.open("GET",i,!0),r.responseType="blob",r.onload=function(){200===this.status?e(this.response):n()},r.onerror=n,r.send()}catch(o){n()}})):0===e.indexOf("data:")?yh(e):null;var i},xh=function(n){return new ge(function(e){var t=new(ae.getOrDie("FileReader"));t.onloadend=function(){e(t.result)},t.readAsDataURL(n)})},wh=bh,Nh=0,Eh=function(e){return(e||"blobid")+Nh++},Sh=function(n,r,o,t){var i,a;0!==r.src.indexOf("blob:")?(i=wh(r.src).data,(a=n.findFirst(function(e){return e.base64()===i}))?o({image:r,blobInfo:a}):Ch(r.src).then(function(e){a=n.create(Eh(),e,i),n.add(a),o({image:r,blobInfo:a})},function(e){t(e)})):(a=n.getByUri(r.src))?o({image:r,blobInfo:a}):Ch(r.src).then(function(t){xh(t).then(function(e){i=wh(e).data,a=n.create(Eh(),t,i),n.add(a),o({image:r,blobInfo:a})})},function(e){t(e)})},kh=function(e){return e?re(e.getElementsByTagName("img")):[]},Th=0,Ah={uuid:function(e){return e+Th+++(t=function(){return Math.round(4294967295*Math.random()).toString(36)},"s"+(new Date).getTime().toString(36)+t()+t()+t());var t}};function Rh(u){var n,o,t,e,i,r,a,s,c,l=(n=[],o=function(e){var t,n,r;if(!e.blob||!e.base64)throw new Error("blob and base64 representations of the image are required for BlobInfo to be created");return t=e.id||Ah.uuid("blobid"),n=e.name||t,{id:q(t),name:q(n),filename:q(n+"."+(r=e.blob.type,{"image/jpeg":"jpg","image/jpg":"jpg","image/gif":"gif","image/png":"png"}[r.toLowerCase()]||"dat")),blob:q(e.blob),base64:q(e.base64),blobUri:q(e.blobUri||se.createObjectURL(e.blob)),uri:q(e.uri)}},{create:function(e,t,n,r){if(R(e))return o({id:e,name:r,blob:t,base64:n});if(_(e))return o(e);throw new Error("Unknown input type")},add:function(e){t(e.id())||n.push(e)},get:t=function(t){return e(function(e){return e.id()===t})},getByUri:function(t){return e(function(e){return e.blobUri()===t})},findFirst:e=function(e){return z(n,e)[0]},removeByUri:function(t){n=z(n,function(e){return e.blobUri()!==t||(se.revokeObjectURL(e.blobUri()),!1)})},destroy:function(){F(n,function(e){se.revokeObjectURL(e.blobUri())}),n=[]}}),f=(a={},s=function(e,t){return{status:e,resultUri:t}},{hasBlobUri:c=function(e){return e in a},getResultUri:function(e){var t=a[e];return t?t.resultUri:null},isPending:function(e){return!!c(e)&&1===a[e].status},isUploaded:function(e){return!!c(e)&&2===a[e].status},markPending:function(e){a[e]=s(1,null)},markUploaded:function(e,t){a[e]=s(2,t)},removeFailed:function(e){delete a[e]},destroy:function(){a={}}}),d=[],m=function(t){return function(e){return u.selection?t(e):[]}},g=function(e,t,n){for(var r=0;-1!==(r=e.indexOf(t,r))&&(e=e.substring(0,r)+n+e.substr(r+t.length),r+=n.length-t.length+1),-1!==r;);return e},p=function(e,t,n){return e=g(e,'src="'+t+'"','src="'+n+'"'),e=g(e,'data-mce-src="'+t+'"','data-mce-src="'+n+'"')},h=function(t,n){F(u.undoManager.data,function(e){"fragmented"===e.type?e.fragments=W(e.fragments,function(e){return p(e,t,n)}):e.content=p(e.content,t,n)})},v=function(){return u.notificationManager.open({text:u.translate("Image uploading..."),type:"info",timeout:-1,progressBar:!0})},b=function(e,t){l.removeByUri(e.src),h(e.src,t),u.$(e).attr({src:lg(u)?t+"?"+(new Date).getTime():t,"data-mce-src":u.convertURL(t,"src")})},y=function(n){return i||(i=hh(f,{url:dg(u),basePath:mg(u),credentials:gg(u),handler:pg(u)})),w().then(m(function(r){var e;return e=W(r,function(e){return e.blobInfo}),i.upload(e,v).then(m(function(e){var t=W(e,function(e,t){var n=r[t].image;return e.status&&fg(u)?b(n,e.url):e.error&&dh.uploadError(u,e.error),{element:n,status:e.status}});return n&&n(t),t}))}))},C=function(e){if(cg(u))return y(e)},x=function(t){return!1!==Q(d,function(e){return e(t)})&&(0!==t.getAttribute("src").indexOf("data:")||sg(u)(t))},w=function(){var o,i,a;return r||(o=f,i=l,a={},r={findAll:function(e,n){var t;n||(n=q(!0)),t=z(kh(e),function(e){var t=e.src;return!!me.fileApi&&!e.hasAttribute("data-mce-bogus")&&!e.hasAttribute("data-mce-placeholder")&&!(!t||t===me.transparentSrc)&&(0===t.indexOf("blob:")?!o.isUploaded(t)&&n(e):0===t.indexOf("data:")&&n(e))});var r=W(t,function(n){if(a[n.src])return new ge(function(t){a[n.src].then(function(e){if("string"==typeof e)return e;t({image:n,blobInfo:e.blobInfo})})});var e=new ge(function(e,t){Sh(i,n,e,t)}).then(function(e){return delete a[e.image.src],e})["catch"](function(e){return delete a[n.src],e});return a[n.src]=e});return ge.all(r)}}),r.findAll(u.getBody(),x).then(m(function(e){return e=z(e,function(e){return"string"!=typeof e||(dh.displayError(u,e),!1)}),F(e,function(e){h(e.image.src,e.blobInfo.blobUri()),e.image.src=e.blobInfo.blobUri(),e.image.removeAttribute("data-mce-src")}),e}))},N=function(e){return e.replace(/src="(blob:[^"]+)"/g,function(e,n){var t=f.getResultUri(n);if(t)return'src="'+t+'"';var r=l.getByUri(n);return r||(r=U(u.editorManager.get(),function(e,t){return e||t.editorUpload&&t.editorUpload.blobCache.getByUri(n)},null)),r?'src="data:'+r.blob().type+";base64,"+r.base64()+'"':e})};return u.on("setContent",function(){cg(u)?C():w()}),u.on("RawSaveContent",function(e){e.content=N(e.content)}),u.on("getContent",function(e){e.source_view||"raw"===e.format||(e.content=N(e.content))}),u.on("PostRender",function(){u.parser.addNodeFilter("img",function(e){F(e,function(e){var t=e.attr("src");if(!l.getByUri(t)){var n=f.getResultUri(t);n&&e.attr("src",n)}})})}),{blobCache:l,addFilter:function(e){d.push(e)},uploadImages:y,uploadImagesAuto:C,scanForImages:w,destroy:function(){l.destroy(),f.destroy(),r=i=null}}}var _h=function(e,t){return e.hasOwnProperty(t.nodeName)},Dh=function(t,e,n){return r=Il(or.fromDom(n),or.fromDom(e)),X(r,function(e){return _h(t,e.dom())}).isSome();var r},Bh=function(e,t){if(Bo.isText(t)){if(0===t.nodeValue.length)return!0;if(/^\s+$/.test(t.nodeValue)&&(!t.nextSibling||_h(e,t.nextSibling)))return!0}return!1},Oh=function(e){var t,n,r,o,i,a,u,s,c,l,f,d=e.settings,m=e.dom,g=e.selection,p=e.schema,h=p.getBlockElements(),v=g.getStart(),b=e.getBody();if(f=d.forced_root_block,v&&Bo.isElement(v)&&f&&(l=b.nodeName.toLowerCase(),p.isValidChild(l,f.toLowerCase())&&!Dh(h,b,v))){for(n=(t=g.getRng()).startContainer,r=t.startOffset,o=t.endContainer,i=t.endOffset,c=Kp(e),v=b.firstChild;v;)if(y=h,C=v,Bo.isText(C)||Bo.isElement(C)&&!_h(y,C)&&!cc(C)){if(Bh(h,v)){v=(u=v).nextSibling,m.remove(u);continue}a||(a=m.create(f,e.settings.forced_root_block_attrs),v.parentNode.insertBefore(a,v),s=!0),v=(u=v).nextSibling,a.appendChild(u)}else a=null,v=v.nextSibling;var y,C;s&&c&&(t.setStart(n,r),t.setEnd(o,i),g.setRng(t),e.nodeChanged())}},Ph=function(e){e.settings.forced_root_block&&e.on("NodeChange",d(Oh,e))},Lh=function(t){return qr(t).fold(q([t]),function(e){return[t].concat(Lh(e))})},Ih=function(t){return $r(t).fold(q([t]),function(e){return"br"===sr(e)?Fr(e).map(function(e){return[t].concat(Ih(e))}).getOr([]):[t].concat(Ih(e))})},Mh=function(o,e){return Wa([(i=e,a=i.startContainer,u=i.startOffset,Bo.isText(a)?0===u?A.some(or.fromDom(a)):A.none():A.from(a.childNodes[u]).map(or.fromDom)),(t=e,n=t.endContainer,r=t.endOffset,Bo.isText(n)?r===n.data.length?A.some(or.fromDom(n)):A.none():A.from(n.childNodes[r-1]).map(or.fromDom))],function(e,t){var n=V(Lh(o),d(Pr,e)),r=V(Ih(o),d(Pr,t));return n.isSome()&&r.isSome()}).getOr(!1);var t,n,r,i,a,u},Fh=function(e,t,n,r){var o=n,i=new oo(n,o),a=e.schema.getNonEmptyElements();do{if(3===n.nodeType&&0!==Gt.trim(n.nodeValue).length)return void(r?t.setStart(n,0):t.setEnd(n,n.nodeValue.length));if(a[n.nodeName]&&!/^(TD|TH)$/.test(n.nodeName))return void(r?t.setStartBefore(n):"BR"===n.nodeName?t.setEndBefore(n):t.setEndAfter(n));if(me.ie&&me.ie<11&&e.isBlock(n)&&e.isEmpty(n))return void(r?t.setStart(n,0):t.setEnd(n,0))}while(n=r?i.next():i.prev());"BODY"===o.nodeName&&(r?t.setStart(o,0):t.setEnd(o,o.childNodes.length))},zh=function(e){var t=e.selection.getSel();return t&&0<t.rangeCount};function Uh(i){var r,o=[];"onselectionchange"in i.getDoc()||i.on("NodeChange Click MouseUp KeyUp Focus",function(e){var t,n;n={startContainer:(t=i.selection.getRng()).startContainer,startOffset:t.startOffset,endContainer:t.endContainer,endOffset:t.endOffset},"nodechange"!==e.type&&Ng(n,r)||i.fire("SelectionChange"),r=n}),i.on("contextmenu",function(){i.fire("SelectionChange")}),i.on("SelectionChange",function(){var e=i.selection.getStart(!0);!e||!me.range&&i.selection.isCollapsed()||zh(i)&&!function(e){var t,n;if((n=i.$(e).parentsUntil(i.getBody()).add(e)).length===o.length){for(t=n.length;0<=t&&n[t]===o[t];t--);if(-1===t)return o=n,!0}return o=n,!1}(e)&&i.dom.isChildOf(e,i.getBody())&&i.nodeChanged({selectionChange:!0})}),i.on("MouseUp",function(e){!e.isDefaultPrevented()&&zh(i)&&("IMG"===i.selection.getNode().nodeName?be.setEditorTimeout(i,function(){i.nodeChanged()}):i.nodeChanged())}),this.nodeChanged=function(e){var t,n,r,o=i.selection;i.initialized&&o&&!i.settings.disable_nodechange&&!i.readonly&&(r=i.getBody(),(t=o.getStart(!0)||r).ownerDocument===i.getDoc()&&i.dom.isChildOf(t,r)||(t=r),n=[],i.dom.getParent(t,function(e){if(e===r)return!0;n.push(e)}),(e=e||{}).element=t,e.parents=n,i.fire("NodeChange",e))}}var Vh,Hh,jh=function(e){var t,n,r,o;return o=e.getBoundingClientRect(),n=(t=e.ownerDocument).documentElement,r=t.defaultView,{top:o.top+r.pageYOffset-n.clientTop,left:o.left+r.pageXOffset-n.clientLeft}},qh=function(e,t){return n=(u=e).inline?jh(u.getBody()):{left:0,top:0},a=(i=e).getBody(),r=i.inline?{left:a.scrollLeft,top:a.scrollTop}:{left:0,top:0},{pageX:(o=function(e,t){if(t.target.ownerDocument!==e.getDoc()){var n=jh(e.getContentAreaContainer()),r=(i=(o=e).getBody(),a=o.getDoc().documentElement,u={left:i.scrollLeft,top:i.scrollTop},s={left:i.scrollLeft||a.scrollLeft,top:i.scrollTop||a.scrollTop},o.inline?u:s);return{left:t.pageX-n.left+r.left,top:t.pageY-n.top+r.top}}var o,i,a,u,s;return{left:t.pageX,top:t.pageY}}(e,t)).left-n.left+r.left,pageY:o.top-n.top+r.top};var n,r,o,i,a,u},$h=Bo.isContentEditableFalse,Wh=Bo.isContentEditableTrue,Kh=function(e){e&&e.parentNode&&e.parentNode.removeChild(e)},Xh=function(u,s){return function(e){if(0===e.button){var t=V(s.dom.getParents(e.target),Ya($h,Wh)).getOr(null);if(i=s.getBody(),$h(a=t)&&a!==i){var n=s.dom.getPos(t),r=s.getBody(),o=s.getDoc().documentElement;u.element=t,u.screenX=e.screenX,u.screenY=e.screenY,u.maxX=(s.inline?r.scrollWidth:o.offsetWidth)-2,u.maxY=(s.inline?r.scrollHeight:o.offsetHeight)-2,u.relX=e.pageX-n.x,u.relY=e.pageY-n.y,u.width=t.offsetWidth,u.height=t.offsetHeight,u.ghost=function(e,t,n,r){var o=t.cloneNode(!0);e.dom.setStyles(o,{width:n,height:r}),e.dom.setAttrib(o,"data-mce-selected",null);var i=e.dom.create("div",{"class":"mce-drag-container","data-mce-bogus":"all",unselectable:"on",contenteditable:"false"});return e.dom.setStyles(i,{position:"absolute",opacity:.5,overflow:"hidden",border:0,padding:0,margin:0,width:n,height:r}),e.dom.setStyles(o,{margin:0,boxSizing:"border-box"}),i.appendChild(o),i}(s,t,u.width,u.height)}}var i,a}},Yh=function(l,f){return function(e){if(l.dragging&&(s=(i=f).selection,c=s.getSel().getRangeAt(0).startContainer,a=3===c.nodeType?c.parentNode:c,u=l.element,a!==u&&!i.dom.isChildOf(a,u)&&!$h(a))){var t=(r=l.element,(o=r.cloneNode(!0)).removeAttribute("data-mce-selected"),o),n=f.fire("drop",{targetClone:t,clientX:e.clientX,clientY:e.clientY});n.isDefaultPrevented()||(t=n.targetClone,f.undoManager.transact(function(){Kh(l.element),f.insertContent(f.dom.getOuterHTML(t)),f._selectionOverrides.hideFakeCaret()}))}var r,o,i,a,u,s,c;Gh(l)}},Gh=function(e){e.dragging=!1,e.element=null,Kh(e.ghost)},Jh=function(e){var t,n,r,o,i,a,p,h,v,u,s,c={};t=hi.DOM,a=H.document,n=Xh(c,e),p=c,h=e,v=be.throttle(function(e,t){h._selectionOverrides.hideFakeCaret(),h.selection.placeCaretAt(e,t)},0),r=function(e){var t,n,r,o,i,a,u,s,c,l,f,d,m=Math.max(Math.abs(e.screenX-p.screenX),Math.abs(e.screenY-p.screenY));if(p.element&&!p.dragging&&10<m){if(h.fire("dragstart",{target:p.element}).isDefaultPrevented())return;p.dragging=!0,h.focus()}if(p.dragging){var g=(f=p,{pageX:(d=qh(h,e)).pageX-f.relX,pageY:d.pageY+5});c=p.ghost,l=h.getBody(),c.parentNode!==l&&l.appendChild(c),t=p.ghost,n=g,r=p.width,o=p.height,i=p.maxX,a=p.maxY,s=u=0,t.style.left=n.pageX+"px",t.style.top=n.pageY+"px",n.pageX+r>i&&(u=n.pageX+r-i),n.pageY+o>a&&(s=n.pageY+o-a),t.style.width=r-u+"px",t.style.height=o-s+"px",v(e.clientX,e.clientY)}},o=Yh(c,e),u=c,i=function(){u.dragging&&s.fire("dragend"),Gh(u)},(s=e).on("mousedown",n),e.on("mousemove",r),e.on("mouseup",o),t.bind(a,"mousemove",r),t.bind(a,"mouseup",i),e.on("remove",function(){t.unbind(a,"mousemove",r),t.unbind(a,"mouseup",i)})},Qh=function(e){var n;Jh(e),(n=e).on("drop",function(e){var t="undefined"!=typeof e.clientX?n.getDoc().elementFromPoint(e.clientX,e.clientY):null;($h(t)||$h(n.dom.getContentEditableParent(t)))&&e.preventDefault()})},Zh=function(e){return U(e,function(e,t){return e.concat(function(t){var e=function(e){return W(e,function(e){return(e=Ma(e)).node=t,e})};if(Bo.isElement(t))return e(t.getClientRects());if(Bo.isText(t)){var n=t.ownerDocument.createRange();return n.setStart(t,0),n.setEnd(t,t.data.length),e(n.getClientRects())}}(t))},[])};(Hh=Vh||(Vh={}))[Hh.Up=-1]="Up",Hh[Hh.Down=1]="Down";var ev=function(o,i,a,e,u,t){var n,s,c=0,l=[],r=function(e){var t,n,r;for(r=Zh([e]),-1===o&&(r=r.reverse()),t=0;t<r.length;t++)if(n=r[t],!a(n,s)){if(0<l.length&&i(n,$t.last(l))&&c++,n.line=c,u(n))return!0;l.push(n)}};return(s=$t.last(t.getClientRects()))&&(r(n=t.getNode()),function(e,t,n,r){for(;r=ms(r,e,La,t);)if(n(r))return}(o,e,r,n)),l},tv=d(ev,Vh.Up,Ua,Va),nv=d(ev,Vh.Down,Va,Ua),rv=function(n){return function(e){return t=n,e.line>t;var t}},ov=function(n){return function(e){return t=n,e.line===t;var t}},iv=Bo.isContentEditableFalse,av=ms,uv=function(e,t){return Math.abs(e.left-t)},sv=function(e,t){return Math.abs(e.right-t)},cv=function(e,t){return e>=t.left&&e<=t.right},lv=function(e,o){return $t.reduce(e,function(e,t){var n,r;return n=Math.min(uv(e,o),sv(e,o)),r=Math.min(uv(t,o),sv(t,o)),cv(o,t)?t:cv(o,e)?e:r===n&&iv(t.node)?t:r<n?t:e})},fv=function(e,t,n,r){for(;r=av(r,e,La,t);)if(n(r))return},dv=function(e,t,n){var r,o,i,a,u,s,c,l=Zh(z(re(e.getElementsByTagName("*")),rs)),f=z(l,function(e){return n>=e.top&&n<=e.bottom});return(r=lv(f,t))&&(r=lv((a=e,c=function(t,e){var n;return n=z(Zh([e]),function(e){return!t(e,u)}),s=s.concat(n),0===n.length},(s=[]).push(u=r),fv(Vh.Up,a,d(c,Ua),u.node),fv(Vh.Down,a,d(c,Va),u.node),s),t))&&rs(r.node)?(i=t,{node:(o=r).node,before:uv(o,i)<sv(o,i)}):null},mv=function(i,a,e){return!e.collapsed&&U(e.getClientRects(),function(e,t){return e||(o=a,(r=i)>=(n=t).left&&r<=n.right&&o>=n.top&&o<=n.bottom);var n,r,o},!1)},gv=function(t){var e=Oi(function(){if(!t.removed&&t.selection.getRng().collapsed){var e=Um(t,t.selection.getRng(),!1);t.selection.setRng(e)}},0);t.on("focus",function(){e.throttle()}),t.on("blur",function(){e.cancel()})},pv={BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,END:35,HOME:36,modifierPressed:function(e){return e.shiftKey||e.ctrlKey||e.altKey||this.metaKeyPressed(e)},metaKeyPressed:function(e){return me.mac?e.metaKey:e.ctrlKey&&!e.altKey}},hv=Bo.isContentEditableTrue,vv=Bo.isContentEditableFalse,bv=function(e,t){for(var n=e.getBody();t&&t!==n;){if(hv(t)||vv(t))return t;t=t.parentNode}return null},yv=function(g){var p,e,t,a=g.getBody(),o=ts(g.getBody(),function(e){return g.dom.isBlock(e)},function(){return Kp(g)}),h="sel-"+g.dom.uniqueId(),u=function(e){e&&g.selection.setRng(e)},s=function(){return g.selection.getRng()},v=function(e,t,n,r){return void 0===r&&(r=!0),g.fire("ShowCaret",{target:t,direction:e,before:n}).isDefaultPrevented()?null:(r&&g.selection.scrollIntoView(t,-1===e),o.show(n,t))},b=function(e,t){return t=xs(e,a,t),-1===e?vu.fromRangeStart(t):vu.fromRangeEnd(t)},n=function(e){return va(e)||wa(e)||Na(e)},y=function(e){return n(e.startContainer)||n(e.endContainer)},c=function(e,t){var n,r,o,i,a,u,s,c,l,f,d=g.$,m=g.dom;if(!e)return null;if(e.collapsed){if(!y(e))if(!1===t){if(c=b(-1,e),rs(c.getNode(!0)))return v(-1,c.getNode(!0),!1,!1);if(rs(c.getNode()))return v(-1,c.getNode(),!c.isAtEnd(),!1)}else{if(c=b(1,e),rs(c.getNode()))return v(1,c.getNode(),!c.isAtEnd(),!1);if(rs(c.getNode(!0)))return v(1,c.getNode(!0),!1,!1)}return null}return i=e.startContainer,a=e.startOffset,u=e.endOffset,3===i.nodeType&&0===a&&vv(i.parentNode)&&(i=i.parentNode,a=m.nodeIndex(i),i=i.parentNode),1!==i.nodeType?null:(u===a+1&&(n=i.childNodes[a]),vv(n)?(l=f=n.cloneNode(!0),(s=g.fire("ObjectSelected",{target:n,targetClone:l})).isDefaultPrevented()?null:(r=Yi(or.fromDom(g.getBody()),"#"+h).fold(function(){return d([])},function(e){return d([e.dom()])}),l=s.targetClone,0===r.length&&(r=d('<div data-mce-bogus="all" class="mce-offscreen-selection"></div>').attr("id",h)).appendTo(g.getBody()),e=g.dom.createRng(),l===f&&me.ie?(r.empty().append('<p style="font-size: 0" data-mce-bogus="all">\xa0</p>').append(l),e.setStartAfter(r[0].firstChild.firstChild),e.setEndAfter(l)):(r.empty().append("\xa0").append(l).append("\xa0"),e.setStart(r[0].firstChild,1),e.setEnd(r[0].lastChild,0)),r.css({top:m.getPos(n,g.getBody()).y}),r[0].focus(),(o=g.selection.getSel()).removeAllRanges(),o.addRange(e),F(ji(or.fromDom(g.getBody()),"*[data-mce-selected]"),function(e){xr(e,"data-mce-selected")}),n.setAttribute("data-mce-selected","1"),p=n,C(),e)):null)},l=function(){p&&(p.removeAttribute("data-mce-selected"),Yi(or.fromDom(g.getBody()),"#"+h).each(Di),p=null),Yi(or.fromDom(g.getBody()),"#"+h).each(Di),p=null},C=function(){o.hide()};return me.ceFalse&&(function(){g.on("mouseup",function(e){var t=s();t.collapsed&&eh(g,e.clientX,e.clientY)&&u(zm(g,t,!1))}),g.on("click",function(e){var t;(t=bv(g,e.target))&&(vv(t)&&(e.preventDefault(),g.focus()),hv(t)&&g.dom.isChildOf(t,g.selection.getNode())&&l())}),g.on("blur NewBlock",function(){l()}),g.on("ResizeWindow FullscreenStateChanged",function(){return o.reposition()});var n,r,i=function(e,t){var n,r,o=g.dom.getParent(e,g.dom.isBlock),i=g.dom.getParent(t,g.dom.isBlock);return!(!o||!g.dom.isChildOf(o,i)||!1!==vv(bv(g,o)))||o&&(n=o,r=i,!(g.dom.getParent(n,g.dom.isBlock)===g.dom.getParent(r,g.dom.isBlock)))&&function(e){var t=Hs(e);if(!e.firstChild)return!1;var n=vu.before(e.firstChild),r=t.next(n);return r&&!mf(r)&&!gf(r)}(o)};r=!1,(n=g).on("touchstart",function(){r=!1}),n.on("touchmove",function(){r=!0}),n.on("touchend",function(e){var t=bv(n,e.target);vv(t)&&(r||(e.preventDefault(),c(Fm(n,t))))}),g.on("mousedown",function(e){var t,n=e.target;if((n===a||"HTML"===n.nodeName||g.dom.isChildOf(n,a))&&!1!==eh(g,e.clientX,e.clientY))if(t=bv(g,n))vv(t)?(e.preventDefault(),c(Fm(g,t))):(l(),hv(t)&&e.shiftKey||mv(e.clientX,e.clientY,g.selection.getRng())||(C(),g.selection.placeCaretAt(e.clientX,e.clientY)));else if(!1===rs(n)){l(),C();var r=dv(a,e.clientX,e.clientY);if(r&&!i(e.target,r.node)){e.preventDefault();var o=v(1,r.node,r.before,!1);g.getBody().focus(),u(o)}}}),g.on("keypress",function(e){pv.modifierPressed(e)||(e.keyCode,vv(g.selection.getNode())&&e.preventDefault())}),g.on("getSelectionRange",function(e){var t=e.range;if(p){if(!p.parentNode)return void(p=null);(t=t.cloneRange()).selectNode(p),e.range=t}}),g.on("setSelectionRange",function(e){var t;(t=c(e.range,e.forward))&&(e.range=t)}),g.on("AfterSetSelectionRange",function(e){var t,n=e.range;y(n)||"mcepastebin"===n.startContainer.parentNode.id||C(),t=n.startContainer.parentNode,g.dom.hasClass(t,"mce-offscreen-selection")||l()}),g.on("copy",function(e){var t,n=e.clipboardData;if(!e.isDefaultPrevented()&&e.clipboardData&&!me.ie){var r=(t=g.dom.get(h))?t.getElementsByTagName("*")[0]:t;r&&(e.preventDefault(),n.clearData(),n.setData("text/html",r.outerHTML),n.setData("text/plain",r.outerText))}}),Qh(g),gv(g)}(),e=g.contentStyles,t=".mce-content-body",e.push(o.getCss()),e.push(t+" .mce-offscreen-selection {position: absolute;left: -9999999999px;max-width: 1000000px;}"+t+" *[contentEditable=false] {cursor: default;}"+t+" *[contentEditable=true] {cursor: text;}")),{showCaret:v,showBlockCaretContainer:function(e){e.hasAttribute("data-mce-caret")&&(Ea(e),u(s()),g.selection.scrollIntoView(e[0]))},hideFakeCaret:C,destroy:function(){o.destroy(),p=null}}},Cv=function(e,t,n){var r,o,i,a,u=1;for(a=e.getShortEndedElements(),(i=/<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g).lastIndex=r=n;o=i.exec(t);){if(r=i.lastIndex,"/"===o[1])u--;else if(!o[1]){if(o[2]in a)continue;u++}if(0===u)break}return r},xv=function(e,t){var n=e.exec(t);if(n){var r=n[1],o=n[2];return"string"==typeof r&&"data-mce-bogus"===r.toLowerCase()?o:null}return null};function wv(z,U){void 0===U&&(U=ri());var e=function(){};!1!==(z=z||{}).fix_self_closing&&(z.fix_self_closing=!0);var V=z.comment?z.comment:e,H=z.cdata?z.cdata:e,j=z.text?z.text:e,q=z.start?z.start:e,$=z.end?z.end:e,W=z.pi?z.pi:e,K=z.doctype?z.doctype:e;return{parse:function(e){var t,n,r,d,o,i,a,m,u,s,g,c,p,l,f,h,v,b,y,C,x,w,N,E,S,k,T,A,R,_=0,D=[],B=0,O=Wo.decode,P=Gt.makeMap("src,href,data,background,formaction,poster,xlink:href"),L=/((java|vb)script|mhtml):/i,I=function(e){var t,n;for(t=D.length;t--&&D[t].name!==e;);if(0<=t){for(n=D.length-1;t<=n;n--)(e=D[n]).valid&&$(e.name);D.length=t}},M=function(e,t,n,r,o){var i,a,u,s,c;if(n=(t=t.toLowerCase())in g?t:O(n||r||o||""),p&&!m&&0==(0===(u=t).indexOf("data-")||0===u.indexOf("aria-"))){if(!(i=b[t])&&y){for(a=y.length;a--&&!(i=y[a]).pattern.test(t););-1===a&&(i=null)}if(!i)return;if(i.validValues&&!(n in i.validValues))return}if(P[t]&&!z.allow_script_urls){var l=n.replace(/[\s\u0000-\u001F]+/g,"");try{l=decodeURIComponent(l)}catch(f){l=unescape(l)}if(L.test(l))return;if(c=l,!(s=z).allow_html_data_urls&&(/^data:image\//i.test(c)?!1===s.allow_svg_data_urls&&/^data:image\/svg\+xml/i.test(c):/^data:/i.test(c)))return}m&&(t in P||0===t.indexOf("on"))||(d.map[t]=n,d.push({name:t,value:n}))};for(S=new RegExp("<(?:(?:!--([\\w\\W]*?)--\x3e)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)>)|(?:([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g"),k=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g,s=U.getShortEndedElements(),E=z.self_closing_elements||U.getSelfClosingElements(),g=U.getBoolAttrs(),p=z.validate,u=z.remove_internals,R=z.fix_self_closing,T=U.getSpecialElements(),N=e+">";t=S.exec(N);){if(_<t.index&&j(O(e.substr(_,t.index-_))),n=t[6])":"===(n=n.toLowerCase()).charAt(0)&&(n=n.substr(1)),I(n);else if(n=t[7]){if(t.index+t[0].length>e.length){j(O(e.substr(t.index))),_=t.index+t[0].length;continue}":"===(n=n.toLowerCase()).charAt(0)&&(n=n.substr(1)),c=n in s,R&&E[n]&&0<D.length&&D[D.length-1].name===n&&I(n);var F=xv(k,t[8]);if(null!==F){if("all"===F){_=Cv(U,e,S.lastIndex),S.lastIndex=_;continue}f=!1}if(!p||(l=U.getElementRule(n))){if(f=!0,p&&(b=l.attributes,y=l.attributePatterns),(v=t[8])?((m=-1!==v.indexOf("data-mce-type"))&&u&&(f=!1),(d=[]).map={},v.replace(k,M)):(d=[]).map={},p&&!m){if(C=l.attributesRequired,x=l.attributesDefault,w=l.attributesForced,l.removeEmptyAttrs&&!d.length&&(f=!1),w)for(o=w.length;o--;)a=(h=w[o]).name,"{$uid}"===(A=h.value)&&(A="mce_"+B++),d.map[a]=A,d.push({name:a,value:A});if(x)for(o=x.length;o--;)(a=(h=x[o]).name)in d.map||("{$uid}"===(A=h.value)&&(A="mce_"+B++),d.map[a]=A,d.push({name:a,value:A}));if(C){for(o=C.length;o--&&!(C[o]in d.map););-1===o&&(f=!1)}if(h=d.map["data-mce-bogus"]){if("all"===h){_=Cv(U,e,S.lastIndex),S.lastIndex=_;continue}f=!1}}f&&q(n,d,c)}else f=!1;if(r=T[n]){r.lastIndex=_=t.index+t[0].length,(t=r.exec(e))?(f&&(i=e.substr(_,t.index-_)),_=t.index+t[0].length):(i=e.substr(_),_=e.length),f&&(0<i.length&&j(i,!0),$(n)),S.lastIndex=_;continue}c||(v&&v.indexOf("/")===v.length-1?f&&$(n):D.push({name:n,valid:f}))}else(n=t[1])?(">"===n.charAt(0)&&(n=" "+n),z.allow_conditional_comments||"[if"!==n.substr(0,3).toLowerCase()||(n=" "+n),V(n)):(n=t[2])?H(n.replace(/<!--|-->/g,"")):(n=t[3])?K(n):(n=t[4])&&W(n,t[5]);_=t.index+t[0].length}for(_<e.length&&j(O(e.substr(_))),o=D.length-1;0<=o;o--)(n=D[o]).valid&&$(n.name)}}}(wv||(wv={})).findEndTag=Cv;var Nv=wv,Ev=function(e,t){var n,r,o,i,a,u,s,c,l=t,f=/<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g,d=e.schema;for(u=e.getTempAttrs(),s=l,c=new RegExp(["\\s?("+u.join("|")+')="[^"]+"'].join("|"),"gi"),l=s.replace(c,""),a=d.getShortEndedElements();i=f.exec(l);)r=f.lastIndex,o=i[0].length,n=a[i[1]]?r:Nv.findEndTag(d,l,r),l=l.substring(0,r-o)+l.substring(n),f.lastIndex=r-o;return da(l)},Sv={trimExternal:Ev,trimInternal:Ev},kv=0,Tv=2,Av=1,Rv=function(g,p){var e=g.length+p.length+2,h=new Array(e),v=new Array(e),c=function(e,t,n,r,o){var i=l(e,t,n,r);if(null===i||i.start===t&&i.diag===t-r||i.end===e&&i.diag===e-n)for(var a=e,u=n;a<t||u<r;)a<t&&u<r&&g[a]===p[u]?(o.push([0,g[a]]),++a,++u):r-n<t-e?(o.push([2,g[a]]),++a):(o.push([1,p[u]]),++u);else{c(e,i.start,n,i.start-i.diag,o);for(var s=i.start;s<i.end;++s)o.push([0,g[s]]);c(i.end,t,i.end-i.diag,r,o)}},b=function(e,t,n,r){for(var o=e;o-t<r&&o<n&&g[o]===p[o-t];)++o;return{start:e,end:o,diag:t}},l=function(e,t,n,r){var o=t-e,i=r-n;if(0===o||0===i)return null;var a,u,s,c,l,f=o-i,d=i+o,m=(d%2==0?d:d+1)/2;for(h[1+m]=e,v[1+m]=t+1,a=0;a<=m;++a){for(u=-a;u<=a;u+=2){for(s=u+m,u===-a||u!==a&&h[s-1]<h[s+1]?h[s]=h[s+1]:h[s]=h[s-1]+1,l=(c=h[s])-e+n-u;c<t&&l<r&&g[c]===p[l];)h[s]=++c,++l;if(f%2!=0&&f-a<=u&&u<=f+a&&v[s-f]<=h[s])return b(v[s-f],u+e-n,t,r)}for(u=f-a;u<=f+a;u+=2){for(s=u+m-f,u===f-a||u!==f+a&&v[s+1]<=v[s-1]?v[s]=v[s+1]-1:v[s]=v[s-1],l=(c=v[s]-1)-e+n-u;e<=c&&n<=l&&g[c]===p[l];)v[s]=c--,l--;if(f%2==0&&-a<=u&&u<=a&&v[s]<=h[s+f])return b(v[s],u+e-n,t,r)}}},t=[];return c(0,g.length,0,p.length,t),t},_v=function(e){return Bo.isElement(e)?e.outerHTML:Bo.isText(e)?Wo.encodeRaw(e.data,!1):Bo.isComment(e)?"\x3c!--"+e.data+"--\x3e":""},Dv=function(e,t,n){var r=function(e){var t,n,r;for(r=H.document.createElement("div"),t=H.document.createDocumentFragment(),e&&(r.innerHTML=e);n=r.firstChild;)t.appendChild(n);return t}(t);if(e.hasChildNodes()&&n<e.childNodes.length){var o=e.childNodes[n];o.parentNode.insertBefore(r,o)}else e.appendChild(r)},Bv=function(e){return z(W(re(e.childNodes),_v),function(e){return 0<e.length})},Ov=function(e,t){var n,r,o,i=W(re(t.childNodes),_v);return n=Rv(i,e),r=t,o=0,F(n,function(e){e[0]===kv?o++:e[0]===Av?(Dv(r,e[1],o),o++):e[0]===Tv&&function(e,t){if(e.hasChildNodes()&&t<e.childNodes.length){var n=e.childNodes[t];n.parentNode.removeChild(n)}}(r,o)}),t},Pv=Pi(A.none()),Lv=function(e){return{type:"fragmented",fragments:e,content:"",bookmark:null,beforeBookmark:null}},Iv=function(e){return{type:"complete",fragments:null,content:e,bookmark:null,beforeBookmark:null}},Mv=function(e){return"fragmented"===e.type?e.fragments.join(""):e.content},Fv=function(e){var t=or.fromTag("body",Pv.get().getOrThunk(function(){var e=H.document.implementation.createHTMLDocument("undo");return Pv.set(A.some(e)),e}));return sa(t,Mv(e)),F(ji(t,"*[data-mce-bogus]"),Bi),t.dom().innerHTML},zv=function(n){var e,t,r;return e=Bv(n.getBody()),-1!==(t=(r=J(e,function(e){var t=Sv.trimInternal(n.serializer,e);return 0<t.length?[t]:[]})).join("")).indexOf("</iframe>")?Lv(r):Iv(t)},Uv=function(e,t,n){"fragmented"===t.type?Ov(t.fragments,e.getBody()):e.setContent(t.content,{format:"raw"}),e.selection.moveToBookmark(n?t.beforeBookmark:t.bookmark)},Vv=function(e,t){return!(!e||!t)&&(r=t,Mv(e)===Mv(r)||(n=t,Fv(e)===Fv(n)));var n,r};function Hv(u){var s,r,o=this,c=0,l=[],t=0,f=function(){return 0===t},i=function(e){f()&&(o.typing=e)},d=function(e){u.setDirty(e)},a=function(e){i(!1),o.add({},e)},n=function(){o.typing&&(i(!1),o.add())};return u.on("init",function(){o.add()}),u.on("BeforeExecCommand",function(e){var t=e.command;"Undo"!==t&&"Redo"!==t&&"mceRepaint"!==t&&(n(),o.beforeChange())}),u.on("ExecCommand",function(e){var t=e.command;"Undo"!==t&&"Redo"!==t&&"mceRepaint"!==t&&a(e)}),u.on("ObjectResizeStart Cut",function(){o.beforeChange()}),u.on("SaveContent ObjectResized blur",a),u.on("DragEnd",a),u.on("KeyUp",function(e){var t=e.keyCode;e.isDefaultPrevented()||((33<=t&&t<=36||37<=t&&t<=40||45===t||e.ctrlKey)&&(a(),u.nodeChanged()),46!==t&&8!==t||u.nodeChanged(),r&&o.typing&&!1===Vv(zv(u),l[0])&&(!1===u.isDirty()&&(d(!0),u.fire("change",{level:l[0],lastLevel:null})),u.fire("TypingUndo"),r=!1,u.nodeChanged()))}),u.on("KeyDown",function(e){var t=e.keyCode;if(!e.isDefaultPrevented())if(33<=t&&t<=36||37<=t&&t<=40||45===t)o.typing&&a(e);else{var n=e.ctrlKey&&!e.altKey||e.metaKey;!(t<16||20<t)||224===t||91===t||o.typing||n||(o.beforeChange(),i(!0),o.add({},e),r=!0)}}),u.on("MouseDown",function(e){o.typing&&a(e)}),u.on("input",function(e){var t;e.inputType&&("insertReplacementText"===e.inputType||"insertText"===(t=e).inputType&&null===t.data)&&a(e)}),u.addShortcut("meta+z","","Undo"),u.addShortcut("meta+y,meta+shift+z","","Redo"),u.on("AddUndo Undo Redo ClearUndos",function(e){e.isDefaultPrevented()||u.nodeChanged()}),o={data:l,typing:!1,beforeChange:function(){f()&&(s=Fu.getUndoBookmark(u.selection))},add:function(e,t){var n,r,o,i=u.settings;if(o=zv(u),e=e||{},e=Gt.extend(e,o),!1===f()||u.removed)return null;if(r=l[c],u.fire("BeforeAddUndo",{level:e,lastLevel:r,originalEvent:t}).isDefaultPrevented())return null;if(r&&Vv(r,e))return null;if(l[c]&&(l[c].beforeBookmark=s),i.custom_undo_redo_levels&&l.length>i.custom_undo_redo_levels){for(n=0;n<l.length-1;n++)l[n]=l[n+1];l.length--,c=l.length}e.bookmark=Fu.getUndoBookmark(u.selection),c<l.length-1&&(l.length=c+1),l.push(e),c=l.length-1;var a={level:e,lastLevel:r,originalEvent:t};return u.fire("AddUndo",a),0<c&&(d(!0),u.fire("change",a)),e},undo:function(){var e;return o.typing&&(o.add(),o.typing=!1,i(!1)),0<c&&(e=l[--c],Uv(u,e,!0),d(!0),u.fire("undo",{level:e})),e},redo:function(){var e;return c<l.length-1&&(e=l[++c],Uv(u,e,!1),d(!0),u.fire("redo",{level:e})),e},clear:function(){l=[],c=0,o.typing=!1,o.data=l,u.fire("ClearUndos")},hasUndo:function(){return 0<c||o.typing&&l[0]&&!Vv(zv(u),l[0])},hasRedo:function(){return c<l.length-1&&!o.typing},transact:function(e){return n(),o.beforeChange(),o.ignore(e),o.add()},ignore:function(e){try{t++,e()}finally{t--}},extra:function(e,t){var n,r;o.transact(e)&&(r=l[c].bookmark,n=l[c-1],Uv(u,n,!0),o.transact(t)&&(l[c-1].beforeBookmark=r))}}}var jv,qv,$v={},Wv=$t.filter,Kv=$t.each;qv=function(e){var t,n,r=e.selection.getRng();t=Bo.matchNodeNames("pre"),r.collapsed||(n=e.selection.getSelectedBlocks(),Kv(Wv(Wv(n,t),function(e){return t(e.previousSibling)&&-1!==$t.indexOf(n,e.previousSibling)}),function(e){var t,n;t=e.previousSibling,hn(n=e).remove(),hn(t).append("<br><br>").append(n.childNodes)}))},$v[jv="pre"]||($v[jv]=[]),$v[jv].push(qv);var Xv=function(e,t){Kv($v[e],function(e){e(t)})},Yv=/^(src|href|style)$/,Gv=Gt.each,Jv=mc.isEq,Qv=function(e,t,n){return e.isChildOf(t,n)&&t!==n&&!e.isBlock(n)},Zv=function(e,t,n){var r,o,i;return r=t[n?"startContainer":"endContainer"],o=t[n?"startOffset":"endOffset"],Bo.isElement(r)&&(i=r.childNodes.length-1,!n&&o&&o--,r=r.childNodes[i<o?i:o]),Bo.isText(r)&&n&&o>=r.nodeValue.length&&(r=new oo(r,e.getBody()).next()||r),Bo.isText(r)&&!n&&0===o&&(r=new oo(r,e.getBody()).prev()||r),r},eb=function(e,t,n,r){var o=e.create(n,r);return t.parentNode.insertBefore(o,t),o.appendChild(t),o},tb=function(e,t,n,r,o){var i=or.fromDom(t),a=or.fromDom(e.create(r,o)),u=n?Vr(i):Ur(i);return Ri(a,u),n?(Si(i,a),Ti(a,i)):(ki(i,a),Ai(a,i)),a.dom()},nb=function(e,t,n,r){return!(t=mc.getNonWhiteSpaceSibling(t,n,r))||"BR"===t.nodeName||e.isBlock(t)},rb=function(e,n,r,o,i){var t,a,u,s,c,l,f,d,m,g,p,h,v,b,y=e.dom;if(c=y,!(Jv(l=o,(f=n).inline)||Jv(l,f.block)||(f.selector?Bo.isElement(l)&&c.is(l,f.selector):void 0)||(s=o,n.links&&"A"===s.tagName)))return!1;if("all"!==n.remove)for(Gv(n.styles,function(e,t){e=mc.normalizeStyleValue(y,mc.replaceVars(e,r),t),"number"==typeof t&&(t=e,i=0),(n.remove_similar||!i||Jv(mc.getStyle(y,i,t),e))&&y.setStyle(o,t,""),u=1}),u&&""===y.getAttrib(o,"style")&&(o.removeAttribute("style"),o.removeAttribute("data-mce-style")),Gv(n.attributes,function(e,t){var n;if(e=mc.replaceVars(e,r),"number"==typeof t&&(t=e,i=0),!i||Jv(y.getAttrib(i,t),e)){if("class"===t&&(e=y.getAttrib(o,t))&&(n="",Gv(e.split(/\s+/),function(e){/mce\-\w+/.test(e)&&(n+=(n?" ":"")+e)}),n))return void y.setAttrib(o,t,n);"class"===t&&o.removeAttribute("className"),Yv.test(t)&&o.removeAttribute("data-mce-"+t),o.removeAttribute(t)}}),Gv(n.classes,function(e){e=mc.replaceVars(e,r),i&&!y.hasClass(i,e)||y.removeClass(o,e)}),a=y.getAttribs(o),t=0;t<a.length;t++){var C=a[t].nodeName;if(0!==C.indexOf("_")&&0!==C.indexOf("data-"))return!1}return"none"!==n.remove?(d=e,g=n,h=(m=o).parentNode,v=d.dom,b=d.settings.forced_root_block,g.block&&(b?h===v.getRoot()&&(g.list_block&&Jv(m,g.list_block)||Gv(Gt.grep(m.childNodes),function(e){mc.isValid(d,b,e.nodeName.toLowerCase())?p?p.appendChild(e):(p=eb(v,e,b),v.setAttribs(p,d.settings.forced_root_block_attrs)):p=0})):v.isBlock(m)&&!v.isBlock(h)&&(nb(v,m,!1)||nb(v,m.firstChild,!0,1)||m.insertBefore(v.create("br"),m.firstChild),nb(v,m,!0)||nb(v,m.lastChild,!1,1)||m.appendChild(v.create("br")))),g.selector&&g.inline&&!Jv(g.inline,m)||v.remove(m,1),!0):void 0},ob=rb,ib=function(s,c,l,e,f){var t,n,d=s.formatter.get(c),m=d[0],a=!0,u=s.dom,r=s.selection,i=function(e){var n,t,r,o,i,a,u=(n=s,t=e,r=c,o=l,i=f,Gv(mc.getParents(n.dom,t.parentNode).reverse(),function(e){var t;a||"_start"===e.id||"_end"===e.id||(t=hm.matchNode(n,e,r,o,i))&&!1!==t.split&&(a=e)}),a);return function(e,t,n,r,o,i,a,u){var s,c,l,f,d,m,g=e.dom;if(n){for(m=n.parentNode,s=r.parentNode;s&&s!==m;s=s.parentNode){for(c=g.clone(s,!1),d=0;d<t.length;d++)if(rb(e,t[d],u,c,c)){c=0;break}c&&(l&&c.appendChild(l),f||(f=c),l=c)}!i||a.mixed&&g.isBlock(n)||(r=g.split(n,r)),l&&(o.parentNode.insertBefore(l,o),f.appendChild(o))}return r}(s,d,u,e,e,!0,m,l)},g=function(e){var t,n,r,o,i;if(Bo.isElement(e)&&u.getContentEditable(e)&&(o=a,a="true"===u.getContentEditable(e),i=!0),t=Gt.grep(e.childNodes),a&&!i)for(n=0,r=d.length;n<r&&!rb(s,d[n],l,e,e);n++);if(m.deep&&t.length){for(n=0,r=t.length;n<r;n++)g(t[n]);i&&(a=o)}},p=function(e){var t,n=u.get(e?"_start":"_end"),r=n[e?"firstChild":"lastChild"];return cc(t=r)&&Bo.isElement(t)&&("_start"===t.id||"_end"===t.id)&&(r=r[e?"firstChild":"lastChild"]),Bo.isText(r)&&0===r.data.length&&(r=e?n.previousSibling||n.nextSibling:n.nextSibling||n.previousSibling),u.remove(n,!0),r},o=function(e){var t,n,r=e.commonAncestorContainer;if(e=Sc(s,e,d,!0),m.split){if(e=bm(e),(t=Zv(s,e,!0))!==(n=Zv(s,e))){if(/^(TR|TH|TD)$/.test(t.nodeName)&&t.firstChild&&(t="TR"===t.nodeName?t.firstChild.firstChild||t:t.firstChild||t),r&&/^T(HEAD|BODY|FOOT|R)$/.test(r.nodeName)&&/^(TH|TD)$/.test(n.nodeName)&&n.firstChild&&(n=n.firstChild||n),Qv(u,t,n)){var o=A.from(t.firstChild).getOr(t);return i(tb(u,o,!0,"span",{id:"_start","data-mce-type":"bookmark"})),void p(!0)}if(Qv(u,n,t))return o=A.from(n.lastChild).getOr(n),i(tb(u,o,!1,"span",{id:"_end","data-mce-type":"bookmark"})),void p(!1);t=eb(u,t,"span",{id:"_start","data-mce-type":"bookmark"}),n=eb(u,n,"span",{id:"_end","data-mce-type":"bookmark"}),i(t),i(n),t=p(!0),n=p()}else t=n=i(t);e.startContainer=t.parentNode?t.parentNode:t,e.startOffset=u.nodeIndex(t),e.endContainer=n.parentNode?n.parentNode:n,e.endOffset=u.nodeIndex(n)+1}Tc(u,e,function(e){Gv(e,function(e){g(e),Bo.isElement(e)&&"underline"===s.dom.getStyle(e,"text-decoration")&&e.parentNode&&"underline"===mc.getTextDecoration(u,e.parentNode)&&rb(s,{deep:!1,exact:!0,inline:"span",styles:{textDecoration:"underline"}},null,e)})})};if(e)e.nodeType?((n=u.createRng()).setStartBefore(e),n.setEndAfter(e),o(n)):o(e);else if("false"!==u.getContentEditable(r.getNode()))r.isCollapsed()&&m.inline&&!u.select("td[data-mce-selected],th[data-mce-selected]").length?function(e,t,n,r){var o,i,a,u,s,c,l,f=e.dom,d=e.selection,m=[],g=d.getRng();for(o=g.startContainer,i=g.startOffset,3===(s=o).nodeType&&(i!==o.nodeValue.length&&(u=!0),s=s.parentNode);s;){if(hm.matchNode(e,s,t,n,r)){c=s;break}s.nextSibling&&(u=!0),m.push(s),s=s.parentNode}if(c)if(u){a=d.getBookmark(),g.collapse(!0);var p=Sc(e,g,e.formatter.get(t),!0);p=bm(p),e.formatter.remove(t,n,p),d.moveToBookmark(a)}else{l=Vu(e.getBody(),c);var h=Nm(!1).dom(),v=Am(m,h);km(e,h,l||c),Em(e,l,!1),d.setCursorLocation(v,1),f.isEmpty(c)&&f.remove(c)}}(s,c,l,f):(t=Fu.getPersistentBookmark(s.selection,!0),o(r.getRng()),r.moveToBookmark(t),m.inline&&hm.match(s,c,l,r.getStart())&&mc.moveStart(u,r,r.getRng()),s.nodeChanged());else{e=r.getNode();for(var h=0,v=d.length;h<v&&(!d[h].ceFalseOverride||!rb(s,d[h],l,e,e));h++);}},ab=Gt.each,ub=function(e){return e&&1===e.nodeType&&!cc(e)&&!Uu(e)&&!Bo.isBogus(e)},sb=function(e,t){var n;for(n=e;n;n=n[t]){if(3===n.nodeType&&0!==n.nodeValue.length)return e;if(1===n.nodeType&&!cc(n))return n}return e},cb=function(e,t,n){var r,o,i=new $c(e);if(t&&n&&(t=sb(t,"previousSibling"),n=sb(n,"nextSibling"),i.compare(t,n))){for(r=t.nextSibling;r&&r!==n;)r=(o=r).nextSibling,t.appendChild(o);return e.remove(n),Gt.each(Gt.grep(n.childNodes),function(e){t.appendChild(e)}),t}return n},lb=function(e,t,n){ab(e.childNodes,function(e){ub(e)&&(t(e)&&n(e),e.hasChildNodes()&&lb(e,t,n))})},fb=function(n,e){return d(function(e,t){return!(!t||!mc.getStyle(n,t,e))},e)},db=function(r,e,t){return d(function(e,t,n){r.setStyle(n,e,t),""===n.getAttribute("style")&&n.removeAttribute("style"),mb(r,n)},e,t)},mb=function(e,t){"SPAN"===t.nodeName&&0===e.getAttribs(t).length&&e.remove(t,!0)},gb=function(e,t){var n;1===t.nodeType&&t.parentNode&&1===t.parentNode.nodeType&&(n=mc.getTextDecoration(e,t.parentNode),e.getStyle(t,"color")&&n?e.setStyle(t,"text-decoration",n):e.getStyle(t,"text-decoration")===n&&e.setStyle(t,"text-decoration",null))},pb=function(n,e,r,o){ab(e,function(t){ab(n.dom.select(t.inline,o),function(e){ub(e)&&ob(n,t,r,e,t.exact?e:null)}),function(r,e,t){if(e.clear_child_styles){var n=e.links?"*:not(a)":"*";ab(r.select(n,t),function(n){ub(n)&&ab(e.styles,function(e,t){r.setStyle(n,t,"")})})}}(n.dom,t,o)})},hb=function(e,t,n,r){(t.styles.color||t.styles.textDecoration)&&(Gt.walk(r,d(gb,e),"childNodes"),gb(e,r))},vb=function(e,t,n,r){t.styles&&t.styles.backgroundColor&&lb(r,fb(e,"fontSize"),db(e,"backgroundColor",mc.replaceVars(t.styles.backgroundColor,n)))},bb=function(e,t,n,r){"sub"!==t.inline&&"sup"!==t.inline||(lb(r,fb(e,"fontSize"),db(e,"fontSize","")),e.remove(e.select("sup"===t.inline?"sub":"sup",r),!0))},yb=function(e,t,n,r){r&&!1!==t.merge_siblings&&(r=cb(e,mc.getNonWhiteSpaceSibling(r),r),r=cb(e,r,mc.getNonWhiteSpaceSibling(r,!0)))},Cb=function(t,n,r,o,i){hm.matchNode(t,i.parentNode,r,o)&&ob(t,n,o,i)||n.merge_with_parents&&t.dom.getParent(i.parentNode,function(e){if(hm.matchNode(t,e,r,o))return ob(t,n,o,i),!0})},xb=Gt.each,wb=function(g,p,h,r){var e,t,v=g.formatter.get(p),b=v[0],o=!r&&g.selection.isCollapsed(),i=g.dom,n=g.selection,y=function(n,e){if(e=e||b,n){if(e.onformat&&e.onformat(n,e,h,r),xb(e.styles,function(e,t){i.setStyle(n,t,mc.replaceVars(e,h))}),e.styles){var t=i.getAttrib(n,"style");t&&n.setAttribute("data-mce-style",t)}xb(e.attributes,function(e,t){i.setAttrib(n,t,mc.replaceVars(e,h))}),xb(e.classes,function(e){e=mc.replaceVars(e,h),i.hasClass(n,e)||i.addClass(n,e)})}},C=function(e,t){var n=!1;return!!b.selector&&(xb(e,function(e){if(!("collapsed"in e&&e.collapsed!==o))return i.is(t,e.selector)&&!Uu(t)?(y(t,e),!(n=!0)):void 0}),n)},a=function(s,e,t,c){var l,f,d=[],m=!0;l=b.inline||b.block,f=s.create(l),y(f),Tc(s,e,function(e){var a,u=function(e){var t,n,r,o;if(o=m,t=e.nodeName.toLowerCase(),n=e.parentNode.nodeName.toLowerCase(),1===e.nodeType&&s.getContentEditable(e)&&(o=m,m="true"===s.getContentEditable(e),r=!0),mc.isEq(t,"br"))return a=0,void(b.block&&s.remove(e));if(b.wrapper&&hm.matchNode(g,e,p,h))a=0;else{if(m&&!r&&b.block&&!b.wrapper&&mc.isTextBlock(g,t)&&mc.isValid(g,n,l))return e=s.rename(e,l),y(e),d.push(e),void(a=0);if(b.selector){var i=C(v,e);if(!b.inline||i)return void(a=0)}!m||r||!mc.isValid(g,l,t)||!mc.isValid(g,n,l)||!c&&3===e.nodeType&&1===e.nodeValue.length&&65279===e.nodeValue.charCodeAt(0)||Uu(e)||b.inline&&s.isBlock(e)?(a=0,xb(Gt.grep(e.childNodes),u),r&&(m=o),a=0):(a||(a=s.clone(f,!1),e.parentNode.insertBefore(a,e),d.push(a)),a.appendChild(e))}};xb(e,u)}),!0===b.links&&xb(d,function(e){var t=function(e){"A"===e.nodeName&&y(e,b),xb(Gt.grep(e.childNodes),t)};t(e)}),xb(d,function(e){var t,n,r,o,i,a=function(e){var n=!1;return xb(e.childNodes,function(e){if((t=e)&&1===t.nodeType&&!cc(t)&&!Uu(t)&&!Bo.isBogus(t))return n=e,!1;var t}),n};n=0,xb(e.childNodes,function(e){mc.isWhiteSpaceNode(e)||cc(e)||n++}),t=n,!(1<d.length)&&s.isBlock(e)||0!==t?(b.inline||b.wrapper)&&(b.exact||1!==t||((o=a(r=e))&&!cc(o)&&hm.matchName(s,o,b)&&(i=s.clone(o,!1),y(i),s.replace(i,r,!0),s.remove(o,1)),e=i||r),pb(g,v,h,e),Cb(g,b,p,h,e),vb(s,b,h,e),bb(s,b,h,e),yb(s,b,h,e)):s.remove(e,1)})};if("false"!==i.getContentEditable(n.getNode())){if(b){if(r)r.nodeType?C(v,r)||((t=i.createRng()).setStartBefore(r),t.setEndAfter(r),a(i,Sc(g,t,v),0,!0)):a(i,r,0,!0);else if(o&&b.inline&&!i.select("td[data-mce-selected],th[data-mce-selected]").length)!function(e,t,n){var r,o,i,a,u,s,c=e.selection;a=(r=c.getRng(!0)).startOffset,s=r.startContainer.nodeValue,(o=Vu(e.getBody(),c.getStart()))&&(i=wm(o));var l,f,d=/[^\s\u00a0\u00ad\u200b\ufeff]/;s&&0<a&&a<s.length&&d.test(s.charAt(a))&&d.test(s.charAt(a-1))?(u=c.getBookmark(),r.collapse(!0),r=Sc(e,r,e.formatter.get(t)),r=bm(r),e.formatter.apply(t,n,r),c.moveToBookmark(u)):(o&&i.nodeValue===ym||(l=e.getDoc(),f=Nm(!0).dom(),i=(o=l.importNode(f,!0)).firstChild,r.insertNode(o),a=1),e.formatter.apply(t,n,o),c.setCursorLocation(i,a))}(g,p,h);else{var u=g.selection.getNode();g.settings.forced_root_block||!v[0].defaultBlock||i.getParent(u,i.isBlock)||wb(g,v[0].defaultBlock),g.selection.setRng(el(g.selection.getRng())),e=Fu.getPersistentBookmark(g.selection,!0),a(i,Sc(g,n.getRng(),v)),b.styles&&hb(i,b,h,u),n.moveToBookmark(e),mc.moveStart(i,n,n.getRng()),g.nodeChanged()}Xv(p,g)}}else{r=n.getNode();for(var s=0,c=v.length;s<c;s++)if(v[s].ceFalseOverride&&i.is(r,v[s].selector))return void y(r,v[s])}},Nb={applyFormat:wb},Eb=Gt.each,Sb=function(e,t,n,r,o){var i,a,u,s,c,l,f,d;null===t.get()&&(a=e,u={},(i=t).set({}),a.on("NodeChange",function(n){var r=mc.getParents(a.dom,n.element),o={};r=Gt.grep(r,function(e){return 1===e.nodeType&&!e.getAttribute("data-mce-bogus")}),Eb(i.get(),function(e,n){Eb(r,function(t){return a.formatter.matchNode(t,n,{},e.similar)?(u[n]||(Eb(e,function(e){e(!0,{node:t,format:n,parents:r})}),u[n]=e),o[n]=e,!1):!hm.matchesUnInheritedFormatSelector(a,t,n)&&void 0})}),Eb(u,function(e,t){o[t]||(delete u[t],Eb(e,function(e){e(!1,{node:n.element,format:t,parents:r})}))})})),c=n,l=r,f=o,d=(s=t).get(),Eb(c.split(","),function(e){d[e]||(d[e]=[],d[e].similar=f),d[e].push(l)}),s.set(d)},kb={get:function(r){var t={valigntop:[{selector:"td,th",styles:{verticalAlign:"top"}}],valignmiddle:[{selector:"td,th",styles:{verticalAlign:"middle"}}],valignbottom:[{selector:"td,th",styles:{verticalAlign:"bottom"}}],alignleft:[{selector:"figure.image",collapsed:!1,classes:"align-left",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"left"},inherit:!1,preview:!1,defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"left"},preview:"font-family font-size"}],aligncenter:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"center"},inherit:!1,preview:"font-family font-size",defaultBlock:"div"},{selector:"figure.image",collapsed:!1,classes:"align-center",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"img",collapsed:!1,styles:{display:"block",marginLeft:"auto",marginRight:"auto"},preview:!1},{selector:"table",collapsed:!1,styles:{marginLeft:"auto",marginRight:"auto"},preview:"font-family font-size"}],alignright:[{selector:"figure.image",collapsed:!1,classes:"align-right",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"right"},inherit:!1,preview:"font-family font-size",defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"right"},preview:"font-family font-size"}],alignjustify:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"justify"},inherit:!1,defaultBlock:"div",preview:"font-family font-size"}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:!0},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:!0},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},links:!0,remove_similar:!0,clear_child_styles:!0},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},links:!0,remove_similar:!0,clear_child_styles:!0},fontname:{inline:"span",toggle:!1,styles:{fontFamily:"%value"},clear_child_styles:!0},fontsize:{inline:"span",toggle:!1,styles:{fontSize:"%value"},clear_child_styles:!0},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},code:{inline:"code"},link:{inline:"a",selector:"a",remove:"all",split:!0,deep:!0,onmatch:function(){return!0},onformat:function(n,e,t){Gt.each(t,function(e,t){r.setAttrib(n,t,e)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins",remove:"all",split:!0,expand:!1,block_expand:!0,deep:!0},{selector:"span",attributes:["style","class"],remove:"empty",split:!0,expand:!1,deep:!0},{selector:"*",attributes:["style","class"],split:!1,expand:!1,deep:!0}]};return Gt.each("p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp".split(/\s/),function(e){t[e]={block:e,remove:"all"}}),t}},Tb=Gt.each,Ab=hi.DOM,Rb=function(e,t){var n,o,r,m=t&&t.schema||ri({}),g=function(e){var t,n,r;return o="string"==typeof e?{name:e,classes:[],attrs:{}}:e,t=Ab.create(o.name),n=t,(r=o).classes.length&&Ab.addClass(n,r.classes.join(" ")),Ab.setAttribs(n,r.attrs),t},p=function(n,e,t){var r,o,i,a,u,s,c,l,f=0<e.length&&e[0],d=f&&f.name;if(u=d,s="string"!=typeof(a=n)?a.nodeName.toLowerCase():a,c=m.getElementRule(s),i=!(!(l=c&&c.parentsRequired)||!l.length)&&(u&&-1!==Gt.inArray(l,u)?u:l[0]))d===i?(o=e[0],e=e.slice(1)):o=i;else if(f)o=e[0],e=e.slice(1);else if(!t)return n;return o&&(r=g(o)).appendChild(n),t&&(r||(r=Ab.create("div")).appendChild(n),Gt.each(t,function(e){var t=g(e);r.insertBefore(t,n)})),p(r,e,o&&o.siblings)};return e&&e.length?(o=e[0],n=g(o),(r=Ab.create("div")).appendChild(p(n,e.slice(1),o.siblings)),r):""},_b=function(e){var t,a={classes:[],attrs:{}};return"*"!==(e=a.selector=Gt.trim(e))&&(t=e.replace(/(?:([#\.]|::?)([\w\-]+)|(\[)([^\]]+)\]?)/g,function(e,t,n,r,o){switch(t){case"#":a.attrs.id=n;break;case".":a.classes.push(n);break;case":":-1!==Gt.inArray("checked disabled enabled read-only required".split(" "),n)&&(a.attrs[n]=n)}if("["===r){var i=o.match(/([\w\-]+)(?:\=\"([^\"]+))?/);i&&(a.attrs[i[1]]=i[2])}return""})),a.name=t||"div",a},Db=function(e){return e&&"string"==typeof e?(e=(e=e.split(/\s*,\s*/)[0]).replace(/\s*(~\+|~|\+|>)\s*/g,"$1"),Gt.map(e.split(/(?:>|\s+(?![^\[\]]+\]))/),function(e){var t=Gt.map(e.split(/(?:~\+|~|\+)/),_b),n=t.pop();return t.length&&(n.siblings=t),n}).reverse()):[]},Bb=function(n,e){var t,r,o,i,a,u,s="";if(!1===(u=n.settings.preview_styles))return"";"string"!=typeof u&&(u="font-family font-size font-weight font-style text-decoration text-transform color background-color border border-radius outline text-shadow");var c=function(e){return e.replace(/%(\w+)/g,"")};if("string"==typeof e){if(!(e=n.formatter.get(e)))return;e=e[0]}return"preview"in e&&!1===(u=e.preview)?"":(t=e.block||e.inline||"span",(i=Db(e.selector)).length?(i[0].name||(i[0].name=t),t=e.selector,r=Rb(i,n)):r=Rb([t],n),o=Ab.select(t,r)[0]||r.firstChild,Tb(e.styles,function(e,t){(e=c(e))&&Ab.setStyle(o,t,e)}),Tb(e.attributes,function(e,t){(e=c(e))&&Ab.setAttrib(o,t,e)}),Tb(e.classes,function(e){e=c(e),Ab.hasClass(o,e)||Ab.addClass(o,e)}),n.fire("PreviewFormats"),Ab.setStyles(r,{position:"absolute",left:-65535}),n.getBody().appendChild(r),a=Ab.getStyle(n.getBody(),"fontSize",!0),a=/px$/.test(a)?parseInt(a,10):0,Tb(u.split(" "),function(e){var t=Ab.getStyle(o,e,!0);if(!("background-color"===e&&/transparent|rgba\s*\([^)]+,\s*0\)/.test(t)&&(t=Ab.getStyle(n.getBody(),e,!0),"#ffffff"===Ab.toHex(t).toLowerCase())||"color"===e&&"#000000"===Ab.toHex(t).toLowerCase())){if("font-size"===e&&/em|%$/.test(t)){if(0===a)return;t=parseFloat(t)/(/%$/.test(t)?100:1)*a+"px"}"border"===e&&t&&(s+="padding:0 2px;"),s+=e+":"+t+";"}}),n.fire("AfterPreviewFormats"),Ab.remove(r),s)},Ob=function(e,t,n,r,o){var i=t.get(n);!hm.match(e,n,r,o)||"toggle"in i[0]&&!i[0].toggle?Nb.applyFormat(e,n,r,o):ib(e,n,r,o)},Pb=function(e){e.addShortcut("meta+b","","Bold"),e.addShortcut("meta+i","","Italic"),e.addShortcut("meta+u","","Underline");for(var t=1;t<=6;t++)e.addShortcut("access+"+t,"",["FormatBlock",!1,"h"+t]);e.addShortcut("access+7","",["FormatBlock",!1,"p"]),e.addShortcut("access+8","",["FormatBlock",!1,"div"]),e.addShortcut("access+9","",["FormatBlock",!1,"address"])};function Lb(e){var t,n,r,o=(t=e,n={},(r=function(e,t){e&&("string"!=typeof e?Gt.each(e,function(e,t){r(t,e)}):(t=t.length?t:[t],Gt.each(t,function(e){"undefined"==typeof e.deep&&(e.deep=!e.selector),"undefined"==typeof e.split&&(e.split=!e.selector||e.inline),"undefined"==typeof e.remove&&e.selector&&!e.inline&&(e.remove="none"),e.selector&&e.inline&&(e.mixed=!0,e.block_expand=!0),"string"==typeof e.classes&&(e.classes=e.classes.split(/\s+/))}),n[e]=t))})(kb.get(t.dom)),r(t.settings.formats),{get:function(e){return e?n[e]:n},register:r,unregister:function(e){return e&&n[e]&&delete n[e],n}}),i=Pi(null);return Pb(e),Rm(e),{get:o.get,register:o.register,unregister:o.unregister,apply:d(Nb.applyFormat,e),remove:d(ib,e),toggle:d(Ob,e,o),match:d(hm.match,e),matchAll:d(hm.matchAll,e),matchNode:d(hm.matchNode,e),canApply:d(hm.canApply,e),formatChanged:d(Sb,e,i),getCssText:d(Bb,e)}}var Ib,Mb=Object.prototype.hasOwnProperty,Fb=(Ib=function(e,t){return t},function(){for(var e=new Array(arguments.length),t=0;t<e.length;t++)e[t]=arguments[t];if(0===e.length)throw new Error("Can't merge zero objects");for(var n={},r=0;r<e.length;r++){var o=e[r];for(var i in o)Mb.call(o,i)&&(n[i]=Ib(n[i],o[i]))}return n}),zb={register:function(t,s,c){t.addAttributeFilter("data-mce-tabindex",function(e,t){for(var n,r=e.length;r--;)(n=e[r]).attr("tabindex",n.attributes.map["data-mce-tabindex"]),n.attr(t,null)}),t.addAttributeFilter("src,href,style",function(e,t){for(var n,r,o=e.length,i="data-mce-"+t,a=s.url_converter,u=s.url_converter_scope;o--;)(r=(n=e[o]).attributes.map[i])!==undefined?(n.attr(t,0<r.length?r:null),n.attr(i,null)):(r=n.attributes.map[t],"style"===t?r=c.serializeStyle(c.parseStyle(r),n.name):a&&(r=a.call(u,r,t,n.name)),n.attr(t,0<r.length?r:null))}),t.addAttributeFilter("class",function(e){for(var t,n,r=e.length;r--;)(n=(t=e[r]).attr("class"))&&(n=t.attr("class").replace(/(?:^|\s)mce-item-\w+(?!\S)/g,""),t.attr("class",0<n.length?n:null))}),t.addAttributeFilter("data-mce-type",function(e,t,n){for(var r,o=e.length;o--;)"bookmark"!==(r=e[o]).attributes.map["data-mce-type"]||n.cleanup||(A.from(r.firstChild).exists(function(e){return!la(e.value)})?r.unwrap():r.remove())}),t.addNodeFilter("noscript",function(e){for(var t,n=e.length;n--;)(t=e[n].firstChild)&&(t.value=Wo.decode(t.value))}),t.addNodeFilter("script,style",function(e,t){for(var n,r,o,i=e.length,a=function(e){return e.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi,"").replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")};i--;)r=(n=e[i]).firstChild?n.firstChild.value:"","script"===t?((o=n.attr("type"))&&n.attr("type","mce-no/type"===o?null:o.replace(/^mce\-/,"")),"xhtml"===s.element_format&&0<r.length&&(n.firstChild.value="// <![CDATA[\n"+a(r)+"\n// ]]>")):"xhtml"===s.element_format&&0<r.length&&(n.firstChild.value="\x3c!--\n"+a(r)+"\n--\x3e")}),t.addNodeFilter("#comment",function(e){for(var t,n=e.length;n--;)0===(t=e[n]).value.indexOf("[CDATA[")?(t.name="#cdata",t.type=4,t.value=t.value.replace(/^\[CDATA\[|\]\]$/g,"")):0===t.value.indexOf("mce:protected ")&&(t.name="#text",t.type=3,t.raw=!0,t.value=unescape(t.value).substr(14))}),t.addNodeFilter("xml:namespace,input",function(e,t){for(var n,r=e.length;r--;)7===(n=e[r]).type?n.remove():1===n.type&&("input"!==t||"type"in n.attributes.map||n.attr("type","text"))}),t.addAttributeFilter("data-mce-type",function(e){F(e,function(e){"format-caret"===e.attr("data-mce-type")&&(e.isEmpty(t.schema.getNonEmptyElements())?e.remove():e.unwrap())})}),t.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style,data-mce-selected,data-mce-expando,data-mce-type,data-mce-resize",function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)})},trimTrailingBr:function(e){var t,n,r=function(e){return e&&"br"===e.name};r(t=e.lastChild)&&r(n=t.prev)&&(t.remove(),n.remove())}},Ub={process:function(e,t,n){return f=n,(l=e)&&l.hasEventListeners("PreProcess")&&!f.no_events?(o=t,i=n,c=(r=e).dom,o=o.cloneNode(!0),(a=H.document.implementation).createHTMLDocument&&(u=a.createHTMLDocument(""),Gt.each("BODY"===o.nodeName?o.childNodes:[o],function(e){u.body.appendChild(u.importNode(e,!0))}),o="BODY"!==o.nodeName?u.body.firstChild:u.body,s=c.doc,c.doc=u),hp(r,Fb(i,{node:o})),s&&(c.doc=s),o):t;var r,o,i,a,u,s,c,l,f}},Vb=function(e,a,u){e.addNodeFilter("font",function(e){F(e,function(e){var t,n=a.parse(e.attr("style")),r=e.attr("color"),o=e.attr("face"),i=e.attr("size");r&&(n.color=r),o&&(n["font-family"]=o),i&&(n["font-size"]=u[parseInt(e.attr("size"),10)-1]),e.name="span",e.attr("style",a.serialize(n)),t=e,F(["color","face","size"],function(e){t.attr(e,null)})})})},Hb=function(e,t){var n,r=ii();t.convert_fonts_to_spans&&Vb(e,r,Gt.explode(t.font_size_legacy_values)),n=r,e.addNodeFilter("strike",function(e){F(e,function(e){var t=n.parse(e.attr("style"));t["text-decoration"]="line-through",e.name="span",e.attr("style",n.serialize(t))})})},jb={register:function(e,t){t.inline_styles&&Hb(e,t)}},qb=/^[ \t\r\n]*$/,$b={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11},Wb=function(e,t,n){var r,o,i=n?"lastChild":"firstChild",a=n?"prev":"next";if(e[i])return e[i];if(e!==t){if(r=e[a])return r;for(o=e.parent;o&&o!==t;o=o.parent)if(r=o[a])return r}},Kb=function(){function a(e,t){this.name=e,1===(this.type=t)&&(this.attributes=[],this.attributes.map={})}return a.create=function(e,t){var n,r;if(n=new a(e,$b[e]||1),t)for(r in t)n.attr(r,t[r]);return n},a.prototype.replace=function(e){return e.parent&&e.remove(),this.insert(e,this),this.remove(),this},a.prototype.attr=function(e,t){var n,r;if("string"!=typeof e){for(r in e)this.attr(r,e[r]);return this}if(n=this.attributes){if(t!==undefined){if(null===t){if(e in n.map)for(delete n.map[e],r=n.length;r--;)if(n[r].name===e)return n=n.splice(r,1),this;return this}if(e in n.map){for(r=n.length;r--;)if(n[r].name===e){n[r].value=t;break}}else n.push({name:e,value:t});return n.map[e]=t,this}return n.map[e]}},a.prototype.clone=function(){var e,t,n,r,o,i=new a(this.name,this.type);if(n=this.attributes){for((o=[]).map={},e=0,t=n.length;e<t;e++)"id"!==(r=n[e]).name&&(o[o.length]={name:r.name,value:r.value},o.map[r.name]=r.value);i.attributes=o}return i.value=this.value,i.shortEnded=this.shortEnded,i},a.prototype.wrap=function(e){return this.parent.insert(e,this),e.append(this),this},a.prototype.unwrap=function(){var e,t;for(e=this.firstChild;e;)t=e.next,this.insert(e,this,!0),e=t;this.remove()},a.prototype.remove=function(){var e=this.parent,t=this.next,n=this.prev;return e&&(e.firstChild===this?(e.firstChild=t)&&(t.prev=null):n.next=t,e.lastChild===this?(e.lastChild=n)&&(n.next=null):t.prev=n,this.parent=this.next=this.prev=null),this},a.prototype.append=function(e){var t;return e.parent&&e.remove(),(t=this.lastChild)?((t.next=e).prev=t,this.lastChild=e):this.lastChild=this.firstChild=e,e.parent=this,e},a.prototype.insert=function(e,t,n){var r;return e.parent&&e.remove(),r=t.parent||this,n?(t===r.firstChild?r.firstChild=e:t.prev.next=e,e.prev=t.prev,(e.next=t).prev=e):(t===r.lastChild?r.lastChild=e:t.next.prev=e,e.next=t.next,(e.prev=t).next=e),e.parent=r,e},a.prototype.getAll=function(e){var t,n=[];for(t=this.firstChild;t;t=Wb(t,this))t.name===e&&n.push(t);return n},a.prototype.empty=function(){var e,t,n;if(this.firstChild){for(e=[],n=this.firstChild;n;n=Wb(n,this))e.push(n);for(t=e.length;t--;)(n=e[t]).parent=n.firstChild=n.lastChild=n.next=n.prev=null}return this.firstChild=this.lastChild=null,this},a.prototype.isEmpty=function(e,t,n){var r,o,i=this.firstChild;if(t=t||{},i)do{if(1===i.type){if(i.attributes.map["data-mce-bogus"])continue;if(e[i.name])return!1;for(r=i.attributes.length;r--;)if("name"===(o=i.attributes[r].name)||0===o.indexOf("data-mce-bookmark"))return!1}if(8===i.type)return!1;if(3===i.type&&!qb.test(i.value))return!1;if(3===i.type&&i.parent&&t[i.parent.name]&&qb.test(i.value))return!1;if(n&&n(i))return!1}while(i=Wb(i,this));return!0},a.prototype.walk=function(e){return Wb(this,null,e)},a}(),Xb=function(e,t,n,r){(e.padd_empty_with_br||t.insert)&&n[r.name]?r.empty().append(new Kb("br",1)).shortEnded=!0:r.empty().append(new Kb("#text",3)).value="\xa0"},Yb=function(e){return Gb(e,"#text")&&"\xa0"===e.firstChild.value},Gb=function(e,t){return e&&e.firstChild&&e.firstChild===e.lastChild&&e.firstChild.name===t},Jb=function(r,e,t,n){return n.isEmpty(e,t,function(e){return t=e,(n=r.getElementRule(t.name))&&n.paddEmpty;var t,n})},Qb=function(e,t){return e&&(t[e.name]||"br"===e.name)},Zb=function(e,p){var h=e.schema;p.remove_trailing_brs&&e.addNodeFilter("br",function(e,t,n){var r,o,i,a,u,s,c,l,f=e.length,d=Gt.extend({},h.getBlockElements()),m=h.getNonEmptyElements(),g=h.getNonEmptyElements();for(d.body=1,r=0;r<f;r++)if(i=(o=e[r]).parent,d[o.parent.name]&&o===i.lastChild){for(u=o.prev;u;){if("span"!==(s=u.name)||"bookmark"!==u.attr("data-mce-type")){if("br"!==s)break;if("br"===s){o=null;break}}u=u.prev}o&&(o.remove(),Jb(h,m,g,i)&&(c=h.getElementRule(i.name))&&(c.removeEmpty?i.remove():c.paddEmpty&&Xb(p,n,d,i)))}else{for(a=o;i&&i.firstChild===a&&i.lastChild===a&&!d[(a=i).name];)i=i.parent;a===i&&!0!==p.padd_empty_with_br&&((l=new Kb("#text",3)).value="\xa0",o.replace(l))}}),e.addAttributeFilter("href",function(e){var t,n,r,o=e.length;if(!p.allow_unsafe_link_target)for(;o--;)"a"===(t=e[o]).name&&"_blank"===t.attr("target")&&t.attr("rel",(n=t.attr("rel"),r=n?Gt.trim(n):"",/\b(noopener)\b/g.test(r)?r:r.split(" ").filter(function(e){return 0<e.length}).concat(["noopener"]).sort().join(" ")))}),p.allow_html_in_named_anchor||e.addAttributeFilter("id,name",function(e){for(var t,n,r,o,i=e.length;i--;)if("a"===(o=e[i]).name&&o.firstChild&&!o.attr("href"))for(r=o.parent,t=o.lastChild;n=t.prev,r.insert(t,o),t=n;);}),p.fix_list_elements&&e.addNodeFilter("ul,ol",function(e){for(var t,n,r=e.length;r--;)if("ul"===(n=(t=e[r]).parent).name||"ol"===n.name)if(t.prev&&"li"===t.prev.name)t.prev.append(t);else{var o=new Kb("li",1);o.attr("style","list-style-type: none"),t.wrap(o)}}),p.validate&&h.getValidClasses()&&e.addAttributeFilter("class",function(e){for(var t,n,r,o,i,a,u,s=e.length,c=h.getValidClasses();s--;){for(n=(t=e[s]).attr("class").split(" "),i="",r=0;r<n.length;r++)o=n[r],u=!1,(a=c["*"])&&a[o]&&(u=!0),a=c[t.name],!u&&a&&a[o]&&(u=!0),u&&(i&&(i+=" "),i+=o);i.length||(i=null),t.attr("class",i)}})},ey=Gt.makeMap,ty=Gt.each,ny=Gt.explode,ry=Gt.extend;function oy(k,T){void 0===T&&(T=ri());var A={},R=[],_={},D={};(k=k||{}).validate=!("validate"in k)||k.validate,k.root_name=k.root_name||"body";var B=function(e){var t,n,r;(n=e.name)in A&&((r=_[n])?r.push(e):_[n]=[e]),t=R.length;for(;t--;)(n=R[t].name)in e.attributes.map&&((r=D[n])?r.push(e):D[n]=[e]);return e},e={schema:T,addAttributeFilter:function(e,n){ty(ny(e),function(e){var t;for(t=0;t<R.length;t++)if(R[t].name===e)return void R[t].callbacks.push(n);R.push({name:e,callbacks:[n]})})},getAttributeFilters:function(){return[].concat(R)},addNodeFilter:function(e,n){ty(ny(e),function(e){var t=A[e];t||(A[e]=t=[]),t.push(n)})},getNodeFilters:function(){var e=[];for(var t in A)A.hasOwnProperty(t)&&e.push({name:t,callbacks:A[t]});return e},filterNode:B,parse:function(e,a){var t,n,r,o,i,u,s,c,l,f,d,m=[];a=a||{},_={},D={},l=ry(ey("script,style,head,html,body,title,meta,param"),T.getBlockElements());var g=T.getNonEmptyElements(),p=T.children,h=k.validate,v="forced_root_block"in a?a.forced_root_block:k.forced_root_block,b=T.getWhiteSpaceElements(),y=/^[ \t\r\n]+/,C=/[ \t\r\n]+$/,x=/[ \t\r\n]+/g,w=/^[ \t\r\n]+$/;f=b.hasOwnProperty(a.context)||b.hasOwnProperty(k.root_name);var N=function(e,t){var n,r=new Kb(e,t);return e in A&&((n=_[e])?n.push(r):_[e]=[r]),r},E=function(e){var t,n,r,o,i=T.getBlockElements();for(t=e.prev;t&&3===t.type;){if(0<(r=t.value.replace(C,"")).length)return void(t.value=r);if(n=t.next){if(3===n.type&&n.value.length){t=t.prev;continue}if(!i[n.name]&&"script"!==n.name&&"style"!==n.name){t=t.prev;continue}}o=t.prev,t.remove(),t=o}};t=Nv({validate:h,allow_script_urls:k.allow_script_urls,allow_conditional_comments:k.allow_conditional_comments,self_closing_elements:function(e){var t,n={};for(t in e)"li"!==t&&"p"!==t&&(n[t]=e[t]);return n}(T.getSelfClosingElements()),cdata:function(e){d.append(N("#cdata",4)).value=e},text:function(e,t){var n;f||(e=e.replace(x," "),Qb(d.lastChild,l)&&(e=e.replace(y,""))),0!==e.length&&((n=N("#text",3)).raw=!!t,d.append(n).value=e)},comment:function(e){d.append(N("#comment",8)).value=e},pi:function(e,t){d.append(N(e,7)).value=t,E(d)},doctype:function(e){d.append(N("#doctype",10)).value=e,E(d)},start:function(e,t,n){var r,o,i,a,u;if(i=h?T.getElementRule(e):{}){for((r=N(i.outputName||e,1)).attributes=t,r.shortEnded=n,d.append(r),(u=p[d.name])&&p[r.name]&&!u[r.name]&&m.push(r),o=R.length;o--;)(a=R[o].name)in t.map&&((s=D[a])?s.push(r):D[a]=[r]);l[e]&&E(r),n||(d=r),!f&&b[e]&&(f=!0)}},end:function(e){var t,n,r,o,i;if(n=h?T.getElementRule(e):{}){if(l[e]&&!f){if((t=d.firstChild)&&3===t.type)if(0<(r=t.value.replace(y,"")).length)t.value=r,t=t.next;else for(o=t.next,t.remove(),t=o;t&&3===t.type;)r=t.value,o=t.next,(0===r.length||w.test(r))&&(t.remove(),t=o),t=o;if((t=d.lastChild)&&3===t.type)if(0<(r=t.value.replace(C,"")).length)t.value=r,t=t.prev;else for(o=t.prev,t.remove(),t=o;t&&3===t.type;)r=t.value,o=t.prev,(0===r.length||w.test(r))&&(t.remove(),t=o),t=o}if(f&&b[e]&&(f=!1),n.removeEmpty&&Jb(T,g,b,d)&&!d.attributes.map.name&&!d.attr("id"))return i=d.parent,l[d.name]?d.empty().remove():d.unwrap(),void(d=i);n.paddEmpty&&(Yb(d)||Jb(T,g,b,d))&&Xb(k,a,l,d),d=d.parent}}},T);var S=d=new Kb(a.context||k.root_name,11);if(t.parse(e),h&&m.length&&(a.context?a.invalid=!0:function(e){var t,n,r,o,i,a,u,s,c,l,f,d,m,g,p,h;for(d=ey("tr,td,th,tbody,thead,tfoot,table"),l=T.getNonEmptyElements(),f=T.getWhiteSpaceElements(),m=T.getTextBlockElements(),g=T.getSpecialElements(),t=0;t<e.length;t++)if((n=e[t]).parent&&!n.fixed)if(m[n.name]&&"li"===n.parent.name){for(p=n.next;p&&m[p.name];)p.name="li",p.fixed=!0,n.parent.insert(p,n.parent),p=p.next;n.unwrap(n)}else{for(o=[n],r=n.parent;r&&!T.isValidChild(r.name,n.name)&&!d[r.name];r=r.parent)o.push(r);if(r&&1<o.length){for(o.reverse(),i=a=B(o[0].clone()),c=0;c<o.length-1;c++){for(T.isValidChild(a.name,o[c].name)?(u=B(o[c].clone()),a.append(u)):u=a,s=o[c].firstChild;s&&s!==o[c+1];)h=s.next,u.append(s),s=h;a=u}Jb(T,l,f,i)?r.insert(n,o[0],!0):(r.insert(i,o[0],!0),r.insert(n,i)),r=o[0],(Jb(T,l,f,r)||Gb(r,"br"))&&r.empty().remove()}else if(n.parent){if("li"===n.name){if((p=n.prev)&&("ul"===p.name||"ul"===p.name)){p.append(n);continue}if((p=n.next)&&("ul"===p.name||"ul"===p.name)){p.insert(n,p.firstChild,!0);continue}n.wrap(B(new Kb("ul",1)));continue}T.isValidChild(n.parent.name,"div")&&T.isValidChild("div",n.name)?n.wrap(B(new Kb("div",1))):g[n.name]?n.empty().remove():n.unwrap()}}}(m)),v&&("body"===S.name||a.isRootContent)&&function(){var e,t,n=S.firstChild,r=function(e){e&&((n=e.firstChild)&&3===n.type&&(n.value=n.value.replace(y,"")),(n=e.lastChild)&&3===n.type&&(n.value=n.value.replace(C,"")))};if(T.isValidChild(S.name,v.toLowerCase())){for(;n;)e=n.next,3===n.type||1===n.type&&"p"!==n.name&&!l[n.name]&&!n.attr("data-mce-type")?(t||((t=N(v,1)).attr(k.forced_root_block_attrs),S.insert(t,n)),t.append(n)):(r(t),t=null),n=e;r(t)}}(),!a.invalid){for(c in _){for(s=A[c],i=(n=_[c]).length;i--;)n[i].parent||n.splice(i,1);for(r=0,o=s.length;r<o;r++)s[r](n,c,a)}for(r=0,o=R.length;r<o;r++)if((s=R[r]).name in D){for(i=(n=D[s.name]).length;i--;)n[i].parent||n.splice(i,1);for(i=0,u=s.callbacks.length;i<u;i++)s.callbacks[i](n,s.name,a)}}return S}};return Zb(e,k),jb.register(e,k),e}var iy=function(e,t,n){-1===Gt.inArray(t,n)&&(e.addAttributeFilter(n,function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)}),t.push(n))},ay=function(e,t,n){var r=da(n.getInner?t.innerHTML:e.getOuterHTML(t));return n.selection||Co(or.fromDom(t))?r:Gt.trim(r)},uy=function(e,t,n){var r=n.selection?Fb({forced_root_block:!1},n):n,o=e.parse(t,r);return zb.trimTrailingBr(o),o},sy=function(e,t,n,r,o){var i,a,u,s,c=(i=r,Jc(t,n).serialize(i));return a=e,s=c,!(u=o).no_events&&a?vp(a,Fb(u,{content:s})).content:s};function cy(e,t){var a,u,s,c,l,n,r=(a=e,n=["data-mce-selected"],s=(u=t)&&u.dom?u.dom:hi.DOM,c=u&&u.schema?u.schema:ri(a),a.entity_encoding=a.entity_encoding||"named",a.remove_trailing_brs=!("remove_trailing_brs"in a)||a.remove_trailing_brs,l=oy(a,c),zb.register(l,a,s),{schema:c,addNodeFilter:l.addNodeFilter,addAttributeFilter:l.addAttributeFilter,serialize:function(e,t){var n=Fb({format:"html"},t||{}),r=Ub.process(u,e,n),o=ay(s,r,n),i=uy(l,o,n);return"tree"===n.format?i:sy(u,a,c,i,n)},addRules:function(e){c.addValidElements(e)},setRules:function(e){c.setValidElements(e)},addTempAttr:d(iy,l,n),getTempAttrs:function(){return n}});return{schema:r.schema,addNodeFilter:r.addNodeFilter,addAttributeFilter:r.addAttributeFilter,serialize:r.serialize,addRules:r.addRules,setRules:r.setRules,addTempAttr:r.addTempAttr,getTempAttrs:r.getTempAttrs}}function ly(e){return{getBookmark:d(uc,e),moveToBookmark:d(sc,e)}}(ly||(ly={})).isBookmarkNode=cc;var fy,dy,my=ly,gy=Bo.isContentEditableFalse,py=Bo.isContentEditableTrue,hy=function(r,a){var u,s,c,l,f,d,m,g,p,h,v,b,i,y,C,x,w,N=a.dom,E=Gt.each,S=a.getDoc(),k=H.document,T=Math.abs,A=Math.round,R=a.getBody();l={nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};var e=".mce-content-body";a.contentStyles.push(e+" div.mce-resizehandle {position: absolute;border: 1px solid black;box-sizing: content-box;background: #FFF;width: 7px;height: 7px;z-index: 10000}"+e+" .mce-resizehandle:hover {background: #000}"+e+" img[data-mce-selected],"+e+" hr[data-mce-selected] {outline: 1px solid black;resize: none}"+e+" .mce-clonedresizable {position: absolute;"+(me.gecko?"":"outline: 1px dashed black;")+"opacity: .5;filter: alpha(opacity=50);z-index: 10000}"+e+" .mce-resize-helper {background: #555;background: rgba(0,0,0,0.75);border-radius: 3px;border: 1px;color: white;display: none;font-family: sans-serif;font-size: 12px;white-space: nowrap;line-height: 14px;margin: 5px 10px;padding: 5px;position: absolute;z-index: 10001}");var _=function(e){return e&&("IMG"===e.nodeName||a.dom.is(e,"figure.image"))},n=function(e){var t,n,r=e.target;t=e,n=a.selection.getRng(),!_(t.target)||mv(t.clientX,t.clientY,n)||e.isDefaultPrevented()||(e.preventDefault(),a.selection.select(r))},D=function(e){return a.dom.is(e,"figure.image")?e.querySelector("img"):e},B=function(e){var t=a.settings.object_resizing;return!1!==t&&!me.iOS&&("string"!=typeof t&&(t="table,img,figure.image,div"),"false"!==e.getAttribute("data-mce-resize")&&e!==a.getBody()&&Br(or.fromDom(e),t))},O=function(e){var t,n,r,o;t=e.screenX-d,n=e.screenY-m,y=t*f[2]+h,C=n*f[3]+v,y=y<5?5:y,C=C<5?5:C,(_(u)&&!1!==a.settings.resize_img_proportional?!pv.modifierPressed(e):pv.modifierPressed(e)||_(u)&&f[2]*f[3]!=0)&&(T(t)>T(n)?(C=A(y*b),y=A(C/b)):(y=A(C/b),C=A(y*b))),N.setStyles(D(s),{width:y,height:C}),r=0<(r=f.startPos.x+t)?r:0,o=0<(o=f.startPos.y+n)?o:0,N.setStyles(c,{left:r,top:o,display:"block"}),c.innerHTML=y+" &times; "+C,f[2]<0&&s.clientWidth<=y&&N.setStyle(s,"left",g+(h-y)),f[3]<0&&s.clientHeight<=C&&N.setStyle(s,"top",p+(v-C)),(t=R.scrollWidth-x)+(n=R.scrollHeight-w)!=0&&N.setStyles(c,{left:r-t,top:o-n}),i||(xp(a,u,h,v),i=!0)},P=function(){i=!1;var e=function(e,t){t&&(u.style[e]||!a.schema.isValid(u.nodeName.toLowerCase(),e)?N.setStyle(D(u),e,t):N.setAttrib(D(u),e,t))};e("width",y),e("height",C),N.unbind(S,"mousemove",O),N.unbind(S,"mouseup",P),k!==S&&(N.unbind(k,"mousemove",O),N.unbind(k,"mouseup",P)),N.remove(s),N.remove(c),o(u),wp(a,u,y,C),N.setAttrib(u,"style",N.getAttrib(u,"style")),a.nodeChanged()},o=function(e){var t,r,o,n,i;L(),F(),t=N.getPos(e,R),g=t.x,p=t.y,i=e.getBoundingClientRect(),r=i.width||i.right-i.left,o=i.height||i.bottom-i.top,u!==e&&(u=e,y=C=0),n=a.fire("ObjectSelected",{target:e}),B(e)&&!n.isDefaultPrevented()?E(l,function(n,e){var t;(t=N.get("mceResizeHandle"+e))&&N.remove(t),t=N.add(R,"div",{id:"mceResizeHandle"+e,"data-mce-bogus":"all","class":"mce-resizehandle",unselectable:!0,style:"cursor:"+e+"-resize; margin:0; padding:0"}),11===me.ie&&(t.contentEditable=!1),N.bind(t,"mousedown",function(e){var t;e.stopImmediatePropagation(),e.preventDefault(),d=(t=e).screenX,m=t.screenY,h=D(u).clientWidth,v=D(u).clientHeight,b=v/h,(f=n).startPos={x:r*n[0]+g,y:o*n[1]+p},x=R.scrollWidth,w=R.scrollHeight,s=u.cloneNode(!0),N.addClass(s,"mce-clonedresizable"),N.setAttrib(s,"data-mce-bogus","all"),s.contentEditable=!1,s.unSelectabe=!0,N.setStyles(s,{left:g,top:p,margin:0}),s.removeAttribute("data-mce-selected"),R.appendChild(s),N.bind(S,"mousemove",O),N.bind(S,"mouseup",P),k!==S&&(N.bind(k,"mousemove",O),N.bind(k,"mouseup",P)),c=N.add(R,"div",{"class":"mce-resize-helper","data-mce-bogus":"all"},h+" &times; "+v)}),n.elm=t,N.setStyles(t,{left:r*n[0]+g-t.offsetWidth/2,top:o*n[1]+p-t.offsetHeight/2})}):L(),u.setAttribute("data-mce-selected","1")},L=function(){var e,t;for(e in F(),u&&u.removeAttribute("data-mce-selected"),l)(t=N.get("mceResizeHandle"+e))&&(N.unbind(t),N.remove(t))},I=function(e){var t,n=function(e,t){if(e)do{if(e===t)return!0}while(e=e.parentNode)};i||a.removed||(E(N.select("img[data-mce-selected],hr[data-mce-selected]"),function(e){e.removeAttribute("data-mce-selected")}),t="mousedown"===e.type?e.target:r.getNode(),n(t=N.$(t).closest("table,img,figure.image,hr")[0],R)&&(z(),n(r.getStart(!0),t)&&n(r.getEnd(!0),t))?o(t):L())},M=function(e){return gy(function(e,t){for(;t&&t!==e;){if(py(t)||gy(t))return t;t=t.parentNode}return null}(a.getBody(),e))},F=function(){for(var e in l){var t=l[e];t.elm&&(N.unbind(t.elm),delete t.elm)}},z=function(){try{a.getDoc().execCommand("enableObjectResizing",!1,!1)}catch(e){}};return a.on("init",function(){z(),me.ie&&11<=me.ie&&(a.on("mousedown click",function(e){var t=e.target,n=t.nodeName;i||!/^(TABLE|IMG|HR)$/.test(n)||M(t)||(2!==e.button&&a.selection.select(t,"TABLE"===n),"mousedown"===e.type&&a.nodeChanged())}),a.dom.bind(R,"mscontrolselect",function(e){var t=function(e){be.setEditorTimeout(a,function(){a.selection.select(e)})};if(M(e.target))return e.preventDefault(),void t(e.target);/^(TABLE|IMG|HR)$/.test(e.target.nodeName)&&(e.preventDefault(),"IMG"===e.target.tagName&&t(e.target))}));var t=be.throttle(function(e){a.composing||I(e)});a.on("nodechange ResizeEditor ResizeWindow drop FullscreenStateChanged",t),a.on("keyup compositionend",function(e){u&&"TABLE"===u.nodeName&&t(e)}),a.on("hide blur",L),a.on("contextmenu",n)}),a.on("remove",F),{isResizable:B,showResizeRect:o,hideResizeRect:L,updateResizeRect:I,destroy:function(){u=s=null}}},vy=function(e){for(var t=0,n=0,r=e;r&&r.nodeType;)t+=r.offsetLeft||0,n+=r.offsetTop||0,r=r.offsetParent;return{x:t,y:n}},by=function(e,t,n){var r,o,i,a,u,s=e.dom,c=s.getRoot(),l=0;if(u={elm:t,alignToTop:n},e.fire("scrollIntoView",u),!u.isDefaultPrevented()&&Bo.isElement(t)){if(!1===n&&(l=t.offsetHeight),"BODY"!==c.nodeName){var f=e.selection.getScrollContainer();if(f)return r=vy(t).y-vy(f).y+l,a=f.clientHeight,void((r<(i=f.scrollTop)||i+a<r+25)&&(f.scrollTop=r<i?r:r-a+25))}o=s.getViewPort(e.getWin()),r=s.getPos(t).y+l,i=o.y,a=o.h,(r<o.y||i+a<r+25)&&e.getWin().scrollTo(0,r<i?r:r-a+25)}},yy=function(d,e){te(gu.fromRangeStart(e).getClientRects()).each(function(e){var t,n,r,o,i,a,u,s,c,l=function(e){if(e.inline)return e.getBody().getBoundingClientRect();var t=e.getWin();return{left:0,right:t.innerWidth,top:0,bottom:t.innerHeight,width:t.innerWidth,height:t.innerHeight}}(d),f={x:(i=t=l,a=n=e,a.left>i.left&&a.right<i.right?0:a.left<i.left?a.left-i.left:a.right-i.right),y:(r=t,o=n,o.top>r.top&&o.bottom<r.bottom?0:o.top<r.top?o.top-r.top:o.bottom-r.bottom)};s=0!==f.x?0<f.x?f.x+4:f.x-4:0,c=0!==f.y?0<f.y?f.y+4:f.y-4:0,(u=d).inline?(u.getBody().scrollLeft+=s,u.getBody().scrollTop+=c):u.getWin().scrollBy(s,c)})},Cy=function(e){return Bo.isContentEditableTrue(e)||Bo.isContentEditableFalse(e)},xy=function(e,t,n){var r,o,i,a,u,s=n;if(s.caretPositionFromPoint)(o=s.caretPositionFromPoint(e,t))&&((r=n.createRange()).setStart(o.offsetNode,o.offset),r.collapse(!0));else if(n.caretRangeFromPoint)r=n.caretRangeFromPoint(e,t);else if(s.body.createTextRange){r=s.body.createTextRange();try{r.moveToPoint(e,t),r.collapse(!0)}catch(c){r=function(e,n,t){var r,o,i;if(r=t.elementFromPoint(e,n),o=t.body.createTextRange(),r&&"HTML"!==r.tagName||(r=t.body),o.moveToElementText(r),0<(i=(i=Gt.toArray(o.getClientRects())).sort(function(e,t){return(e=Math.abs(Math.max(e.top-n,e.bottom-n)))-(t=Math.abs(Math.max(t.top-n,t.bottom-n)))})).length){n=(i[0].bottom+i[0].top)/2;try{return o.moveToPoint(e,n),o.collapse(!0),o}catch(a){}}return null}(e,t,n)}return i=r,a=n.body,u=i&&i.parentElement?i.parentElement():null,Bo.isContentEditableFalse(function(e,t,n){for(;e&&e!==t;){if(n(e))return e;e=e.parentNode}return null}(u,a,Cy))?null:i}return r},wy=function(n,e){return W(e,function(e){var t=n.fire("GetSelectionRange",{range:e});return t.range!==e?t.range:e})},Ny=function(e,t){var n=(t||H.document).createDocumentFragment();return F(e,function(e){n.appendChild(e.dom())}),or.fromDom(n)},Ey=Sr("element","width","rows"),Sy=Sr("element","cells"),ky=Sr("x","y"),Ty=function(e,t){var n=parseInt(Cr(e,t),10);return isNaN(n)?1:n},Ay=function(e){return U(e,function(e,t){return t.cells().length>e?t.cells().length:e},0)},Ry=function(e,t){for(var n=e.rows(),r=0;r<n.length;r++)for(var o=n[r].cells(),i=0;i<o.length;i++)if(Pr(o[i],t))return A.some(ky(i,r));return A.none()},_y=function(e,t,n,r,o){for(var i=[],a=e.rows(),u=n;u<=o;u++){var s=a[u].cells(),c=t<r?s.slice(t,r+1):s.slice(r,t+1);i.push(Sy(a[u].element(),c))}return i},Dy=function(e){var o=Ey(aa(e),0,[]);return F(ji(e,"tr"),function(n,r){F(ji(n,"td,th"),function(e,t){!function(e,t,n,r,o){for(var i=Ty(o,"rowspan"),a=Ty(o,"colspan"),u=e.rows(),s=n;s<n+i;s++){u[s]||(u[s]=Sy(ua(r),[]));for(var c=t;c<t+a;c++)u[s].cells()[c]=s===n&&c===t?o:aa(o)}}(o,function(e,t,n){for(;r=t,o=n,i=void 0,((i=e.rows())[o]?i[o].cells():[])[r];)t++;var r,o,i;return t}(o,t,r),r,n,e)})}),Ey(o.element(),Ay(o.rows()),o.rows())},By=function(e){return n=W((t=e).rows(),function(e){var t=W(e.cells(),function(e){var t=ua(e);return xr(t,"colspan"),xr(t,"rowspan"),t}),n=aa(e.element());return Ri(n,t),n}),r=aa(t.element()),o=or.fromTag("tbody"),Ri(o,n),Ai(r,o),r;var t,n,r,o},Oy=function(l,e,t){return Ry(l,e).bind(function(c){return Ry(l,t).map(function(e){return t=l,r=e,o=(n=c).x(),i=n.y(),a=r.x(),u=r.y(),s=i<u?_y(t,o,i,a,u):_y(t,o,u,a,i),Ey(t.element(),Ay(s),s);var t,n,r,o,i,a,u,s})})},Py=function(n,t){return V(n,function(e){return"li"===sr(e)&&Mh(e,t)}).fold(q([]),function(e){return(t=n,V(t,function(e){return"ul"===sr(e)||"ol"===sr(e)})).map(function(e){return[or.fromTag("li"),or.fromTag(sr(e))]}).getOr([]);var t})},Ly=function(e,t){var n,r=or.fromDom(t.commonAncestorContainer),o=Ml(r,e),i=z(o,function(e){return fo(e)||co(e)}),a=Py(o,t),u=i.concat(a.length?a:ho(n=r)?Mr(n).filter(po).fold(q([]),function(e){return[n,e]}):po(n)?[n]:[]);return W(u,aa)},Iy=function(){return Ny([])},My=function(e,t){return n=or.fromDom(t.cloneContents()),r=Ly(e,t),o=U(r,function(e,t){return Ai(t,e),t},n),0<r.length?Ny([o]):o;var n,r,o},Fy=function(e,o){return(t=e,n=o[0],Xi(n,"table",d(Pr,t))).bind(function(e){var t=o[0],n=o[o.length-1],r=Dy(e);return Oy(r,t,n).map(function(e){return Ny([By(e)])})}).getOrThunk(Iy);var t,n},zy=function(e,t){var n,r,o=Qd(t,e);return 0<o.length?Fy(e,o):(n=e,0<(r=t).length&&r[0].collapsed?Iy():My(n,r[0]))},Uy=function(e,t){if(void 0===t&&(t={}),t.get=!0,t.format=t.format||"html",t.selection=!0,(t=e.fire("BeforeGetContent",t)).isDefaultPrevented())return e.fire("GetContent",t),t.content;if("text"===t.format)return c=e,A.from(c.selection.getRng()).map(function(e){var t=c.dom.add(c.getBody(),"div",{"data-mce-bogus":"all",style:"overflow: hidden; opacity: 0;"},e.cloneContents()),n=da(t.innerText);return c.dom.remove(t),n}).getOr("");t.getInner=!0;var n,r,o,i,a,u,s,c,l=(r=t,i=(n=e).selection.getRng(),a=n.dom.create("body"),u=n.selection.getSel(),s=wy(n,Wd(u)),(o=r.contextual?zy(or.fromDom(n.getBody()),s).dom():i.cloneContents())&&a.appendChild(o),n.selection.serializer.serialize(a,r));return"tree"===t.format?l:(t.content=e.selection.isCollapsed()?"":l,e.fire("GetContent",t),t.content)},Vy=function(e,t,n){var r,o,i,a=e.selection.getRng(),u=e.getDoc();if((n=n||{format:"html"}).set=!0,n.selection=!0,n.content=t,n.no_events||!(n=e.fire("BeforeSetContent",n)).isDefaultPrevented()){if(t=n.content,a.insertNode){t+='<span id="__caret">_</span>',a.startContainer===u&&a.endContainer===u?u.body.innerHTML=t:(a.deleteContents(),0===u.body.childNodes.length?u.body.innerHTML=t:a.createContextualFragment?a.insertNode(a.createContextualFragment(t)):(o=u.createDocumentFragment(),i=u.createElement("div"),o.appendChild(i),i.outerHTML=t,a.insertNode(o))),r=e.dom.get("__caret"),(a=u.createRange()).setStartBefore(r),a.setEndBefore(r),e.selection.setRng(a),e.dom.remove("__caret");try{e.selection.setRng(a)}catch(s){}}else a.item&&(u.execCommand("Delete",!1,null),a=e.getRng()),/^\s+/.test(t)?(a.pasteHTML('<span id="__mce_tmp">_</span>'+t),e.dom.remove("__mce_tmp")):a.pasteHTML(t);n.no_events||e.fire("SetContent",n)}else e.fire("SetContent",n)},Hy=function(e,t,n,r,o){var i=n?t.startContainer:t.endContainer,a=n?t.startOffset:t.endOffset;return A.from(i).map(or.fromDom).map(function(e){return r&&t.collapsed?e:jr(e,o(e,a)).getOr(e)}).bind(function(e){return lr(e)?A.some(e):Mr(e)}).map(function(e){return e.dom()}).getOr(e)},jy=function(e,t,n){return Hy(e,t,!0,n,function(e,t){return Math.min(e.dom().childNodes.length,t)})},qy=function(e,t,n){return Hy(e,t,!1,n,function(e,t){return 0<t?t-1:t})},$y=function(e,t){for(var n=e;e&&Bo.isText(e)&&0===e.length;)e=t?e.nextSibling:e.previousSibling;return e||n},Wy=Gt.each,Ky=function(e){return!!e.select},Xy=function(e){return!(!e||!e.ownerDocument)&&Lr(or.fromDom(e.ownerDocument),or.fromDom(e))},Yy=function(u,s,e,c){var n,t,l,f,a,r=function(e,t){return Vy(c,e,t)},o=function(e){var t=m();t.collapse(!!e),i(t)},d=function(){return s.getSelection?s.getSelection():s.document.selection},m=function(){var e,t,n,r,o=function(e,t,n){try{return t.compareBoundaryPoints(e,n)}catch(r){return-1}};if(!s)return null;if(null==(r=s.document))return null;if(c.bookmark!==undefined&&!1===Kp(c)){var i=np(c);if(i.isSome())return i.map(function(e){return wy(c,[e])[0]}).getOr(r.createRange())}try{(e=d())&&(t=0<e.rangeCount?e.getRangeAt(0):e.createRange?e.createRange():r.createRange())}catch(a){}return(t=wy(c,[t])[0])||(t=r.createRange?r.createRange():r.body.createTextRange()),t.setStart&&9===t.startContainer.nodeType&&t.collapsed&&(n=u.getRoot(),t.setStart(n,0),t.setEnd(n,0)),l&&f&&(0===o(t.START_TO_START,t,l)&&0===o(t.END_TO_END,t,l)?t=f:f=l=null),t},i=function(e,t){var n,r;if((o=e)&&(Ky(o)||Xy(o.startContainer)&&Xy(o.endContainer))){var o,i=Ky(e)?e:null;if(i){f=null;try{i.select()}catch(a){}}else{if(n=d(),e=c.fire("SetSelectionRange",{range:e,forward:t}).range,n){f=e;try{n.removeAllRanges(),n.addRange(e)}catch(a){}!1===t&&n.extend&&(n.collapse(e.endContainer,e.endOffset),n.extend(e.startContainer,e.startOffset)),l=0<n.rangeCount?n.getRangeAt(0):null}e.collapsed||e.startContainer!==e.endContainer||!n.setBaseAndExtent||me.ie||e.endOffset-e.startOffset<2&&e.startContainer.hasChildNodes()&&(r=e.startContainer.childNodes[e.startOffset])&&"IMG"===r.tagName&&(n.setBaseAndExtent(e.startContainer,e.startOffset,e.endContainer,e.endOffset),n.anchorNode===e.startContainer&&n.focusNode===e.endContainer||n.setBaseAndExtent(r,0,r,1)),c.fire("AfterSetSelectionRange",{range:e,forward:t})}}},g=function(){var e,t,n=d();return!(n&&n.anchorNode&&n.focusNode)||((e=u.createRng()).setStart(n.anchorNode,n.anchorOffset),e.collapse(!0),(t=u.createRng()).setStart(n.focusNode,n.focusOffset),t.collapse(!0),e.compareBoundaryPoints(e.START_TO_START,t)<=0)},p={bookmarkManager:null,controlSelection:null,dom:u,win:s,serializer:e,editor:c,collapse:o,setCursorLocation:function(e,t){var n=u.createRng();e?(n.setStart(e,t),n.setEnd(e,t),i(n),o(!1)):(Fh(u,n,c.getBody(),!0),i(n))},getContent:function(e){return Uy(c,e)},setContent:r,getBookmark:function(e,t){return n.getBookmark(e,t)},moveToBookmark:function(e){return n.moveToBookmark(e)},select:function(e,t){var r,n,o;return(r=u,n=e,o=t,A.from(n).map(function(e){var t=r.nodeIndex(e),n=r.createRng();return n.setStart(e.parentNode,t),n.setEnd(e.parentNode,t+1),o&&(Fh(r,n,e,!0),Fh(r,n,e,!1)),n})).each(i),e},isCollapsed:function(){var e=m(),t=d();return!(!e||e.item)&&(e.compareEndPoints?0===e.compareEndPoints("StartToEnd",e):!t||e.collapsed)},isForward:g,setNode:function(e){return r(u.getOuterHTML(e)),e},getNode:function(){return e=c.getBody(),(t=m())?(r=t.startContainer,o=t.endContainer,i=t.startOffset,a=t.endOffset,n=t.commonAncestorContainer,!t.collapsed&&(r===o&&a-i<2&&r.hasChildNodes()&&(n=r.childNodes[i]),3===r.nodeType&&3===o.nodeType&&(r=r.length===i?$y(r.nextSibling,!0):r.parentNode,o=0===a?$y(o.previousSibling,!1):o.parentNode,r&&r===o))?r:n&&3===n.nodeType?n.parentNode:n):e;var e,t,n,r,o,i,a},getSel:d,setRng:i,getRng:m,getStart:function(e){return jy(c.getBody(),m(),e)},getEnd:function(e){return qy(c.getBody(),m(),e)},getSelectedBlocks:function(e,t){return function(e,t,n,r){var o,i,a=[];if(i=e.getRoot(),n=e.getParent(n||jy(i,t,t.collapsed),e.isBlock),r=e.getParent(r||qy(i,t,t.collapsed),e.isBlock),n&&n!==i&&a.push(n),n&&r&&n!==r)for(var u=new oo(o=n,i);(o=u.next())&&o!==r;)e.isBlock(o)&&a.push(o);return r&&n!==r&&r!==i&&a.push(r),a}(u,m(),e,t)},normalize:function(){var e=m(),t=d();if(!Xd(t)&&zh(c)){var n=_g(u,e);return n.each(function(e){i(e,g())}),n.getOr(e)}return e},selectorChanged:function(e,t){var i;return a||(a={},i={},c.on("NodeChange",function(e){var n=e.element,r=u.getParents(n,null,u.getRoot()),o={};Wy(a,function(e,n){Wy(r,function(t){if(u.is(t,n))return i[n]||(Wy(e,function(e){e(!0,{node:t,selector:n,parents:r})}),i[n]=e),o[n]=e,!1})}),Wy(i,function(e,t){o[t]||(delete i[t],Wy(e,function(e){e(!1,{node:n,selector:t,parents:r})}))})})),a[e]||(a[e]=[]),a[e].push(t),p},getScrollContainer:function(){for(var e,t=u.getRoot();t&&"BODY"!==t.nodeName;){if(t.scrollHeight>t.clientHeight){e=t;break}t=t.parentNode}return e},scrollIntoView:function(e,t){return by(c,e,t)},placeCaretAt:function(e,t){return i(xy(e,t,c.getDoc()))},getBoundingClientRect:function(){var e=m();return e.collapsed?vu.fromRangeStart(e).getClientRects()[0]:e.getBoundingClientRect()},destroy:function(){s=l=f=null,t.destroy()}};return n=my(p),t=hy(p,c),p.bookmarkManager=n,p.controlSelection=t,p};(dy=fy||(fy={}))[dy.Br=0]="Br",dy[dy.Block=1]="Block",dy[dy.Wrap=2]="Wrap",dy[dy.Eol=3]="Eol";var Gy=function(e,t){return e===pu.Backwards?t.reverse():t},Jy=function(e,t,n,r){for(var o,i,a,u,s,c,l=Hs(n),f=r,d=[];f&&(s=l,c=f,o=t===pu.Forwards?s.next(c):s.prev(c));){if(Bo.isBr(o.getNode(!1)))return t===pu.Forwards?{positions:Gy(t,d).concat([o]),breakType:fy.Br,breakAt:A.some(o)}:{positions:Gy(t,d),breakType:fy.Br,breakAt:A.some(o)};if(o.isVisible()){if(e(f,o)){var m=(i=t,a=f,u=o,Bo.isBr(u.getNode(i===pu.Forwards))?fy.Br:!1===ps(a,u)?fy.Block:fy.Wrap);return{positions:Gy(t,d),breakType:m,breakAt:A.some(o)}}d.push(o),f=o}else f=o}return{positions:Gy(t,d),breakType:fy.Eol,breakAt:A.none()}},Qy=function(n,r,o,e){return r(o,e).breakAt.map(function(e){var t=r(o,e).positions;return n===pu.Backwards?t.concat(e):[e].concat(t)}).getOr([])},Zy=function(e,i){return U(e,function(e,o){return e.fold(function(){return A.some(o)},function(r){return Wa([te(r.getClientRects()),te(o.getClientRects())],function(e,t){var n=Math.abs(i-e.left);return Math.abs(i-t.left)<=n?o:r}).or(e)})},A.none())},eC=function(t,e){return te(e.getClientRects()).bind(function(e){return Zy(t,e.left)})},tC=d(Jy,gu.isAbove,-1),nC=d(Jy,gu.isBelow,1),rC=d(Qy,-1,tC),oC=d(Qy,1,nC),iC=Bo.isContentEditableFalse,aC=Ha,uC=function(e,t,n,r){var o=e===pu.Forwards,i=o?mf:gf;if(!r.collapsed){var a=aC(r);if(iC(a))return Mm(e,t,a,e===pu.Backwards,!0)}var u=pa(r.startContainer),s=Ns(e,t.getBody(),r);if(i(s))return Fm(t,s.getNode(!o));var c=yl.normalizePosition(o,n(s));if(!c)return u?r:null;if(i(c))return Mm(e,t,c.getNode(!o),o,!0);var l=n(c);return l&&i(l)&&ks(c,l)?Mm(e,t,l.getNode(!o),o,!0):u?Um(t,c.toRange(),!0):null},sC=function(e,t,n,r){var o,i,a,u,s,c,l,f,d;if(d=aC(r),o=Ns(e,t.getBody(),r),i=n(t.getBody(),rv(1),o),a=z(i,ov(1)),s=$t.last(o.getClientRects()),(mf(o)||hf(o))&&(d=o.getNode()),(gf(o)||vf(o))&&(d=o.getNode(!0)),!s)return null;if(c=s.left,(u=lv(a,c))&&iC(u.node))return l=Math.abs(c-u.left),f=Math.abs(c-u.right),Mm(e,t,u.node,l<f,!0);if(d){var m=function(e,t,n,r){var o,i,a,u,s,c,l=Hs(t),f=[],d=0,m=function(e){return $t.last(e.getClientRects())};1===e?(o=l.next,i=Va,a=Ua,u=vu.after(r)):(o=l.prev,i=Ua,a=Va,u=vu.before(r)),c=m(u);do{if(u.isVisible()&&!a(s=m(u),c)){if(0<f.length&&i(s,$t.last(f))&&d++,(s=Ma(s)).position=u,s.line=d,n(s))return f;f.push(s)}}while(u=o(u));return f}(e,t.getBody(),rv(1),d);if(u=lv(z(m,ov(1)),c))return Um(t,u.position.toRange(),!0);if(u=$t.last(z(m,ov(0))))return Um(t,u.position.toRange(),!0)}},cC=function(e,t,n){var r,o,i,a,u=Hs(e.getBody()),s=d(Ss,u.next),c=d(Ss,u.prev);if(n.collapsed&&e.settings.forced_root_block){if(!(r=e.dom.getParent(n.startContainer,"PRE")))return;(1===t?s(vu.fromRangeStart(n)):c(vu.fromRangeStart(n)))||(a=(i=e).dom.create(eg(i)),(!me.ie||11<=me.ie)&&(a.innerHTML='<br data-mce-bogus="1">'),o=a,1===t?e.$(r).after(o):e.$(r).before(o),e.selection.select(o,!0),e.selection.collapse())}},lC=function(l,f){return function(){var e,t,n,r,o,i,a,u,s,c=(t=f,r=Hs((e=l).getBody()),o=d(Ss,r.next),i=d(Ss,r.prev),a=t?pu.Forwards:pu.Backwards,u=t?o:i,s=e.selection.getRng(),(n=uC(a,e,u,s))?n:(n=cC(e,a,s))||null);return!!c&&(l.selection.setRng(c),!0)}},fC=function(u,s){return function(){var e,t,n,r,o,i,a=(r=(t=s)?1:-1,o=t?nv:tv,i=(e=u).selection.getRng(),(n=sC(r,e,o,i))?n:(n=cC(e,r,i))||null);return!!a&&(u.selection.setRng(a),!0)}},dC=function(r,o){return function(){var t,e=o?vu.fromRangeEnd(r.selection.getRng()):vu.fromRangeStart(r.selection.getRng()),n=o?nC(r.getBody(),e):tC(r.getBody(),e);return(o?ne(n.positions):te(n.positions)).filter((t=o,function(e){return t?gf(e):mf(e)})).fold(q(!1),function(e){return r.selection.setRng(e.toRange()),!0})}},mC=function(e,t,n,r,o){var i,a,u,s,c=ji(or.fromDom(n),"td,th,caption").map(function(e){return e.dom()}),l=z((i=e,J(c,function(e){var t,n,r=(t=Ma(e.getBoundingClientRect()),n=-1,{left:t.left-n,top:t.top-n,right:t.right+2*n,bottom:t.bottom+2*n,width:t.width+n,height:t.height+n});return[{x:r.left,y:i(r),cell:e},{x:r.right,y:i(r),cell:e}]})),function(e){return t(e,o)});return(a=l,u=r,s=o,U(a,function(e,r){return e.fold(function(){return A.some(r)},function(e){var t=Math.sqrt(Math.abs(e.x-u)+Math.abs(e.y-s)),n=Math.sqrt(Math.abs(r.x-u)+Math.abs(r.y-s));return A.some(n<t?r:e)})},A.none())).map(function(e){return e.cell})},gC=d(mC,function(e){return e.bottom},function(e,t){return e.y<t}),pC=d(mC,function(e){return e.top},function(e,t){return e.y>t}),hC=function(t,n){return te(n.getClientRects()).bind(function(e){return gC(t,e.left,e.top)}).bind(function(e){return eC((t=e,Zs.lastPositionIn(t).map(function(e){return tC(t,e).positions.concat(e)}).getOr([])),n);var t})},vC=function(t,n){return ne(n.getClientRects()).bind(function(e){return pC(t,e.left,e.top)}).bind(function(e){return eC((t=e,Zs.firstPositionIn(t).map(function(e){return[e].concat(nC(t,e).positions)}).getOr([])),n);var t})},bC=function(e,t){e.selection.setRng(t),yy(e,t)},yC=function(e,t,n){var r,o,i,a,u=e(t,n);return(a=u).breakType===fy.Wrap&&0===a.positions.length||!Bo.isBr(n.getNode())&&(i=u).breakType===fy.Br&&1===i.positions.length?(r=e,o=t,!u.breakAt.map(function(e){return r(o,e).breakAt.isSome()}).getOr(!1)):u.breakAt.isNone()},CC=d(yC,tC),xC=d(yC,nC),wC=function(e,t,n,r){var o,i,a,u,s=e.selection.getRng(),c=t?1:-1;if(ns()&&(o=t,i=s,a=n,u=vu.fromRangeStart(i),Zs.positionIn(!o,a).map(function(e){return e.isEqual(u)}).getOr(!1))){var l=Mm(c,e,n,!t,!0);return bC(e,l),!0}return!1},NC=function(e,t){var n=t.getNode(e);return Bo.isElement(n)&&"TABLE"===n.nodeName?A.some(n):A.none()},EC=function(u,s,c){var e=NC(!!s,c),t=!1===s;e.fold(function(){return bC(u,c.toRange())},function(a){return Zs.positionIn(t,u.getBody()).filter(function(e){return e.isEqual(c)}).fold(function(){return bC(u,c.toRange())},function(e){return n=s,o=a,t=c,void((i=eg(r=u))?r.undoManager.transact(function(){var e=or.fromTag(i);yr(e,tg(r)),Ai(e,or.fromTag("br")),n?ki(or.fromDom(o),e):Si(or.fromDom(o),e);var t=r.dom.createRng();t.setStart(e.dom(),0),t.setEnd(e.dom(),0),bC(r,t)}):bC(r,t.toRange()));var n,r,o,t,i})})},SC=function(e,t,n,r){var o,i,a,u,s,c,l=e.selection.getRng(),f=vu.fromRangeStart(l),d=e.getBody();if(!t&&CC(r,f)){var m=(u=d,hC(s=n,c=f).orThunk(function(){return te(c.getClientRects()).bind(function(e){return Zy(rC(u,vu.before(s)),e.left)})}).getOr(vu.before(s)));return EC(e,t,m),!0}return!(!t||!xC(r,f))&&(o=d,m=vC(i=n,a=f).orThunk(function(){return te(a.getClientRects()).bind(function(e){return Zy(oC(o,vu.after(i)),e.left)})}).getOr(vu.after(i)),EC(e,t,m),!0)},kC=function(t,n){return function(){return A.from(t.dom.getParent(t.selection.getNode(),"td,th")).bind(function(e){return A.from(t.dom.getParent(e,"table")).map(function(e){return wC(t,n,e)})}).getOr(!1)}},TC=function(n,r){return function(){return A.from(n.dom.getParent(n.selection.getNode(),"td,th")).bind(function(t){return A.from(n.dom.getParent(t,"table")).map(function(e){return SC(n,r,e,t)})}).getOr(!1)}},AC=function(e){return M(["figcaption"],sr(e))},RC=function(e){var t=H.document.createRange();return t.setStartBefore(e.dom()),t.setEndBefore(e.dom()),t},_C=function(e,t,n){n?Ai(e,t):Ti(e,t)},DC=function(e,t,n,r){return""===t?(l=e,f=r,d=or.fromTag("br"),_C(l,d,f),RC(d)):(o=e,i=r,a=t,u=n,s=or.fromTag(a),c=or.fromTag("br"),yr(s,u),Ai(s,c),_C(o,s,i),RC(c));var o,i,a,u,s,c,l,f,d},BC=function(e,t,n){return t?(o=e.dom(),nC(o,n).breakAt.isNone()):(r=e.dom(),tC(r,n).breakAt.isNone());var r,o},OC=function(t,n){var e,r,o,i=or.fromDom(t.getBody()),a=vu.fromRangeStart(t.selection.getRng()),u=eg(t),s=tg(t);return(e=a,r=i,o=d(Pr,r),Ki(or.fromDom(e.container()),lo,o).filter(AC)).exists(function(){if(BC(i,n,a)){var e=DC(i,u,s,n);return t.selection.setRng(e),!0}return!1})},PC=function(e,t){return function(){return!!e.selection.isCollapsed()&&OC(e,t)}},LC=function(e,r){return J(W(e,function(e){return Fb({shiftKey:!1,altKey:!1,ctrlKey:!1,metaKey:!1,keyCode:0,action:o},e)}),function(e){return t=e,(n=r).keyCode===t.keyCode&&n.shiftKey===t.shiftKey&&n.altKey===t.altKey&&n.ctrlKey===t.ctrlKey&&n.metaKey===t.metaKey?[e]:[];var t,n})},IC=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r=Array.prototype.slice.call(arguments,1);return function(){return e.apply(null,r)}},MC=function(e,t){return V(LC(e,t),function(e){return e.action()})},FC=function(i,a){i.on("keydown",function(e){var t,n,r,o;!1===e.isDefaultPrevented()&&(t=i,n=a,r=e,o=nr.detect().os,MC([{keyCode:pv.RIGHT,action:lC(t,!0)},{keyCode:pv.LEFT,action:lC(t,!1)},{keyCode:pv.UP,action:fC(t,!1)},{keyCode:pv.DOWN,action:fC(t,!0)},{keyCode:pv.RIGHT,action:kC(t,!0)},{keyCode:pv.LEFT,action:kC(t,!1)},{keyCode:pv.UP,action:TC(t,!1)},{keyCode:pv.DOWN,action:TC(t,!0)},{keyCode:pv.RIGHT,action:kd.move(t,n,!0)},{keyCode:pv.LEFT,action:kd.move(t,n,!1)},{keyCode:pv.RIGHT,ctrlKey:!o.isOSX(),altKey:o.isOSX(),action:kd.moveNextWord(t,n)},{keyCode:pv.LEFT,ctrlKey:!o.isOSX(),altKey:o.isOSX(),action:kd.movePrevWord(t,n)},{keyCode:pv.UP,action:PC(t,!1)},{keyCode:pv.DOWN,action:PC(t,!0)}],r).each(function(e){r.preventDefault()}))})},zC=function(o,i){o.on("keydown",function(e){var t,n,r;!1===e.isDefaultPrevented()&&(t=o,n=i,r=e,MC([{keyCode:pv.BACKSPACE,action:IC(Ff,t,!1)},{keyCode:pv.DELETE,action:IC(Ff,t,!0)},{keyCode:pv.BACKSPACE,action:IC(Hm,t,!1)},{keyCode:pv.DELETE,action:IC(Hm,t,!0)},{keyCode:pv.BACKSPACE,action:IC(_d,t,n,!1)},{keyCode:pv.DELETE,action:IC(_d,t,n,!0)},{keyCode:pv.BACKSPACE,action:IC(cm,t,!1)},{keyCode:pv.DELETE,action:IC(cm,t,!0)},{keyCode:pv.BACKSPACE,action:IC(Gl,t,!1)},{keyCode:pv.DELETE,action:IC(Gl,t,!0)},{keyCode:pv.BACKSPACE,action:IC(Wl,t,!1)},{keyCode:pv.DELETE,action:IC(Wl,t,!0)},{keyCode:pv.BACKSPACE,action:IC(Pm,t,!1)},{keyCode:pv.DELETE,action:IC(Pm,t,!0)}],r).each(function(e){r.preventDefault()}))}),o.on("keyup",function(e){var t,n;!1===e.isDefaultPrevented()&&(t=o,n=e,MC([{keyCode:pv.BACKSPACE,action:IC(zf,t)},{keyCode:pv.DELETE,action:IC(zf,t)}],n))})},UC=function(e){return A.from(e.dom.getParent(e.selection.getStart(!0),e.dom.isBlock))},VC=function(e,t){var n,r,o,i=t,a=e.dom,u=e.schema.getMoveCaretBeforeOnEnterElements();if(t){if(/^(LI|DT|DD)$/.test(t.nodeName)){var s=function(e){for(;e;){if(1===e.nodeType||3===e.nodeType&&e.data&&/[\r\n\s]/.test(e.data))return e;e=e.nextSibling}}(t.firstChild);s&&/^(UL|OL|DL)$/.test(s.nodeName)&&t.insertBefore(a.doc.createTextNode("\xa0"),t.firstChild)}if(o=a.createRng(),t.normalize(),t.hasChildNodes()){for(n=new oo(t,t);r=n.current();){if(Bo.isText(r)){o.setStart(r,0),o.setEnd(r,0);break}if(u[r.nodeName.toLowerCase()]){o.setStartBefore(r),o.setEndBefore(r);break}i=r,r=n.next()}r||(o.setStart(i,0),o.setEnd(i,0))}else Bo.isBr(t)?t.nextSibling&&a.isBlock(t.nextSibling)?(o.setStartBefore(t),o.setEndBefore(t)):(o.setStartAfter(t),o.setEndAfter(t)):(o.setStart(t,0),o.setEnd(t,0));e.selection.setRng(o),a.remove(void 0),e.selection.scrollIntoView(t)}},HC=function(e,t){var n,r,o=e.getRoot();for(n=t;n!==o&&"false"!==e.getContentEditable(n);)"true"===e.getContentEditable(n)&&(r=n),n=n.parentNode;return n!==o?r:o},jC=UC,qC=function(e){return UC(e).fold(q(""),function(e){return e.nodeName.toUpperCase()})},$C=function(e){return UC(e).filter(function(e){return ho(or.fromDom(e))}).isSome()},WC=function(e,t){return e&&e.parentNode&&e.parentNode.nodeName===t},KC=function(e){return e&&/^(OL|UL|LI)$/.test(e.nodeName)},XC=function(e){var t=e.parentNode;return/^(LI|DT|DD)$/.test(t.nodeName)?t:e},YC=function(e,t,n){for(var r=e[n?"firstChild":"lastChild"];r&&!Bo.isElement(r);)r=r[n?"nextSibling":"previousSibling"];return r===t},GC=function(e,t,n,r,o){var i=e.dom,a=e.selection.getRng();if(n!==e.getBody()){var u;KC(u=n)&&KC(u.parentNode)&&(o="LI");var s,c,l=o?t(o):i.create("BR");if(YC(n,r,!0)&&YC(n,r,!1))WC(n,"LI")?i.insertAfter(l,XC(n)):i.replace(l,n);else if(YC(n,r,!0))WC(n,"LI")?(i.insertAfter(l,XC(n)),l.appendChild(i.doc.createTextNode(" ")),l.appendChild(n)):n.parentNode.insertBefore(l,n);else if(YC(n,r,!1))i.insertAfter(l,XC(n));else{n=XC(n);var f=a.cloneRange();f.setStartAfter(r),f.setEndAfter(n);var d=f.extractContents();"LI"===o&&(c="LI",(s=d).firstChild&&s.firstChild.nodeName===c)?(l=d.firstChild,i.insertAfter(d,n)):(i.insertAfter(d,n),i.insertAfter(l,n))}i.remove(r),VC(e,l)}},JC=function(e){e.innerHTML='<br data-mce-bogus="1">'},QC=function(e,t){return e.nodeName===t||e.previousSibling&&e.previousSibling.nodeName===t},ZC=function(e,t){return t&&e.isBlock(t)&&!/^(TD|TH|CAPTION|FORM)$/.test(t.nodeName)&&!/^(fixed|absolute)/i.test(t.style.position)&&"true"!==e.getContentEditable(t)},ex=function(e,t,n){return!1===Bo.isText(t)?n:e?1===n&&t.data.charAt(n-1)===fa?0:n:n===t.data.length-1&&t.data.charAt(n)===fa?t.data.length:n},tx=function(e,t){var n,r,o=e.getRoot();for(n=t;n!==o&&"false"!==e.getContentEditable(n);)"true"===e.getContentEditable(n)&&(r=n),n=n.parentNode;return n!==o?r:o},nx=function(e,t){var n=eg(e);n&&n.toLowerCase()===t.tagName.toLowerCase()&&e.dom.setAttribs(t,tg(e))},rx=function(a,e){var t,u,s,i,c,n,r,o,l,f,d,m,g,p,h,v,b,y,C,x=a.dom,w=a.schema,N=w.getNonEmptyElements(),E=a.selection.getRng(),S=function(e){var t,n,r,o=s,i=w.getTextInlineElements();if(e||"TABLE"===f||"HR"===f?(t=x.create(e||m),nx(a,t)):t=c.cloneNode(!1),r=t,!1===og(a))x.setAttrib(t,"style",null),x.setAttrib(t,"class",null);else do{if(i[o.nodeName]){if(Uu(o)||cc(o))continue;n=o.cloneNode(!1),x.setAttrib(n,"id",""),t.hasChildNodes()?n.appendChild(t.firstChild):r=n,t.appendChild(n)}}while((o=o.parentNode)&&o!==u);return JC(r),t},k=function(e){var t,n,r,o;if(o=ex(e,s,i),Bo.isText(s)&&(e?0<o:o<s.nodeValue.length))return!1;if(s.parentNode===c&&g&&!e)return!0;if(e&&Bo.isElement(s)&&s===c.firstChild)return!0;if(QC(s,"TABLE")||QC(s,"HR"))return g&&!e||!g&&e;for(t=new oo(s,c),Bo.isText(s)&&(e&&0===o?t.prev():e||o!==s.nodeValue.length||t.next());n=t.current();){if(Bo.isElement(n)){if(!n.getAttribute("data-mce-bogus")&&(r=n.nodeName.toLowerCase(),N[r]&&"br"!==r))return!1}else if(Bo.isText(n)&&!/^[ \t\r\n]*$/.test(n.nodeValue))return!1;e?t.prev():t.next()}return!0},T=function(){r=/^(H[1-6]|PRE|FIGURE)$/.test(f)&&"HGROUP"!==d?S(m):S(),ig(a)&&ZC(x,l)&&x.isEmpty(c)?r=x.split(l,c):x.insertAfter(r,c),VC(a,r)};_g(x,E).each(function(e){E.setStart(e.startContainer,e.startOffset),E.setEnd(e.endContainer,e.endOffset)}),s=E.startContainer,i=E.startOffset,m=eg(a),n=e.shiftKey,Bo.isElement(s)&&s.hasChildNodes()&&(g=i>s.childNodes.length-1,s=s.childNodes[Math.min(i,s.childNodes.length-1)]||s,i=g&&Bo.isText(s)?s.nodeValue.length:0),(u=tx(x,s))&&((m&&!n||!m&&n)&&(s=function(e,t,n,r,o){var i,a,u,s,c,l,f,d=t||"P",m=e.dom,g=tx(m,r);if(!(a=m.getParent(r,m.isBlock))||!ZC(m,a)){if(l=(a=a||g)===e.getBody()||(f=a)&&/^(TD|TH|CAPTION)$/.test(f.nodeName)?a.nodeName.toLowerCase():a.parentNode.nodeName.toLowerCase(),!a.hasChildNodes())return i=m.create(d),nx(e,i),a.appendChild(i),n.setStart(i,0),n.setEnd(i,0),i;for(s=r;s.parentNode!==a;)s=s.parentNode;for(;s&&!m.isBlock(s);)s=(u=s).previousSibling;if(u&&e.schema.isValidChild(l,d.toLowerCase())){for(i=m.create(d),nx(e,i),u.parentNode.insertBefore(i,u),s=u;s&&!m.isBlock(s);)c=s.nextSibling,i.appendChild(s),s=c;n.setStart(r,o),n.setEnd(r,o)}}return r}(a,m,E,s,i)),c=x.getParent(s,x.isBlock),l=c?x.getParent(c.parentNode,x.isBlock):null,f=c?c.nodeName.toUpperCase():"","LI"!==(d=l?l.nodeName.toUpperCase():"")||e.ctrlKey||(l=(c=l).parentNode,f=d),/^(LI|DT|DD)$/.test(f)&&x.isEmpty(c)?GC(a,S,l,c,m):m&&c===a.getBody()||(m=m||"P",pa(c)?(r=Ea(c),x.isEmpty(c)&&JC(c),VC(a,r)):k()?T():k(!0)?(r=c.parentNode.insertBefore(S(),c),VC(a,QC(c,"HR")?r:c)):((t=(y=E,C=y.cloneRange(),C.setStart(y.startContainer,ex(!0,y.startContainer,y.startOffset)),C.setEnd(y.endContainer,ex(!1,y.endContainer,y.endOffset)),C).cloneRange()).setEndAfter(c),o=t.extractContents(),b=o,F(Hi(or.fromDom(b),fr),function(e){var t=e.dom();t.nodeValue=da(t.nodeValue)}),function(e){for(;Bo.isText(e)&&(e.nodeValue=e.nodeValue.replace(/^[\r\n]+/,"")),e=e.firstChild;);}(o),r=o.firstChild,x.insertAfter(o,c),function(e,t,n){var r,o=n,i=[];if(o){for(;o=o.firstChild;){if(e.isBlock(o))return;Bo.isElement(o)&&!t[o.nodeName.toLowerCase()]&&i.push(o)}for(r=i.length;r--;)!(o=i[r]).hasChildNodes()||o.firstChild===o.lastChild&&""===o.firstChild.nodeValue?e.remove(o):(a=e,(u=o)&&"A"===u.nodeName&&a.isEmpty(u)&&e.remove(o));var a,u}}(x,N,r),p=x,(h=c).normalize(),(v=h.lastChild)&&!/^(left|right)$/gi.test(p.getStyle(v,"float",!0))||p.add(h,"br"),x.isEmpty(c)&&JC(c),r.normalize(),x.isEmpty(r)?(x.remove(r),T()):VC(a,r)),x.setAttrib(r,"id",""),a.fire("NewBlock",{newBlock:r})))},ox=function(e,t){return jC(e).filter(function(e){return 0<t.length&&Br(or.fromDom(e),t)}).isSome()},ix=function(e){return ox(e,ng(e))},ax=function(e){return ox(e,rg(e))},ux=Jl([{br:[]},{block:[]},{none:[]}]),sx=function(e,t){return ax(e)},cx=function(n){return function(e,t){return""===eg(e)===n}},lx=function(n){return function(e,t){return $C(e)===n}},fx=function(n,r){return function(e,t){return qC(e)===n.toUpperCase()===r}},dx=function(e){return fx("pre",e)},mx=function(n){return function(e,t){return Zm(e)===n}},gx=function(e,t){return ix(e)},px=function(e,t){return t},hx=function(e){var t=eg(e),n=HC(e.dom,e.selection.getStart());return n&&e.schema.isValidChild(n.nodeName,t||"P")},vx=function(e,t){return function(n,r){return U(e,function(e,t){return e&&t(n,r)},!0)?A.some(t):A.none()}},bx=function(e,t){return Gf([vx([sx],ux.none()),vx([fx("summary",!0)],ux.br()),vx([dx(!0),mx(!1),px],ux.br()),vx([dx(!0),mx(!1)],ux.block()),vx([dx(!0),mx(!0),px],ux.block()),vx([dx(!0),mx(!0)],ux.br()),vx([lx(!0),px],ux.br()),vx([lx(!0)],ux.block()),vx([cx(!0),px,hx],ux.block()),vx([cx(!0)],ux.br()),vx([gx],ux.br()),vx([cx(!1),px],ux.br()),vx([hx],ux.block())],[e,t.shiftKey]).getOr(ux.none())},yx=function(e,t){bx(e,t).fold(function(){Ug(e,t)},function(){rx(e,t)},o)},Cx=function(o){o.on("keydown",function(e){var t,n,r;e.keyCode===pv.ENTER&&(t=o,(n=e).isDefaultPrevented()||(n.preventDefault(),(r=t.undoManager).typing&&(r.typing=!1,r.add()),t.undoManager.transact(function(){!1===t.selection.isCollapsed()&&t.execCommand("Delete"),yx(t,n)})))})},xx=function(n,r){var e=r.container(),t=r.offset();return Bo.isText(e)?(e.insertData(t,n),A.some(gu(e,t+n.length))):Es(r).map(function(e){var t=or.fromText(n);return r.isAtEnd()?ki(e,t):Si(e,t),gu(t.dom(),n.length)})},wx=d(xx,"\xa0"),Nx=d(xx," "),Ex=function(e,t,n){return Zs.navigateIgnore(e,t,n,ff)},Sx=function(t,n,r){var e=z(Ml(or.fromDom(r.container()),n),lo);return te(e).fold(function(){return Ex(t,n.dom(),r).forall(function(e){return!1===ps(e,r,n.dom())})},function(e){return Ex(t,e.dom(),r).isNone()})},kx=d(Sx,!1),Tx=d(Sx,!0),Ax=function(e){return gu.isTextPosition(e)&&!e.isAtStart()&&!e.isAtEnd()},Rx=function(e,t){var n=z(Ml(or.fromDom(t.container()),e),lo);return te(n).getOr(e)},_x=function(e,t){return Ax(t)?lf(t):lf(t)||Zs.prevPosition(Rx(e,t).dom(),t).exists(lf)},Dx=function(e,t){return Ax(t)?cf(t):cf(t)||Zs.nextPosition(Rx(e,t).dom(),t).exists(cf)},Bx=function(e){return Es(e).bind(function(e){return Ki(e,lr)}).exists(function(e){return t=Nr(e,"white-space"),M(["pre","pre-line","pre-wrap"],t);var t})},Ox=function(e,t){return o=e,i=t,Zs.prevPosition(o.dom(),i).isNone()||(n=e,r=t,Zs.nextPosition(n.dom(),r).isNone())||kx(e,t)||Tx(e,t)||tf(e,t)||ef(e,t);var n,r,o,i},Px=function(e,t){var n,r,o,i=(r=(n=t).container(),o=n.offset(),Bo.isText(r)&&o<r.data.length?gu(r,o+1):n);return!Bx(i)&&(Tx(e,i)||ef(e,i)||Dx(e,i))},Lx=function(e,t){return n=e,!Bx(r=t)&&(kx(n,r)||tf(n,r)||_x(n,r))||Px(e,t);var n,r},Ix=function(e,t){return of(e.charAt(t))},Mx=function(e){var t=e.container();return Bo.isText(t)&&Kn(t.data,"\xa0")},Fx=function(e){var t=e.data,n=W(t.split(""),function(e,t,n){return of(e)&&0<t&&t<n.length-1&&uf(n[t-1])&&uf(n[t+1])?" ":e}).join("");return n!==t&&(e.data=n,!0)},zx=function(l,e){return A.some(e).filter(Mx).bind(function(e){var t,n,r,o,i,a,u,s,c=e.container();return i=l,u=(a=c).data,s=gu(a,0),Ix(u,0)&&!Lx(i,s)&&(a.data=" "+u.slice(1),1)||Fx(c)||(t=l,r=(n=c).data,o=gu(n,r.length-1),Ix(r,r.length-1)&&!Lx(t,o)&&(n.data=r.slice(0,-1)+" ",1))?A.some(e):A.none()})},Ux=function(t){var e=or.fromDom(t.getBody());t.selection.isCollapsed()&&zx(e,gu.fromRangeStart(t.selection.getRng())).each(function(e){t.selection.setRng(e.toRange())})},Vx=function(r,o){return function(e){return t=r,!Bx(n=e)&&(Ox(t,n)||_x(t,n)||Dx(t,n))?wx(o):Nx(o);var t,n}},Hx=function(e){var t,n,r=vu.fromRangeStart(e.selection.getRng()),o=or.fromDom(e.getBody());if(e.selection.isCollapsed()){var i=d(yl.isInlineTarget,e),a=vu.fromRangeStart(e.selection.getRng());return gd(i,e.getBody(),a).bind((n=o,function(e){return e.fold(function(e){return Zs.prevPosition(n.dom(),vu.before(e))},function(e){return Zs.firstPositionIn(e)},function(e){return Zs.lastPositionIn(e)},function(e){return Zs.nextPosition(n.dom(),vu.after(e))})})).bind(Vx(o,r)).exists((t=e,function(e){return t.selection.setRng(e.toRange()),t.nodeChanged(),!0}))}return!1},jx=function(r){r.on("keydown",function(e){var t,n;!1===e.isDefaultPrevented()&&(t=r,n=e,MC([{keyCode:pv.SPACEBAR,action:IC(Hx,t)}],n).each(function(e){n.preventDefault()}))})},qx=function(e,t){var n;t.hasAttribute("data-mce-caret")&&(Ea(t),(n=e).selection.setRng(n.selection.getRng()),e.selection.scrollIntoView(t))},$x=function(e,t){var n,r=(n=e,Yi(or.fromDom(n.getBody()),"*[data-mce-caret]").fold(q(null),function(e){return e.dom()}));if(r)return"compositionstart"===t.type?(t.preventDefault(),t.stopPropagation(),void qx(e,r)):void(ba(r)&&(qx(e,r),e.undoManager.add()))},Wx=function(e){e.on("keyup compositionstart",d($x,e))},Kx=nr.detect().browser,Xx=function(t){var e,n;e=t,n=Oi(function(){e.composing||Ux(e)},0),Kx.isIE()&&(e.on("keypress",function(e){n.throttle()}),e.on("remove",function(e){n.cancel()})),t.on("input",function(e){!1===e.isComposing&&Ux(t)})},Yx=function(r){r.on("keydown",function(e){var t,n;!1===e.isDefaultPrevented()&&(t=r,n=e,MC([{keyCode:pv.END,action:dC(t,!0)},{keyCode:pv.HOME,action:dC(t,!1)}],n).each(function(e){n.preventDefault()}))})},Gx=function(e){var t=kd.setupSelectedState(e);Wx(e),FC(e,t),zC(e,t),Cx(e),jx(e),Xx(e),Yx(e)};function Jx(u){var s,n,r,o=Gt.each,c=pv.BACKSPACE,l=pv.DELETE,f=u.dom,d=u.selection,e=u.settings,t=u.parser,i=me.gecko,a=me.ie,m=me.webkit,g="data:text/mce-internal,",p=a?"Text":"URL",h=function(e,t){try{u.getDoc().execCommand(e,!1,t)}catch(n){}},v=function(e){return e.isDefaultPrevented()},b=function(){u.shortcuts.add("meta+a",null,"SelectAll")},y=function(){u.on("keydown",function(e){if(!v(e)&&e.keyCode===c&&d.isCollapsed()&&0===d.getRng().startOffset){var t=d.getNode().previousSibling;if(t&&t.nodeName&&"table"===t.nodeName.toLowerCase())return e.preventDefault(),!1}})},C=function(){u.inline||(u.contentStyles.push("body {min-height: 150px}"),u.on("click",function(e){var t;if("HTML"===e.target.nodeName){if(11<me.ie)return void u.getBody().focus();t=u.selection.getRng(),u.getBody().focus(),u.selection.setRng(t),u.selection.normalize(),u.nodeChanged()}}))};return u.on("keydown",function(e){var t,n,r,o,i;if(!v(e)&&e.keyCode===pv.BACKSPACE&&(n=(t=d.getRng()).startContainer,r=t.startOffset,o=f.getRoot(),i=n,t.collapsed&&0===r)){for(;i&&i.parentNode&&i.parentNode.firstChild===i&&i.parentNode!==o;)i=i.parentNode;"BLOCKQUOTE"===i.tagName&&(u.formatter.toggle("blockquote",null,i),(t=f.createRng()).setStart(n,0),t.setEnd(n,0),d.setRng(t))}}),s=function(e){var t=f.create("body"),n=e.cloneContents();return t.appendChild(n),d.serializer.serialize(t,{format:"html"})},u.on("keydown",function(e){var t,n,r,o,i,a=e.keyCode;if(!v(e)&&(a===l||a===c)){if(t=u.selection.isCollapsed(),n=u.getBody(),t&&!f.isEmpty(n))return;if(!t&&(r=u.selection.getRng(),o=s(r),(i=f.createRng()).selectNode(u.getBody()),o!==s(i)))return;e.preventDefault(),u.setContent(""),n.firstChild&&f.isBlock(n.firstChild)?u.selection.setCursorLocation(n.firstChild,0):u.selection.setCursorLocation(n,0),u.nodeChanged()}}),me.windowsPhone||u.on("keyup focusin mouseup",function(e){pv.modifierPressed(e)||d.normalize()},!0),m&&(u.settings.content_editable||f.bind(u.getDoc(),"mousedown mouseup",function(e){var t;if(e.target===u.getDoc().documentElement)if(t=d.getRng(),u.getBody().focus(),"mousedown"===e.type){if(va(t.startContainer))return;d.placeCaretAt(e.clientX,e.clientY)}else d.setRng(t)}),u.on("click",function(e){var t=e.target;/^(IMG|HR)$/.test(t.nodeName)&&"false"!==f.getContentEditableParent(t)&&(e.preventDefault(),u.selection.select(t),u.nodeChanged()),"A"===t.nodeName&&f.hasClass(t,"mce-item-anchor")&&(e.preventDefault(),d.select(t))}),e.forced_root_block&&u.on("init",function(){h("DefaultParagraphSeparator",e.forced_root_block)}),u.on("init",function(){u.dom.bind(u.getBody(),"submit",function(e){e.preventDefault()})}),y(),t.addNodeFilter("br",function(e){for(var t=e.length;t--;)"Apple-interchange-newline"===e[t].attr("class")&&e[t].remove()}),me.iOS?(u.inline||u.on("keydown",function(){H.document.activeElement===H.document.body&&u.getWin().focus()}),C(),u.on("click",function(e){var t=e.target;do{if("A"===t.tagName)return void e.preventDefault()}while(t=t.parentNode)}),u.contentStyles.push(".mce-content-body {-webkit-touch-callout: none}")):b()),11<=me.ie&&(C(),y()),me.ie&&(b(),h("AutoUrlDetect",!1),u.on("dragstart",function(e){var t,n,r;(t=e).dataTransfer&&(u.selection.isCollapsed()&&"IMG"===t.target.tagName&&d.select(t.target),0<(n=u.selection.getContent()).length&&(r=g+escape(u.id)+","+escape(n),t.dataTransfer.setData(p,r)))}),u.on("drop",function(e){if(!v(e)){var t=(i=e).dataTransfer&&(a=i.dataTransfer.getData(p))&&0<=a.indexOf(g)?(a=a.substr(g.length).split(","),{id:unescape(a[0]),html:unescape(a[1])}):null;if(t&&t.id!==u.id){e.preventDefault();var n=xy(e.x,e.y,u.getDoc());d.setRng(n),r=t.html,o=!0,u.queryCommandSupported("mceInsertClipboardContent")?u.execCommand("mceInsertClipboardContent",!1,{content:r,internal:o}):u.execCommand("mceInsertContent",!1,r)}}var r,o,i,a})),i&&(u.on("keydown",function(e){if(!v(e)&&e.keyCode===c){if(!u.getBody().getElementsByTagName("hr").length)return;if(d.isCollapsed()&&0===d.getRng().startOffset){var t=d.getNode(),n=t.previousSibling;if("HR"===t.nodeName)return f.remove(t),void e.preventDefault();n&&n.nodeName&&"hr"===n.nodeName.toLowerCase()&&(f.remove(n),e.preventDefault())}}}),H.Range.prototype.getClientRects||u.on("mousedown",function(e){if(!v(e)&&"HTML"===e.target.nodeName){var t=u.getBody();t.blur(),be.setEditorTimeout(u,function(){t.focus()})}}),n=function(){var e=f.getAttribs(d.getStart().cloneNode(!1));return function(){var t=d.getStart();t!==u.getBody()&&(f.setAttrib(t,"style",null),o(e,function(e){t.setAttributeNode(e.cloneNode(!0))}))}},r=function(){return!d.isCollapsed()&&f.getParent(d.getStart(),f.isBlock)!==f.getParent(d.getEnd(),f.isBlock)},u.on("keypress",function(e){var t;if(!v(e)&&(8===e.keyCode||46===e.keyCode)&&r())return t=n(),u.getDoc().execCommand("delete",!1,null),t(),e.preventDefault(),!1}),f.bind(u.getDoc(),"cut",function(e){var t;!v(e)&&r()&&(t=n(),be.setEditorTimeout(u,function(){t()}))}),e.readonly||u.on("BeforeExecCommand MouseDown",function(){h("StyleWithCSS",!1),h("enableInlineTableEditing",!1),e.object_resizing||h("enableObjectResizing",!1)}),u.on("SetContent ExecCommand",function(e){"setcontent"!==e.type&&"mceInsertLink"!==e.command||o(f.select("a"),function(e){var t=e.parentNode,n=f.getRoot();if(t.lastChild===e){for(;t&&!f.isBlock(t);){if(t.parentNode.lastChild!==t||t===n)return;t=t.parentNode}f.add(t,"br",{"data-mce-bogus":1})}})}),u.contentStyles.push("img:-moz-broken {-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}"),me.mac&&u.on("keydown",function(e){!pv.metaKeyPressed(e)||e.shiftKey||37!==e.keyCode&&39!==e.keyCode||(e.preventDefault(),u.selection.getSel().modify("move",37===e.keyCode?"backward":"forward","lineboundary"))}),y()),{refreshContentEditable:function(){},isHidden:function(){var e;return!i||u.removed?0:!(e=u.selection.getSel())||!e.rangeCount||0===e.rangeCount}}}var Qx=function(e){return Bo.isElement(e)&&go(or.fromDom(e))},Zx=function(t){t.on("click",function(e){3<=e.detail&&function(e){var t=e.selection.getRng(),n=gu.fromRangeStart(t),r=gu.fromRangeEnd(t);if(gu.isElementPosition(n)){var o=n.container();Qx(o)&&Zs.firstPositionIn(o).each(function(e){return t.setStart(e.container(),e.offset())})}gu.isElementPosition(r)&&(o=n.container(),Qx(o)&&Zs.lastPositionIn(o).each(function(e){return t.setEnd(e.container(),e.offset())})),e.selection.setRng(el(t))}(t)})},ew=function(e){var t,n;(t=e).on("click",function(e){t.dom.getParent(e.target,"details")&&e.preventDefault()}),(n=e).parser.addNodeFilter("details",function(e){F(e,function(e){e.attr("data-mce-open",e.attr("open")),e.attr("open","open")})}),n.serializer.addNodeFilter("details",function(e){F(e,function(e){var t=e.attr("data-mce-open");e.attr("open",R(t)?t:null),e.attr("data-mce-open",null)})})},tw=hi.DOM,nw=function(e){var t;e.bindPendingEventDelegates(),e.initialized=!0,e.fire("init"),e.focus(!0),e.nodeChanged({initial:!0}),e.execCallback("init_instance_callback",e),(t=e).settings.auto_focus&&be.setEditorTimeout(t,function(){var e;(e=!0===t.settings.auto_focus?t:t.editorManager.get(t.settings.auto_focus)).destroyed||e.focus()},100)},rw=function(t,e){var n,r,u,o,i,a,s,c,l,f=t.settings,d=t.getElement(),m=t.getDoc();f.inline||(t.getElement().style.visibility=t.orgVisibility),e||f.content_editable||(m.open(),m.write(t.iframeHTML),m.close()),f.content_editable&&(t.on("remove",function(){var e=this.getBody();tw.removeClass(e,"mce-content-body"),tw.removeClass(e,"mce-edit-focus"),tw.setAttrib(e,"contentEditable",null)}),tw.addClass(d,"mce-content-body"),t.contentDocument=m=f.content_document||H.document,t.contentWindow=f.content_window||H.window,t.bodyElement=d,f.content_document=f.content_window=null,f.root_name=d.nodeName.toLowerCase()),(n=t.getBody()).disabled=!0,t.readonly=f.readonly,t.readonly||(t.inline&&"static"===tw.getStyle(n,"position",!0)&&(n.style.position="relative"),n.contentEditable=t.getParam("content_editable_state",!0)),n.disabled=!1,t.editorUpload=Rh(t),t.schema=ri(f),t.dom=hi(m,{keep_values:!0,url_converter:t.convertURL,url_converter_scope:t,hex_colors:f.force_hex_style_colors,class_filter:f.class_filter,update_styles:!0,root_element:t.inline?t.getBody():null,collect:f.content_editable,schema:t.schema,contentCssCors:hg(t),onSetAttrib:function(e){t.fire("SetAttrib",e)}}),t.parser=((o=oy((u=t).settings,u.schema)).addAttributeFilter("src,href,style,tabindex",function(e,t){for(var n,r,o,i=e.length,a=u.dom;i--;)if(r=(n=e[i]).attr(t),o="data-mce-"+t,!n.attributes.map[o]){if(0===r.indexOf("data:")||0===r.indexOf("blob:"))continue;"style"===t?((r=a.serializeStyle(a.parseStyle(r),n.name)).length||(r=null),n.attr(o,r),n.attr(t,r)):"tabindex"===t?(n.attr(o,r),n.attr(t,null)):n.attr(o,u.convertURL(r,t,n.name))}}),o.addNodeFilter("script",function(e){for(var t,n,r=e.length;r--;)0!==(n=(t=e[r]).attr("type")||"no/type").indexOf("mce-")&&t.attr("type","mce-"+n)}),o.addNodeFilter("#cdata",function(e){for(var t,n=e.length;n--;)(t=e[n]).type=8,t.name="#comment",t.value="[CDATA["+t.value+"]]"}),o.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(e){for(var t,n=e.length,r=u.schema.getNonEmptyElements();n--;)(t=e[n]).isEmpty(r)&&0===t.getAll("br").length&&(t.append(new Kb("br",1)).shortEnded=!0)}),o),t.serializer=cy(f,t),t.selection=Yy(t.dom,t.getWin(),t.serializer,t),t.annotator=Pc(t),t.formatter=Lb(t),t.undoManager=Hv(t),t._nodeChangeDispatcher=new Uh(t),t._selectionOverrides=yv(t),ew(t),Zx(t),Gx(t),Ph(t),t.fire("PreInit"),f.browser_spellcheck||f.gecko_spellcheck||(m.body.spellcheck=!1,tw.setAttrib(n,"spellcheck","false")),t.quirks=Jx(t),t.fire("PostRender"),f.directionality&&(n.dir=f.directionality),f.nowrap&&(n.style.whiteSpace="nowrap"),f.protect&&t.on("BeforeSetContent",function(t){Gt.each(f.protect,function(e){t.content=t.content.replace(e,function(e){return"\x3c!--mce:protected "+escape(e)+"--\x3e"})})}),t.on("SetContent",function(){t.addVisual(t.getBody())}),t.load({initial:!0,format:"html"}),t.startContent=t.getContent({format:"raw"}),t.on("compositionstart compositionend",function(e){t.composing="compositionstart"===e.type}),0<t.contentStyles.length&&(r="",Gt.each(t.contentStyles,function(e){r+=e+"\r\n"}),t.dom.addStyle(r)),(i=t,i.inline?tw.styleSheetLoader:i.dom.styleSheetLoader).loadAll(t.contentCSS,function(e){nw(t)},function(e){nw(t)}),f.content_style&&(a=t,s=f.content_style,c=or.fromDom(a.getDoc().head),l=or.fromTag("style"),br(l,"type","text/css"),Ai(l,or.fromText(s)),Ai(c,l))},ow=hi.DOM,iw=function(e,t){var n,r,o,i,a,u,s,c=e.editorManager.translate("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),l=(n=e.id,r=c,o=t.height,i=Km(e),s=or.fromTag("iframe"),yr(s,i),yr(s,{id:n+"_ifr",frameBorder:"0",allowTransparency:"true",title:r}),wr(s,{width:"100%",height:(a=o,u="number"==typeof a?a+"px":a,u||""),display:"block"}),s).dom();l.onload=function(){l.onload=null,e.fire("load")};var f,d,m,g,p=function(e,t){if(H.document.domain!==H.window.location.hostname&&me.ie&&me.ie<12){var n=Ah.uuid("mce");e[n]=function(){rw(e)};var r='javascript:(function(){document.open();document.domain="'+H.document.domain+'";var ed = window.parent.tinymce.get("'+e.id+'");document.write(ed.iframeHTML);document.close();ed.'+n+"(true);})()";return ow.setAttrib(t,"src",r),!0}return!1}(e,l);return e.contentAreaContainer=t.iframeContainer,e.iframeElement=l,e.iframeHTML=(g=Xm(f=e)+"<html><head>",Ym(f)!==f.documentBaseUrl&&(g+='<base href="'+f.documentBaseURI.getURI()+'" />'),g+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />',d=Gm(f),m=Jm(f),Qm(f)&&(g+='<meta http-equiv="Content-Security-Policy" content="'+Qm(f)+'" />'),g+='</head><body id="'+d+'" class="mce-content-body '+m+'" data-id="'+f.id+'"><br></body></html>'),ow.add(t.iframeContainer,l),p},aw=function(e,t){var n=iw(e,t);t.editorContainer&&(ow.get(t.editorContainer).style.display=e.orgDisplay,e.hidden=ow.isHidden(t.editorContainer)),e.getElement().style.display="none",ow.setAttrib(e.id,"aria-hidden","true"),n||rw(e)},uw=hi.DOM,sw=function(t,n,e){var r=mh.get(e),o=mh.urls[e]||t.documentBaseUrl.replace(/\/$/,"");if(e=Gt.trim(e),r&&-1===Gt.inArray(n,e)){if(Gt.each(mh.dependencies(e),function(e){sw(t,n,e)}),t.plugins[e])return;try{var i=new r(t,o,t.$);(t.plugins[e]=i).init&&(i.init(t,o),n.push(e))}catch(jN){dh.pluginInitError(t,e,jN)}}},cw=function(e){return e.replace(/^\-/,"")},lw=function(e){return{editorContainer:e,iframeContainer:e}},fw=function(e){var t,n,r=e.getElement();return e.inline?lw(null):(t=r,n=uw.create("div"),uw.insertAfter(n,t),lw(n))},dw=function(e){var t,n,r,o,i,a,u,s,c,l,f,d=e.settings,m=e.getElement();return e.orgDisplay=m.style.display,R(d.theme)?(l=(o=e).settings,f=o.getElement(),i=l.width||uw.getStyle(f,"width")||"100%",a=l.height||uw.getStyle(f,"height")||f.offsetHeight,u=l.min_height||100,(s=/^[0-9\.]+(|px)$/i).test(""+i)&&(i=Math.max(parseInt(i,10),100)),s.test(""+a)&&(a=Math.max(parseInt(a,10),u)),c=o.theme.renderUI({targetNode:f,width:i,height:a,deltaWidth:l.delta_width,deltaHeight:l.delta_height}),l.content_editable||(a=(c.iframeHeight||a)+("number"==typeof a?c.deltaHeight||0:""))<u&&(a=u),c.height=a,c):P(d.theme)?(r=(t=e).getElement(),(n=t.settings.theme(t,r)).editorContainer.nodeType&&(n.editorContainer.id=n.editorContainer.id||t.id+"_parent"),n.iframeContainer&&n.iframeContainer.nodeType&&(n.iframeContainer.id=n.iframeContainer.id||t.id+"_iframecontainer"),n.height=n.iframeHeight?n.iframeHeight:r.offsetHeight,n):fw(e)},mw=function(t){var e,n,r,o,i,a,u=t.settings,s=t.getElement();return t.rtl=u.rtl_ui||t.editorManager.i18n.rtl,t.editorManager.i18n.setCode(u.language),u.aria_label=u.aria_label||uw.getAttrib(s,"aria-label",t.getLang("aria.rich_text_area")),t.fire("ScriptsLoaded"),o=(n=t).settings.theme,R(o)?(n.settings.theme=cw(o),r=gh.get(o),n.theme=new r(n,gh.urls[o]),n.theme.init&&n.theme.init(n,gh.urls[o]||n.documentBaseUrl.replace(/\/$/,""),n.$)):n.theme={},i=t,a=[],Gt.each(i.settings.plugins.split(/[ ,]/),function(e){sw(i,a,cw(e))}),e=dw(t),t.editorContainer=e.editorContainer?e.editorContainer:null,u.content_css&&Gt.each(Gt.explode(u.content_css),function(e){t.contentCSS.push(t.documentBaseURI.toAbsolute(e))}),u.content_editable?rw(t):aw(t,e)},gw=hi.DOM,pw=function(e){return"-"===e.charAt(0)},hw=function(i,a){var u=xi.ScriptLoader;!function(e,t,n,r){var o=t.settings,i=o.theme;if(R(i)){if(!pw(i)&&!gh.urls.hasOwnProperty(i)){var a=o.theme_url;a?gh.load(i,t.documentBaseURI.toAbsolute(a)):gh.load(i,"themes/"+i+"/theme"+n+".js")}e.loadQueue(function(){gh.waitFor(i,r)})}else r()}(u,i,a,function(){var e,t,n,r,o;e=u,(n=(t=i).settings).language&&"en"!==n.language&&!n.language_url&&(n.language_url=t.editorManager.baseURL+"/langs/"+n.language+".js"),n.language_url&&!t.editorManager.i18n.data[n.language]&&e.add(n.language_url),r=i.settings,o=a,Gt.isArray(r.plugins)&&(r.plugins=r.plugins.join(" ")),Gt.each(r.external_plugins,function(e,t){mh.load(t,e),r.plugins+=" "+t}),Gt.each(r.plugins.split(/[ ,]/),function(e){if((e=Gt.trim(e))&&!mh.urls[e])if(pw(e)){e=e.substr(1,e.length);var t=mh.dependencies(e);Gt.each(t,function(e){var t={prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"};e=mh.createUrl(t,e),mh.load(e.resource,e)})}else mh.load(e,{prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"})}),u.loadQueue(function(){i.removed||mw(i)},i,function(e){dh.pluginLoadError(i,e[0]),i.removed||mw(i)})})},vw=function(t){var e=t.settings,n=t.id,r=function(){gw.unbind(H.window,"ready",r),t.render()};if(Te.Event.domLoaded){if(t.getElement()&&me.contentEditable){e.inline?t.inline=!0:(t.orgVisibility=t.getElement().style.visibility,t.getElement().style.visibility="hidden");var o=t.getElement().form||gw.getParent(n,"form");o&&(t.formElement=o,e.hidden_input&&!/TEXTAREA|INPUT/i.test(t.getElement().nodeName)&&(gw.insertAfter(gw.create("input",{type:"hidden",name:n}),n),t.hasHiddenInput=!0),t.formEventDelegate=function(e){t.fire(e.type,e)},gw.bind(o,"submit reset",t.formEventDelegate),t.on("reset",function(){t.setContent(t.startContent,{format:"raw"})}),!e.submit_patch||o.submit.nodeType||o.submit.length||o._mceOldSubmit||(o._mceOldSubmit=o.submit,o.submit=function(){return t.editorManager.triggerSave(),t.setDirty(!1),o._mceOldSubmit(o)})),t.windowManager=rh(t),t.notificationManager=nh(t),"xml"===e.encoding&&t.on("GetContent",function(e){e.save&&(e.content=gw.encode(e.content))}),e.add_form_submit_trigger&&t.on("submit",function(){t.initialized&&t.save()}),e.add_unload_trigger&&(t._beforeUnload=function(){!t.initialized||t.destroyed||t.isHidden()||t.save({format:"raw",no_events:!0,set_dirty:!1})},t.editorManager.on("BeforeUnload",t._beforeUnload)),t.editorManager.add(t),hw(t,t.suffix)}}else gw.bind(H.window,"ready",r)},bw=function(e,t,n){var r=e.sidebars?e.sidebars:[];r.push({name:t,settings:n}),e.sidebars=r},yw=Gt.each,Cw=Gt.trim,xw="source protocol authority userInfo user password host port relative path directory file query anchor".split(" "),ww={ftp:21,http:80,https:443,mailto:25},Nw=function(r,e){var t,n,o=this;if(r=Cw(r),t=(e=o.settings=e||{}).base_uri,/^([\w\-]+):([^\/]{2})/i.test(r)||/^\s*#/.test(r))o.source=r;else{var i=0===r.indexOf("//");0!==r.indexOf("/")||i||(r=(t&&t.protocol||"http")+"://mce_host"+r),/^[\w\-]*:?\/\//.test(r)||(n=e.base_uri?e.base_uri.path:new Nw(H.document.location.href).directory,""==e.base_uri.protocol?r="//mce_host"+o.toAbsPath(n,r):(r=/([^#?]*)([#?]?.*)/.exec(r),r=(t&&t.protocol||"http")+"://mce_host"+o.toAbsPath(n,r[1])+r[2])),r=r.replace(/@@/g,"(mce_at)"),r=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(r),yw(xw,function(e,t){var n=r[t];n&&(n=n.replace(/\(mce_at\)/g,"@@")),o[e]=n}),t&&(o.protocol||(o.protocol=t.protocol),o.userInfo||(o.userInfo=t.userInfo),o.port||"mce_host"!==o.host||(o.port=t.port),o.host&&"mce_host"!==o.host||(o.host=t.host),o.source=""),i&&(o.protocol="")}};Nw.prototype={setPath:function(e){e=/^(.*?)\/?(\w+)?$/.exec(e),this.path=e[0],this.directory=e[1],this.file=e[2],this.source="",this.getURI()},toRelative:function(e){var t;if("./"===e)return e;if("mce_host"!==(e=new Nw(e,{base_uri:this})).host&&this.host!==e.host&&e.host||this.port!==e.port||this.protocol!==e.protocol&&""!==e.protocol)return e.getURI();var n=this.getURI(),r=e.getURI();return n===r||"/"===n.charAt(n.length-1)&&n.substr(0,n.length-1)===r?n:(t=this.toRelPath(this.path,e.path),e.query&&(t+="?"+e.query),e.anchor&&(t+="#"+e.anchor),t)},toAbsolute:function(e,t){return(e=new Nw(e,{base_uri:this})).getURI(t&&this.isSameOrigin(e))},isSameOrigin:function(e){if(this.host==e.host&&this.protocol==e.protocol){if(this.port==e.port)return!0;var t=ww[this.protocol];if(t&&(this.port||t)==(e.port||t))return!0}return!1},toRelPath:function(e,t){var n,r,o,i=0,a="";if(e=(e=e.substring(0,e.lastIndexOf("/"))).split("/"),n=t.split("/"),e.length>=n.length)for(r=0,o=e.length;r<o;r++)if(r>=n.length||e[r]!==n[r]){i=r+1;break}if(e.length<n.length)for(r=0,o=n.length;r<o;r++)if(r>=e.length||e[r]!==n[r]){i=r+1;break}if(1===i)return t;for(r=0,o=e.length-(i-1);r<o;r++)a+="../";for(r=i-1,o=n.length;r<o;r++)a+=r!==i-1?"/"+n[r]:n[r];return a},toAbsPath:function(e,t){var n,r,o,i=0,a=[];for(r=/\/$/.test(t)?"/":"",e=e.split("/"),t=t.split("/"),yw(e,function(e){e&&a.push(e)}),e=a,n=t.length-1,a=[];0<=n;n--)0!==t[n].length&&"."!==t[n]&&(".."!==t[n]?0<i?i--:a.push(t[n]):i++);return 0!==(o=(n=e.length-i)<=0?a.reverse().join("/"):e.slice(0,n).join("/")+"/"+a.reverse().join("/")).indexOf("/")&&(o="/"+o),r&&o.lastIndexOf("/")!==o.length-1&&(o+=r),o},getURI:function(e){var t,n=this;return n.source&&!e||(t="",e||(n.protocol?t+=n.protocol+"://":t+="//",n.userInfo&&(t+=n.userInfo+"@"),n.host&&(t+=n.host),n.port&&(t+=":"+n.port)),n.path&&(t+=n.path),n.query&&(t+="?"+n.query),n.anchor&&(t+="#"+n.anchor),n.source=t),n.source}},Nw.parseDataUri=function(e){var t,n;return e=decodeURIComponent(e).split(","),(n=/data:([^;]+)/.exec(e[0]))&&(t=n[1]),{type:t,data:e[1]}},Nw.getDocumentBaseUrl=function(e){var t;return t=0!==e.protocol.indexOf("http")&&"file:"!==e.protocol?e.href:e.protocol+"//"+e.host+e.pathname,/^[^:]+:\/\/\/?[^\/]+\//.test(t)&&(t=t.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(t)||(t+="/")),t};var Ew=function(e,t,n){var r,o,i,a,u;if(t.format=t.format?t.format:"html",t.get=!0,t.getInner=!0,t.no_events||e.fire("BeforeGetContent",t),"raw"===t.format)r=Gt.trim(Sv.trimExternal(e.serializer,n.innerHTML));else if("text"===t.format)r=da(n.innerText||n.textContent);else{if("tree"===t.format)return e.serializer.serialize(n,t);i=(o=e).serializer.serialize(n,t),a=eg(o),u=new RegExp("^(<"+a+"[^>]*>(&nbsp;|&#160;|\\s|\xa0|<br \\/>|)<\\/"+a+">[\r\n]*|<br \\/>[\r\n]*)$"),r=i.replace(u,"")}return"text"===t.format||Co(or.fromDom(n))?t.content=r:t.content=Gt.trim(r),t.no_events||e.fire("GetContent",t),t.content},Sw=function(e,t){t(e),e.firstChild&&Sw(e.firstChild,t),e.next&&Sw(e.next,t)},kw=function(e,t,n){var r=function(e,n,t){var r={},o={},i=[];for(var a in t.firstChild&&Sw(t.firstChild,function(t){F(e,function(e){e.name===t.name&&(r[e.name]?r[e.name].nodes.push(t):r[e.name]={filter:e,nodes:[t]})}),F(n,function(e){"string"==typeof t.attr(e.name)&&(o[e.name]?o[e.name].nodes.push(t):o[e.name]={filter:e,nodes:[t]})})}),r)r.hasOwnProperty(a)&&i.push(r[a]);for(var a in o)o.hasOwnProperty(a)&&i.push(o[a]);return i}(e,t,n);F(r,function(t){F(t.filter.callbacks,function(e){e(t.nodes,t.filter.name,{})})})},Tw=function(e){return e instanceof Kb},Aw=function(e,t){var r;e.dom.setHTML(e.getBody(),t),Kp(r=e)&&Zs.firstPositionIn(r.getBody()).each(function(e){var t=e.getNode(),n=Bo.isTable(t)?Zs.firstPositionIn(t).getOr(e):e;r.selection.setRng(n.toRange())})},Rw=function(u,s,c){return void 0===c&&(c={}),c.format=c.format?c.format:"html",c.set=!0,c.content=Tw(s)?"":s,Tw(s)||c.no_events||(u.fire("BeforeSetContent",c),s=c.content),A.from(u.getBody()).fold(q(s),function(e){return Tw(s)?function(e,t,n,r){kw(e.parser.getNodeFilters(),e.parser.getAttributeFilters(),n);var o=Jc({validate:e.validate},e.schema).serialize(n);return r.content=Co(or.fromDom(t))?o:Gt.trim(o),Aw(e,r.content),r.no_events||e.fire("SetContent",r),n}(u,e,s,c):(t=u,n=e,o=c,0===(r=s).length||/^\s+$/.test(r)?(a='<br data-mce-bogus="1">',"TABLE"===n.nodeName?r="<tr><td>"+a+"</td></tr>":/^(UL|OL)$/.test(n.nodeName)&&(r="<li>"+a+"</li>"),(i=eg(t))&&t.schema.isValidChild(n.nodeName.toLowerCase(),i.toLowerCase())?(r=a,r=t.dom.createHTML(i,t.settings.forced_root_block_attrs,r)):r||(r='<br data-mce-bogus="1">'),Aw(t,r),t.fire("SetContent",o)):("raw"!==o.format&&(r=Jc({validate:t.validate},t.schema).serialize(t.parser.parse(r,{isRootContent:!0,insert:!0}))),o.content=Co(or.fromDom(n))?r:Gt.trim(r),Aw(t,o.content),o.no_events||t.fire("SetContent",o)),o.content);var t,n,r,o,i,a})},_w=hi.DOM,Dw=function(e){return A.from(e).each(function(e){return e.destroy()})},Bw=function(e){if(!e.removed){var t=e._selectionOverrides,n=e.editorUpload,r=e.getBody(),o=e.getElement();r&&e.save({is_removing:!0}),e.removed=!0,e.unbindAllNativeEvents(),e.hasHiddenInput&&o&&_w.remove(o.nextSibling),bp(e),e.editorManager.remove(e),!e.inline&&r&&(i=e,_w.setStyle(i.id,"display",i.orgDisplay)),yp(e),_w.remove(e.getContainer()),Dw(t),Dw(n),e.destroy()}var i},Ow=function(e,t){var n,r,o,i=e.selection,a=e.dom;e.destroyed||(t||e.removed?(t||(e.editorManager.off("beforeunload",e._beforeUnload),e.theme&&e.theme.destroy&&e.theme.destroy(),Dw(i),Dw(a)),(r=(n=e).formElement)&&(r._mceOldSubmit&&(r.submit=r._mceOldSubmit,r._mceOldSubmit=null),_w.unbind(r,"submit reset",n.formEventDelegate)),(o=e).contentAreaContainer=o.formElement=o.container=o.editorContainer=null,o.bodyElement=o.contentDocument=o.contentWindow=null,o.iframeElement=o.targetElm=null,o.selection&&(o.selection=o.selection.win=o.selection.dom=o.selection.dom.doc=null),e.destroyed=!0):e.remove())},Pw=hi.DOM,Lw=Gt.extend,Iw=Gt.each,Mw=Gt.resolve,Fw=me.ie,zw=function(e,t,n){var r,o,i,a,u,s,c,l=this,f=l.documentBaseUrl=n.documentBaseURL,d=n.baseURI;r=l,o=e,i=f,a=n.defaultSettings,u=t,c={id:o,theme:"modern",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:i,add_form_submit_trigger:!0,submit_patch:!0,add_unload_trigger:!0,convert_urls:!0,relative_urls:!0,remove_script_host:!0,object_resizing:!0,doctype:"<!DOCTYPE html>",visual:!0,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",forced_root_block:"p",hidden_input:!0,render_ui:!0,indentation:"40px",inline_styles:!0,convert_fonts_to_spans:!0,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",entity_encoding:"named",url_converter:(s=r).convertURL,url_converter_scope:s,ie7_compat:!0},t=ml(al,c,a,u),l.settings=t,Ei.language=t.language||"en",Ei.languageLoad=t.language_load,Ei.baseURL=n.baseURL,l.id=e,l.setDirty(!1),l.plugins={},l.documentBaseURI=new Nw(t.document_base_url,{base_uri:d}),l.baseURI=d,l.contentCSS=[],l.contentStyles=[],l.shortcuts=new Mp(l),l.loadedCSS={},l.editorCommands=new lp(l),l.suffix=n.suffix,l.editorManager=n,l.inline=t.inline,l.buttons={},l.menuItems={},t.cache_suffix&&(me.cacheSuffix=t.cache_suffix.replace(/^[\?\&]+/,"")),!1===t.override_viewport&&(me.overrideViewPort=!1),n.fire("SetupEditor",{editor:l}),l.execCallback("setup",l),l.$=hn.overrideDefaults(function(){return{context:l.inline?l.getBody():l.getDoc(),element:l.getBody()}})};Lw(zw.prototype={render:function(){vw(this)},focus:function(e){Wp(this,e)},hasFocus:function(){return Kp(this)},execCallback:function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r,o=this.settings[e];if(o)return this.callbackLookup&&(r=this.callbackLookup[e])&&(o=r.func,r=r.scope),"string"==typeof o&&(r=(r=o.replace(/\.\w+$/,""))?Mw(r):0,o=Mw(o),this.callbackLookup=this.callbackLookup||{},this.callbackLookup[e]={func:o,scope:r}),o.apply(r||this,Array.prototype.slice.call(arguments,1))},translate:function(e){if(e&&Gt.is(e,"string")){var n=this.settings.language||"en",r=this.editorManager.i18n;e=r.data[n+"."+e]||e.replace(/\{\#([^\}]+)\}/g,function(e,t){return r.data[n+"."+t]||"{#"+t+"}"})}return this.editorManager.translate(e)},getLang:function(e,t){return this.editorManager.i18n.data[(this.settings.language||"en")+"."+e]||(t!==undefined?t:"{#"+e+"}")},getParam:function(e,t,n){return hl(this,e,t,n)},nodeChanged:function(e){this._nodeChangeDispatcher.nodeChanged(e)},addButton:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),t.stateSelector&&"undefined"==typeof t.active&&(t.active=!1),t.text||t.icon||(t.icon=e),t.tooltip=t.tooltip||t.title,n.buttons[e]=t},addSidebar:function(e,t){return bw(this,e,t)},addMenuItem:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),n.menuItems[e]=t},addContextToolbar:function(e,t){var n,r=this;r.contextToolbars=r.contextToolbars||[],"string"==typeof e&&(n=e,e=function(e){return r.dom.is(e,n)}),r.contextToolbars.push({id:Ah.uuid("mcet"),predicate:e,items:t})},addCommand:function(e,t,n){this.editorCommands.addCommand(e,t,n)},addQueryStateHandler:function(e,t,n){this.editorCommands.addQueryStateHandler(e,t,n)},addQueryValueHandler:function(e,t,n){this.editorCommands.addQueryValueHandler(e,t,n)},addShortcut:function(e,t,n,r){this.shortcuts.add(e,t,n,r)},execCommand:function(e,t,n,r){return this.editorCommands.execCommand(e,t,n,r)},queryCommandState:function(e){return this.editorCommands.queryCommandState(e)},queryCommandValue:function(e){return this.editorCommands.queryCommandValue(e)},queryCommandSupported:function(e){return this.editorCommands.queryCommandSupported(e)},show:function(){this.hidden&&(this.hidden=!1,this.inline?this.getBody().contentEditable=!0:(Pw.show(this.getContainer()),Pw.hide(this.id)),this.load(),this.fire("show"))},hide:function(){var e=this,t=e.getDoc();e.hidden||(Fw&&t&&!e.inline&&t.execCommand("SelectAll"),e.save(),e.inline?(e.getBody().contentEditable=!1,e===e.editorManager.focusedEditor&&(e.editorManager.focusedEditor=null)):(Pw.hide(e.getContainer()),Pw.setStyle(e.id,"display",e.orgDisplay)),e.hidden=!0,e.fire("hide"))},isHidden:function(){return!!this.hidden},setProgressState:function(e,t){this.fire("ProgressState",{state:e,time:t})},load:function(e){var t,n=this.getElement();return this.removed?"":n?((e=e||{}).load=!0,t=this.setContent(n.value!==undefined?n.value:n.innerHTML,e),e.element=n,e.no_events||this.fire("LoadContent",e),e.element=n=null,t):void 0},save:function(e){var t,n,r=this,o=r.getElement();if(o&&r.initialized&&!r.removed)return(e=e||{}).save=!0,e.element=o,e.content=r.getContent(e),e.no_events||r.fire("SaveContent",e),"raw"===e.format&&r.fire("RawSaveContent",e),t=e.content,/TEXTAREA|INPUT/i.test(o.nodeName)?o.value=t:(!e.is_removing&&r.inline||(o.innerHTML=t),(n=Pw.getParent(r.id,"form"))&&Iw(n.elements,function(e){if(e.name===r.id)return e.value=t,!1})),e.element=o=null,!1!==e.set_dirty&&r.setDirty(!1),t},setContent:function(e,t){return Rw(this,e,t)},getContent:function(e){return t=this,void 0===(n=e)&&(n={}),A.from(t.getBody()).fold(q("tree"===n.format?new Kb("body",11):""),function(e){return Ew(t,n,e)});var t,n},insertContent:function(e,t){t&&(e=Lw({content:e},t)),this.execCommand("mceInsertContent",!1,e)},isDirty:function(){return!this.isNotDirty},setDirty:function(e){var t=!this.isNotDirty;this.isNotDirty=!e,e&&e!==t&&this.fire("dirty")},setMode:function(e){var t,n;(n=e)!==kp(t=this)&&(t.initialized?Sp(t,"readonly"===n):t.on("init",function(){Sp(t,"readonly"===n)}),Cp(t,n))},getContainer:function(){return this.container||(this.container=Pw.get(this.editorContainer||this.id+"_parent")),this.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return this.targetElm||(this.targetElm=Pw.get(this.id)),this.targetElm},getWin:function(){var e;return this.contentWindow||(e=this.iframeElement)&&(this.contentWindow=e.contentWindow),this.contentWindow},getDoc:function(){var e;return this.contentDocument||(e=this.getWin())&&(this.contentDocument=e.document),this.contentDocument},getBody:function(){var e=this.getDoc();return this.bodyElement||(e?e.body:null)},convertURL:function(e,t,n){var r=this.settings;return r.urlconverter_callback?this.execCallback("urlconverter_callback",e,n,!0,t):!r.convert_urls||n&&"LINK"===n.nodeName||0===e.indexOf("file:")||0===e.length?e:r.relative_urls?this.documentBaseURI.toRelative(e):e=this.documentBaseURI.toAbsolute(e,r.remove_script_host)},addVisual:function(e){var n,r=this,o=r.settings,i=r.dom;e=e||r.getBody(),r.hasVisual===undefined&&(r.hasVisual=o.visual),Iw(i.select("table,a",e),function(e){var t;switch(e.nodeName){case"TABLE":return n=o.visual_table_class||"mce-item-table",void((t=i.getAttrib(e,"border"))&&"0"!==t||!r.hasVisual?i.removeClass(e,n):i.addClass(e,n));case"A":return void(i.getAttrib(e,"href")||(t=i.getAttrib(e,"name")||e.id,n=o.visual_anchor_class||"mce-item-anchor",t&&r.hasVisual?i.addClass(e,n):i.removeClass(e,n)))}}),r.fire("VisualAid",{element:e,hasVisual:r.hasVisual})},remove:function(){Bw(this)},destroy:function(e){Ow(this,e)},uploadImages:function(e){return this.editorUpload.uploadImages(e)},_scanForImages:function(){return this.editorUpload.scanForImages()}},Bp);var Uw,Vw,Hw,jw={isEditorUIElement:function(e){return-1!==e.className.toString().indexOf("mce-")}},qw=function(n,e){var t,r;nr.detect().browser.isIE()?(r=n).on("focusout",function(){ep(r)}):(t=e,n.on("mouseup touchend",function(e){t.throttle()})),n.on("keyup nodechange",function(e){var t;"nodechange"===(t=e).type&&t.selectionChange||ep(n)})},$w=function(e){var t,n,r,o=Oi(function(){ep(e)},0);e.inline&&(t=e,n=o,r=function(){n.throttle()},hi.DOM.bind(H.document,"mouseup",r),t.on("remove",function(){hi.DOM.unbind(H.document,"mouseup",r)})),e.on("init",function(){qw(e,o)}),e.on("remove",function(){o.cancel()})},Ww=hi.DOM,Kw=function(e){return jw.isEditorUIElement(e)},Xw=function(t,e){var n=t?t.settings.custom_ui_selector:"";return null!==Ww.getParent(e,function(e){return Kw(e)||!!n&&t.dom.is(e,n)})},Yw=function(r,e){var t=e.editor;$w(t),t.on("focusin",function(){var e=r.focusedEditor;e!==this&&(e&&e.fire("blur",{focusedEditor:this}),r.setActive(this),(r.focusedEditor=this).fire("focus",{blurredEditor:e}),this.focus(!0))}),t.on("focusout",function(){var t=this;be.setEditorTimeout(t,function(){var e=r.focusedEditor;Xw(t,function(){try{return H.document.activeElement}catch(e){return H.document.body}}())||e!==t||(t.fire("blur",{focusedEditor:null}),r.focusedEditor=null)})}),Uw||(Uw=function(e){var t,n=r.activeEditor;t=e.target,n&&t.ownerDocument===H.document&&(t===H.document.body||Xw(n,t)||r.focusedEditor!==n||(n.fire("blur",{focusedEditor:null}),r.focusedEditor=null))},Ww.bind(H.document,"focusin",Uw))},Gw=function(e,t){e.focusedEditor===t.editor&&(e.focusedEditor=null),e.activeEditor||(Ww.unbind(H.document,"focusin",Uw),Uw=null)},Jw=function(e){e.on("AddEditor",d(Yw,e)),e.on("RemoveEditor",d(Gw,e))},Qw=hi.DOM,Zw=Gt.explode,eN=Gt.each,tN=Gt.extend,nN=0,rN=!1,oN=[],iN=[],aN=function(t){var n=t.type;eN(Hw.get(),function(e){switch(n){case"scroll":e.fire("ScrollWindow",t);break;case"resize":e.fire("ResizeWindow",t)}})},uN=function(e){e!==rN&&(e?hn(window).on("resize scroll",aN):hn(window).off("resize scroll",aN),rN=e)},sN=function(t){var e=iN;delete oN[t.id];for(var n=0;n<oN.length;n++)if(oN[n]===t){oN.splice(n,1);break}return iN=z(iN,function(e){return t!==e}),Hw.activeEditor===t&&(Hw.activeEditor=0<iN.length?iN[0]:null),Hw.focusedEditor===t&&(Hw.focusedEditor=null),e.length!==iN.length};tN(Hw={defaultSettings:{},$:hn,majorVersion:"4",minorVersion:"9.4",releaseDate:"2019-03-20",editors:oN,i18n:ah,activeEditor:null,settings:{},setup:function(){var e,t,n,r,o="";if(t=Nw.getDocumentBaseUrl(H.document.location),/^[^:]+:\/\/\/?[^\/]+\//.test(t)&&(t=t.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(t)||(t+="/")),n=window.tinymce||window.tinyMCEPreInit)e=n.base||n.baseURL,o=n.suffix;else{for(var i=H.document.getElementsByTagName("script"),a=0;a<i.length;a++){var u=(r=i[a].src).substring(r.lastIndexOf("/"));if(/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(r)){-1!==u.indexOf(".min")&&(o=".min"),e=r.substring(0,r.lastIndexOf("/"));break}}!e&&H.document.currentScript&&(-1!==(r=H.document.currentScript.src).indexOf(".min")&&(o=".min"),e=r.substring(0,r.lastIndexOf("/")))}this.baseURL=new Nw(t).toAbsolute(e),this.documentBaseURL=t,this.baseURI=new Nw(this.baseURL),this.suffix=o,Jw(this)},overrideDefaults:function(e){var t,n;(t=e.base_url)&&(this.baseURL=new Nw(this.documentBaseURL).toAbsolute(t.replace(/\/+$/,"")),this.baseURI=new Nw(this.baseURL)),n=e.suffix,e.suffix&&(this.suffix=n);var r=(this.defaultSettings=e).plugin_base_urls;for(var o in r)Ei.PluginManager.urls[o]=r[o]},init:function(r){var n,u,s=this;u=Gt.makeMap("area base basefont br col frame hr img input isindex link meta param embed source wbr track colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu"," ");var c=function(e){var t=e.id;return t||(t=(t=e.name)&&!Qw.get(t)?e.name:Qw.uniqueId(),e.setAttribute("id",t)),t},l=function(e,t){return t.constructor===RegExp?t.test(e.className):Qw.hasClass(e,t)},f=function(e){n=e},e=function(){var o,i=0,a=[],n=function(e,t,n){var r=new zw(e,t,s);a.push(r),r.on("init",function(){++i===o.length&&f(a)}),r.targetElm=r.targetElm||n,r.render()};Qw.unbind(window,"ready",e),function(e){var t=r[e];t&&t.apply(s,Array.prototype.slice.call(arguments,2))}("onpageload"),o=hn.unique(function(t){var e,n=[];if(me.ie&&me.ie<11)return dh.initError("TinyMCE does not support the browser you are using. For a list of supported browsers please see: https://www.tinymce.com/docs/get-started/system-requirements/"),[];if(t.types)return eN(t.types,function(e){n=n.concat(Qw.select(e.selector))}),n;if(t.selector)return Qw.select(t.selector);if(t.target)return[t.target];switch(t.mode){case"exact":0<(e=t.elements||"").length&&eN(Zw(e),function(t){var e;(e=Qw.get(t))?n.push(e):eN(H.document.forms,function(e){eN(e.elements,function(e){e.name===t&&(t="mce_editor_"+nN++,Qw.setAttrib(e,"id",t),n.push(e))})})});break;case"textareas":case"specific_textareas":eN(Qw.select("textarea"),function(e){t.editor_deselector&&l(e,t.editor_deselector)||t.editor_selector&&!l(e,t.editor_selector)||n.push(e)})}return n}(r)),r.types?eN(r.types,function(t){Gt.each(o,function(e){return!Qw.is(e,t.selector)||(n(c(e),tN({},r,t),e),!1)})}):(Gt.each(o,function(e){var t;(t=s.get(e.id))&&t.initialized&&!(t.getContainer()||t.getBody()).parentNode&&(sN(t),t.unbindAllNativeEvents(),t.destroy(!0),t.removed=!0,t=null)}),0===(o=Gt.grep(o,function(e){return!s.get(e.id)})).length?f([]):eN(o,function(e){var t;t=e,r.inline&&t.tagName.toLowerCase()in u?dh.initError("Could not initialize inline editor on invalid inline target element",e):n(c(e),r,e)}))};return s.settings=r,Qw.bind(window,"ready",e),new ge(function(t){n?t(n):f=function(e){t(e)}})},get:function(t){return 0===arguments.length?iN.slice(0):R(t)?V(iN,function(e){return e.id===t}).getOr(null):L(t)&&iN[t]?iN[t]:null},add:function(e){var t=this;return oN[e.id]===e||(null===t.get(e.id)&&("length"!==e.id&&(oN[e.id]=e),oN.push(e),iN.push(e)),uN(!0),t.activeEditor=e,t.fire("AddEditor",{editor:e}),Vw||(Vw=function(){t.fire("BeforeUnload")},Qw.bind(window,"beforeunload",Vw))),e},createEditor:function(e,t){return this.add(new zw(e,t,this))},remove:function(e){var t,n,r=this;if(e){if(!R(e))return n=e,B(r.get(n.id))?null:(sN(n)&&r.fire("RemoveEditor",{editor:n}),0===iN.length&&Qw.unbind(window,"beforeunload",Vw),n.remove(),uN(0<iN.length),n);eN(Qw.select(e),function(e){(n=r.get(e.id))&&r.remove(n)})}else for(t=iN.length-1;0<=t;t--)r.remove(iN[t])},execCommand:function(e,t,n){var r=this.get(n);switch(e){case"mceAddEditor":return this.get(n)||new zw(n,this.settings,this).render(),!0;case"mceRemoveEditor":return r&&r.remove(),!0;case"mceToggleEditor":return r?r.isHidden()?r.show():r.hide():this.execCommand("mceAddEditor",0,n),!0}return!!this.activeEditor&&this.activeEditor.execCommand(e,t,n)},triggerSave:function(){eN(iN,function(e){e.save()})},addI18n:function(e,t){ah.add(e,t)},translate:function(e){return ah.translate(e)},setActive:function(e){var t=this.activeEditor;this.activeEditor!==e&&(t&&t.fire("deactivate",{relatedTarget:e}),e.fire("activate",{relatedTarget:t})),this.activeEditor=e}},pp),Hw.setup();var cN,lN=Hw;function fN(n){return{walk:function(e,t){return Tc(n,e,t)},split:bm,normalize:function(t){return _g(n,t).fold(q(!1),function(e){return t.setStart(e.startContainer,e.startOffset),t.setEnd(e.endContainer,e.endOffset),!0})}}}(cN=fN||(fN={})).compareRanges=Ng,cN.getCaretRangeFromPoint=xy,cN.getSelectedNode=Ha,cN.getNode=ja;var dN,mN,gN=fN,pN=Math.min,hN=Math.max,vN=Math.round,bN=function(e,t,n){var r,o,i,a,u,s;return r=t.x,o=t.y,i=e.w,a=e.h,u=t.w,s=t.h,"b"===(n=(n||"").split(""))[0]&&(o+=s),"r"===n[1]&&(r+=u),"c"===n[0]&&(o+=vN(s/2)),"c"===n[1]&&(r+=vN(u/2)),"b"===n[3]&&(o-=a),"r"===n[4]&&(r-=i),"c"===n[3]&&(o-=vN(a/2)),"c"===n[4]&&(r-=vN(i/2)),yN(r,o,i,a)},yN=function(e,t,n,r){return{x:e,y:t,w:n,h:r}},CN={inflate:function(e,t,n){return yN(e.x-t,e.y-n,e.w+2*t,e.h+2*n)},relativePosition:bN,findBestRelativePosition:function(e,t,n,r){var o,i;for(i=0;i<r.length;i++)if((o=bN(e,t,r[i])).x>=n.x&&o.x+o.w<=n.w+n.x&&o.y>=n.y&&o.y+o.h<=n.h+n.y)return r[i];return null},intersect:function(e,t){var n,r,o,i;return n=hN(e.x,t.x),r=hN(e.y,t.y),o=pN(e.x+e.w,t.x+t.w),i=pN(e.y+e.h,t.y+t.h),o-n<0||i-r<0?null:yN(n,r,o-n,i-r)},clamp:function(e,t,n){var r,o,i,a,u,s,c,l,f,d;return u=e.x,s=e.y,c=e.x+e.w,l=e.y+e.h,f=t.x+t.w,d=t.y+t.h,r=hN(0,t.x-u),o=hN(0,t.y-s),i=hN(0,c-f),a=hN(0,l-d),u+=r,s+=o,n&&(c+=r,l+=o,u-=i,s-=a),yN(u,s,(c-=i)-u,(l-=a)-s)},create:yN,fromClientRect:function(e){return yN(e.left,e.top,e.width,e.height)}},xN={},wN={add:function(e,t){xN[e.toLowerCase()]=t},has:function(e){return!!xN[e.toLowerCase()]},get:function(e){var t=e.toLowerCase(),n=xN.hasOwnProperty(t)?xN[t]:null;if(null===n)throw new Error("Could not find module for type: "+e);return n},create:function(e,t){var n;if("string"==typeof e?(t=t||{}).type=e:e=(t=e).type,e=e.toLowerCase(),!(n=xN[e]))throw new Error("Could not find control by type: "+e);return(n=new n(t)).type=e,n}},NN=Gt.each,EN=Gt.extend,SN=function(){};SN.extend=dN=function(n){var e,t,r,o=this.prototype,i=function(){var e,t,n;if(!mN&&(this.init&&this.init.apply(this,arguments),t=this.Mixins))for(e=t.length;e--;)(n=t[e]).init&&n.init.apply(this,arguments)},a=function(){return this},u=function(n,r){return function(){var e,t=this._super;return this._super=o[n],e=r.apply(this,arguments),this._super=t,e}};for(t in mN=!0,e=new this,mN=!1,n.Mixins&&(NN(n.Mixins,function(e){for(var t in e)"init"!==t&&(n[t]=e[t])}),o.Mixins&&(n.Mixins=o.Mixins.concat(n.Mixins))),n.Methods&&NN(n.Methods.split(","),function(e){n[e]=a}),n.Properties&&NN(n.Properties.split(","),function(e){var t="_"+e;n[e]=function(e){return e!==undefined?(this[t]=e,this):this[t]}}),n.Statics&&NN(n.Statics,function(e,t){i[t]=e}),n.Defaults&&o.Defaults&&(n.Defaults=EN({},o.Defaults,n.Defaults)),n)"function"==typeof(r=n[t])&&o[t]?e[t]=u(t,r):e[t]=r;return i.prototype=e,(i.constructor=i).extend=dN,i};var kN=Math.min,TN=Math.max,AN=Math.round,RN=function(e,n){var r,o,t,i;if(n=n||'"',null===e)return"null";if("string"==(t=typeof e))return o="\bb\tt\nn\ff\rr\"\"''\\\\",n+e.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(e,t){return'"'===n&&"'"===e?e:(r=o.indexOf(t))+1?"\\"+o.charAt(r+1):(e=t.charCodeAt().toString(16),"\\u"+"0000".substring(e.length)+e)})+n;if("object"===t){if(e.hasOwnProperty&&"[object Array]"===Object.prototype.toString.call(e)){for(r=0,o="[";r<e.length;r++)o+=(0<r?",":"")+RN(e[r],n);return o+"]"}for(i in o="{",e)e.hasOwnProperty(i)&&(o+="function"!=typeof e[i]?(1<o.length?","+n:n)+i+n+":"+RN(e[i],n):"");return o+"}"}return""+e},_N={serialize:RN,parse:function(e){try{return JSON.parse(e)}catch(t){}}},DN={callbacks:{},count:0,send:function(t){var n=this,r=hi.DOM,o=t.count!==undefined?t.count:n.count,i="tinymce_jsonp_"+o;n.callbacks[o]=function(e){r.remove(i),delete n.callbacks[o],t.callback(e)},r.add(r.doc.body,"script",{id:i,src:t.url,type:"text/javascript"}),n.count++}},BN={send:function(e){var t,n=0,r=function(){!e.async||4===t.readyState||1e4<n++?(e.success&&n<1e4&&200===t.status?e.success.call(e.success_scope,""+t.responseText,t,e):e.error&&e.error.call(e.error_scope,1e4<n?"TIMED_OUT":"GENERAL",t,e),t=null):setTimeout(r,10)};if(e.scope=e.scope||this,e.success_scope=e.success_scope||e.scope,e.error_scope=e.error_scope||e.scope,e.async=!1!==e.async,e.data=e.data||"",BN.fire("beforeInitialize",{settings:e}),t=ph()){if(t.overrideMimeType&&t.overrideMimeType(e.content_type),t.open(e.type||(e.data?"POST":"GET"),e.url,e.async),e.crossDomain&&(t.withCredentials=!0),e.content_type&&t.setRequestHeader("Content-Type",e.content_type),e.requestheaders&&Gt.each(e.requestheaders,function(e){t.setRequestHeader(e.key,e.value)}),t.setRequestHeader("X-Requested-With","XMLHttpRequest"),(t=BN.fire("beforeSend",{xhr:t,settings:e}).xhr).send(e.data),!e.async)return r();setTimeout(r,10)}}};Gt.extend(BN,pp);var ON,PN,LN,IN,MN=Gt.extend,FN=function(e){this.settings=MN({},e),this.count=0};FN.sendRPC=function(e){return(new FN).send(e)},FN.prototype={send:function(n){var r=n.error,o=n.success;(n=MN(this.settings,n)).success=function(e,t){void 0===(e=_N.parse(e))&&(e={error:"JSON Parse error."}),e.error?r.call(n.error_scope||n.scope,e.error,t):o.call(n.success_scope||n.scope,e.result)},n.error=function(e,t){r&&r.call(n.error_scope||n.scope,e,t)},n.data=_N.serialize({id:n.id||"c"+this.count++,method:n.method,params:n.params}),n.content_type="application/json",BN.send(n)}};try{ON=H.window.localStorage}catch(jN){PN={},LN=[],IN={getItem:function(e){var t=PN[e];return t||null},setItem:function(e,t){LN.push(e),PN[e]=String(t)},key:function(e){return LN[e]},removeItem:function(t){LN=LN.filter(function(e){return e===t}),delete PN[t]},clear:function(){LN=[],PN={}},length:0},Object.defineProperty(IN,"length",{get:function(){return LN.length},configurable:!1,enumerable:!1}),ON=IN}var zN,UN=lN,VN={geom:{Rect:CN},util:{Promise:ge,Delay:be,Tools:Gt,VK:pv,URI:Nw,Class:SN,EventDispatcher:dp,Observable:pp,I18n:ah,XHR:BN,JSON:_N,JSONRequest:FN,JSONP:DN,LocalStorage:ON,Color:function(e){var n={},u=0,s=0,c=0,t=function(e){var t;return"object"==typeof e?"r"in e?(u=e.r,s=e.g,c=e.b):"v"in e&&function(e,t,n){var r,o,i,a;if(e=(parseInt(e,10)||0)%360,t=parseInt(t,10)/100,n=parseInt(n,10)/100,t=TN(0,kN(t,1)),n=TN(0,kN(n,1)),0!==t){switch(r=e/60,i=(o=n*t)*(1-Math.abs(r%2-1)),a=n-o,Math.floor(r)){case 0:u=o,s=i,c=0;break;case 1:u=i,s=o,c=0;break;case 2:u=0,s=o,c=i;break;case 3:u=0,s=i,c=o;break;case 4:u=i,s=0,c=o;break;case 5:u=o,s=0,c=i;break;default:u=s=c=0}u=AN(255*(u+a)),s=AN(255*(s+a)),c=AN(255*(c+a))}else u=s=c=AN(255*n)}(e.h,e.s,e.v):(t=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(e))?(u=parseInt(t[1],10),s=parseInt(t[2],10),c=parseInt(t[3],10)):(t=/#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(e))?(u=parseInt(t[1],16),s=parseInt(t[2],16),c=parseInt(t[3],16)):(t=/#([0-F])([0-F])([0-F])/gi.exec(e))&&(u=parseInt(t[1]+t[1],16),s=parseInt(t[2]+t[2],16),c=parseInt(t[3]+t[3],16)),u=u<0?0:255<u?255:u,s=s<0?0:255<s?255:s,c=c<0?0:255<c?255:c,n};return e&&t(e),n.toRgb=function(){return{r:u,g:s,b:c}},n.toHsv=function(){return e=u,t=s,n=c,o=0,(i=kN(e/=255,kN(t/=255,n/=255)))===(a=TN(e,TN(t,n)))?{h:0,s:0,v:100*(o=i)}:(r=(a-i)/a,{h:AN(60*((e===i?3:n===i?1:5)-(e===i?t-n:n===i?e-t:n-e)/((o=a)-i))),s:AN(100*r),v:AN(100*o)});var e,t,n,r,o,i,a},n.toHex=function(){var e=function(e){return 1<(e=parseInt(e,10).toString(16)).length?e:"0"+e};return"#"+e(u)+e(s)+e(c)},n.parse=t,n}},dom:{EventUtils:Te,Sizzle:Tt,DomQuery:hn,TreeWalker:oo,DOMUtils:hi,ScriptLoader:xi,RangeUtils:gN,Serializer:cy,ControlSelection:hy,BookmarkManager:my,Selection:Yy,Event:Te.Event},html:{Styles:ii,Entities:Wo,Node:Kb,Schema:ri,SaxParser:Nv,DomParser:oy,Writer:Gc,Serializer:Jc},ui:{Factory:wN},Env:me,AddOnManager:Ei,Annotator:Pc,Formatter:Lb,UndoManager:Hv,EditorCommands:lp,WindowManager:rh,NotificationManager:nh,EditorObservable:Bp,Shortcuts:Mp,Editor:zw,FocusManager:jw,EditorManager:lN,DOM:hi.DOM,ScriptLoader:xi.ScriptLoader,PluginManager:Ei.PluginManager,ThemeManager:Ei.ThemeManager,trim:Gt.trim,isArray:Gt.isArray,is:Gt.is,toArray:Gt.toArray,makeMap:Gt.makeMap,each:Gt.each,map:Gt.map,grep:Gt.grep,inArray:Gt.inArray,extend:Gt.extend,create:Gt.create,walk:Gt.walk,createNS:Gt.createNS,resolve:Gt.resolve,explode:Gt.explode,_addCacheSuffix:Gt._addCacheSuffix,isOpera:me.opera,isWebKit:me.webkit,isIE:me.ie,isGecko:me.gecko,isMac:me.mac},HN=UN=Gt.extend(UN,VN);zN=HN,window.tinymce=zN,window.tinyMCE=zN,function(e){if("object"==typeof module)try{module.exports=e}catch(t){}}(HN)}(window);
\ No newline at end of file
+// 4.9.9 (2020-03-25)
+!function(V){"use strict";var o=function(){},H=function(n,r){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return n(r.apply(null,e))}},q=function(e){return function(){return e}},$=function(e){return e};function d(r){for(var o=[],e=1;e<arguments.length;e++)o[e-1]=arguments[e];return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=o.concat(e);return r.apply(null,n)}}var e,t,n,r,i,a,u,s,c,l,f,m,g,p,h,v,y=function(n){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return!n.apply(null,e)}},b=q(!1),C=q(!0),x=function(){return w},w=(e=function(e){return e.isNone()},r={fold:function(e,t){return e()},is:b,isSome:b,isNone:C,getOr:n=function(e){return e},getOrThunk:t=function(e){return e()},getOrDie:function(e){throw new Error(e||"error: getOrDie called on none.")},getOrNull:q(null),getOrUndefined:q(undefined),or:n,orThunk:t,map:x,each:o,bind:x,exists:b,forall:C,filter:x,equals:e,equals_:e,toArray:function(){return[]},toString:q("none()")},Object.freeze&&Object.freeze(r),r),N=function(n){var e=q(n),t=function(){return o},r=function(e){return e(n)},o={fold:function(e,t){return t(n)},is:function(e){return n===e},isSome:C,isNone:b,getOr:e,getOrThunk:e,getOrDie:e,getOrNull:e,getOrUndefined:e,or:t,orThunk:t,map:function(e){return N(e(n))},each:function(e){e(n)},bind:r,exists:r,forall:r,filter:function(e){return e(n)?o:w},toArray:function(){return[n]},toString:function(){return"some("+n+")"},equals:function(e){return e.is(n)},equals_:function(e,t){return e.fold(b,function(e){return t(n,e)})}};return o},_={some:N,none:x,from:function(e){return null===e||e===undefined?w:N(e)}},E=function(t){return function(e){return function(e){if(null===e)return"null";var t=typeof e;return"object"===t&&(Array.prototype.isPrototypeOf(e)||e.constructor&&"Array"===e.constructor.name)?"array":"object"===t&&(String.prototype.isPrototypeOf(e)||e.constructor&&"String"===e.constructor.name)?"string":t}(e)===t}},S=E("string"),T=E("object"),k=E("array"),A=E("null"),R=E("boolean"),D=E("function"),O=E("number"),B=Array.prototype.slice,P=Array.prototype.indexOf,I=Array.prototype.push,L=function(e,t){return P.call(e,t)},F=function(e,t){return-1<L(e,t)},M=function(e,t){for(var n=0,r=e.length;n<r;n++)if(t(e[n],n))return!0;return!1},W=function(e,t){for(var n=e.length,r=new Array(n),o=0;o<n;o++){var i=e[o];r[o]=t(i,o)}return r},z=function(e,t){for(var n=0,r=e.length;n<r;n++)t(e[n],n)},K=function(e,t){for(var n=[],r=[],o=0,i=e.length;o<i;o++){var a=e[o];(t(a,o)?n:r).push(a)}return{pass:n,fail:r}},U=function(e,t){for(var n=[],r=0,o=e.length;r<o;r++){var i=e[r];t(i,r)&&n.push(i)}return n},j=function(e,t,n){return z(e,function(e){n=t(n,e)}),n},X=function(e,t){for(var n=0,r=e.length;n<r;n++){var o=e[n];if(t(o,n))return _.some(o)}return _.none()},Y=function(e,t){for(var n=0,r=e.length;n<r;n++)if(t(e[n],n))return _.some(n);return _.none()},G=function(e,t){return function(e){for(var t=[],n=0,r=e.length;n<r;++n){if(!k(e[n]))throw new Error("Arr.flatten item "+n+" was not an array, input: "+e);I.apply(t,e[n])}return t}(W(e,t))},J=function(e,t){for(var n=0,r=e.length;n<r;++n)if(!0!==t(e[n],n))return!1;return!0},Q=function(e,t){return U(e,function(e){return!F(t,e)})},Z=function(e){return 0===e.length?_.none():_.some(e[0])},ee=function(e){return 0===e.length?_.none():_.some(e[e.length-1])},te=D(Array.from)?Array.from:function(e){return B.call(e)},ne="undefined"!=typeof V.window?V.window:Function("return this;")(),re=function(e,t){return function(e,t){for(var n=t!==undefined&&null!==t?t:ne,r=0;r<e.length&&n!==undefined&&null!==n;++r)n=n[e[r]];return n}(e.split("."),t)},oe={getOrDie:function(e,t){var n=re(e,t);if(n===undefined||null===n)throw new Error(e+" not available on this browser");return n}},ie=function(){return oe.getOrDie("URL")},ae={createObjectURL:function(e){return ie().createObjectURL(e)},revokeObjectURL:function(e){ie().revokeObjectURL(e)}},ue=V.navigator,se=ue.userAgent,ce=function(e){return"matchMedia"in V.window&&V.matchMedia(e).matches};m=/Android/.test(se),a=(a=!(i=/WebKit/.test(se))&&/MSIE/gi.test(se)&&/Explorer/gi.test(ue.appName))&&/MSIE (\w+)\./.exec(se)[1],u=-1!==se.indexOf("Trident/")&&(-1!==se.indexOf("rv:")||-1!==ue.appName.indexOf("Netscape"))&&11,s=-1!==se.indexOf("Edge/")&&!a&&!u&&12,a=a||u||s,c=!i&&!u&&/Gecko/.test(se),l=-1!==se.indexOf("Mac"),f=/(iPad|iPhone)/.test(se),g="FormData"in V.window&&"FileReader"in V.window&&"URL"in V.window&&!!ae.createObjectURL,p=ce("only screen and (max-device-width: 480px)")&&(m||f),h=ce("only screen and (min-width: 800px)")&&(m||f),v=-1!==se.indexOf("Windows Phone"),s&&(i=!1);var le,fe={opera:!1,webkit:i,ie:a,gecko:c,mac:l,iOS:f,android:m,contentEditable:!f||g||534<=parseInt(se.match(/AppleWebKit\/(\d*)/)[1],10),transparentSrc:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",caretAfter:8!==a,range:V.window.getSelection&&"Range"in V.window,documentMode:a&&!s?V.document.documentMode||7:10,fileApi:g,ceFalse:!1===a||8<a,cacheSuffix:null,container:null,overrideViewPort:null,experimentalShadowDom:!1,canHaveCSP:!1===a||11<a,desktop:!p&&!h,windowsPhone:v},de=window.Promise?window.Promise:function(){function r(e,t){return function(){e.apply(t,arguments)}}var e=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i=function(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],l(e,r(o,this),r(u,this))},t=i.immediateFn||"function"==typeof setImmediate&&setImmediate||function(e){setTimeout(e,1)};function a(r){var o=this;null!==this._state?t(function(){var e=o._state?r.onFulfilled:r.onRejected;if(null!==e){var t;try{t=e(o._value)}catch(n){return void r.reject(n)}r.resolve(t)}else(o._state?r.resolve:r.reject)(o._value)}):this._deferreds.push(r)}function o(e){try{if(e===this)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var t=e.then;if("function"==typeof t)return void l(r(t,e),r(o,this),r(u,this))}this._state=!0,this._value=e,s.call(this)}catch(n){u.call(this,n)}}function u(e){this._state=!1,this._value=e,s.call(this)}function s(){for(var e=0,t=this._deferreds.length;e<t;e++)a.call(this,this._deferreds[e]);this._deferreds=null}function c(e,t,n,r){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.resolve=n,this.reject=r}function l(e,t,n){var r=!1;try{e(function(e){r||(r=!0,t(e))},function(e){r||(r=!0,n(e))})}catch(o){if(r)return;r=!0,n(o)}}return i.prototype["catch"]=function(e){return this.then(null,e)},i.prototype.then=function(n,r){var o=this;return new i(function(e,t){a.call(o,new c(n,r,e,t))})},i.all=function(){var s=Array.prototype.slice.call(1===arguments.length&&e(arguments[0])?arguments[0]:arguments);return new i(function(o,i){if(0===s.length)return o([]);var a=s.length;function u(t,e){try{if(e&&("object"==typeof e||"function"==typeof e)){var n=e.then;if("function"==typeof n)return void n.call(e,function(e){u(t,e)},i)}s[t]=e,0==--a&&o(s)}catch(r){i(r)}}for(var e=0;e<s.length;e++)u(e,s[e])})},i.resolve=function(t){return t&&"object"==typeof t&&t.constructor===i?t:new i(function(e){e(t)})},i.reject=function(n){return new i(function(e,t){t(n)})},i.race=function(o){return new i(function(e,t){for(var n=0,r=o.length;n<r;n++)o[n].then(e,t)})},i}(),me=function(e,t){return"number"!=typeof t&&(t=0),setTimeout(e,t)},ge=function(e,t){return"number"!=typeof t&&(t=1),setInterval(e,t)},pe=function(t,n){var r,e;return(e=function(){var e=arguments;clearTimeout(r),r=me(function(){t.apply(this,e)},n)}).stop=function(){clearTimeout(r)},e},he={requestAnimationFrame:function(e,t){le?le.then(e):le=new de(function(e){t||(t=V.document.body),function(e,t){var n,r=V.window.requestAnimationFrame,o=["ms","moz","webkit"];for(n=0;n<o.length&&!r;n++)r=V.window[o[n]+"RequestAnimationFrame"];r||(r=function(e){V.window.setTimeout(e,0)}),r(e,t)}(e,t)}).then(e)},setTimeout:me,setInterval:ge,setEditorTimeout:function(e,t,n){return me(function(){e.removed||t()},n)},setEditorInterval:function(e,t,n){var r;return r=ge(function(){e.removed?clearInterval(r):t()},n)},debounce:pe,throttle:pe,clearInterval:function(e){return clearInterval(e)},clearTimeout:function(e){return clearTimeout(e)}},ve=/^(?:mouse|contextmenu)|click/,ye={keyLocation:1,layerX:1,layerY:1,returnValue:1,webkitMovementX:1,webkitMovementY:1,keyIdentifier:1},be=function(){return!1},Ce=function(){return!0},xe=function(e,t,n,r){e.addEventListener?e.addEventListener(t,n,r||!1):e.attachEvent&&e.attachEvent("on"+t,n)},we=function(e,t,n,r){e.removeEventListener?e.removeEventListener(t,n,r||!1):e.detachEvent&&e.detachEvent("on"+t,n)},Ne=function(e,t){var n,r,o=t||{};for(n in e)ye[n]||(o[n]=e[n]);if(o.target||(o.target=o.srcElement||V.document),fe.experimentalShadowDom&&(o.target=function(e,t){if(e.composedPath){var n=e.composedPath();if(n&&0<n.length)return n[0]}return t}(e,o.target)),e&&ve.test(e.type)&&e.pageX===undefined&&e.clientX!==undefined){var i=o.target.ownerDocument||V.document,a=i.documentElement,u=i.body;o.pageX=e.clientX+(a&&a.scrollLeft||u&&u.scrollLeft||0)-(a&&a.clientLeft||u&&u.clientLeft||0),o.pageY=e.clientY+(a&&a.scrollTop||u&&u.scrollTop||0)-(a&&a.clientTop||u&&u.clientTop||0)}return o.preventDefault=function(){o.isDefaultPrevented=Ce,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},o.stopPropagation=function(){o.isPropagationStopped=Ce,e&&(e.stopPropagation?e.stopPropagation():e.cancelBubble=!0)},!(o.stopImmediatePropagation=function(){o.isImmediatePropagationStopped=Ce,o.stopPropagation()})==((r=o).isDefaultPrevented===Ce||r.isDefaultPrevented===be)&&(o.isDefaultPrevented=be,o.isPropagationStopped=be,o.isImmediatePropagationStopped=be),"undefined"==typeof o.metaKey&&(o.metaKey=!1),o},Ee=function(e,t,n){var r=e.document,o={type:"ready"};if(n.domLoaded)t(o);else{var i=function(){return"complete"===r.readyState||"interactive"===r.readyState&&r.body},a=function(){n.domLoaded||(n.domLoaded=!0,t(o))},u=function(){i()&&(we(r,"readystatechange",u),a())},s=function(){try{r.documentElement.doScroll("left")}catch(e){return void he.setTimeout(s)}a()};!r.addEventListener||fe.ie&&fe.ie<11?(xe(r,"readystatechange",u),r.documentElement.doScroll&&e.self===e.top&&s()):i()?a():xe(e,"DOMContentLoaded",a),xe(e,"load",a)}},Se=function(){var m,g,p,h,v,y=this,b={};g="mce-data-"+(+new Date).toString(32),h="onmouseenter"in V.document.documentElement,p="onfocusin"in V.document.documentElement,v={mouseenter:"mouseover",mouseleave:"mouseout"},m=1,y.domLoaded=!1,y.events=b;var C=function(e,t){var n,r,o,i,a=b[t];if(n=a&&a[e.type])for(r=0,o=n.length;r<o;r++)if((i=n[r])&&!1===i.func.call(i.scope,e)&&e.preventDefault(),e.isImmediatePropagationStopped())return};y.bind=function(e,t,n,r){var o,i,a,u,s,c,l,f=V.window,d=function(e){C(Ne(e||f.event),o)};if(e&&3!==e.nodeType&&8!==e.nodeType){for(e[g]?o=e[g]:(o=m++,e[g]=o,b[o]={}),r=r||e,a=(t=t.split(" ")).length;a--;)c=d,s=l=!1,"DOMContentLoaded"===(u=t[a])&&(u="ready"),y.domLoaded&&"ready"===u&&"complete"===e.readyState?n.call(r,Ne({type:u})):(h||(s=v[u])&&(c=function(e){var t,n;if(t=e.currentTarget,(n=e.relatedTarget)&&t.contains)n=t.contains(n);else for(;n&&n!==t;)n=n.parentNode;n||((e=Ne(e||f.event)).type="mouseout"===e.type?"mouseleave":"mouseenter",e.target=t,C(e,o))}),p||"focusin"!==u&&"focusout"!==u||(l=!0,s="focusin"===u?"focus":"blur",c=function(e){(e=Ne(e||f.event)).type="focus"===e.type?"focusin":"focusout",C(e,o)}),(i=b[o][u])?"ready"===u&&y.domLoaded?n({type:u}):i.push({func:n,scope:r}):(b[o][u]=i=[{func:n,scope:r}],i.fakeName=s,i.capture=l,i.nativeHandler=c,"ready"===u?Ee(e,c,y):xe(e,s||u,c,l)));return e=i=0,n}},y.unbind=function(e,t,n){var r,o,i,a,u,s;if(!e||3===e.nodeType||8===e.nodeType)return y;if(r=e[g]){if(s=b[r],t){for(i=(t=t.split(" ")).length;i--;)if(o=s[u=t[i]]){if(n)for(a=o.length;a--;)if(o[a].func===n){var c=o.nativeHandler,l=o.fakeName,f=o.capture;(o=o.slice(0,a).concat(o.slice(a+1))).nativeHandler=c,o.fakeName=l,o.capture=f,s[u]=o}n&&0!==o.length||(delete s[u],we(e,o.fakeName||u,o.nativeHandler,o.capture))}}else{for(u in s)o=s[u],we(e,o.fakeName||u,o.nativeHandler,o.capture);s={}}for(u in s)return y;delete b[r];try{delete e[g]}catch(d){e[g]=null}}return y},y.fire=function(e,t,n){var r;if(!e||3===e.nodeType||8===e.nodeType)return y;for((n=Ne(null,n)).type=t,n.target=e;(r=e[g])&&C(n,r),(e=e.parentNode||e.ownerDocument||e.defaultView||e.parentWindow)&&!n.isPropagationStopped(););return y},y.clean=function(e){var t,n,r=y.unbind;if(!e||3===e.nodeType||8===e.nodeType)return y;if(e[g]&&r(e),e.getElementsByTagName||(e=e.document),e&&e.getElementsByTagName)for(r(e),t=(n=e.getElementsByTagName("*")).length;t--;)(e=n[t])[g]&&r(e);return y},y.destroy=function(){b={}},y.cancel=function(e){return e&&(e.preventDefault(),e.stopImmediatePropagation()),!1}};Se.Event=new Se,Se.Event.bind(V.window,"ready",function(){});var Te,ke,_e,Ae,Re,De,Oe,Be,Pe,Ie,Le,Fe,Me,ze,Ue,je,Ve,He,qe="sizzle"+-new Date,$e=V.window.document,We=0,Ke=0,Xe=Tt(),Ye=Tt(),Ge=Tt(),Je=function(e,t){return e===t&&(Le=!0),0},Qe=typeof undefined,Ze={}.hasOwnProperty,et=[],tt=et.pop,nt=et.push,rt=et.push,ot=et.slice,it=et.indexOf||function(e){for(var t=0,n=this.length;t<n;t++)if(this[t]===e)return t;return-1},at="[\\x20\\t\\r\\n\\f]",ut="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",st="\\["+at+"*("+ut+")(?:"+at+"*([*^$|!~]?=)"+at+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+ut+"))|)"+at+"*\\]",ct=":("+ut+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+st+")*)|.*)\\)|)",lt=new RegExp("^"+at+"+|((?:^|[^\\\\])(?:\\\\.)*)"+at+"+$","g"),ft=new RegExp("^"+at+"*,"+at+"*"),dt=new RegExp("^"+at+"*([>+~]|"+at+")"+at+"*"),mt=new RegExp("="+at+"*([^\\]'\"]*?)"+at+"*\\]","g"),gt=new RegExp(ct),pt=new RegExp("^"+ut+"$"),ht={ID:new RegExp("^#("+ut+")"),CLASS:new RegExp("^\\.("+ut+")"),TAG:new RegExp("^("+ut+"|[*])"),ATTR:new RegExp("^"+st),PSEUDO:new RegExp("^"+ct),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+at+"*(even|odd|(([+-]|)(\\d*)n|)"+at+"*(?:([+-]|)"+at+"*(\\d+)|))"+at+"*\\)|)","i"),bool:new RegExp("^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$","i"),needsContext:new RegExp("^"+at+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+at+"*((?:-\\d)?\\d*)"+at+"*\\)|)(?=[^-]|$)","i")},vt=/^(?:input|select|textarea|button)$/i,yt=/^h\d$/i,bt=/^[^{]+\{\s*\[native \w/,Ct=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,xt=/[+~]/,wt=/'|\\/g,Nt=new RegExp("\\\\([\\da-f]{1,6}"+at+"?|("+at+")|.)","ig"),Et=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)};try{rt.apply(et=ot.call($e.childNodes),$e.childNodes),et[$e.childNodes.length].nodeType}catch(oE){rt={apply:et.length?function(e,t){nt.apply(e,ot.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}var St=function(e,t,n,r){var o,i,a,u,s,c,l,f,d,m;if((t?t.ownerDocument||t:$e)!==Me&&Fe(t),n=n||[],!e||"string"!=typeof e)return n;if(1!==(u=(t=t||Me).nodeType)&&9!==u)return[];if(Ue&&!r){if(o=Ct.exec(e))if(a=o[1]){if(9===u){if(!(i=t.getElementById(a))||!i.parentNode)return n;if(i.id===a)return n.push(i),n}else if(t.ownerDocument&&(i=t.ownerDocument.getElementById(a))&&He(t,i)&&i.id===a)return n.push(i),n}else{if(o[2])return rt.apply(n,t.getElementsByTagName(e)),n;if((a=o[3])&&ke.getElementsByClassName)return rt.apply(n,t.getElementsByClassName(a)),n}if(ke.qsa&&(!je||!je.test(e))){if(f=l=qe,d=t,m=9===u&&e,1===u&&"object"!==t.nodeName.toLowerCase()){for(c=De(e),(l=t.getAttribute("id"))?f=l.replace(wt,"\\$&"):t.setAttribute("id",f),f="[id='"+f+"'] ",s=c.length;s--;)c[s]=f+Pt(c[s]);d=xt.test(e)&&Ot(t.parentNode)||t,m=c.join(",")}if(m)try{return rt.apply(n,d.querySelectorAll(m)),n}catch(g){}finally{l||t.removeAttribute("id")}}}return Be(e.replace(lt,"$1"),t,n,r)};function Tt(){var r=[];return function e(t,n){return r.push(t+" ")>_e.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function kt(e){return e[qe]=!0,e}function _t(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||1<<31)-(~e.sourceIndex||1<<31);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function At(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function Rt(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function Dt(a){return kt(function(i){return i=+i,kt(function(e,t){for(var n,r=a([],e.length,i),o=r.length;o--;)e[n=r[o]]&&(e[n]=!(t[n]=e[n]))})})}function Ot(e){return e&&typeof e.getElementsByTagName!==Qe&&e}for(Te in ke=St.support={},Re=St.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},Fe=St.setDocument=function(e){var t,s=e?e.ownerDocument||e:$e,n=s.defaultView;return s!==Me&&9===s.nodeType&&s.documentElement?(ze=(Me=s).documentElement,Ue=!Re(s),n&&n!==function(e){try{return e.top}catch(t){}return null}(n)&&(n.addEventListener?n.addEventListener("unload",function(){Fe()},!1):n.attachEvent&&n.attachEvent("onunload",function(){Fe()})),ke.attributes=!0,ke.getElementsByTagName=!0,ke.getElementsByClassName=bt.test(s.getElementsByClassName),ke.getById=!0,_e.find.ID=function(e,t){if(typeof t.getElementById!==Qe&&Ue){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},_e.filter.ID=function(e){var t=e.replace(Nt,Et);return function(e){return e.getAttribute("id")===t}},_e.find.TAG=ke.getElementsByTagName?function(e,t){if(typeof t.getElementsByTagName!==Qe)return t.getElementsByTagName(e)}:function(e,t){var n,r=[],o=0,i=t.getElementsByTagName(e);if("*"===e){for(;n=i[o++];)1===n.nodeType&&r.push(n);return r}return i},_e.find.CLASS=ke.getElementsByClassName&&function(e,t){if(Ue)return t.getElementsByClassName(e)},Ve=[],je=[],ke.disconnectedMatch=!0,je=je.length&&new RegExp(je.join("|")),Ve=Ve.length&&new RegExp(Ve.join("|")),t=bt.test(ze.compareDocumentPosition),He=t||bt.test(ze.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},Je=t?function(e,t){if(e===t)return Le=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!ke.sortDetached&&t.compareDocumentPosition(e)===n?e===s||e.ownerDocument===$e&&He($e,e)?-1:t===s||t.ownerDocument===$e&&He($e,t)?1:Ie?it.call(Ie,e)-it.call(Ie,t):0:4&n?-1:1)}:function(e,t){if(e===t)return Le=!0,0;var n,r=0,o=e.parentNode,i=t.parentNode,a=[e],u=[t];if(!o||!i)return e===s?-1:t===s?1:o?-1:i?1:Ie?it.call(Ie,e)-it.call(Ie,t):0;if(o===i)return _t(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;a[r]===u[r];)r++;return r?_t(a[r],u[r]):a[r]===$e?-1:u[r]===$e?1:0},s):Me},St.matches=function(e,t){return St(e,null,null,t)},St.matchesSelector=function(e,t){if((e.ownerDocument||e)!==Me&&Fe(e),t=t.replace(mt,"='$1']"),ke.matchesSelector&&Ue&&(!Ve||!Ve.test(t))&&(!je||!je.test(t)))try{var n=(void 0).call(e,t);if(n||ke.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(oE){}return 0<St(t,Me,null,[e]).length},St.contains=function(e,t){return(e.ownerDocument||e)!==Me&&Fe(e),He(e,t)},St.attr=function(e,t){(e.ownerDocument||e)!==Me&&Fe(e);var n=_e.attrHandle[t.toLowerCase()],r=n&&Ze.call(_e.attrHandle,t.toLowerCase())?n(e,t,!Ue):undefined;return r!==undefined?r:ke.attributes||!Ue?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},St.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},St.uniqueSort=function(e){var t,n=[],r=0,o=0;if(Le=!ke.detectDuplicates,Ie=!ke.sortStable&&e.slice(0),e.sort(Je),Le){for(;t=e[o++];)t===e[o]&&(r=n.push(o));for(;r--;)e.splice(n[r],1)}return Ie=null,e},Ae=St.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=Ae(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r++];)n+=Ae(t);return n},(_e=St.selectors={cacheLength:50,createPseudo:kt,match:ht,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Nt,Et),e[3]=(e[3]||e[4]||e[5]||"").replace(Nt,Et),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||St.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&St.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return ht.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&gt.test(n)&&(t=De(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Nt,Et).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=Xe[e+" "];return t||(t=new RegExp("(^|"+at+")"+e+"("+at+"|$)"))&&Xe(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==Qe&&e.getAttribute("class")||"")})},ATTR:function(n,r,o){return function(e){var t=St.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===o:"!="===r?t!==o:"^="===r?o&&0===t.indexOf(o):"*="===r?o&&-1<t.indexOf(o):"$="===r?o&&t.slice(-o.length)===o:"~="===r?-1<(" "+t+" ").indexOf(o):"|="===r&&(t===o||t.slice(0,o.length+1)===o+"-"))}},CHILD:function(m,e,t,g,p){var h="nth"!==m.slice(0,3),v="last"!==m.slice(-4),y="of-type"===e;return 1===g&&0===p?function(e){return!!e.parentNode}:function(e,t,n){var r,o,i,a,u,s,c=h!==v?"nextSibling":"previousSibling",l=e.parentNode,f=y&&e.nodeName.toLowerCase(),d=!n&&!y;if(l){if(h){for(;c;){for(i=e;i=i[c];)if(y?i.nodeName.toLowerCase()===f:1===i.nodeType)return!1;s=c="only"===m&&!s&&"nextSibling"}return!0}if(s=[v?l.firstChild:l.lastChild],v&&d){for(u=(r=(o=l[qe]||(l[qe]={}))[m]||[])[0]===We&&r[1],a=r[0]===We&&r[2],i=u&&l.childNodes[u];i=++u&&i&&i[c]||(a=u=0)||s.pop();)if(1===i.nodeType&&++a&&i===e){o[m]=[We,u,a];break}}else if(d&&(r=(e[qe]||(e[qe]={}))[m])&&r[0]===We)a=r[1];else for(;(i=++u&&i&&i[c]||(a=u=0)||s.pop())&&((y?i.nodeName.toLowerCase()!==f:1!==i.nodeType)||!++a||(d&&((i[qe]||(i[qe]={}))[m]=[We,a]),i!==e)););return(a-=p)===g||a%g==0&&0<=a/g}}},PSEUDO:function(e,i){var t,a=_e.pseudos[e]||_e.setFilters[e.toLowerCase()]||St.error("unsupported pseudo: "+e);return a[qe]?a(i):1<a.length?(t=[e,e,"",i],_e.setFilters.hasOwnProperty(e.toLowerCase())?kt(function(e,t){for(var n,r=a(e,i),o=r.length;o--;)e[n=it.call(e,r[o])]=!(t[n]=r[o])}):function(e){return a(e,0,t)}):a}},pseudos:{not:kt(function(e){var r=[],o=[],u=Oe(e.replace(lt,"$1"));return u[qe]?kt(function(e,t,n,r){for(var o,i=u(e,null,r,[]),a=e.length;a--;)(o=i[a])&&(e[a]=!(t[a]=o))}):function(e,t,n){return r[0]=e,u(r,null,n,o),!o.pop()}}),has:kt(function(t){return function(e){return 0<St(t,e).length}}),contains:kt(function(t){return t=t.replace(Nt,Et),function(e){return-1<(e.textContent||e.innerText||Ae(e)).indexOf(t)}}),lang:kt(function(n){return pt.test(n||"")||St.error("unsupported lang: "+n),n=n.replace(Nt,Et).toLowerCase(),function(e){var t;do{if(t=Ue?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=V.window.location&&V.window.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===ze},focus:function(e){return e===Me.activeElement&&(!Me.hasFocus||Me.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return!1===e.disabled},disabled:function(e){return!0===e.disabled},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!_e.pseudos.empty(e)},header:function(e){return yt.test(e.nodeName)},input:function(e){return vt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:Dt(function(){return[0]}),last:Dt(function(e,t){return[t-1]}),eq:Dt(function(e,t,n){return[n<0?n+t:n]}),even:Dt(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:Dt(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:Dt(function(e,t,n){for(var r=n<0?n+t:n;0<=--r;)e.push(r);return e}),gt:Dt(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=_e.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})_e.pseudos[Te]=At(Te);for(Te in{submit:!0,reset:!0})_e.pseudos[Te]=Rt(Te);function Bt(){}function Pt(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function It(a,e,t){var u=e.dir,s=t&&"parentNode"===u,c=Ke++;return e.first?function(e,t,n){for(;e=e[u];)if(1===e.nodeType||s)return a(e,t,n)}:function(e,t,n){var r,o,i=[We,c];if(n){for(;e=e[u];)if((1===e.nodeType||s)&&a(e,t,n))return!0}else for(;e=e[u];)if(1===e.nodeType||s){if((r=(o=e[qe]||(e[qe]={}))[u])&&r[0]===We&&r[1]===c)return i[2]=r[2];if((o[u]=i)[2]=a(e,t,n))return!0}}}function Lt(o){return 1<o.length?function(e,t,n){for(var r=o.length;r--;)if(!o[r](e,t,n))return!1;return!0}:o[0]}function Ft(e,t,n,r,o){for(var i,a=[],u=0,s=e.length,c=null!=t;u<s;u++)(i=e[u])&&(n&&!n(i,r,o)||(a.push(i),c&&t.push(u)));return a}function Mt(m,g,p,h,v,e){return h&&!h[qe]&&(h=Mt(h)),v&&!v[qe]&&(v=Mt(v,e)),kt(function(e,t,n,r){var o,i,a,u=[],s=[],c=t.length,l=e||function(e,t,n){for(var r=0,o=t.length;r<o;r++)St(e,t[r],n);return n}(g||"*",n.nodeType?[n]:n,[]),f=!m||!e&&g?l:Ft(l,u,m,n,r),d=p?v||(e?m:c||h)?[]:t:f;if(p&&p(f,d,n,r),h)for(o=Ft(d,s),h(o,[],n,r),i=o.length;i--;)(a=o[i])&&(d[s[i]]=!(f[s[i]]=a));if(e){if(v||m){if(v){for(o=[],i=d.length;i--;)(a=d[i])&&o.push(f[i]=a);v(null,d=[],o,r)}for(i=d.length;i--;)(a=d[i])&&-1<(o=v?it.call(e,a):u[i])&&(e[o]=!(t[o]=a))}}else d=Ft(d===t?d.splice(c,d.length):d),v?v(null,t,d,r):rt.apply(t,d)})}function zt(e){for(var r,t,n,o=e.length,i=_e.relative[e[0].type],a=i||_e.relative[" "],u=i?1:0,s=It(function(e){return e===r},a,!0),c=It(function(e){return-1<it.call(r,e)},a,!0),l=[function(e,t,n){return!i&&(n||t!==Pe)||((r=t).nodeType?s(e,t,n):c(e,t,n))}];u<o;u++)if(t=_e.relative[e[u].type])l=[It(Lt(l),t)];else{if((t=_e.filter[e[u].type].apply(null,e[u].matches))[qe]){for(n=++u;n<o&&!_e.relative[e[n].type];n++);return Mt(1<u&&Lt(l),1<u&&Pt(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(lt,"$1"),t,u<n&&zt(e.slice(u,n)),n<o&&zt(e=e.slice(n)),n<o&&Pt(e))}l.push(t)}return Lt(l)}Bt.prototype=_e.filters=_e.pseudos,_e.setFilters=new Bt,De=St.tokenize=function(e,t){var n,r,o,i,a,u,s,c=Ye[e+" "];if(c)return t?0:c.slice(0);for(a=e,u=[],s=_e.preFilter;a;){for(i in n&&!(r=ft.exec(a))||(r&&(a=a.slice(r[0].length)||a),u.push(o=[])),n=!1,(r=dt.exec(a))&&(n=r.shift(),o.push({value:n,type:r[0].replace(lt," ")}),a=a.slice(n.length)),_e.filter)!(r=ht[i].exec(a))||s[i]&&!(r=s[i](r))||(n=r.shift(),o.push({value:n,type:i,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?St.error(e):Ye(e,u).slice(0)},Oe=St.compile=function(e,t){var n,h,v,y,b,r,o=[],i=[],a=Ge[e+" "];if(!a){for(t||(t=De(e)),n=t.length;n--;)(a=zt(t[n]))[qe]?o.push(a):i.push(a);(a=Ge(e,(h=i,y=0<(v=o).length,b=0<h.length,r=function(e,t,n,r,o){var i,a,u,s=0,c="0",l=e&&[],f=[],d=Pe,m=e||b&&_e.find.TAG("*",o),g=We+=null==d?1:Math.random()||.1,p=m.length;for(o&&(Pe=t!==Me&&t);c!==p&&null!=(i=m[c]);c++){if(b&&i){for(a=0;u=h[a++];)if(u(i,t,n)){r.push(i);break}o&&(We=g)}y&&((i=!u&&i)&&s--,e&&l.push(i))}if(s+=c,y&&c!==s){for(a=0;u=v[a++];)u(l,f,t,n);if(e){if(0<s)for(;c--;)l[c]||f[c]||(f[c]=tt.call(r));f=Ft(f)}rt.apply(r,f),o&&!e&&0<f.length&&1<s+v.length&&St.uniqueSort(r)}return o&&(We=g,Pe=d),l},y?kt(r):r))).selector=e}return a},Be=St.select=function(e,t,n,r){var o,i,a,u,s,c="function"==typeof e&&e,l=!r&&De(e=c.selector||e);if(n=n||[],1===l.length){if(2<(i=l[0]=l[0].slice(0)).length&&"ID"===(a=i[0]).type&&ke.getById&&9===t.nodeType&&Ue&&_e.relative[i[1].type]){if(!(t=(_e.find.ID(a.matches[0].replace(Nt,Et),t)||[])[0]))return n;c&&(t=t.parentNode),e=e.slice(i.shift().value.length)}for(o=ht.needsContext.test(e)?0:i.length;o--&&(a=i[o],!_e.relative[u=a.type]);)if((s=_e.find[u])&&(r=s(a.matches[0].replace(Nt,Et),xt.test(i[0].type)&&Ot(t.parentNode)||t))){if(i.splice(o,1),!(e=r.length&&Pt(i)))return rt.apply(n,r),n;break}}return(c||Oe(e,l))(r,t,!Ue,n,xt.test(e)&&Ot(t.parentNode)||t),n},ke.sortStable=qe.split("").sort(Je).join("")===qe,ke.detectDuplicates=!!Le,Fe(),ke.sortDetached=!0;var Ut=Array.isArray,jt=function(e,t,n){var r,o;if(!e)return 0;if(n=n||e,e.length!==undefined){for(r=0,o=e.length;r<o;r++)if(!1===t.call(n,e[r],r,e))return 0}else for(r in e)if(e.hasOwnProperty(r)&&!1===t.call(n,e[r],r,e))return 0;return 1},Vt=function(e,t,n){var r,o;for(r=0,o=e.length;r<o;r++)if(t.call(n,e[r],r,e))return r;return-1},Ht={isArray:Ut,toArray:function(e){var t,n,r=e;if(!Ut(e))for(r=[],t=0,n=e.length;t<n;t++)r[t]=e[t];return r},each:jt,map:function(n,r){var o=[];return jt(n,function(e,t){o.push(r(e,t,n))}),o},filter:function(n,r){var o=[];return jt(n,function(e,t){r&&!r(e,t,n)||o.push(e)}),o},indexOf:function(e,t){var n,r;if(e)for(n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},reduce:function(e,t,n,r){var o=0;for(arguments.length<3&&(n=e[0]);o<e.length;o++)n=t.call(r,n,e[o],o);return n},findIndex:Vt,find:function(e,t,n){var r=Vt(e,t,n);return-1!==r?e[r]:undefined},last:function(e){return e[e.length-1]}},qt=/^\s*|\s*$/g,$t=function(e){return null===e||e===undefined?"":(""+e).replace(qt,"")},Wt=function(e,t){return t?!("array"!==t||!Ht.isArray(e))||typeof e===t:e!==undefined},Kt=function(e,n,r,o){o=o||this,e&&(r&&(e=e[r]),Ht.each(e,function(e,t){if(!1===n.call(o,e,t,r))return!1;Kt(e,n,r,o)}))},Xt={trim:$t,isArray:Ht.isArray,is:Wt,toArray:Ht.toArray,makeMap:function(e,t,n){var r;for(t=t||",","string"==typeof(e=e||[])&&(e=e.split(t)),n=n||{},r=e.length;r--;)n[e[r]]={};return n},each:Ht.each,map:Ht.map,grep:Ht.filter,inArray:Ht.indexOf,hasOwn:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},extend:function(e,t){for(var n,r,o,i=[],a=2;a<arguments.length;a++)i[a-2]=arguments[a];var u,s=arguments;for(n=1,r=s.length;n<r;n++)for(o in t=s[n])t.hasOwnProperty(o)&&(u=t[o])!==undefined&&(e[o]=u);return e},create:function(e,t,n){var r,o,i,a,u,s=this,c=0;if(e=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(e),i=e[3].match(/(^|\.)(\w+)$/i)[2],!(o=s.createNS(e[3].replace(/\.\w+$/,""),n))[i]){if("static"===e[2])return o[i]=t,void(this.onCreate&&this.onCreate(e[2],e[3],o[i]));t[i]||(t[i]=function(){},c=1),o[i]=t[i],s.extend(o[i].prototype,t),e[5]&&(r=s.resolve(e[5]).prototype,a=e[5].match(/\.(\w+)$/i)[1],u=o[i],o[i]=c?function(){return r[a].apply(this,arguments)}:function(){return this.parent=r[a],u.apply(this,arguments)},o[i].prototype[i]=o[i],s.each(r,function(e,t){o[i].prototype[t]=r[t]}),s.each(t,function(e,t){r[t]?o[i].prototype[t]=function(){return this.parent=r[t],e.apply(this,arguments)}:t!==i&&(o[i].prototype[t]=e)})),s.each(t["static"],function(e,t){o[i][t]=e})}},walk:Kt,createNS:function(e,t){var n,r;for(t=t||V.window,e=e.split("."),n=0;n<e.length;n++)t[r=e[n]]||(t[r]={}),t=t[r];return t},resolve:function(e,t){var n,r;for(t=t||V.window,n=0,r=(e=e.split(".")).length;n<r&&(t=t[e[n]]);n++);return t},explode:function(e,t){return!e||Wt(e,"array")?e:Ht.map(e.split(t||","),$t)},_addCacheSuffix:function(e){var t=fe.cacheSuffix;return t&&(e+=(-1===e.indexOf("?")?"?":"&")+t),e}},Yt=V.document,Gt=Array.prototype.push,Jt=Array.prototype.slice,Qt=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,Zt=Se.Event,en=Xt.makeMap("children,contents,next,prev"),tn=function(e){return void 0!==e},nn=function(e){return"string"==typeof e},rn=function(e,t){var n,r,o;for(o=(t=t||Yt).createElement("div"),n=t.createDocumentFragment(),o.innerHTML=e;r=o.firstChild;)n.appendChild(r);return n},on=function(e,t,n,r){var o;if(nn(t))t=rn(t,bn(e[0]));else if(t.length&&!t.nodeType){if(t=gn.makeArray(t),r)for(o=t.length-1;0<=o;o--)on(e,t[o],n,r);else for(o=0;o<t.length;o++)on(e,t[o],n,r);return e}if(t.nodeType)for(o=e.length;o--;)n.call(e[o],t);return e},an=function(e,t){return e&&t&&-1!==(" "+e.className+" ").indexOf(" "+t+" ")},un=function(e,t,n){var r,o;return t=gn(t)[0],e.each(function(){var e=this;n&&r===e.parentNode||(r=e.parentNode,o=t.cloneNode(!1),e.parentNode.insertBefore(o,e)),o.appendChild(e)}),e},sn=Xt.makeMap("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"," "),cn=Xt.makeMap("checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected"," "),ln={"for":"htmlFor","class":"className",readonly:"readOnly"},fn={"float":"cssFloat"},dn={},mn={},gn=function(e,t){return new gn.fn.init(e,t)},pn=/^\s*|\s*$/g,hn=function(e){return null===e||e===undefined?"":(""+e).replace(pn,"")},vn=function(e,t){var n,r,o,i;if(e)if((n=e.length)===undefined){for(r in e)if(e.hasOwnProperty(r)&&(i=e[r],!1===t.call(i,r,i)))break}else for(o=0;o<n&&(i=e[o],!1!==t.call(i,o,i));o++);return e},yn=function(e,n){var r=[];return vn(e,function(e,t){n(t,e)&&r.push(t)}),r},bn=function(e){return e?9===e.nodeType?e:e.ownerDocument:Yt};gn.fn=gn.prototype={constructor:gn,selector:"",context:null,length:0,init:function(e,t){var n,r,o=this;if(!e)return o;if(e.nodeType)return o.context=o[0]=e,o.length=1,o;if(t&&t.nodeType)o.context=t;else{if(t)return gn(e).attr(t);o.context=t=V.document}if(nn(e)){if(!(n="<"===(o.selector=e).charAt(0)&&">"===e.charAt(e.length-1)&&3<=e.length?[null,e,null]:Qt.exec(e)))return gn(t).find(e);if(n[1])for(r=rn(e,bn(t)).firstChild;r;)Gt.call(o,r),r=r.nextSibling;else{if(!(r=bn(t).getElementById(n[2])))return o;if(r.id!==n[2])return o.find(e);o.length=1,o[0]=r}}else this.add(e,!1);return o},toArray:function(){return Xt.toArray(this)},add:function(e,t){var n,r,o=this;if(nn(e))return o.add(gn(e));if(!1!==t)for(n=gn.unique(o.toArray().concat(gn.makeArray(e))),o.length=n.length,r=0;r<n.length;r++)o[r]=n[r];else Gt.apply(o,gn.makeArray(e));return o},attr:function(t,n){var e,r=this;if("object"==typeof t)vn(t,function(e,t){r.attr(e,t)});else{if(!tn(n)){if(r[0]&&1===r[0].nodeType){if((e=dn[t])&&e.get)return e.get(r[0],t);if(cn[t])return r.prop(t)?t:undefined;null===(n=r[0].getAttribute(t,2))&&(n=undefined)}return n}this.each(function(){var e;if(1===this.nodeType){if((e=dn[t])&&e.set)return void e.set(this,n);null===n?this.removeAttribute(t,2):this.setAttribute(t,n,2)}})}return r},removeAttr:function(e){return this.attr(e,null)},prop:function(e,t){var n=this;if("object"==typeof(e=ln[e]||e))vn(e,function(e,t){n.prop(e,t)});else{if(!tn(t))return n[0]&&n[0].nodeType&&e in n[0]?n[0][e]:t;this.each(function(){1===this.nodeType&&(this[e]=t)})}return n},css:function(n,r){var e,o,i=this,t=function(e){return e.replace(/-(\D)/g,function(e,t){return t.toUpperCase()})},a=function(e){return e.replace(/[A-Z]/g,function(e){return"-"+e})};if("object"==typeof n)vn(n,function(e,t){i.css(e,t)});else if(tn(r))n=t(n),"number"!=typeof r||sn[n]||(r=r.toString()+"px"),i.each(function(){var e=this.style;if((o=mn[n])&&o.set)o.set(this,r);else{try{this.style[fn[n]||n]=r}catch(t){}null!==r&&""!==r||(e.removeProperty?e.removeProperty(a(n)):e.removeAttribute(n))}});else{if(e=i[0],(o=mn[n])&&o.get)return o.get(e);if(!e.ownerDocument.defaultView)return e.currentStyle?e.currentStyle[t(n)]:"";try{return e.ownerDocument.defaultView.getComputedStyle(e,null).getPropertyValue(a(n))}catch(u){return undefined}}return i},remove:function(){for(var e,t=this.length;t--;)e=this[t],Zt.clean(e),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var e,t=this.length;t--;)for(e=this[t];e.firstChild;)e.removeChild(e.firstChild);return this},html:function(e){var t,n=this;if(tn(e)){t=n.length;try{for(;t--;)n[t].innerHTML=e}catch(r){gn(n[t]).empty().append(e)}return n}return n[0]?n[0].innerHTML:""},text:function(e){var t,n=this;if(tn(e)){for(t=n.length;t--;)"innerText"in n[t]?n[t].innerText=e:n[0].textContent=e;return n}return n[0]?n[0].innerText||n[0].textContent:""},append:function(){return on(this,arguments,function(e){(1===this.nodeType||this.host&&1===this.host.nodeType)&&this.appendChild(e)})},prepend:function(){return on(this,arguments,function(e){(1===this.nodeType||this.host&&1===this.host.nodeType)&&this.insertBefore(e,this.firstChild)},!0)},before:function(){return this[0]&&this[0].parentNode?on(this,arguments,function(e){this.parentNode.insertBefore(e,this)}):this},after:function(){return this[0]&&this[0].parentNode?on(this,arguments,function(e){this.parentNode.insertBefore(e,this.nextSibling)},!0):this},appendTo:function(e){return gn(e).append(this),this},prependTo:function(e){return gn(e).prepend(this),this},replaceWith:function(e){return this.before(e).remove()},wrap:function(e){return un(this,e)},wrapAll:function(e){return un(this,e,!0)},wrapInner:function(e){return this.each(function(){gn(this).contents().wrapAll(e)}),this},unwrap:function(){return this.parent().each(function(){gn(this).replaceWith(this.childNodes)})},clone:function(){var e=[];return this.each(function(){e.push(this.cloneNode(!0))}),gn(e)},addClass:function(e){return this.toggleClass(e,!0)},removeClass:function(e){return this.toggleClass(e,!1)},toggleClass:function(o,i){var e=this;return"string"!=typeof o||(-1!==o.indexOf(" ")?vn(o.split(" "),function(){e.toggleClass(this,i)}):e.each(function(e,t){var n,r;(r=an(t,o))!==i&&(n=t.className,r?t.className=hn((" "+n+" ").replace(" "+o+" "," ")):t.className+=n?" "+o:o)})),e},hasClass:function(e){return an(this[0],e)},each:function(e){return vn(this,e)},on:function(e,t){return this.each(function(){Zt.bind(this,e,t)})},off:function(e,t){return this.each(function(){Zt.unbind(this,e,t)})},trigger:function(e){return this.each(function(){"object"==typeof e?Zt.fire(this,e.type,e):Zt.fire(this,e)})},show:function(){return this.css("display","")},hide:function(){return this.css("display","none")},slice:function(){return new gn(Jt.apply(this,arguments))},eq:function(e){return-1===e?this.slice(e):this.slice(e,+e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},find:function(e){var t,n,r=[];for(t=0,n=this.length;t<n;t++)gn.find(e,this[t],r);return gn(r)},filter:function(n){return gn("function"==typeof n?yn(this.toArray(),function(e,t){return n(t,e)}):gn.filter(n,this.toArray()))},closest:function(n){var r=[];return n instanceof gn&&(n=n[0]),this.each(function(e,t){for(;t;){if("string"==typeof n&&gn(t).is(n)){r.push(t);break}if(t===n){r.push(t);break}t=t.parentNode}}),gn(r)},offset:function(e){var t,n,r,o,i=0,a=0;return e?this.css(e):((t=this[0])&&(r=(n=t.ownerDocument).documentElement,t.getBoundingClientRect&&(i=(o=t.getBoundingClientRect()).left+(r.scrollLeft||n.body.scrollLeft)-r.clientLeft,a=o.top+(r.scrollTop||n.body.scrollTop)-r.clientTop)),{left:i,top:a})},push:Gt,sort:[].sort,splice:[].splice},Xt.extend(gn,{extend:Xt.extend,makeArray:function(e){return(t=e)&&t===t.window||e.nodeType?[e]:Xt.toArray(e);var t},inArray:function(e,t){var n;if(t.indexOf)return t.indexOf(e);for(n=t.length;n--;)if(t[n]===e)return n;return-1},isArray:Xt.isArray,each:vn,trim:hn,grep:yn,find:St,expr:St.selectors,unique:St.uniqueSort,text:St.getText,contains:St.contains,filter:function(e,t,n){var r=t.length;for(n&&(e=":not("+e+")");r--;)1!==t[r].nodeType&&t.splice(r,1);return t=1===t.length?gn.find.matchesSelector(t[0],e)?[t[0]]:[]:gn.find.matches(e,t)}});var Cn=function(e,t,n){var r=[],o=e[t];for("string"!=typeof n&&n instanceof gn&&(n=n[0]);o&&9!==o.nodeType;){if(n!==undefined){if(o===n)break;if("string"==typeof n&&gn(o).is(n))break}1===o.nodeType&&r.push(o),o=o[t]}return r},xn=function(e,t,n,r){var o=[];for(r instanceof gn&&(r=r[0]);e;e=e[t])if(!n||e.nodeType===n){if(r!==undefined){if(e===r)break;if("string"==typeof r&&gn(e).is(r))break}o.push(e)}return o},wn=function(e,t,n){for(e=e[t];e;e=e[t])if(e.nodeType===n)return e;return null};vn({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return Cn(e,"parentNode")},next:function(e){return wn(e,"nextSibling",1)},prev:function(e){return wn(e,"previousSibling",1)},children:function(e){return xn(e.firstChild,"nextSibling",1)},contents:function(e){return Xt.toArray(("iframe"===e.nodeName?e.contentDocument||e.contentWindow.document:e).childNodes)}},function(e,r){gn.fn[e]=function(t){var n=[];return this.each(function(){var e=r.call(n,this,t,n);e&&(gn.isArray(e)?n.push.apply(n,e):n.push(e))}),1<this.length&&(en[e]||(n=gn.unique(n)),0===e.indexOf("parents")&&(n=n.reverse())),n=gn(n),t?n.filter(t):n}}),vn({parentsUntil:function(e,t){return Cn(e,"parentNode",t)},nextUntil:function(e,t){return xn(e,"nextSibling",1,t).slice(1)},prevUntil:function(e,t){return xn(e,"previousSibling",1,t).slice(1)}},function(r,o){gn.fn[r]=function(t,e){var n=[];return this.each(function(){var e=o.call(n,this,t,n);e&&(gn.isArray(e)?n.push.apply(n,e):n.push(e))}),1<this.length&&(n=gn.unique(n),0!==r.indexOf("parents")&&"prevUntil"!==r||(n=n.reverse())),n=gn(n),e?n.filter(e):n}}),gn.fn.is=function(e){return!!e&&0<this.filter(e).length},gn.fn.init.prototype=gn.fn,gn.overrideDefaults=function(n){var r,o=function(e,t){return r=r||n(),0===arguments.length&&(e=r.element),t||(t=r.context),new o.fn.init(e,t)};return gn.extend(o,this),o};var Nn=function(n,r,e){vn(e,function(e,t){n[e]=n[e]||{},n[e][r]=t})};fe.ie&&fe.ie<8&&(Nn(dn,"get",{maxlength:function(e){var t=e.maxLength;return 2147483647===t?undefined:t},size:function(e){var t=e.size;return 20===t?undefined:t},"class":function(e){return e.className},style:function(e){var t=e.style.cssText;return 0===t.length?undefined:t}}),Nn(dn,"set",{"class":function(e,t){e.className=t},style:function(e,t){e.style.cssText=t}})),fe.ie&&fe.ie<9&&(fn["float"]="styleFloat",Nn(mn,"set",{opacity:function(e,t){var n=e.style;null===t||""===t?n.removeAttribute("filter"):(n.zoom=1,n.filter="alpha(opacity="+100*t+")")}})),gn.attrHooks=dn,gn.cssHooks=mn;var En,Sn,Tn,kn,_n,An,Rn,Dn=function(e,t){var n=function(e,t){for(var n=0;n<e.length;n++){var r=e[n];if(r.test(t))return r}return undefined}(e,t);if(!n)return{major:0,minor:0};var r=function(e){return Number(t.replace(n,"$"+e))};return Bn(r(1),r(2))},On=function(){return Bn(0,0)},Bn=function(e,t){return{major:e,minor:t}},Pn={nu:Bn,detect:function(e,t){var n=String(t).toLowerCase();return 0===e.length?On():Dn(e,n)},unknown:On},In="Firefox",Ln=function(e,t){return function(){return t===e}},Fn=function(e){var t=e.current;return{current:t,version:e.version,isEdge:Ln("Edge",t),isChrome:Ln("Chrome",t),isIE:Ln("IE",t),isOpera:Ln("Opera",t),isFirefox:Ln(In,t),isSafari:Ln("Safari",t)}},Mn={unknown:function(){return Fn({current:undefined,version:Pn.unknown()})},nu:Fn,edge:q("Edge"),chrome:q("Chrome"),ie:q("IE"),opera:q("Opera"),firefox:q(In),safari:q("Safari")},zn="Windows",Un="Android",jn="Solaris",Vn="FreeBSD",Hn=function(e,t){return function(){return t===e}},qn=function(e){var t=e.current;return{current:t,version:e.version,isWindows:Hn(zn,t),isiOS:Hn("iOS",t),isAndroid:Hn(Un,t),isOSX:Hn("OSX",t),isLinux:Hn("Linux",t),isSolaris:Hn(jn,t),isFreeBSD:Hn(Vn,t)}},$n={unknown:function(){return qn({current:undefined,version:Pn.unknown()})},nu:qn,windows:q(zn),ios:q("iOS"),android:q(Un),linux:q("Linux"),osx:q("OSX"),solaris:q(jn),freebsd:q(Vn)},Wn=function(e,t){var n=String(t).toLowerCase();return X(e,function(e){return e.search(n)})},Kn=function(e,n){return Wn(e,n).map(function(e){var t=Pn.detect(e.versionRegexes,n);return{current:e.name,version:t}})},Xn=function(e,n){return Wn(e,n).map(function(e){var t=Pn.detect(e.versionRegexes,n);return{current:e.name,version:t}})},Yn=function(e,t){return-1!==e.indexOf(t)},Gn=function(e){return e.replace(/^\s+|\s+$/g,"")},Jn=function(e){return e.replace(/\s+$/g,"")},Qn=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,Zn=function(t){return function(e){return Yn(e,t)}},er=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:function(e){return Yn(e,"edge/")&&Yn(e,"chrome")&&Yn(e,"safari")&&Yn(e,"applewebkit")}},{name:"Chrome",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,Qn],search:function(e){return Yn(e,"chrome")&&!Yn(e,"chromeframe")}},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:function(e){return Yn(e,"msie")||Yn(e,"trident")}},{name:"Opera",versionRegexes:[Qn,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:Zn("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:Zn("firefox")},{name:"Safari",versionRegexes:[Qn,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:function(e){return(Yn(e,"safari")||Yn(e,"mobile/"))&&Yn(e,"applewebkit")}}],tr=[{name:"Windows",search:Zn("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:function(e){return Yn(e,"iphone")||Yn(e,"ipad")},versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:Zn("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"OSX",search:Zn("os x"),versionRegexes:[/.*?os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:Zn("linux"),versionRegexes:[]},{name:"Solaris",search:Zn("sunos"),versionRegexes:[]},{name:"FreeBSD",search:Zn("freebsd"),versionRegexes:[]}],nr={browsers:q(er),oses:q(tr)},rr=function(e){var t,n,r,o,i,a,u,s,c,l,f,d=nr.browsers(),m=nr.oses(),g=Kn(d,e).fold(Mn.unknown,Mn.nu),p=Xn(m,e).fold($n.unknown,$n.nu);return{browser:g,os:p,deviceType:(n=g,r=e,o=(t=p).isiOS()&&!0===/ipad/i.test(r),i=t.isiOS()&&!o,a=t.isAndroid()&&3===t.version.major,u=t.isAndroid()&&4===t.version.major,s=o||a||u&&!0===/mobile/i.test(r),c=t.isiOS()||t.isAndroid(),l=c&&!s,f=n.isSafari()&&t.isiOS()&&!1===/safari/i.test(r),{isiPad:q(o),isiPhone:q(i),isTablet:q(s),isPhone:q(l),isTouch:q(c),isAndroid:t.isAndroid,isiOS:t.isiOS,isWebView:q(f)})}},or={detect:(En=function(){var e=V.navigator.userAgent;return rr(e)},Tn=!1,function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];return Tn||(Tn=!0,Sn=En.apply(null,e)),Sn})},ir=function(e){if(null===e||e===undefined)throw new Error("Node cannot be null or undefined");return{dom:q(e)}},ar={fromHtml:function(e,t){var n=(t||V.document).createElement("div");if(n.innerHTML=e,!n.hasChildNodes()||1<n.childNodes.length)throw V.console.error("HTML does not have a single root node",e),new Error("HTML must have a single root node");return ir(n.childNodes[0])},fromTag:function(e,t){var n=(t||V.document).createElement(e);return ir(n)},fromText:function(e,t){var n=(t||V.document).createTextNode(e);return ir(n)},fromDom:ir,fromPoint:function(e,t,n){var r=e.dom();return _.from(r.elementFromPoint(t,n)).map(ir)}},ur=(V.Node.ATTRIBUTE_NODE,V.Node.CDATA_SECTION_NODE,V.Node.COMMENT_NODE,V.Node.DOCUMENT_NODE),sr=(V.Node.DOCUMENT_TYPE_NODE,V.Node.DOCUMENT_FRAGMENT_NODE,V.Node.ELEMENT_NODE),cr=V.Node.TEXT_NODE,lr=(V.Node.PROCESSING_INSTRUCTION_NODE,V.Node.ENTITY_REFERENCE_NODE,V.Node.ENTITY_NODE,V.Node.NOTATION_NODE,function(e){return e.dom().nodeName.toLowerCase()}),fr=function(t){return function(e){return e.dom().nodeType===t}},dr=fr(sr),mr=fr(cr),gr=Object.keys,pr=Object.hasOwnProperty,hr=function(e,t){for(var n=gr(e),r=0,o=n.length;r<o;r++){var i=n[r];t(e[i],i)}},vr=function(e,r){var o={};return hr(e,function(e,t){var n=r(e,t);o[n.k]=n.v}),o},yr=function(e,n){var r={},o={};return hr(e,function(e,t){(n(e,t)?r:o)[t]=e}),{t:r,f:o}},br=function(e){return e.style!==undefined&&D(e.style.getPropertyValue)},Cr=function(e,t,n){if(!(S(n)||R(n)||O(n)))throw V.console.error("Invalid call to Attr.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")},xr=function(e,t,n){Cr(e.dom(),t,n)},wr=function(e,t){var n=e.dom();hr(t,function(e,t){Cr(n,t,e)})},Nr=function(e,t){var n=e.dom().getAttribute(t);return null===n?undefined:n},Er=function(e,t){e.dom().removeAttribute(t)},Sr=function(e,t){var n=e.dom();hr(t,function(e,t){!function(e,t,n){if(!S(n))throw V.console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);br(e)&&e.style.setProperty(t,n)}(n,t,e)})},Tr=function(e,t){var n,r,o=e.dom(),i=V.window.getComputedStyle(o).getPropertyValue(t),a=""!==i||(r=mr(n=e)?n.dom().parentNode:n.dom())!==undefined&&null!==r&&r.ownerDocument.body.contains(r)?i:kr(o,t);return null===a?undefined:a},kr=function(e,t){return br(e)?e.style.getPropertyValue(t):""},_r=function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];return function(){for(var n=[],e=0;e<arguments.length;e++)n[e]=arguments[e];if(t.length!==n.length)throw new Error('Wrong number of arguments to struct. Expected "['+t.length+']", got '+n.length+" arguments");var r={};return z(t,function(e,t){r[e]=q(n[t])}),r}},Ar=function(e,t){for(var n=[],r=function(e){return n.push(e),t(e)},o=t(e);(o=o.bind(r)).isSome(););return n},Rr=function(){return oe.getOrDie("Node")},Dr=function(e,t,n){return 0!=(e.compareDocumentPosition(t)&n)},Or=function(e,t){return Dr(e,t,Rr().DOCUMENT_POSITION_CONTAINED_BY)},Br=sr,Pr=ur,Ir=function(e,t){var n=e.dom();if(n.nodeType!==Br)return!1;var r=n;if(r.matches!==undefined)return r.matches(t);if(r.msMatchesSelector!==undefined)return r.msMatchesSelector(t);if(r.webkitMatchesSelector!==undefined)return r.webkitMatchesSelector(t);if(r.mozMatchesSelector!==undefined)return r.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")},Lr=function(e){return e.nodeType!==Br&&e.nodeType!==Pr||0===e.childElementCount},Fr=function(e,t){return e.dom()===t.dom()},Mr=or.detect().browser.isIE()?function(e,t){return Or(e.dom(),t.dom())}:function(e,t){var n=e.dom(),r=t.dom();return n!==r&&n.contains(r)},zr=function(e){return ar.fromDom(e.dom().ownerDocument)},Ur=function(e){return ar.fromDom(e.dom().ownerDocument.defaultView)},jr=function(e){return _.from(e.dom().parentNode).map(ar.fromDom)},Vr=function(e){return _.from(e.dom().previousSibling).map(ar.fromDom)},Hr=function(e){return _.from(e.dom().nextSibling).map(ar.fromDom)},qr=function(e){return t=Ar(e,Vr),(n=B.call(t,0)).reverse(),n;var t,n},$r=function(e){return Ar(e,Hr)},Wr=function(e){return W(e.dom().childNodes,ar.fromDom)},Kr=function(e,t){var n=e.dom().childNodes;return _.from(n[t]).map(ar.fromDom)},Xr=function(e){return Kr(e,0)},Yr=function(e){return Kr(e,e.dom().childNodes.length-1)},Gr=(_r("element","offset"),or.detect().browser),Jr=function(e){return X(e,dr)},Qr={getPos:function(e,t,n){var r,o,i,a=0,u=0,s=e.ownerDocument;if(n=n||e,t){if(n===e&&t.getBoundingClientRect&&"static"===Tr(ar.fromDom(e),"position"))return{x:a=(o=t.getBoundingClientRect()).left+(s.documentElement.scrollLeft||e.scrollLeft)-s.documentElement.clientLeft,y:u=o.top+(s.documentElement.scrollTop||e.scrollTop)-s.documentElement.clientTop};for(r=t;r&&r!==n&&r.nodeType;)a+=r.offsetLeft||0,u+=r.offsetTop||0,r=r.offsetParent;for(r=t.parentNode;r&&r!==n&&r.nodeType;)a-=r.scrollLeft||0,u-=r.scrollTop||0,r=r.parentNode;u+=(i=ar.fromDom(t),Gr.isFirefox()&&"table"===lr(i)?Jr(Wr(i)).filter(function(e){return"caption"===lr(e)}).bind(function(o){return Jr($r(o)).map(function(e){var t=e.dom().offsetTop,n=o.dom().offsetTop,r=o.dom().offsetHeight;return t<=n?-r:0})}).getOr(0):0)}return{x:a,y:u}}},Zr={},eo={exports:Zr};kn=undefined,_n=Zr,An=eo,Rn=undefined,function(e){"object"==typeof _n&&void 0!==An?An.exports=e():"function"==typeof kn&&kn.amd?kn([],e):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).EphoxContactWrapper=e()}(function(){return function i(a,u,s){function c(t,e){if(!u[t]){if(!a[t]){var n="function"==typeof Rn&&Rn;if(!e&&n)return n(t,!0);if(l)return l(t,!0);var r=new Error("Cannot find module '"+t+"'");throw r.code="MODULE_NOT_FOUND",r}var o=u[t]={exports:{}};a[t][0].call(o.exports,function(e){return c(a[t][1][e]||e)},o,o.exports,i,a,u,s)}return u[t].exports}for(var l="function"==typeof Rn&&Rn,e=0;e<s.length;e++)c(s[e]);return c}({1:[function(e,t,n){var r,o,i=t.exports={};function a(){throw new Error("setTimeout has not been defined")}function u(){throw new Error("clearTimeout has not been defined")}function s(e){if(r===setTimeout)return setTimeout(e,0);if((r===a||!r)&&setTimeout)return r=setTimeout,setTimeout(e,0);try{return r(e,0)}catch(oE){try{return r.call(null,e,0)}catch(oE){return r.call(this,e,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:a}catch(oE){r=a}try{o="function"==typeof clearTimeout?clearTimeout:u}catch(oE){o=u}}();var c,l=[],f=!1,d=-1;function m(){f&&c&&(f=!1,c.length?l=c.concat(l):d=-1,l.length&&g())}function g(){if(!f){var e=s(m);f=!0;for(var t=l.length;t;){for(c=l,l=[];++d<t;)c&&c[d].run();d=-1,t=l.length}c=null,f=!1,function(e){if(o===clearTimeout)return clearTimeout(e);if((o===u||!o)&&clearTimeout)return o=clearTimeout,clearTimeout(e);try{o(e)}catch(oE){try{return o.call(null,e)}catch(oE){return o.call(this,e)}}}(e)}}function p(e,t){this.fun=e,this.array=t}function h(){}i.nextTick=function(e){var t=new Array(arguments.length-1);if(1<arguments.length)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];l.push(new p(e,t)),1!==l.length||f||s(g)},p.prototype.run=function(){this.fun.apply(null,this.array)},i.title="browser",i.browser=!0,i.env={},i.argv=[],i.version="",i.versions={},i.on=h,i.addListener=h,i.once=h,i.off=h,i.removeListener=h,i.removeAllListeners=h,i.emit=h,i.prependListener=h,i.prependOnceListener=h,i.listeners=function(e){return[]},i.binding=function(e){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(e){throw new Error("process.chdir is not supported")},i.umask=function(){return 0}},{}],2:[function(e,f,t){(function(n){!function(e){var t=setTimeout;function r(){}function i(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=undefined,this._deferreds=[],l(e,this)}function o(n,r){for(;3===n._state;)n=n._value;0!==n._state?(n._handled=!0,i._immediateFn(function(){var e=1===n._state?r.onFulfilled:r.onRejected;if(null!==e){var t;try{t=e(n._value)}catch(oE){return void u(r.promise,oE)}a(r.promise,t)}else(1===n._state?a:u)(r.promise,n._value)})):n._deferreds.push(r)}function a(e,t){try{if(t===e)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if(t instanceof i)return e._state=3,e._value=t,void s(e);if("function"==typeof n)return void l((r=n,o=t,function(){r.apply(o,arguments)}),e)}e._state=1,e._value=t,s(e)}catch(oE){u(e,oE)}var r,o}function u(e,t){e._state=2,e._value=t,s(e)}function s(e){2===e._state&&0===e._deferreds.length&&i._immediateFn(function(){e._handled||i._unhandledRejectionFn(e._value)});for(var t=0,n=e._deferreds.length;t<n;t++)o(e,e._deferreds[t]);e._deferreds=null}function c(e,t,n){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.promise=n}function l(e,t){var n=!1;try{e(function(e){n||(n=!0,a(t,e))},function(e){n||(n=!0,u(t,e))})}catch(r){if(n)return;n=!0,u(t,r)}}i.prototype["catch"]=function(e){return this.then(null,e)},i.prototype.then=function(e,t){var n=new this.constructor(r);return o(this,new c(e,t,n)),n},i.all=function(e){var s=Array.prototype.slice.call(e);return new i(function(o,i){if(0===s.length)return o([]);var a=s.length;function u(t,e){try{if(e&&("object"==typeof e||"function"==typeof e)){var n=e.then;if("function"==typeof n)return void n.call(e,function(e){u(t,e)},i)}s[t]=e,0==--a&&o(s)}catch(r){i(r)}}for(var e=0;e<s.length;e++)u(e,s[e])})},i.resolve=function(t){return t&&"object"==typeof t&&t.constructor===i?t:new i(function(e){e(t)})},i.reject=function(n){return new i(function(e,t){t(n)})},i.race=function(o){return new i(function(e,t){for(var n=0,r=o.length;n<r;n++)o[n].then(e,t)})},i._immediateFn="function"==typeof n?function(e){n(e)}:function(e){t(e,0)},i._unhandledRejectionFn=function(e){"undefined"!=typeof console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)},i._setImmediateFn=function(e){i._immediateFn=e},i._setUnhandledRejectionFn=function(e){i._unhandledRejectionFn=e},void 0!==f&&f.exports?f.exports=i:e.Promise||(e.Promise=i)}(this)}).call(this,e("timers").setImmediate)},{timers:3}],3:[function(s,e,c){(function(e,t){var r=s("process/browser.js").nextTick,n=Function.prototype.apply,o=Array.prototype.slice,i={},a=0;function u(e,t){this._id=e,this._clearFn=t}c.setTimeout=function(){return new u(n.call(setTimeout,window,arguments),clearTimeout)},c.setInterval=function(){return new u(n.call(setInterval,window,arguments),clearInterval)},c.clearTimeout=c.clearInterval=function(e){e.close()},u.prototype.unref=u.prototype.ref=function(){},u.prototype.close=function(){this._clearFn.call(window,this._id)},c.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},c.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},c._unrefActive=c.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;0<=t&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},c.setImmediate="function"==typeof e?e:function(e){var t=a++,n=!(arguments.length<2)&&o.call(arguments,1);return i[t]=!0,r(function(){i[t]&&(n?e.apply(null,n):e.call(null),c.clearImmediate(t))}),t},c.clearImmediate="function"==typeof t?t:function(e){delete i[e]}}).call(this,s("timers").setImmediate,s("timers").clearImmediate)},{"process/browser.js":1,timers:3}],4:[function(e,t,n){var r=e("promise-polyfill"),o="undefined"!=typeof window?window:Function("return this;")();t.exports={boltExport:o.Promise||r}},{"promise-polyfill":2}]},{},[4])(4)});var to=eo.exports.boltExport,no=function(e){var n=_.none(),t=[],r=function(e){o()?a(e):t.push(e)},o=function(){return n.isSome()},i=function(e){z(e,a)},a=function(t){n.each(function(e){V.setTimeout(function(){t(e)},0)})};return e(function(e){n=_.some(e),i(t),t=[]}),{get:r,map:function(n){return no(function(t){r(function(e){t(n(e))})})},isReady:o}},ro={nu:no,pure:function(t){return no(function(e){e(t)})}},oo=function(e){V.setTimeout(function(){throw e},0)},io=function(n){var e=function(e){n().then(e,oo)};return{map:function(e){return io(function(){return n().then(e)})},bind:function(t){return io(function(){return n().then(function(e){return t(e).toPromise()})})},anonBind:function(e){return io(function(){return n().then(function(){return e.toPromise()})})},toLazy:function(){return ro.nu(e)},toCached:function(){var e=null;return io(function(){return null===e&&(e=n()),e})},toPromise:n,get:e}},ao={nu:function(e){return io(function(){return new to(e)})},pure:function(e){return io(function(){return to.resolve(e)})}},uo=function(a,e){return e(function(r){var o=[],i=0;0===a.length?r([]):z(a,function(e,t){var n;e.get((n=t,function(e){o[n]=e,++i>=a.length&&r(o)}))})})},so=function(e){return uo(e,ao.nu)},co=function(n){return{is:function(e){return n===e},isValue:C,isError:b,getOr:q(n),getOrThunk:q(n),getOrDie:q(n),or:function(e){return co(n)},orThunk:function(e){return co(n)},fold:function(e,t){return t(n)},map:function(e){return co(e(n))},mapError:function(e){return co(n)},each:function(e){e(n)},bind:function(e){return e(n)},exists:function(e){return e(n)},forall:function(e){return e(n)},toOption:function(){return _.some(n)}}},lo=function(n){return{is:b,isValue:b,isError:C,getOr:$,getOrThunk:function(e){return e()},getOrDie:function(){return e=String(n),function(){throw new Error(e)}();var e},or:function(e){return e},orThunk:function(e){return e()},fold:function(e,t){return e(n)},map:function(e){return lo(n)},mapError:function(e){return lo(e(n))},each:o,bind:function(e){return lo(n)},exists:b,forall:C,toOption:_.none}},fo={value:co,error:lo,fromOption:function(e,t){return e.fold(function(){return lo(t)},co)}};function mo(e,u){var t=e,n=function(e,t,n,r){var o,i;if(e){if(!r&&e[t])return e[t];if(e!==u){if(o=e[n])return o;for(i=e.parentNode;i&&i!==u;i=i.parentNode)if(o=i[n])return o}}};this.current=function(){return t},this.next=function(e){return t=n(t,"firstChild","nextSibling",e)},this.prev=function(e){return t=n(t,"lastChild","previousSibling",e)},this.prev2=function(e){return t=function(e,t,n,r){var o,i,a;if(e){if(o=e[n],u&&o===u)return;if(o){if(!r)for(a=o[t];a;a=a[t])if(!a[t])return a;return o}if((i=e.parentNode)&&i!==u)return i}}(t,"lastChild","previousSibling",e)}}var go,po,ho,vo=function(t){var n;return function(e){return(n=n||function(e,t){for(var n={},r=0,o=e.length;r<o;r++){var i=e[r];n[String(i)]=t(i,r)}return n}(t,q(!0))).hasOwnProperty(lr(e))}},yo=vo(["h1","h2","h3","h4","h5","h6"]),bo=vo(["article","aside","details","div","dt","figcaption","footer","form","fieldset","header","hgroup","html","main","nav","section","summary","body","p","dl","multicol","dd","figure","address","center","blockquote","h1","h2","h3","h4","h5","h6","listing","xmp","pre","plaintext","menu","dir","ul","ol","li","hr","table","tbody","thead","tfoot","th","tr","td","caption"]),Co=function(e){return dr(e)&&!bo(e)},xo=function(e){return dr(e)&&"br"===lr(e)},wo=vo(["h1","h2","h3","h4","h5","h6","p","div","address","pre","form","blockquote","center","dir","fieldset","header","footer","article","section","hgroup","aside","nav","figure"]),No=vo(["ul","ol","dl"]),Eo=vo(["li","dd","dt"]),So=vo(["area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param","embed","source","wbr","track"]),To=vo(["thead","tbody","tfoot"]),ko=vo(["td","th"]),_o=vo(["pre","script","textarea","style"]),Ao=function(t){return function(e){return!!e&&e.nodeType===t}},Ro=Ao(1),Do=function(e){var r=e.toLowerCase().split(" ");return function(e){var t,n;if(e&&e.nodeType)for(n=e.nodeName.toLowerCase(),t=0;t<r.length;t++)if(n===r[t])return!0;return!1}},Oo=function(t){return function(e){if(Ro(e)){if(e.contentEditable===t)return!0;if(e.getAttribute("data-mce-contenteditable")===t)return!0}return!1}},Bo=Ao(3),Po=Ao(8),Io=Ao(9),Lo=Ao(11),Fo=Do("br"),Mo=Oo("true"),zo=Oo("false"),Uo={isText:Bo,isElement:Ro,isComment:Po,isDocument:Io,isDocumentFragment:Lo,isBr:Fo,isContentEditableTrue:Mo,isContentEditableFalse:zo,isRestrictedNode:function(e){return!!e&&!Object.getPrototypeOf(e)},matchNodeNames:Do,hasPropValue:function(t,n){return function(e){return Ro(e)&&e[t]===n}},hasAttribute:function(t,e){return function(e){return Ro(e)&&e.hasAttribute(t)}},hasAttributeValue:function(t,n){return function(e){return Ro(e)&&e.getAttribute(t)===n}},matchStyleValues:function(r,e){var o=e.toLowerCase().split(" ");return function(e){var t;if(Ro(e))for(t=0;t<o.length;t++){var n=e.ownerDocument.defaultView.getComputedStyle(e,null);if((n?n.getPropertyValue(r):null)===o[t])return!0}return!1}},isBogus:function(e){return Ro(e)&&e.hasAttribute("data-mce-bogus")},isBogusAll:function(e){return Ro(e)&&"all"===e.getAttribute("data-mce-bogus")},isTable:function(e){return Ro(e)&&"TABLE"===e.tagName}},jo=function(e){return e&&"SPAN"===e.tagName&&"bookmark"===e.getAttribute("data-mce-type")},Vo=function(e,t){var n,r=t.childNodes;if(!Uo.isElement(t)||!jo(t)){for(n=r.length-1;0<=n;n--)Vo(e,r[n]);if(!1===Uo.isDocument(t)){if(Uo.isText(t)&&0<t.nodeValue.length){var o=Xt.trim(t.nodeValue).length;if(e.isBlock(t.parentNode)||0<o)return;if(0===o&&(a=(i=t).previousSibling&&"SPAN"===i.previousSibling.nodeName,u=i.nextSibling&&"SPAN"===i.nextSibling.nodeName,a&&u))return}else if(Uo.isElement(t)&&(1===(r=t.childNodes).length&&jo(r[0])&&t.parentNode.insertBefore(r[0],t),r.length||So(ar.fromDom(t))))return;e.remove(t)}var i,a,u;return t}},Ho={trimNode:Vo},qo=Xt.makeMap,$o=/[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,Wo=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,Ko=/[<>&\"\']/g,Xo=/&#([a-z0-9]+);?|&([a-z0-9]+);/gi,Yo={128:"\u20ac",130:"\u201a",131:"\u0192",132:"\u201e",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02c6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017d",145:"\u2018",146:"\u2019",147:"\u201c",148:"\u201d",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02dc",153:"\u2122",154:"\u0161",155:"\u203a",156:"\u0153",158:"\u017e",159:"\u0178"};po={'"':"&quot;","'":"&#39;","<":"&lt;",">":"&gt;","&":"&amp;","`":"&#96;"},ho={"&lt;":"<","&gt;":">","&amp;":"&","&quot;":'"',"&apos;":"'"};var Go=function(e,t){var n,r,o,i={};if(e){for(e=e.split(","),t=t||10,n=0;n<e.length;n+=2)r=String.fromCharCode(parseInt(e[n],t)),po[r]||(o="&"+e[n+1]+";",i[r]=o,i[o]=r);return i}};go=Go("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);var Jo=function(e,t){return e.replace(t?$o:Wo,function(e){return po[e]||e})},Qo=function(e,t){return e.replace(t?$o:Wo,function(e){return 1<e.length?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":po[e]||"&#"+e.charCodeAt(0)+";"})},Zo=function(e,t,n){return n=n||go,e.replace(t?$o:Wo,function(e){return po[e]||n[e]||e})},ei={encodeRaw:Jo,encodeAllRaw:function(e){return(""+e).replace(Ko,function(e){return po[e]||e})},encodeNumeric:Qo,encodeNamed:Zo,getEncodeFunc:function(e,t){var n=Go(t)||go,r=qo(e.replace(/\+/g,","));return r.named&&r.numeric?function(e,t){return e.replace(t?$o:Wo,function(e){return po[e]!==undefined?po[e]:n[e]!==undefined?n[e]:1<e.length?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":"&#"+e.charCodeAt(0)+";"})}:r.named?t?function(e,t){return Zo(e,t,n)}:Zo:r.numeric?Qo:Jo},decode:function(e){return e.replace(Xo,function(e,t){return t?65535<(t="x"===t.charAt(0).toLowerCase()?parseInt(t.substr(1),16):parseInt(t,10))?(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t))):Yo[t]||String.fromCharCode(t):ho[e]||go[e]||(n=e,(r=ar.fromTag("div").dom()).innerHTML=n,r.textContent||r.innerText||n);var n,r})}},ti={},ni={},ri=Xt.makeMap,oi=Xt.each,ii=Xt.extend,ai=Xt.explode,ui=Xt.inArray,si=function(e,t){return(e=Xt.trim(e))?e.split(t||" "):[]},ci=function(e){var u,t,n,r,o,i,s={},a=function(e,t,n){var r,o,i,a=function(e,t){var n,r,o={};for(n=0,r=e.length;n<r;n++)o[e[n]]=t||{};return o};for(t=t||"","string"==typeof(n=n||[])&&(n=si(n)),r=(e=si(e)).length;r--;)i={attributes:a(o=si([u,t].join(" "))),attributesOrder:o,children:a(n,ni)},s[e[r]]=i},c=function(e,t){var n,r,o,i;for(n=(e=si(e)).length,t=si(t);n--;)for(r=s[e[n]],o=0,i=t.length;o<i;o++)r.attributes[t[o]]={},r.attributesOrder.push(t[o])};return ti[e]?ti[e]:(u="id accesskey class dir lang style tabindex title role",t="address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul",n="a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd label map noscript object q s samp script select small span strong sub sup textarea u var #text #comment","html4"!==e&&(u+=" contenteditable contextmenu draggable dropzone hidden spellcheck translate",t+=" article aside details dialog figure main header footer hgroup section nav",n+=" audio canvas command datalist mark meter output picture progress time wbr video ruby bdi keygen"),"html5-strict"!==e&&(u+=" xml:lang",n=[n,i="acronym applet basefont big font strike tt"].join(" "),oi(si(i),function(e){a(e,"",n)}),t=[t,o="center dir isindex noframes"].join(" "),r=[t,n].join(" "),oi(si(o),function(e){a(e,"",r)})),r=r||[t,n].join(" "),a("html","manifest","head body"),a("head","","base command link meta noscript script style title"),a("title hr noscript br"),a("base","href target"),a("link","href rel media hreflang type sizes hreflang"),a("meta","name http-equiv content charset"),a("style","media type scoped"),a("script","src async defer type charset"),a("body","onafterprint onbeforeprint onbeforeunload onblur onerror onfocus onhashchange onload onmessage onoffline ononline onpagehide onpageshow onpopstate onresize onscroll onstorage onunload",r),a("address dt dd div caption","",r),a("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn","",n),a("blockquote","cite",r),a("ol","reversed start type","li"),a("ul","","li"),a("li","value",r),a("dl","","dt dd"),a("a","href target rel media hreflang type",n),a("q","cite",n),a("ins del","cite datetime",r),a("img","src sizes srcset alt usemap ismap width height"),a("iframe","src name width height",r),a("embed","src type width height"),a("object","data type typemustmatch name usemap form width height",[r,"param"].join(" ")),a("param","name value"),a("map","name",[r,"area"].join(" ")),a("area","alt coords shape href target rel media hreflang type"),a("table","border","caption colgroup thead tfoot tbody tr"+("html4"===e?" col":"")),a("colgroup","span","col"),a("col","span"),a("tbody thead tfoot","","tr"),a("tr","","td th"),a("td","colspan rowspan headers",r),a("th","colspan rowspan headers scope abbr",r),a("form","accept-charset action autocomplete enctype method name novalidate target",r),a("fieldset","disabled form name",[r,"legend"].join(" ")),a("label","form for",n),a("input","accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"),a("button","disabled form formaction formenctype formmethod formnovalidate formtarget name type value","html4"===e?r:n),a("select","disabled form multiple name required size","option optgroup"),a("optgroup","disabled label","option"),a("option","disabled label selected value"),a("textarea","cols dirname disabled form maxlength name readonly required rows wrap"),a("menu","type label",[r,"li"].join(" ")),a("noscript","",r),"html4"!==e&&(a("wbr"),a("ruby","",[n,"rt rp"].join(" ")),a("figcaption","",r),a("mark rt rp summary bdi","",n),a("canvas","width height",r),a("video","src crossorigin poster preload autoplay mediagroup loop muted controls width height buffered",[r,"track source"].join(" ")),a("audio","src crossorigin preload autoplay mediagroup loop muted controls buffered volume",[r,"track source"].join(" ")),a("picture","","img source"),a("source","src srcset type media sizes"),a("track","kind src srclang label default"),a("datalist","",[n,"option"].join(" ")),a("article section nav aside main header footer","",r),a("hgroup","","h1 h2 h3 h4 h5 h6"),a("figure","",[r,"figcaption"].join(" ")),a("time","datetime",n),a("dialog","open",r),a("command","type label icon disabled checked radiogroup command"),a("output","for form name",n),a("progress","value max",n),a("meter","value min max low high optimum",n),a("details","open",[r,"summary"].join(" ")),a("keygen","autofocus challenge disabled form keytype name")),"html5-strict"!==e&&(c("script","language xml:space"),c("style","xml:space"),c("object","declare classid code codebase codetype archive standby align border hspace vspace"),c("embed","align name hspace vspace"),c("param","valuetype type"),c("a","charset name rev shape coords"),c("br","clear"),c("applet","codebase archive code object alt name width height align hspace vspace"),c("img","name longdesc align border hspace vspace"),c("iframe","longdesc frameborder marginwidth marginheight scrolling align"),c("font basefont","size color face"),c("input","usemap align"),c("select","onchange"),c("textarea"),c("h1 h2 h3 h4 h5 h6 div p legend caption","align"),c("ul","type compact"),c("li","type"),c("ol dl menu dir","compact"),c("pre","width xml:space"),c("hr","align noshade size width"),c("isindex","prompt"),c("table","summary width frame rules cellspacing cellpadding align bgcolor"),c("col","width align char charoff valign"),c("colgroup","width align char charoff valign"),c("thead","align char charoff valign"),c("tr","align char charoff valign bgcolor"),c("th","axis align char charoff valign nowrap bgcolor width height"),c("form","accept"),c("td","abbr axis scope align char charoff valign nowrap bgcolor width height"),c("tfoot","align char charoff valign"),c("tbody","align char charoff valign"),c("area","nohref"),c("body","background bgcolor text link vlink alink")),"html4"!==e&&(c("input button select textarea","autofocus"),c("input textarea","placeholder"),c("a","download"),c("link script img","crossorigin"),c("iframe","sandbox seamless allowfullscreen")),oi(si("a form meter progress dfn"),function(e){s[e]&&delete s[e].children[e]}),delete s.caption.children.table,delete s.script,ti[e]=s)},li=function(e,n){var r;return e&&(r={},"string"==typeof e&&(e={"*":e}),oi(e,function(e,t){r[t]=r[t.toUpperCase()]="map"===n?ri(e,/[, ]/):ai(e,/[, ]/)})),r};function fi(i){var e,t,n,r,o,a,u,s,c,l,f,d,m,N={},g={},E=[],p={},h={},v=function(e,t,n){var r=i[e];return r?r=ri(r,/[, ]/,ri(r.toUpperCase(),/[, ]/)):(r=ti[e])||(r=ri(t," ",ri(t.toUpperCase()," ")),r=ii(r,n),ti[e]=r),r};n=ci((i=i||{}).schema),!1===i.verify_html&&(i.valid_elements="*[*]"),e=li(i.valid_styles),t=li(i.invalid_styles,"map"),s=li(i.valid_classes,"map"),r=v("whitespace_elements","pre script noscript style textarea video audio iframe object code"),o=v("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr"),a=v("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr track"),u=v("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls"),l=v("non_empty_elements","td th iframe video audio object script pre code",a),f=v("move_caret_before_on_enter_elements","table",l),d=v("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside main nav figure"),c=v("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex option datalist select optgroup figcaption details summary",d),m=v("text_inline_elements","span strong b em i font strike u var cite dfn code mark q sup sub samp"),oi((i.special||"script noscript noframes noembed title style textarea xmp").split(" "),function(e){h[e]=new RegExp("</"+e+"[^>]*>","gi")});var S=function(e){return new RegExp("^"+e.replace(/([?+*])/g,".$1")+"$")},y=function(e){var t,n,r,o,i,a,u,s,c,l,f,d,m,g,p,h,v,y,b,C=/^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,x=/^([!\-])?(\w+[\\:]:\w+|[^=:<]+)?(?:([=:<])(.*))?$/,w=/[*?+]/;if(e)for(e=si(e,","),N["@"]&&(h=N["@"].attributes,v=N["@"].attributesOrder),t=0,n=e.length;t<n;t++)if(i=C.exec(e[t])){if(g=i[1],c=i[2],p=i[3],s=i[5],a={attributes:d={},attributesOrder:m=[]},"#"===g&&(a.paddEmpty=!0),"-"===g&&(a.removeEmpty=!0),"!"===i[4]&&(a.removeEmptyAttrs=!0),h){for(y in h)d[y]=h[y];m.push.apply(m,v)}if(s)for(r=0,o=(s=si(s,"|")).length;r<o;r++)if(i=x.exec(s[r])){if(u={},f=i[1],l=i[2].replace(/[\\:]:/g,":"),g=i[3],b=i[4],"!"===f&&(a.attributesRequired=a.attributesRequired||[],a.attributesRequired.push(l),u.required=!0),"-"===f){delete d[l],m.splice(ui(m,l),1);continue}g&&("="===g&&(a.attributesDefault=a.attributesDefault||[],a.attributesDefault.push({name:l,value:b}),u.defaultValue=b),":"===g&&(a.attributesForced=a.attributesForced||[],a.attributesForced.push({name:l,value:b}),u.forcedValue=b),"<"===g&&(u.validValues=ri(b,"?"))),w.test(l)?(a.attributePatterns=a.attributePatterns||[],u.pattern=S(l),a.attributePatterns.push(u)):(d[l]||m.push(l),d[l]=u)}h||"@"!==c||(h=d,v=m),p&&(a.outputName=c,N[p]=a),w.test(c)?(a.pattern=S(c),E.push(a)):N[c]=a}},b=function(e){N={},E=[],y(e),oi(n,function(e,t){g[t]=e.children})},C=function(e){var a=/^(~)?(.+)$/;e&&(ti.text_block_elements=ti.block_elements=null,oi(si(e,","),function(e){var t=a.exec(e),n="~"===t[1],r=n?"span":"div",o=t[2];if(g[o]=g[r],p[o]=r,n||(c[o.toUpperCase()]={},c[o]={}),!N[o]){var i=N[r];delete(i=ii({},i)).removeEmptyAttrs,delete i.removeEmpty,N[o]=i}oi(g,function(e,t){e[r]&&(g[t]=e=ii({},g[t]),e[o]=e[r])})}))},x=function(e){var o=/^([+\-]?)(\w+)\[([^\]]+)\]$/;ti[i.schema]=null,e&&oi(si(e,","),function(e){var t,n,r=o.exec(e);r&&(n=r[1],t=n?g[r[2]]:g[r[2]]={"#comment":{}},t=g[r[2]],oi(si(r[3],"|"),function(e){"-"===n?delete t[e]:t[e]={}}))})},w=function(e){var t,n=N[e];if(n)return n;for(t=E.length;t--;)if((n=E[t]).pattern.test(e))return n};return i.valid_elements?b(i.valid_elements):(oi(n,function(e,t){N[t]={attributes:e.attributes,attributesOrder:e.attributesOrder},g[t]=e.children}),"html5"!==i.schema&&oi(si("strong/b em/i"),function(e){e=si(e,"/"),N[e[1]].outputName=e[0]}),oi(si("ol ul sub sup blockquote span font a table tbody tr strong em b i"),function(e){N[e]&&(N[e].removeEmpty=!0)}),oi(si("p h1 h2 h3 h4 h5 h6 th td pre div address caption li"),function(e){N[e].paddEmpty=!0}),oi(si("span"),function(e){N[e].removeEmptyAttrs=!0})),C(i.custom_elements),x(i.valid_children),y(i.extended_valid_elements),x("+ol[ul|ol],+ul[ul|ol]"),oi({dd:"dl",dt:"dl",li:"ul ol",td:"tr",th:"tr",tr:"tbody thead tfoot",tbody:"table",thead:"table",tfoot:"table",legend:"fieldset",area:"map",param:"video audio object"},function(e,t){N[t]&&(N[t].parentsRequired=si(e))}),i.invalid_elements&&oi(ai(i.invalid_elements),function(e){N[e]&&delete N[e]}),w("span")||y("span[!data-mce-type|*]"),{children:g,elements:N,getValidStyles:function(){return e},getValidClasses:function(){return s},getBlockElements:function(){return c},getInvalidStyles:function(){return t},getShortEndedElements:function(){return a},getTextBlockElements:function(){return d},getTextInlineElements:function(){return m},getBoolAttrs:function(){return u},getElementRule:w,getSelfClosingElements:function(){return o},getNonEmptyElements:function(){return l},getMoveCaretBeforeOnEnterElements:function(){return f},getWhiteSpaceElements:function(){return r},getSpecialElements:function(){return h},isValidChild:function(e,t){var n=g[e.toLowerCase()];return!(!n||!n[t.toLowerCase()])},isValid:function(e,t){var n,r,o=w(e);if(o){if(!t)return!0;if(o.attributes[t])return!0;if(n=o.attributePatterns)for(r=n.length;r--;)if(n[r].pattern.test(e))return!0}return!1},getCustomElements:function(){return p},addValidElements:y,setValidElements:b,addCustomElements:C,addValidChildren:x}}var di=function(e,t,n,r){var o=function(e){return 1<(e=parseInt(e,10).toString(16)).length?e:"0"+e};return"#"+o(t)+o(n)+o(r)};function mi(b,e){var C,t,c,l,x=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,w=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,N=/\s*([^:]+):\s*([^;]+);?/g,E=/\s+$/,S={},T="\ufeff";for(b=b||{},e&&(c=e.getValidStyles(),l=e.getInvalidStyles()),t=("\\\" \\' \\; \\: ; : "+T).split(" "),C=0;C<t.length;C++)S[t[C]]=T+C,S[T+C]=t[C];return{toHex:function(e){return e.replace(x,di)},parse:function(e){var t,n,r,o,i,a,u,s,c={},l=b.url_converter,f=b.url_converter_scope||this,d=function(e,t,n){var r,o,i,a;if((r=c[e+"-top"+t])&&(o=c[e+"-right"+t])&&(i=c[e+"-bottom"+t])&&(a=c[e+"-left"+t])){var u=[r,o,i,a];for(C=u.length-1;C--&&u[C]===u[C+1];);-1<C&&n||(c[e+t]=-1===C?u[0]:u.join(" "),delete c[e+"-top"+t],delete c[e+"-right"+t],delete c[e+"-bottom"+t],delete c[e+"-left"+t])}},m=function(e){var t,n=c[e];if(n){for(t=(n=n.split(" ")).length;t--;)if(n[t]!==n[0])return!1;return c[e]=n[0],!0}},g=function(e){return o=!0,S[e]},p=function(e,t){return o&&(e=e.replace(/\uFEFF[0-9]/g,function(e){return S[e]})),t||(e=e.replace(/\\([\'\";:])/g,"$1")),e},h=function(e){return String.fromCharCode(parseInt(e.slice(1),16))},v=function(e){return e.replace(/\\[0-9a-f]+/gi,h)},y=function(e,t,n,r,o,i){if(o=o||i)return"'"+(o=p(o)).replace(/\'/g,"\\'")+"'";if(t=p(t||n||r),!b.allow_script_urls){var a=t.replace(/[\s\r\n]+/g,"");if(/(java|vb)script:/i.test(a))return"";if(!b.allow_svg_data_urls&&/^data:image\/svg/i.test(a))return""}return l&&(t=l.call(f,t,"style")),"url('"+t.replace(/\'/g,"\\'")+"')"};if(e){for(e=(e=e.replace(/[\u0000-\u001F]/g,"")).replace(/\\[\"\';:\uFEFF]/g,g).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(e){return e.replace(/[;:]/g,g)});t=N.exec(e);)if(N.lastIndex=t.index+t[0].length,n=t[1].replace(E,"").toLowerCase(),r=t[2].replace(E,""),n&&r){if(n=v(n),r=v(r),-1!==n.indexOf(T)||-1!==n.indexOf('"'))continue;if(!b.allow_script_urls&&("behavior"===n||/expression\s*\(|\/\*|\*\//.test(r)))continue;"font-weight"===n&&"700"===r?r="bold":"color"!==n&&"background-color"!==n||(r=r.toLowerCase()),r=(r=r.replace(x,di)).replace(w,y),c[n]=o?p(r,!0):r}d("border","",!0),d("border","-width"),d("border","-color"),d("border","-style"),d("padding",""),d("margin",""),i="border",u="border-style",s="border-color",m(a="border-width")&&m(u)&&m(s)&&(c[i]=c[a]+" "+c[u]+" "+c[s],delete c[a],delete c[u],delete c[s]),"medium none"===c.border&&delete c.border,"none"===c["border-image"]&&delete c["border-image"]}return c},serialize:function(i,e){var t,n,r,o,a,u="",s=function(e){var t,n,r,o;if(t=c[e])for(n=0,r=t.length;n<r;n++)e=t[n],(o=i[e])&&(u+=(0<u.length?" ":"")+e+": "+o+";")};if(e&&c)s("*"),s(e);else for(t in i)!(n=i[t])||l&&(r=t,o=e,a=void 0,(a=l["*"])&&a[r]||(a=l[o])&&a[r])||(u+=(0<u.length?" ":"")+t+": "+n+";");return u}}}var gi,pi=Xt.each,hi=Xt.grep,vi=fe.ie,yi=/^([a-z0-9],?)+$/i,bi=/^[ \t\r\n]*$/,Ci=function(n,r,o){var e={},i=r.keep_values,t={set:function(e,t,n){r.url_converter&&(t=r.url_converter.call(r.url_converter_scope||o(),t,n,e[0])),e.attr("data-mce-"+n,t).attr(n,t)},get:function(e,t){return e.attr("data-mce-"+t)||e.attr(t)}};return e={style:{set:function(e,t){null===t||"object"!=typeof t?(i&&e.attr("data-mce-style",t),e.attr("style",t)):e.css(t)},get:function(e){var t=e.attr("data-mce-style")||e.attr("style");return t=n.serialize(n.parse(t),e[0].nodeName)}}},i&&(e.href=e.src=t),e},xi=function(e,t){var n=t.attr("style"),r=e.serialize(e.parse(n),t[0].nodeName);r||(r=null),t.attr("data-mce-style",r)},wi=function(e,t){var n,r,o=0;if(e)for(n=e.nodeType,e=e.previousSibling;e;e=e.previousSibling)r=e.nodeType,(!t||3!==r||r!==n&&e.nodeValue.length)&&(o++,n=r);return o};function Ni(a,u){var s,c=this;void 0===u&&(u={});var r={},i=V.window,o={},t=0,e=function(m,g){void 0===g&&(g={});var p,h=0,v={};p=g.maxLoadTime||5e3;var y=function(e){m.getElementsByTagName("head")[0].appendChild(e)},n=function(e,t,n){var o,r,i,a,u=function(){for(var e=a.passed,t=e.length;t--;)e[t]();a.status=2,a.passed=[],a.failed=[]},s=function(){for(var e=a.failed,t=e.length;t--;)e[t]();a.status=3,a.passed=[],a.failed=[]},c=function(e,t){e()||((new Date).getTime()-i<p?he.setTimeout(t):s())},l=function(){c(function(){for(var e,t,n=m.styleSheets,r=n.length;r--;)if((t=(e=n[r]).ownerNode?e.ownerNode:e.owningElement)&&t.id===o.id)return u(),!0},l)},f=function(){c(function(){try{var e=r.sheet.cssRules;return u(),!!e}catch(t){}},f)};if(e=Xt._addCacheSuffix(e),v[e]?a=v[e]:(a={passed:[],failed:[]},v[e]=a),t&&a.passed.push(t),n&&a.failed.push(n),1!==a.status)if(2!==a.status)if(3!==a.status){if(a.status=1,(o=m.createElement("link")).rel="stylesheet",o.type="text/css",o.id="u"+h++,o.async=!1,o.defer=!1,i=(new Date).getTime(),g.contentCssCors&&(o.crossOrigin="anonymous"),"onload"in o&&!((d=V.navigator.userAgent.match(/WebKit\/(\d*)/))&&parseInt(d[1],10)<536))o.onload=l,o.onerror=s;else{if(0<V.navigator.userAgent.indexOf("Firefox"))return(r=m.createElement("style")).textContent='@import "'+e+'"',f(),void y(r);l()}var d;y(o),o.href=e}else s();else u()},t=function(t){return ao.nu(function(e){n(t,H(e,q(fo.value(t))),H(e,q(fo.error(t))))})},o=function(e){return e.fold($,$)};return{load:n,loadAll:function(e,n,r){so(W(e,t)).get(function(e){var t=K(e,function(e){return e.isValue()});0<t.fail.length?r(t.fail.map(o)):n(t.pass.map(o))})}}}(a,{contentCssCors:u.contentCssCors}),l=[],f=u.schema?u.schema:fi({}),d=mi({url_converter:u.url_converter,url_converter_scope:u.url_converter_scope},u.schema),m=u.ownEvents?new Se(u.proxy):Se.Event,n=f.getBlockElements(),g=gn.overrideDefaults(function(){return{context:a,element:j.getRoot()}}),p=function(e){if(e&&a&&"string"==typeof e){var t=a.getElementById(e);return t&&t.id!==e?a.getElementsByName(e)[1]:t}return e},h=function(e){return"string"==typeof e&&(e=p(e)),g(e)},v=function(e,t,n){var r,o,i=h(e);return i.length&&(o=(r=s[t])&&r.get?r.get(i,t):i.attr(t)),void 0===o&&(o=n||""),o},y=function(e){var t=p(e);return t?t.attributes:[]},b=function(e,t,n){var r,o;""===n&&(n=null);var i=h(e);r=i.attr(t),i.length&&((o=s[t])&&o.set?o.set(i,n,t):i.attr(t,n),r!==n&&u.onSetAttrib&&u.onSetAttrib({attrElm:i,attrName:t,attrValue:n}))},C=function(){return u.root_element||a.body},x=function(e,t){return Qr.getPos(a.body,p(e),t)},w=function(e,t,n){var r=h(e);return n?r.css(t):("float"===(t=t.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}))&&(t=fe.ie&&fe.ie<12?"styleFloat":"cssFloat"),r[0]&&r[0].style?r[0].style[t]:undefined)},N=function(e){var t,n;return e=p(e),t=w(e,"width"),n=w(e,"height"),-1===t.indexOf("px")&&(t=0),-1===n.indexOf("px")&&(n=0),{w:parseInt(t,10)||e.offsetWidth||e.clientWidth,h:parseInt(n,10)||e.offsetHeight||e.clientHeight}},E=function(e,t){var n;if(!e)return!1;if(!Array.isArray(e)){if("*"===t)return 1===e.nodeType;if(yi.test(t)){var r=t.toLowerCase().split(/,/),o=e.nodeName.toLowerCase();for(n=r.length-1;0<=n;n--)if(r[n]===o)return!0;return!1}if(e.nodeType&&1!==e.nodeType)return!1}var i=Array.isArray(e)?e:[e];return 0<St(t,i[0].ownerDocument||i[0],null,i).length},S=function(e,t,n,r){var o,i=[],a=p(e);for(r=r===undefined,n=n||("BODY"!==C().nodeName?C().parentNode:null),Xt.is(t,"string")&&(t="*"===(o=t)?function(e){return 1===e.nodeType}:function(e){return E(e,o)});a&&a!==n&&a.nodeType&&9!==a.nodeType;){if(!t||"function"==typeof t&&t(a)){if(!r)return[a];i.push(a)}a=a.parentNode}return r?i:null},T=function(e,t,n){var r=t;if(e)for("string"==typeof t&&(r=function(e){return E(e,t)}),e=e[n];e;e=e[n])if("function"==typeof r&&r(e))return e;return null},k=function(e,n,r){var o,t="string"==typeof e?p(e):e;if(!t)return!1;if(Xt.isArray(t)&&(t.length||0===t.length))return o=[],pi(t,function(e,t){e&&("string"==typeof e&&(e=p(e)),o.push(n.call(r,e,t)))}),o;var i=r||c;return n.call(i,t)},_=function(e,t){h(e).each(function(e,n){pi(t,function(e,t){b(n,t,e)})})},A=function(e,r){var t=h(e);vi?t.each(function(e,t){if(!1!==t.canHaveHTML){for(;t.firstChild;)t.removeChild(t.firstChild);try{t.innerHTML="<br>"+r,t.removeChild(t.firstChild)}catch(n){gn("<div></div>").html("<br>"+r).contents().slice(1).appendTo(t)}return r}}):t.html(r)},R=function(e,n,r,o,i){return k(e,function(e){var t="string"==typeof n?a.createElement(n):n;return _(t,r),o&&("string"!=typeof o&&o.nodeType?t.appendChild(o):"string"==typeof o&&A(t,o)),i?t:e.appendChild(t)})},D=function(e,t,n){return R(a.createElement(e),e,t,n,!0)},O=ei.decode,B=ei.encodeAllRaw,P=function(e,t){var n=h(e);return t?n.each(function(){for(var e;e=this.firstChild;)3===e.nodeType&&0===e.data.length?this.removeChild(e):this.parentNode.insertBefore(e,this)}).remove():n.remove(),1<n.length?n.toArray():n[0]},I=function(e,t,n){h(e).toggleClass(t,n).each(function(){""===this.className&&gn(this).attr("class",null)})},L=function(t,e,n){return k(e,function(e){return Xt.is(e,"array")&&(t=t.cloneNode(!0)),n&&pi(hi(e.childNodes),function(e){t.appendChild(e)}),e.parentNode.replaceChild(t,e)})},F=function(){return a.createRange()},M=function(e,t,n,r){if(Xt.isArray(e)){for(var o=e.length;o--;)e[o]=M(e[o],t,n,r);return e}return!u.collect||e!==a&&e!==i||l.push([e,t,n,r]),m.bind(e,t,n,r||j)},z=function(e,t,n){var r;if(Xt.isArray(e)){for(r=e.length;r--;)e[r]=z(e[r],t,n);return e}if(l&&(e===a||e===i))for(r=l.length;r--;){var o=l[r];e!==o[0]||t&&t!==o[1]||n&&n!==o[2]||m.unbind(o[0],o[1],o[2])}return m.unbind(e,t,n)},U=function(e){if(e&&Uo.isElement(e)){var t=e.getAttribute("data-mce-contenteditable");return t&&"inherit"!==t?t:"inherit"!==e.contentEditable?e.contentEditable:null}return null},j={doc:a,settings:u,win:i,files:o,stdMode:!0,boxModel:!0,styleSheetLoader:e,boundEvents:l,styles:d,schema:f,events:m,isBlock:function(e){if("string"==typeof e)return!!n[e];if(e){var t=e.nodeType;if(t)return!(1!==t||!n[e.nodeName])}return!1},$:g,$$:h,root:null,clone:function(t,e){if(!vi||1!==t.nodeType||e)return t.cloneNode(e);if(!e){var n=a.createElement(t.nodeName);return pi(y(t),function(e){b(n,e.nodeName,v(t,e.nodeName))}),n}return null},getRoot:C,getViewPort:function(e){var t=e||i,n=t.document.documentElement;return{x:t.pageXOffset||n.scrollLeft,y:t.pageYOffset||n.scrollTop,w:t.innerWidth||n.clientWidth,h:t.innerHeight||n.clientHeight}},getRect:function(e){var t,n;return e=p(e),t=x(e),n=N(e),{x:t.x,y:t.y,w:n.w,h:n.h}},getSize:N,getParent:function(e,t,n){var r=S(e,t,n,!1);return r&&0<r.length?r[0]:null},getParents:S,get:p,getNext:function(e,t){return T(e,t,"nextSibling")},getPrev:function(e,t){return T(e,t,"previousSibling")},select:function(e,t){return St(e,p(t)||u.root_element||a,[])},is:E,add:R,create:D,createHTML:function(e,t,n){var r,o="";for(r in o+="<"+e,t)t.hasOwnProperty(r)&&null!==t[r]&&"undefined"!=typeof t[r]&&(o+=" "+r+'="'+B(t[r])+'"');return void 0!==n?o+">"+n+"</"+e+">":o+" />"},createFragment:function(e){var t,n=a.createElement("div"),r=a.createDocumentFragment();for(e&&(n.innerHTML=e);t=n.firstChild;)r.appendChild(t);return r},remove:P,setStyle:function(e,t,n){var r=h(e).css(t,n);u.update_styles&&xi(d,r)},getStyle:w,setStyles:function(e,t){var n=h(e).css(t);u.update_styles&&xi(d,n)},removeAllAttribs:function(e){return k(e,function(e){var t,n=e.attributes;for(t=n.length-1;0<=t;t--)e.removeAttributeNode(n.item(t))})},setAttrib:b,setAttribs:_,getAttrib:v,getPos:x,parseStyle:function(e){return d.parse(e)},serializeStyle:function(e,t){return d.serialize(e,t)},addStyle:function(e){var t,n;if(j!==Ni.DOM&&a===V.document){if(r[e])return;r[e]=!0}(n=a.getElementById("mceDefaultStyles"))||((n=a.createElement("style")).id="mceDefaultStyles",n.type="text/css",(t=a.getElementsByTagName("head")[0]).firstChild?t.insertBefore(n,t.firstChild):t.appendChild(n)),n.styleSheet?n.styleSheet.cssText+=e:n.appendChild(a.createTextNode(e))},loadCSS:function(e){var n;j===Ni.DOM||a!==V.document?(e||(e=""),n=a.getElementsByTagName("head")[0],pi(e.split(","),function(e){var t;e=Xt._addCacheSuffix(e),o[e]||(o[e]=!0,t=D("link",{rel:"stylesheet",href:e}),n.appendChild(t))})):Ni.DOM.loadCSS(e)},addClass:function(e,t){h(e).addClass(t)},removeClass:function(e,t){I(e,t,!1)},hasClass:function(e,t){return h(e).hasClass(t)},toggleClass:I,show:function(e){h(e).show()},hide:function(e){h(e).hide()},isHidden:function(e){return"none"===h(e).css("display")},uniqueId:function(e){return(e||"mce_")+t++},setHTML:A,getOuterHTML:function(e){var t="string"==typeof e?p(e):e;return Uo.isElement(t)?t.outerHTML:gn("<div></div>").append(gn(t).clone()).html()},setOuterHTML:function(e,t){h(e).each(function(){try{if("outerHTML"in this)return void(this.outerHTML=t)}catch(e){}P(gn(this).html(t),!0)})},decode:O,encode:B,insertAfter:function(e,t){var r=p(t);return k(e,function(e){var t,n;return t=r.parentNode,(n=r.nextSibling)?t.insertBefore(e,n):t.appendChild(e),e})},replace:L,rename:function(t,e){var n;return t.nodeName!==e.toUpperCase()&&(n=D(e),pi(y(t),function(e){b(n,e.nodeName,v(t,e.nodeName))}),L(n,t,!0)),n||t},findCommonAncestor:function(e,t){for(var n,r=e;r;){for(n=t;n&&r!==n;)n=n.parentNode;if(r===n)break;r=r.parentNode}return!r&&e.ownerDocument?e.ownerDocument.documentElement:r},toHex:function(e){return d.toHex(Xt.trim(e))},run:k,getAttribs:y,isEmpty:function(e,t){var n,r,o,i,a,u,s=0;if(e=e.firstChild){a=new mo(e,e.parentNode),t=t||(f?f.getNonEmptyElements():null),i=f?f.getWhiteSpaceElements():{};do{if(o=e.nodeType,Uo.isElement(e)){var c=e.getAttribute("data-mce-bogus");if(c){e=a.next("all"===c);continue}if(u=e.nodeName.toLowerCase(),t&&t[u]){if("br"===u){s++,e=a.next();continue}return!1}for(n=(r=y(e)).length;n--;)if("name"===(u=r[n].nodeName)||"data-mce-bookmark"===u)return!1}if(8===o)return!1;if(3===o&&!bi.test(e.nodeValue))return!1;if(3===o&&e.parentNode&&i[e.parentNode.nodeName]&&bi.test(e.nodeValue))return!1;e=a.next()}while(e)}return s<=1},createRng:F,nodeIndex:wi,split:function(e,t,n){var r,o,i,a=F();if(e&&t)return a.setStart(e.parentNode,wi(e)),a.setEnd(t.parentNode,wi(t)),r=a.extractContents(),(a=F()).setStart(t.parentNode,wi(t)+1),a.setEnd(e.parentNode,wi(e)+1),o=a.extractContents(),(i=e.parentNode).insertBefore(Ho.trimNode(j,r),e),n?i.insertBefore(n,e):i.insertBefore(t,e),i.insertBefore(Ho.trimNode(j,o),e),P(e),n||t},bind:M,unbind:z,fire:function(e,t,n){return m.fire(e,t,n)},getContentEditable:U,getContentEditableParent:function(e){for(var t=C(),n=null;e&&e!==t&&null===(n=U(e));e=e.parentNode);return n},destroy:function(){if(l)for(var e=l.length;e--;){var t=l[e];m.unbind(t[0],t[1],t[2])}St.setDocument&&St.setDocument()},isChildOf:function(e,t){for(;e;){if(t===e)return!0;e=e.parentNode}return!1},dumpRng:function(e){return"startContainer: "+e.startContainer.nodeName+", startOffset: "+e.startOffset+", endContainer: "+e.endContainer.nodeName+", endOffset: "+e.endOffset}};return s=Ci(d,u,function(){return j}),j}(gi=Ni||(Ni={})).DOM=gi(V.document),gi.nodeIndex=wi;var Ei=Ni,Si=Ei.DOM,Ti=Xt.each,ki=Xt.grep,_i=function(e){return"function"==typeof e},Ai=function(){var l={},o=[],i={},a=[],f=0;this.isDone=function(e){return 2===l[e]},this.markDone=function(e){l[e]=2},this.add=this.load=function(e,t,n,r){l[e]===undefined&&(o.push(e),l[e]=0),t&&(i[e]||(i[e]=[]),i[e].push({success:t,failure:r,scope:n||this}))},this.remove=function(e){delete l[e],delete i[e]},this.loadQueue=function(e,t,n){this.loadScripts(o,e,t,n)},this.loadScripts=function(n,e,t,r){var u,s=[],c=function(t,e){Ti(i[e],function(e){_i(e[t])&&e[t].call(e.scope)}),i[e]=undefined};a.push({success:e,failure:r,scope:t||this}),(u=function(){var e=ki(n);if(n.length=0,Ti(e,function(e){var t,n,r,o,i,a;2!==l[e]?3!==l[e]?1!==l[e]&&(l[e]=1,f++,t=e,n=function(){l[e]=2,f--,c("success",e),u()},r=function(){l[e]=3,f--,s.push(e),c("failure",e),u()},i=(a=Si).uniqueId(),(o=V.document.createElement("script")).id=i,o.type="text/javascript",o.src=Xt._addCacheSuffix(t),o.onload=function(){a.remove(i),o&&(o.onreadystatechange=o.onload=o=null),n()},o.onerror=function(){_i(r)?r():"undefined"!=typeof console&&console.log&&console.log("Failed to load script: "+t)},(V.document.getElementsByTagName("head")[0]||V.document.body).appendChild(o)):c("failure",e):c("success",e)}),!f){var t=a.slice(0);a.length=0,Ti(t,function(e){0===s.length?_i(e.success)&&e.success.call(e.scope):_i(e.failure)&&e.failure.call(e.scope,s)})}})()}};Ai.ScriptLoader=new Ai;var Ri,Di=Xt.each;function Oi(){var r=this,o=[],a={},u={},i=[],s=function(e){var t;return u[e]&&(t=u[e].dependencies),t||[]},c=function(e,t){return"object"==typeof t?t:"string"==typeof e?{prefix:"",resource:t,suffix:""}:{prefix:e.prefix,resource:t,suffix:e.suffix}},l=function(e,n,t,r){var o=s(e);Di(o,function(e){var t=c(n,e);f(t.resource,t,undefined,undefined)}),t&&(r?t.call(r):t.call(Ai))},f=function(e,t,n,r,o){if(!a[e]){var i="string"==typeof t?t:t.prefix+t.resource+t.suffix;0!==i.indexOf("/")&&-1===i.indexOf("://")&&(i=Oi.baseURL+"/"+i),a[e]=i.substring(0,i.lastIndexOf("/")),u[e]?l(e,t,n,r):Ai.ScriptLoader.add(i,function(){return l(e,t,n,r)},r,o)}};return{items:o,urls:a,lookup:u,_listeners:i,get:function(e){return u[e]?u[e].instance:undefined},dependencies:s,requireLangPack:function(e,t){var n=Oi.language;if(n&&!1!==Oi.languageLoad){if(t)if(-1!==(t=","+t+",").indexOf(","+n.substr(0,2)+","))n=n.substr(0,2);else if(-1===t.indexOf(","+n+","))return;Ai.ScriptLoader.add(a[e]+"/langs/"+n+".js")}},add:function(t,e,n){o.push(e),u[t]={instance:e,dependencies:n};var r=K(i,function(e){return e.name===t});return i=r.fail,Di(r.pass,function(e){e.callback()}),e},remove:function(e){delete a[e],delete u[e]},createUrl:c,addComponents:function(e,t){var n=r.urls[e];Di(t,function(e){Ai.ScriptLoader.add(n+"/"+e)})},load:f,waitFor:function(e,t){u.hasOwnProperty(e)?t():i.push({name:e,callback:t})}}}(Ri=Oi||(Oi={})).PluginManager=Ri(),Ri.ThemeManager=Ri();var Bi=function(t,n){jr(t).each(function(e){e.dom().insertBefore(n.dom(),t.dom())})},Pi=function(e,t){Hr(e).fold(function(){jr(e).each(function(e){Li(e,t)})},function(e){Bi(e,t)})},Ii=function(t,n){Xr(t).fold(function(){Li(t,n)},function(e){t.dom().insertBefore(n.dom(),e.dom())})},Li=function(e,t){e.dom().appendChild(t.dom())},Fi=function(t,e){z(e,function(e){Li(t,e)})},Mi=function(e){e.dom().textContent="",z(Wr(e),function(e){zi(e)})},zi=function(e){var t=e.dom();null!==t.parentNode&&t.parentNode.removeChild(t)},Ui=function(e){var t,n=Wr(e);0<n.length&&(t=e,z(n,function(e){Bi(t,e)})),zi(e)},ji=function(n,r){var o=null;return{cancel:function(){null!==o&&(V.clearTimeout(o),o=null)},throttle:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];null===o&&(o=V.setTimeout(function(){n.apply(null,e),o=null},r))}}},Vi=function(e){var t=e,n=function(){return t};return{get:n,set:function(e){t=e},clone:function(){return Vi(n())}}},Hi=function(e,t){var n=Nr(e,t);return n===undefined||""===n?[]:n.split(" ")},qi=function(e){return e.dom().classList!==undefined},$i=function(e,t){return o=t,i=Hi(n=e,r="class").concat([o]),xr(n,r,i.join(" ")),!0;var n,r,o,i},Wi=function(e,t){return o=t,0<(i=U(Hi(n=e,r="class"),function(e){return e!==o})).length?xr(n,r,i.join(" ")):Er(n,r),!1;var n,r,o,i},Ki=function(e,t){qi(e)?e.dom().classList.add(t):$i(e,t)},Xi=function(e){0===(qi(e)?e.dom().classList:Hi(e,"class")).length&&Er(e,"class")},Yi=function(e,t){return qi(e)&&e.dom().classList.contains(t)},Gi=function(e,t){var n=[];return z(Wr(e),function(e){t(e)&&(n=n.concat([e])),n=n.concat(Gi(e,t))}),n},Ji=function(e,t){return n=t,o=(r=e)===undefined?V.document:r.dom(),Lr(o)?[]:W(o.querySelectorAll(n),ar.fromDom);var n,r,o};function Qi(e,t,n,r,o){return e(n,r)?_.some(n):D(o)&&o(n)?_.none():t(n,r,o)}var Zi,ea=function(e,t,n){for(var r=e.dom(),o=D(n)?n:q(!1);r.parentNode;){r=r.parentNode;var i=ar.fromDom(r);if(t(i))return _.some(i);if(o(i))break}return _.none()},ta=function(e,t,n){return Qi(function(e,t){return t(e)},ea,e,t,n)},na=function(e,t,n){return ea(e,function(e){return Ir(e,t)},n)},ra=function(e,t){return n=t,o=(r=e)===undefined?V.document:r.dom(),Lr(o)?_.none():_.from(o.querySelector(n)).map(ar.fromDom);var n,r,o},oa=function(e,t,n){return Qi(Ir,na,e,t,n)},ia=q("mce-annotation"),aa=q("data-mce-annotation"),ua=q("data-mce-annotation-uid"),sa=function(r,e){var t=r.selection.getRng(),n=ar.fromDom(t.startContainer),o=ar.fromDom(r.getBody()),i=e.fold(function(){return"."+ia()},function(e){return"["+aa()+'="'+e+'"]'}),a=Kr(n,t.startOffset).getOr(n),u=oa(a,i,function(e){return Fr(e,o)}),s=function(e,t){return n=t,(r=e.dom())&&r.hasAttribute&&r.hasAttribute(n)?_.some(Nr(e,t)):_.none();var n,r};return u.bind(function(e){return s(e,""+ua()).bind(function(n){return s(e,""+aa()).map(function(e){var t=ca(r,n);return{uid:n,name:e,elements:t}})})})},ca=function(e,t){var n=ar.fromDom(e.getBody());return Ji(n,"["+ua()+'="'+t+'"]')},la=function(i,e){var n,r,o,a=Vi({}),c=function(e,t){u(e,function(e){return t(e),e})},u=function(e,t){var n=a.get(),r=t(n.hasOwnProperty(e)?n[e]:{listeners:[],previous:Vi(_.none())});n[e]=r,a.set(n)},t=(n=function(){var e,t,n,r=a.get(),o=(e=gr(r),(n=B.call(e,0)).sort(t),n);z(o,function(e){u(e,function(u){var s=u.previous.get();return sa(i,_.some(e)).fold(function(){var t;s.isSome()&&(c(t=e,function(e){z(e.listeners,function(e){return e(!1,t)})}),u.previous.set(_.none()))},function(e){var t,n,r,o=e.uid,i=e.name,a=e.elements;s.is(o)||(n=o,r=a,c(t=i,function(e){z(e.listeners,function(e){return e(!0,t,{uid:n,nodes:W(r,function(e){return e.dom()})})})}),u.previous.set(_.some(o)))}),{previous:u.previous,listeners:u.listeners}})})},r=30,o=null,{cancel:function(){null!==o&&(V.clearTimeout(o),o=null)},throttle:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];null!==o&&V.clearTimeout(o),o=V.setTimeout(function(){n.apply(null,e),o=null},r)}});return i.on("remove",function(){t.cancel()}),i.on("nodeChange",function(){t.throttle()}),{addListener:function(e,t){u(e,function(e){return{previous:e.previous,listeners:e.listeners.concat([t])}})}}},fa=function(e,n){e.on("init",function(){e.serializer.addNodeFilter("span",function(e){z(e,function(t){var e;(e=t,_.from(e.attributes.map[aa()]).bind(n.lookup)).each(function(e){!1===e.persistent&&t.unwrap()})})})})},da=function(){return(da=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},ma=0,ga=function(e,t){return ar.fromDom(e.dom().cloneNode(t))},pa=function(e){return ga(e,!1)},ha=function(e){return ga(e,!0)},va=function(e,t){var n,r,o=zr(e).dom(),i=ar.fromDom(o.createDocumentFragment()),a=(n=t,(r=(o||V.document).createElement("div")).innerHTML=n,Wr(ar.fromDom(r)));Fi(i,a),Mi(e),Li(e,i)},ya="\ufeff",ba=function(e){return e===ya},Ca=ya,xa=function(e){return e.replace(new RegExp(ya,"g"),"")},wa=Uo.isElement,Na=Uo.isText,Ea=function(e){return Na(e)&&(e=e.parentNode),wa(e)&&e.hasAttribute("data-mce-caret")},Sa=function(e){return Na(e)&&ba(e.data)},Ta=function(e){return Ea(e)||Sa(e)},ka=function(e){return e.firstChild!==e.lastChild||!Uo.isBr(e.firstChild)},_a=function(e){var t=e.container();return!(!e||!Uo.isText(t))&&(t.data.charAt(e.offset())===Ca||e.isAtStart()&&Sa(t.previousSibling))},Aa=function(e){var t=e.container();return!(!e||!Uo.isText(t))&&(t.data.charAt(e.offset()-1)===Ca||e.isAtEnd()&&Sa(t.nextSibling))},Ra=function(e,t,n){var r,o,i;return(r=t.ownerDocument.createElement(e)).setAttribute("data-mce-caret",n?"before":"after"),r.setAttribute("data-mce-bogus","all"),r.appendChild(((i=V.document.createElement("br")).setAttribute("data-mce-bogus","1"),i)),o=t.parentNode,n?o.insertBefore(r,t):t.nextSibling?o.insertBefore(r,t.nextSibling):o.appendChild(r),r},Da=function(e){return Na(e)&&e.data[0]===Ca},Oa=function(e){return Na(e)&&e.data[e.data.length-1]===Ca},Ba=function(e){return e&&e.hasAttribute("data-mce-caret")?(t=e.getElementsByTagName("br"),n=t[t.length-1],Uo.isBogus(n)&&n.parentNode.removeChild(n),e.removeAttribute("data-mce-caret"),e.removeAttribute("data-mce-bogus"),e.removeAttribute("style"),e.removeAttribute("_moz_abspos"),e):null;var t,n},Pa=Uo.isContentEditableTrue,Ia=Uo.isContentEditableFalse,La=Uo.isBr,Fa=Uo.isText,Ma=Uo.matchNodeNames("script style textarea"),za=Uo.matchNodeNames("img input textarea hr iframe video audio object"),Ua=Uo.matchNodeNames("table"),ja=Ta,Va=function(e){return!ja(e)&&(Fa(e)?!Ma(e.parentNode):za(e)||La(e)||Ua(e)||Ha(e))},Ha=function(e){return!1===(t=e,Uo.isElement(t)&&"true"===t.getAttribute("unselectable"))&&Ia(e);var t},qa=function(e,t){return Va(e)&&function(e,t){for(e=e.parentNode;e&&e!==t;e=e.parentNode){if(Ha(e))return!1;if(Pa(e))return!0}return!0}(e,t)},$a=Math.round,Wa=function(e){return e?{left:$a(e.left),top:$a(e.top),bottom:$a(e.bottom),right:$a(e.right),width:$a(e.width),height:$a(e.height)}:{left:0,top:0,bottom:0,right:0,width:0,height:0}},Ka=function(e,t){return e=Wa(e),t||(e.left=e.left+e.width),e.right=e.left,e.width=0,e},Xa=function(e,t,n){return 0<=e&&e<=Math.min(t.height,n.height)/2},Ya=function(e,t){return e.bottom-e.height/2<t.top||!(e.top>t.bottom)&&Xa(t.top-e.bottom,e,t)},Ga=function(e,t){return e.top>t.bottom||!(e.bottom<t.top)&&Xa(t.bottom-e.top,e,t)},Ja=function(e,t,n){return t>=e.left&&t<=e.right&&n>=e.top&&n<=e.bottom},Qa=function(e){var t=e.startContainer,n=e.startOffset;return t.hasChildNodes()&&e.endOffset===n+1?t.childNodes[n]:null},Za=function(e,t){return 1===e.nodeType&&e.hasChildNodes()&&(t>=e.childNodes.length&&(t=e.childNodes.length-1),e=e.childNodes[t]),e},eu=new RegExp("[\u0300-\u036f\u0483-\u0487\u0488-\u0489\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e3-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2-\u09e3\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62-\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c00\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0c81\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc-\u0ccd\u0cd5-\u0cd6\u0ce2-\u0ce3\u0d01\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62-\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1ab0-\u1abd\u1abe\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab-\u1bad\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1cf8-\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u200c-\u200d\u20d0-\u20dc\u20dd-\u20e0\u20e1\u20e2-\u20e4\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u302e-\u302f\u3099-\u309a\ua66f\ua670-\ua672\ua674-\ua67d\ua69e-\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\ua9e5\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaa7c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\uff9e-\uff9f]"),tu=function(e){return"string"==typeof e&&768<=e.charCodeAt(0)&&eu.test(e)},nu=function(e,t,n){return e.isSome()&&t.isSome()?_.some(n(e.getOrDie(),t.getOrDie())):_.none()},ru=[].slice,ou=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=ru.call(arguments);return function(e){for(var t=0;t<n.length;t++)if(!n[t](e))return!1;return!0}},iu=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=ru.call(arguments);return function(e){for(var t=0;t<n.length;t++)if(n[t](e))return!0;return!1}},au=Uo.isElement,uu=Va,su=Uo.matchStyleValues("display","block table"),cu=Uo.matchStyleValues("float","left right"),lu=ou(au,uu,y(cu)),fu=y(Uo.matchStyleValues("white-space","pre pre-line pre-wrap")),du=Uo.isText,mu=Uo.isBr,gu=Ei.nodeIndex,pu=Za,hu=function(e){return"createRange"in e?e.createRange():Ei.DOM.createRng()},vu=function(e){return e&&/[\r\n\t ]/.test(e)},yu=function(e){return!!e.setStart&&!!e.setEnd},bu=function(e){var t,n=e.startContainer,r=e.startOffset;return!!(vu(e.toString())&&fu(n.parentNode)&&Uo.isText(n)&&(t=n.data,vu(t[r-1])||vu(t[r+1])))},Cu=function(e){return 0===e.left&&0===e.right&&0===e.top&&0===e.bottom},xu=function(e){var t,n,r,o,i,a,u,s;return t=0<(n=e.getClientRects()).length?Wa(n[0]):Wa(e.getBoundingClientRect()),!yu(e)&&mu(e)&&Cu(t)?(i=(r=e).ownerDocument,a=hu(i),u=i.createTextNode("\xa0"),(s=r.parentNode).insertBefore(u,r),a.setStart(u,0),a.setEnd(u,1),o=Wa(a.getBoundingClientRect()),s.removeChild(u),o):Cu(t)&&yu(e)?function(e){var t=e.startContainer,n=e.endContainer,r=e.startOffset,o=e.endOffset;if(t===n&&Uo.isText(n)&&0===r&&1===o){var i=e.cloneRange();return i.setEndAfter(n),xu(i)}return null}(e):t},wu=function(e,t){var n=Ka(e,t);return n.width=1,n.right=n.left+1,n},Nu=function(e){var t,n,r=[],o=function(e){var t,n;0!==e.height&&(0<r.length&&(t=e,n=r[r.length-1],t.left===n.left&&t.top===n.top&&t.bottom===n.bottom&&t.right===n.right)||r.push(e))},i=function(e,t){var n=hu(e.ownerDocument);if(t<e.data.length){if(tu(e.data[t]))return r;if(tu(e.data[t-1])&&(n.setStart(e,t),n.setEnd(e,t+1),!bu(n)))return o(wu(xu(n),!1)),r}0<t&&(n.setStart(e,t-1),n.setEnd(e,t),bu(n)||o(wu(xu(n),!1))),t<e.data.length&&(n.setStart(e,t),n.setEnd(e,t+1),bu(n)||o(wu(xu(n),!0)))};if(du(e.container()))return i(e.container(),e.offset()),r;if(au(e.container()))if(e.isAtEnd())n=pu(e.container(),e.offset()),du(n)&&i(n,n.data.length),lu(n)&&!mu(n)&&o(wu(xu(n),!1));else{if(n=pu(e.container(),e.offset()),du(n)&&i(n,0),lu(n)&&e.isAtEnd())return o(wu(xu(n),!1)),r;t=pu(e.container(),e.offset()-1),lu(t)&&!mu(t)&&(su(t)||su(n)||!lu(n))&&o(wu(xu(t),!1)),lu(n)&&o(wu(xu(n),!0))}return r};function Eu(t,n,e){var r=function(){return e||(e=Nu(Eu(t,n))),e};return{container:q(t),offset:q(n),toRange:function(){var e;return(e=hu(t.ownerDocument)).setStart(t,n),e.setEnd(t,n),e},getClientRects:r,isVisible:function(){return 0<r().length},isAtStart:function(){return du(t),0===n},isAtEnd:function(){return du(t)?n>=t.data.length:n>=t.childNodes.length},isEqual:function(e){return e&&t===e.container()&&n===e.offset()},getNode:function(e){return pu(t,e?n-1:n)}}}(Zi=Eu||(Eu={})).fromRangeStart=function(e){return Zi(e.startContainer,e.startOffset)},Zi.fromRangeEnd=function(e){return Zi(e.endContainer,e.endOffset)},Zi.after=function(e){return Zi(e.parentNode,gu(e)+1)},Zi.before=function(e){return Zi(e.parentNode,gu(e))},Zi.isAbove=function(e,t){return nu(Z(t.getClientRects()),ee(e.getClientRects()),Ya).getOr(!1)},Zi.isBelow=function(e,t){return nu(ee(t.getClientRects()),Z(e.getClientRects()),Ga).getOr(!1)},Zi.isAtStart=function(e){return!!e&&e.isAtStart()},Zi.isAtEnd=function(e){return!!e&&e.isAtEnd()},Zi.isTextPosition=function(e){return!!e&&Uo.isText(e.container())},Zi.isElementPosition=function(e){return!1===Zi.isTextPosition(e)};var Su,Tu,ku=Eu,_u=Uo.isText,Au=Uo.isBogus,Ru=Ei.nodeIndex,Du=function(e){var t=e.parentNode;return Au(t)?Du(t):t},Ou=function(e){return e?Ht.reduce(e.childNodes,function(e,t){return Au(t)&&"BR"!==t.nodeName?e=e.concat(Ou(t)):e.push(t),e},[]):[]},Bu=function(t){return function(e){return t===e}},Pu=function(e){var t,r,n,o;return(_u(e)?"text()":e.nodeName.toLowerCase())+"["+(r=Ou(Du(t=e)),n=Ht.findIndex(r,Bu(t),t),r=r.slice(0,n+1),o=Ht.reduce(r,function(e,t,n){return _u(t)&&_u(r[n-1])&&e++,e},0),r=Ht.filter(r,Uo.matchNodeNames(t.nodeName)),(n=Ht.findIndex(r,Bu(t),t))-o)+"]"},Iu=function(e,t){var n,r,o,i,a,u=[];return n=t.container(),r=t.offset(),_u(n)?o=function(e,t){for(;(e=e.previousSibling)&&_u(e);)t+=e.data.length;return t}(n,r):(r>=(i=n.childNodes).length?(o="after",r=i.length-1):o="before",n=i[r]),u.push(Pu(n)),a=function(e,t,n){var r=[];for(t=t.parentNode;!(t===e||n&&n(t));t=t.parentNode)r.push(t);return r}(e,n),a=Ht.filter(a,y(Uo.isBogus)),(u=u.concat(Ht.map(a,function(e){return Pu(e)}))).reverse().join("/")+","+o},Lu=function(e,t){var n,r,o;return t?(t=(n=t.split(","))[0].split("/"),o=1<n.length?n[1]:"before",(r=Ht.reduce(t,function(e,t){return(t=/([\w\-\(\)]+)\[([0-9]+)\]/.exec(t))?("text()"===t[1]&&(t[1]="#text"),n=e,r=t[1],o=parseInt(t[2],10),i=Ou(n),i=Ht.filter(i,function(e,t){return!_u(e)||!_u(i[t-1])}),(i=Ht.filter(i,Uo.matchNodeNames(r)))[o]):null;var n,r,o,i},e))?_u(r)?function(e,t){for(var n,r=e,o=0;_u(r);){if(n=r.data.length,o<=t&&t<=o+n){e=r,t-=o;break}if(!_u(r.nextSibling)){e=r,t=n;break}o+=n,r=r.nextSibling}return _u(e)&&t>e.data.length&&(t=e.data.length),ku(e,t)}(r,parseInt(o,10)):(o="after"===o?Ru(r)+1:Ru(r),ku(r.parentNode,o)):null):null},Fu=function(e,t){Uo.isText(t)&&0===t.data.length&&e.remove(t)},Mu=function(e,t,n){var r,o,i,a,u,s,c;Uo.isDocumentFragment(n)?(i=e,a=t,u=n,s=_.from(u.firstChild),c=_.from(u.lastChild),a.insertNode(u),s.each(function(e){return Fu(i,e.previousSibling)}),c.each(function(e){return Fu(i,e.nextSibling)})):(r=e,o=n,t.insertNode(o),Fu(r,o.previousSibling),Fu(r,o.nextSibling))},zu=Uo.isContentEditableFalse,Uu=function(e,t,n,r,o){var i,a=r[o?"startContainer":"endContainer"],u=r[o?"startOffset":"endOffset"],s=[],c=0,l=e.getRoot();for(Uo.isText(a)?s.push(n?function(e,t,n){var r,o;for(o=e(t.data.slice(0,n)).length,r=t.previousSibling;r&&Uo.isText(r);r=r.previousSibling)o+=e(r.data).length;return o}(t,a,u):u):(u>=(i=a.childNodes).length&&i.length&&(c=1,u=Math.max(0,i.length-1)),s.push(e.nodeIndex(i[u],n)+c));a&&a!==l;a=a.parentNode)s.push(e.nodeIndex(a,n));return s},ju=function(e,t,n){var r=0;return Xt.each(e.select(t),function(e){if("all"!==e.getAttribute("data-mce-bogus"))return e!==n&&void r++}),r},Vu=function(e,t){var n,r,o,i=t?"start":"end";n=e[i+"Container"],r=e[i+"Offset"],Uo.isElement(n)&&"TR"===n.nodeName&&(n=(o=n.childNodes)[Math.min(t?r:r-1,o.length-1)])&&(r=t?0:n.childNodes.length,e["set"+(t?"Start":"End")](n,r))},Hu=function(e){return Vu(e,!0),Vu(e,!1),e},qu=function(e,t){var n;if(Uo.isElement(e)&&(e=Za(e,t),zu(e)))return e;if(Ta(e)){if(Uo.isText(e)&&Ea(e)&&(e=e.parentNode),n=e.previousSibling,zu(n))return n;if(n=e.nextSibling,zu(n))return n}},$u=function(e,t,n){var r=n.getNode(),o=r?r.nodeName:null,i=n.getRng();if(zu(r)||"IMG"===o)return{name:o,index:ju(n.dom,o,r)};var a,u,s,c,l,f,d,m=qu((a=i).startContainer,a.startOffset)||qu(a.endContainer,a.endOffset);return m?{name:o=m.tagName,index:ju(n.dom,o,m)}:(u=e,c=t,l=i,f=(s=n).dom,(d={}).start=Uu(f,u,c,l,!0),s.isCollapsed()||(d.end=Uu(f,u,c,l,!1)),d)},Wu=function(e,t,n){var r={"data-mce-type":"bookmark",id:t,style:"overflow:hidden;line-height:0px"};return n?e.create("span",r,"&#xFEFF;"):e.create("span",r)},Ku=function(e,t){var n=e.dom,r=e.getRng(),o=n.uniqueId(),i=e.isCollapsed(),a=e.getNode(),u=a.nodeName;if("IMG"===u)return{name:u,index:ju(n,u,a)};var s=Hu(r.cloneRange());if(!i){s.collapse(!1);var c=Wu(n,o+"_end",t);Mu(n,s,c)}(r=Hu(r)).collapse(!0);var l=Wu(n,o+"_start",t);return Mu(n,r,l),e.moveToBookmark({id:o,keep:1}),{id:o}},Xu={getBookmark:function(e,t,n){return 2===t?$u(xa,n,e):3===t?(o=(r=e).getRng(),{start:Iu(r.dom.getRoot(),ku.fromRangeStart(o)),end:Iu(r.dom.getRoot(),ku.fromRangeEnd(o))}):t?{rng:e.getRng()}:Ku(e,!1);var r,o},getUndoBookmark:d($u,$,!0),getPersistentBookmark:Ku},Yu="_mce_caret",Gu=function(e){return Uo.isElement(e)&&e.id===Yu},Ju=function(e,t){for(;t&&t!==e;){if(t.id===Yu)return t;t=t.parentNode}return null},Qu=Uo.isElement,Zu=Uo.isText,es=function(e){var t=e.parentNode;t&&t.removeChild(e)},ts=function(e,t){0===t.length?es(e):e.nodeValue=t},ns=function(e){var t=xa(e);return{count:e.length-t.length,text:t}},rs=function(e,t){return as(e),t},os=function(e,t){var n,r,o,i=t.container(),a=(n=te(i.childNodes),r=e,o=L(n,r),-1===o?_.none():_.some(o)).map(function(e){return e<t.offset()?ku(i,t.offset()-1):t}).getOr(t);return as(e),a},is=function(e,t){return Zu(e)&&t.container()===e?(r=t,o=ns((n=e).data.substr(0,r.offset())),i=ns(n.data.substr(r.offset())),0<(a=o.text+i.text).length?(ts(n,a),ku(n,r.offset()-o.count)):r):rs(e,t);var n,r,o,i,a},as=function(e){if(Qu(e)&&Ta(e)&&(ka(e)?e.removeAttribute("data-mce-caret"):es(e)),Zu(e)){var t=xa(function(e){try{return e.nodeValue}catch(t){return""}}(e));ts(e,t)}},us={removeAndReposition:function(e,t){return ku.isTextPosition(t)?is(e,t):(n=e,(r=t).container()===n.parentNode?os(n,r):rs(n,r));var n,r},remove:as},ss=or.detect().browser,cs=Uo.isContentEditableFalse,ls=function(e,t,n){var r,o,i,a,u,s=Ka(t.getBoundingClientRect(),n);return"BODY"===e.tagName?(r=e.ownerDocument.documentElement,o=e.scrollLeft||r.scrollLeft,i=e.scrollTop||r.scrollTop):(u=e.getBoundingClientRect(),o=e.scrollLeft-u.left,i=e.scrollTop-u.top),s.left+=o,s.right+=o,s.top+=i,s.bottom+=i,s.width=1,0<(a=t.offsetWidth-t.clientWidth)&&(n&&(a*=-1),s.left+=a,s.right+=a),s},fs=function(a,u,e){var t,s,c=Vi(_.none()),l=function(){!function(e){var t,n,r,o,i;for(t=gn("*[contentEditable=false]",e),o=0;o<t.length;o++)r=(n=t[o]).previousSibling,Oa(r)&&(1===(i=r.data).length?r.parentNode.removeChild(r):r.deleteData(i.length-1,1)),r=n.nextSibling,Da(r)&&(1===(i=r.data).length?r.parentNode.removeChild(r):r.deleteData(0,1))}(a),s&&(us.remove(s),s=null),c.get().each(function(e){gn(e.caret).remove(),c.set(_.none())}),clearInterval(t)},f=function(){t=he.setInterval(function(){e()?gn("div.mce-visual-caret",a).toggleClass("mce-visual-caret-hidden"):gn("div.mce-visual-caret",a).addClass("mce-visual-caret-hidden")},500)};return{show:function(t,e){var n,r,o;if(l(),o=e,Uo.isElement(o)&&/^(TD|TH)$/i.test(o.tagName))return null;if(!u(e))return s=function(e,t){var n,r,o;if(r=e.ownerDocument.createTextNode(Ca),o=e.parentNode,t){if(n=e.previousSibling,Na(n)){if(Ta(n))return n;if(Oa(n))return n.splitText(n.data.length-1)}o.insertBefore(r,e)}else{if(n=e.nextSibling,Na(n)){if(Ta(n))return n;if(Da(n))return n.splitText(1),n}e.nextSibling?o.insertBefore(r,e.nextSibling):o.appendChild(r)}return r}(e,t),r=e.ownerDocument.createRange(),cs(s.nextSibling)?(r.setStart(s,0),r.setEnd(s,0)):(r.setStart(s,1),r.setEnd(s,1)),r;s=Ra("p",e,t),n=ls(a,e,t),gn(s).css("top",n.top);var i=gn('<div class="mce-visual-caret" data-mce-bogus="all"></div>').css(n).appendTo(a)[0];return c.set(_.some({caret:i,element:e,before:t})),c.get().each(function(e){t&&gn(e.caret).addClass("mce-visual-caret-before")}),f(),(r=e.ownerDocument.createRange()).setStart(s,0),r.setEnd(s,0),r},hide:l,getCss:function(){return".mce-visual-caret {position: absolute;background-color: black;background-color: currentcolor;}.mce-visual-caret-hidden {display: none;}*[data-mce-caret] {position: absolute;left: -1000px;right: auto;top: 0;margin: 0;padding: 0;}"},reposition:function(){c.get().each(function(e){var t=ls(a,e.element,e.before);gn(e.caret).css(t)})},destroy:function(){return he.clearInterval(t)}}},ds=function(){return ss.isIE()||ss.isEdge()||ss.isFirefox()},ms=function(e){return cs(e)||Uo.isTable(e)&&ds()},gs=Uo.isContentEditableFalse,ps=Uo.matchStyleValues("display","block table table-cell table-caption list-item"),hs=Ta,vs=Ea,ys=Uo.isElement,bs=Va,Cs=function(e){return 0<e},xs=function(e){return e<0},ws=function(e,t){for(var n;n=e(t);)if(!vs(n))return n;return null},Ns=function(e,t,n,r,o){var i=new mo(e,r);if(xs(t)){if((gs(e)||vs(e))&&n(e=ws(i.prev,!0)))return e;for(;e=ws(i.prev,o);)if(n(e))return e}if(Cs(t)){if((gs(e)||vs(e))&&n(e=ws(i.next,!0)))return e;for(;e=ws(i.next,o);)if(n(e))return e}return null},Es=function(e,t){for(;e&&e!==t;){if(ps(e))return e;e=e.parentNode}return null},Ss=function(e,t,n){return Es(e.container(),n)===Es(t.container(),n)},Ts=function(e,t){var n,r;return t?(n=t.container(),r=t.offset(),ys(n)?n.childNodes[r+e]:null):null},ks=function(e,t){var n=t.ownerDocument.createRange();return e?(n.setStartBefore(t),n.setEndBefore(t)):(n.setStartAfter(t),n.setEndAfter(t)),n},_s=function(e,t,n){var r,o,i,a;for(o=e?"previousSibling":"nextSibling";n&&n!==t;){if(r=n[o],hs(r)&&(r=r[o]),gs(r)){if(a=n,Es(r,i=t)===Es(a,i))return r;break}if(bs(r))break;n=n.parentNode}return null},As=d(ks,!0),Rs=d(ks,!1),Ds=function(e,t,n){var r,o,i,a,u=d(_s,!0,t),s=d(_s,!1,t);if(o=n.startContainer,i=n.startOffset,Ea(o)){if(ys(o)||(o=o.parentNode),"before"===(a=o.getAttribute("data-mce-caret"))&&(r=o.nextSibling,ms(r)))return As(r);if("after"===a&&(r=o.previousSibling,ms(r)))return Rs(r)}if(!n.collapsed)return n;if(Uo.isText(o)){if(hs(o)){if(1===e){if(r=s(o))return As(r);if(r=u(o))return Rs(r)}if(-1===e){if(r=u(o))return Rs(r);if(r=s(o))return As(r)}return n}if(Oa(o)&&i>=o.data.length-1)return 1===e&&(r=s(o))?As(r):n;if(Da(o)&&i<=1)return-1===e&&(r=u(o))?Rs(r):n;if(i===o.data.length)return(r=s(o))?As(r):n;if(0===i)return(r=u(o))?Rs(r):n}return n},Os=function(e,t){return _.from(Ts(e?0:-1,t)).filter(gs)},Bs=function(e,t,n){var r=Ds(e,t,n);return-1===e?Eu.fromRangeStart(r):Eu.fromRangeEnd(r)},Ps=function(e){return _.from(e.getNode()).map(ar.fromDom)},Is=function(e,t){for(;t=e(t);)if(t.isVisible())return t;return t},Ls=function(e,t){var n=Ss(e,t);return!(n||!Uo.isBr(e.getNode()))||n};(Tu=Su||(Su={}))[Tu.Backwards=-1]="Backwards",Tu[Tu.Forwards=1]="Forwards";var Fs,Ms,zs,Us=Uo.isContentEditableFalse,js=Uo.isText,Vs=Uo.isElement,Hs=Uo.isBr,qs=Va,$s=function(e){return za(e)||!!Ha(t=e)&&!0!==j(te(t.getElementsByTagName("*")),function(e,t){return e||Pa(t)},!1);var t},Ws=qa,Ks=function(e,t){return e.hasChildNodes()&&t<e.childNodes.length?e.childNodes[t]:null},Xs=function(e,t){if(Cs(e)){if(qs(t.previousSibling)&&!js(t.previousSibling))return ku.before(t);if(js(t))return ku(t,0)}if(xs(e)){if(qs(t.nextSibling)&&!js(t.nextSibling))return ku.after(t);if(js(t))return ku(t,t.data.length)}return xs(e)?Hs(t)?ku.before(t):ku.after(t):ku.before(t)},Ys=function(e,t,n){var r,o,i,a,u;if(!Vs(n)||!t)return null;if(t.isEqual(ku.after(n))&&n.lastChild){if(u=ku.after(n.lastChild),xs(e)&&qs(n.lastChild)&&Vs(n.lastChild))return Hs(n.lastChild)?ku.before(n.lastChild):u}else u=t;var s,c,l,f=u.container(),d=u.offset();if(js(f)){if(xs(e)&&0<d)return ku(f,--d);if(Cs(e)&&d<f.length)return ku(f,++d);r=f}else{if(xs(e)&&0<d&&(o=Ks(f,d-1),qs(o)))return!$s(o)&&(i=Ns(o,e,Ws,o))?js(i)?ku(i,i.data.length):ku.after(i):js(o)?ku(o,o.data.length):ku.before(o);if(Cs(e)&&d<f.childNodes.length&&(o=Ks(f,d),qs(o)))return Hs(o)?(s=n,(l=(c=o).nextSibling)&&qs(l)?js(l)?ku(l,0):ku.before(l):Ys(Su.Forwards,ku.after(c),s)):!$s(o)&&(i=Ns(o,e,Ws,o))?js(i)?ku(i,0):ku.before(i):js(o)?ku(o,0):ku.after(o);r=o||u.getNode()}return(Cs(e)&&u.isAtEnd()||xs(e)&&u.isAtStart())&&(r=Ns(r,e,q(!0),n,!0),Ws(r,n))?Xs(e,r):(o=Ns(r,e,Ws,n),!(a=Ht.last(U(function(e,t){for(var n=[];e&&e!==t;)n.push(e),e=e.parentNode;return n}(f,n),Us)))||o&&a.contains(o)?o?Xs(e,o):null:u=Cs(e)?ku.after(a):ku.before(a))},Gs=function(t){return{next:function(e){return Ys(Su.Forwards,e,t)},prev:function(e){return Ys(Su.Backwards,e,t)}}},Js=function(e){return ku.isTextPosition(e)?0===e.offset():Va(e.getNode())},Qs=function(e){if(ku.isTextPosition(e)){var t=e.container();return e.offset()===t.data.length}return Va(e.getNode(!0))},Zs=function(e,t){return!ku.isTextPosition(e)&&!ku.isTextPosition(t)&&e.getNode()===t.getNode(!0)},ec=function(e,t,n){return e?!Zs(t,n)&&(r=t,!(!ku.isTextPosition(r)&&Uo.isBr(r.getNode())))&&Qs(t)&&Js(n):!Zs(n,t)&&Js(t)&&Qs(n);var r},tc=function(e,t,n){var r=Gs(t);return _.from(e?r.next(n):r.prev(n))},nc=function(t,n,r){return tc(t,n,r).bind(function(e){return Ss(r,e,n)&&ec(t,r,e)?tc(t,n,e):_.some(e)})},rc=function(t,n,e,r){return nc(t,n,e).bind(function(e){return r(e)?rc(t,n,e,r):_.some(e)})},oc=function(e,t){var n,r,o,i,a,u=e?t.firstChild:t.lastChild;return Uo.isText(u)?_.some(ku(u,e?0:u.data.length)):u?Va(u)?_.some(e?ku.before(u):(a=u,Uo.isBr(a)?ku.before(a):ku.after(a))):(r=t,o=u,i=(n=e)?ku.before(o):ku.after(o),tc(n,r,i)):_.none()},ic=d(tc,!0),ac=d(tc,!1),uc={fromPosition:tc,nextPosition:ic,prevPosition:ac,navigate:nc,navigateIgnore:rc,positionIn:oc,firstPositionIn:d(oc,!0),lastPositionIn:d(oc,!1)},sc=function(e,t){return Uo.isElement(t)&&e.isBlock(t)&&!t.innerHTML&&!fe.ie&&(t.innerHTML='<br data-mce-bogus="1" />'),t},cc=function(e,t){return uc.lastPositionIn(e).fold(function(){return!1},function(e){return t.setStart(e.container(),e.offset()),t.setEnd(e.container(),e.offset()),!0})},lc=function(e,t,n){return!(!1!==t.hasChildNodes()||!Ju(e,t)||(o=n,i=(r=t).ownerDocument.createTextNode(Ca),r.appendChild(i),o.setStart(i,0),o.setEnd(i,0),0));var r,o,i},fc=function(e,t,n,r){var o,i,a,u,s=n[t?"start":"end"],c=e.getRoot();if(s){for(a=s[0],i=c,o=s.length-1;1<=o;o--){if(u=i.childNodes,lc(c,i,r))return!0;if(s[o]>u.length-1)return!!lc(c,i,r)||cc(i,r);i=u[s[o]]}3===i.nodeType&&(a=Math.min(s[0],i.nodeValue.length)),1===i.nodeType&&(a=Math.min(s[0],i.childNodes.length)),t?r.setStart(i,a):r.setEnd(i,a)}return!0},dc=function(e){return Uo.isText(e)&&0<e.data.length},mc=function(e,t,n){var r,o,i,a,u,s,c=e.get(n.id+"_"+t),l=n.keep;if(c){if(r=c.parentNode,"start"===t?l?c.hasChildNodes()?(r=c.firstChild,o=1):dc(c.nextSibling)?(r=c.nextSibling,o=0):dc(c.previousSibling)?(r=c.previousSibling,o=c.previousSibling.data.length):(r=c.parentNode,o=e.nodeIndex(c)+1):o=e.nodeIndex(c):l?c.hasChildNodes()?(r=c.firstChild,o=1):dc(c.previousSibling)?(r=c.previousSibling,o=c.previousSibling.data.length):(r=c.parentNode,o=e.nodeIndex(c)):o=e.nodeIndex(c),u=r,s=o,!l){for(a=c.previousSibling,i=c.nextSibling,Xt.each(Xt.grep(c.childNodes),function(e){Uo.isText(e)&&(e.nodeValue=e.nodeValue.replace(/\uFEFF/g,""))});c=e.get(n.id+"_"+t);)e.remove(c,!0);a&&i&&a.nodeType===i.nodeType&&Uo.isText(a)&&!fe.opera&&(o=a.nodeValue.length,a.appendData(i.nodeValue),e.remove(i),u=a,s=o)}return _.some(ku(u,s))}return _.none()},gc=function(e,t){var n,r,o,i,a,u,s,c,l,f,d,m,g,p,h,v,y=e.dom;if(t){if(v=t,Xt.isArray(v.start))return p=t,h=(g=y).createRng(),fc(g,!0,p,h)&&fc(g,!1,p,h)?_.some(h):_.none();if("string"==typeof t.start)return _.some((f=t,d=(l=y).createRng(),m=Lu(l.getRoot(),f.start),d.setStart(m.container(),m.offset()),m=Lu(l.getRoot(),f.end),d.setEnd(m.container(),m.offset()),d));if(t.hasOwnProperty("id"))return s=mc(o=y,"start",i=t),c=mc(o,"end",i),nu(s,(u=s,(a=c).isSome()?a:u),function(e,t){var n=o.createRng();return n.setStart(sc(o,e.container()),e.offset()),n.setEnd(sc(o,t.container()),t.offset()),n});if(t.hasOwnProperty("name"))return n=y,r=t,_.from(n.select(r.name)[r.index]).map(function(e){var t=n.createRng();return t.selectNode(e),t});if(t.hasOwnProperty("rng"))return _.some(t.rng)}return _.none()},pc=function(e,t,n){return Xu.getBookmark(e,t,n)},hc=function(t,e){gc(t,e).each(function(e){t.setRng(e)})},vc=function(e){return Uo.isElement(e)&&"SPAN"===e.tagName&&"bookmark"===e.getAttribute("data-mce-type")},yc=function(e){return e&&/^(IMG)$/.test(e.nodeName)},bc=function(e){return e&&3===e.nodeType&&/^([\t \r\n]+|)$/.test(e.nodeValue)},Cc=function(e,t,n){return"color"!==n&&"backgroundColor"!==n||(t=e.toHex(t)),"fontWeight"===n&&700===t&&(t="bold"),"fontFamily"===n&&(t=t.replace(/[\'\"]/g,"").replace(/,\s+/g,",")),""+t},xc={isInlineBlock:yc,moveStart:function(e,t,n){var r,o,i,a=n.startOffset,u=n.startContainer;if((n.startContainer!==n.endContainer||!yc(n.startContainer.childNodes[n.startOffset]))&&1===u.nodeType)for(a<(i=u.childNodes).length?r=new mo(u=i[a],e.getParent(u,e.isBlock)):(r=new mo(u=i[i.length-1],e.getParent(u,e.isBlock))).next(!0),o=r.current();o;o=r.next())if(3===o.nodeType&&!bc(o))return n.setStart(o,0),void t.setRng(n)},getNonWhiteSpaceSibling:function(e,t,n){if(e)for(t=t?"nextSibling":"previousSibling",e=n?e:e[t];e;e=e[t])if(1===e.nodeType||!bc(e))return e},isTextBlock:function(e,t){return t.nodeType&&(t=t.nodeName),!!e.schema.getTextBlockElements()[t.toLowerCase()]},isValid:function(e,t,n){return e.schema.isValidChild(t,n)},isWhiteSpaceNode:bc,replaceVars:function(e,n){return"string"!=typeof e?e=e(n):n&&(e=e.replace(/%(\w+)/g,function(e,t){return n[t]||e})),e},isEq:function(e,t){return t=t||"",e=""+((e=e||"").nodeName||e),t=""+(t.nodeName||t),e.toLowerCase()===t.toLowerCase()},normalizeStyleValue:Cc,getStyle:function(e,t,n){return Cc(e,e.getStyle(t,n),n)},getTextDecoration:function(t,e){var n;return t.getParent(e,function(e){return(n=t.getStyle(e,"text-decoration"))&&"none"!==n}),n},getParents:function(e,t,n){return e.getParents(t,n,e.getRoot())}},wc=vc,Nc=xc.getParents,Ec=xc.isWhiteSpaceNode,Sc=xc.isTextBlock,Tc=function(e,t){for(void 0===t&&(t=3===e.nodeType?e.length:e.childNodes.length);e&&e.hasChildNodes();)(e=e.childNodes[t])&&(t=3===e.nodeType?e.length:e.childNodes.length);return{node:e,offset:t}},kc=function(e,t){for(var n=t;n;){if(1===n.nodeType&&e.getContentEditable(n))return"false"===e.getContentEditable(n)?n:t;n=n.parentNode}return t},_c=function(e,t,n,r){var o,i,a=n.nodeValue;return void 0===r&&(r=e?a.length:0),e?(o=a.lastIndexOf(" ",r),-1!==(o=(i=a.lastIndexOf("\xa0",r))<o?o:i)&&!t&&(o<r||!e)&&o<=a.length&&o++):(o=a.indexOf(" ",r),i=a.indexOf("\xa0",r),o=-1!==o&&(-1===i||o<i)?o:i),o},Ac=function(e,t,n,r,o,i){var a,u,s,c;if(3===n.nodeType){if(-1!==(s=_c(o,i,n,r)))return{container:n,offset:s};c=n}for(a=new mo(n,e.getParent(n,e.isBlock)||t);u=a[o?"prev":"next"]();)if(3!==u.nodeType||wc(u.parentNode)){if(e.isBlock(u)||xc.isEq(u,"BR"))break}else if(-1!==(s=_c(o,i,c=u)))return{container:u,offset:s};if(c)return{container:c,offset:r=o?0:c.length}},Rc=function(e,t,n,r,o){var i,a,u,s;for(3===r.nodeType&&0===r.nodeValue.length&&r[o]&&(r=r[o]),i=Nc(e,r),a=0;a<i.length;a++)for(u=0;u<t.length;u++)if(!("collapsed"in(s=t[u])&&s.collapsed!==n.collapsed)&&e.is(i[a],s.selector))return i[a];return r},Dc=function(t,e,n,r){var o,i=t.dom,a=i.getRoot();if(e[0].wrapper||(o=i.getParent(n,e[0].block,a)),!o){var u=i.getParent(n,"LI,TD,TH");o=i.getParent(3===n.nodeType?n.parentNode:n,function(e){return e!==a&&Sc(t,e)},u)}if(o&&e[0].wrapper&&(o=Nc(i,o,"ul,ol").reverse()[0]||o),!o)for(o=n;o[r]&&!i.isBlock(o[r])&&(o=o[r],!xc.isEq(o,"br")););return o||n},Oc=function(e,t,n,r,o,i,a){var u,s,c,l,f,d;if(u=s=a?n:o,l=a?"previousSibling":"nextSibling",f=e.getRoot(),3===u.nodeType&&!Ec(u)&&(a?0<r:i<u.nodeValue.length))return u;for(;;){if(!t[0].block_expand&&e.isBlock(s))return s;for(c=s[l];c;c=c[l])if(!wc(c)&&!Ec(c)&&("BR"!==(d=c).nodeName||!d.getAttribute("data-mce-bogus")||d.nextSibling))return s;if(s===f||s.parentNode===f){u=s;break}s=s.parentNode}return u},Bc=function(e,t,n,r){var o,i=t.startContainer,a=t.startOffset,u=t.endContainer,s=t.endOffset,c=e.dom;return 1===i.nodeType&&i.hasChildNodes()&&3===(i=Za(i,a)).nodeType&&(a=0),1===u.nodeType&&u.hasChildNodes()&&3===(u=Za(u,t.collapsed?s:s-1)).nodeType&&(s=u.nodeValue.length),i=kc(c,i),u=kc(c,u),(wc(i.parentNode)||wc(i))&&(i=wc(i)?i:i.parentNode,3===(i=t.collapsed?i.previousSibling||i:i.nextSibling||i).nodeType&&(a=t.collapsed?i.length:0)),(wc(u.parentNode)||wc(u))&&(u=wc(u)?u:u.parentNode,3===(u=t.collapsed?u.nextSibling||u:u.previousSibling||u).nodeType&&(s=t.collapsed?0:u.length)),t.collapsed&&((o=Ac(c,e.getBody(),i,a,!0,r))&&(i=o.container,a=o.offset),(o=Ac(c,e.getBody(),u,s,!1,r))&&(u=o.container,s=o.offset)),n[0].inline&&(u=r?u:function(e,t){var n=Tc(e,t);if(n.node){for(;n.node&&0===n.offset&&n.node.previousSibling;)n=Tc(n.node.previousSibling);n.node&&0<n.offset&&3===n.node.nodeType&&" "===n.node.nodeValue.charAt(n.offset-1)&&1<n.offset&&(e=n.node).splitText(n.offset-1)}return e}(u,s)),(n[0].inline||n[0].block_expand)&&(n[0].inline&&3===i.nodeType&&0!==a||(i=Oc(c,n,i,a,u,s,!0)),n[0].inline&&3===u.nodeType&&s!==u.nodeValue.length||(u=Oc(c,n,i,a,u,s,!1))),n[0].selector&&!1!==n[0].expand&&!n[0].inline&&(i=Rc(c,n,t,i,"previousSibling"),u=Rc(c,n,t,u,"nextSibling")),(n[0].block||n[0].selector)&&(i=Dc(e,n,i,"previousSibling"),u=Dc(e,n,u,"nextSibling"),n[0].block&&(c.isBlock(i)||(i=Oc(c,n,i,a,u,s,!0)),c.isBlock(u)||(u=Oc(c,n,i,a,u,s,!1)))),1===i.nodeType&&(a=c.nodeIndex(i),i=i.parentNode),1===u.nodeType&&(s=c.nodeIndex(u)+1,u=u.parentNode),{startContainer:i,startOffset:a,endContainer:u,endOffset:s}},Pc=Xt.each,Ic=function(e,t,o){var n,r,i,a,u,s,c,l=t.startContainer,f=t.startOffset,d=t.endContainer,m=t.endOffset;if(0<(c=e.select("td[data-mce-selected],th[data-mce-selected]")).length)Pc(c,function(e){o([e])});else{var g,p,h,v=function(e){var t;return 3===(t=e[0]).nodeType&&t===l&&f>=t.nodeValue.length&&e.splice(0,1),t=e[e.length-1],0===m&&0<e.length&&t===d&&3===t.nodeType&&e.splice(e.length-1,1),e},y=function(e,t,n){for(var r=[];e&&e!==n;e=e[t])r.push(e);return r},b=function(e,t){do{if(e.parentNode===t)return e;e=e.parentNode}while(e)},C=function(e,t,n){var r=n?"nextSibling":"previousSibling";for(u=(a=e).parentNode;a&&a!==t;a=u)u=a.parentNode,(s=y(a===e?a:a[r],r)).length&&(n||s.reverse(),o(v(s)))};if(1===l.nodeType&&l.hasChildNodes()&&(l=l.childNodes[f]),1===d.nodeType&&d.hasChildNodes()&&(p=m,h=(g=d).childNodes,--p>h.length-1?p=h.length-1:p<0&&(p=0),d=h[p]||g),l===d)return o(v([l]));for(n=e.findCommonAncestor(l,d),a=l;a;a=a.parentNode){if(a===d)return C(l,n,!0);if(a===n)break}for(a=d;a;a=a.parentNode){if(a===l)return C(d,n);if(a===n)break}r=b(l,n)||l,i=b(d,n)||d,C(l,r,!0),(s=y(r===l?r:r.nextSibling,"nextSibling",i===d?i.nextSibling:i)).length&&o(v(s)),C(d,i)}},Lc=(Fs=mr,Ms="text",{get:function(e){if(!Fs(e))throw new Error("Can only get "+Ms+" value of a "+Ms+" node");return zs(e).getOr("")},getOption:zs=function(e){return Fs(e)?_.from(e.dom().nodeValue):_.none()},set:function(e,t){if(!Fs(e))throw new Error("Can only set raw "+Ms+" value of a "+Ms+" node");e.dom().nodeValue=t}}),Fc=function(e){return Lc.get(e)},Mc=function(r,o,i,a){return jr(o).fold(function(){return"skipping"},function(e){return"br"===a||mr(n=o)&&"\ufeff"===Fc(n)?"valid":dr(t=o)&&Yi(t,ia())?"existing":Gu(o)?"caret":xc.isValid(r,i,a)&&xc.isValid(r,lr(e),i)?"valid":"invalid-child";var t,n})},zc=function(e,t,n,r){var o,i,a=t.uid,u=void 0===a?(o="mce-annotation",i=(new Date).getTime(),o+"_"+Math.floor(1e9*Math.random())+ ++ma+String(i)):a,s=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(n[r[o]]=e[r[o]])}return n}(t,["uid"]),c=ar.fromTag("span",e);Ki(c,ia()),xr(c,""+ua(),u),xr(c,""+aa(),n);var l,f=r(u,s),d=f.attributes,m=void 0===d?{}:d,g=f.classes,p=void 0===g?[]:g;return wr(c,m),l=c,z(p,function(e){Ki(l,e)}),c},Uc=function(i,e,t,n,r){var a=[],u=zc(i.getDoc(),r,t,n),s=Vi(_.none()),c=function(){s.set(_.none())},l=function(e){z(e,o)},o=function(e){var t,n;switch(Mc(i,e,"span",lr(e))){case"invalid-child":c();var r=Wr(e);l(r),c();break;case"valid":var o=s.get().getOrThunk(function(){var e=pa(u);return a.push(e),s.set(_.some(e)),e});Bi(t=e,n=o),Li(n,t)}};return Ic(i.dom,e,function(e){var t;c(),t=W(e,ar.fromDom),l(t)}),a},jc=function(s,c,l,f){s.undoManager.transact(function(){var e,t,n,r,o=s.selection.getRng();if(o.collapsed&&(r=Bc(e=s,t=o,[{inline:!0}],3===(n=t).startContainer.nodeType&&n.startContainer.nodeValue.length>=n.startOffset&&"\xa0"===n.startContainer.nodeValue[n.startOffset]),t.setStart(r.startContainer,r.startOffset),t.setEnd(r.endContainer,r.endOffset),e.selection.setRng(t)),s.selection.getRng().collapsed){var i=zc(s.getDoc(),f,c,l.decorate);va(i,"\xa0"),s.selection.getRng().insertNode(i.dom()),s.selection.select(i.dom())}else{var a=Xu.getPersistentBookmark(s.selection,!1),u=s.selection.getRng();Uc(s,u,c,l.decorate,f),s.selection.moveToBookmark(a)}})};function Vc(s){var n,r=(n={},{register:function(e,t){n[e]={name:e,settings:t}},lookup:function(e){return n.hasOwnProperty(e)?_.from(n[e]).map(function(e){return e.settings}):_.none()}});fa(s,r);var o=la(s);return{register:function(e,t){r.register(e,t)},annotate:function(t,n){r.lookup(t).each(function(e){jc(s,t,e,n)})},annotationChanged:function(e,t){o.addListener(e,t)},remove:function(e){sa(s,_.some(e)).each(function(e){var t=e.elements;z(t,Ui)})},getAll:function(e){var t,n,r,o,i,a,u=(t=s,n=e,r=ar.fromDom(t.getBody()),o=Ji(r,"["+aa()+'="'+n+'"]'),i={},z(o,function(e){var t=Nr(e,ua()),n=i.hasOwnProperty(t)?i[t]:[];i[t]=n.concat([e])}),i);return a=function(e){return W(e,function(e){return e.dom()})},vr(u,function(e,t){return{k:t,v:a(e,t)}})}}}var Hc=function(e){return Xt.grep(e.childNodes,function(e){return"LI"===e.nodeName})},qc=function(e){return e&&e.firstChild&&e.firstChild===e.lastChild&&("\xa0"===(t=e.firstChild).data||Uo.isBr(t));var t},$c=function(e){return 0<e.length&&(!(t=e[e.length-1]).firstChild||qc(t))?e.slice(0,-1):e;var t},Wc=function(e,t){var n=e.getParent(t,e.isBlock);return n&&"LI"===n.nodeName?n:null},Kc=function(e,t){var n=ku.after(e),r=Gs(t).prev(n);return r?r.toRange():null},Xc=function(t,e,n){var r,o,i,a,u=t.parentNode;return Xt.each(e,function(e){u.insertBefore(e,t)}),r=t,o=n,i=ku.before(r),(a=Gs(o).next(i))?a.toRange():null},Yc=function(e,t){var n,r,o,i,a,u,s=t.firstChild,c=t.lastChild;return s&&"meta"===s.name&&(s=s.next),c&&"mce_marker"===c.attr("id")&&(c=c.prev),r=c,u=(n=e).getNonEmptyElements(),r&&(r.isEmpty(u)||(o=r,n.getBlockElements()[o.name]&&(a=o).firstChild&&a.firstChild===a.lastChild&&("br"===(i=o.firstChild).name||"\xa0"===i.value)))&&(c=c.prev),!(!s||s!==c||"ul"!==s.name&&"ol"!==s.name)},Gc=function(e,o,i,t){var n,r,a,u,s,c,l,f,d,m,g,p,h,v,y,b,C,x,w,N=(n=o,r=t,c=e.serialize(r),l=n.createFragment(c),u=(a=l).firstChild,s=a.lastChild,u&&"META"===u.nodeName&&u.parentNode.removeChild(u),s&&"mce_marker"===s.id&&s.parentNode.removeChild(s),a),E=Wc(o,i.startContainer),S=$c(Hc(N.firstChild)),T=o.getRoot(),k=function(e){var t=ku.fromRangeStart(i),n=Gs(o.getRoot()),r=1===e?n.prev(t):n.next(t);return!r||Wc(o,r.getNode())!==E};return k(1)?Xc(E,S,T):k(2)?(f=E,d=S,m=T,o.insertAfter(d.reverse(),f),Kc(d[0],m)):(p=S,h=T,v=g=E,b=(y=i).cloneRange(),C=y.cloneRange(),b.setStartBefore(v),C.setEndAfter(v),x=[b.cloneContents(),C.cloneContents()],(w=g.parentNode).insertBefore(x[0],g),Xt.each(p,function(e){w.insertBefore(e,g)}),w.insertBefore(x[1],g),w.removeChild(g),Kc(p[p.length-1],h))},Jc=function(e,t){return!!Wc(e,t)},Qc=Xt.each,Zc=function(o){this.compare=function(e,t){if(e.nodeName!==t.nodeName)return!1;var n=function(n){var r={};return Qc(o.getAttribs(n),function(e){var t=e.nodeName.toLowerCase();0!==t.indexOf("_")&&"style"!==t&&0!==t.indexOf("data-")&&(r[t]=o.getAttrib(n,t))}),r},r=function(e,t){var n,r;for(r in e)if(e.hasOwnProperty(r)){if(void 0===(n=t[r]))return!1;if(e[r]!==n)return!1;delete t[r]}for(r in t)if(t.hasOwnProperty(r))return!1;return!0};return!(!r(n(e),n(t))||!r(o.parseStyle(o.getAttrib(e,"style")),o.parseStyle(o.getAttrib(t,"style")))||vc(e)||vc(t))}},el=function(e){var t=Ji(e,"br"),n=U(function(e){for(var t=[],n=e.dom();n;)t.push(ar.fromDom(n)),n=n.lastChild;return t}(e).slice(-1),xo);t.length===n.length&&z(n,zi)},tl=function(e){Mi(e),Li(e,ar.fromHtml('<br data-mce-bogus="1">'))},nl=function(n){Yr(n).each(function(t){Vr(t).each(function(e){bo(n)&&xo(t)&&bo(e)&&zi(t)})})},rl=Xt.makeMap;function ol(e){var u,s,c,l,f,d=[];return u=(e=e||{}).indent,s=rl(e.indent_before||""),c=rl(e.indent_after||""),l=ei.getEncodeFunc(e.entity_encoding||"raw",e.entities),f="html"===e.element_format,{start:function(e,t,n){var r,o,i,a;if(u&&s[e]&&0<d.length&&0<(a=d[d.length-1]).length&&"\n"!==a&&d.push("\n"),d.push("<",e),t)for(r=0,o=t.length;r<o;r++)i=t[r],d.push(" ",i.name,'="',l(i.value,!0),'"');d[d.length]=!n||f?">":" />",n&&u&&c[e]&&0<d.length&&0<(a=d[d.length-1]).length&&"\n"!==a&&d.push("\n")},end:function(e){var t;d.push("</",e,">"),u&&c[e]&&0<d.length&&0<(t=d[d.length-1]).length&&"\n"!==t&&d.push("\n")},text:function(e,t){0<e.length&&(d[d.length]=t?e:l(e))},cdata:function(e){d.push("<![CDATA[",e,"]]>")},comment:function(e){d.push("\x3c!--",e,"--\x3e")},pi:function(e,t){t?d.push("<?",e," ",l(t),"?>"):d.push("<?",e,"?>"),u&&d.push("\n")},doctype:function(e){d.push("<!DOCTYPE",e,">",u?"\n":"")},reset:function(){d.length=0},getContent:function(){return d.join("").replace(/\n$/,"")}}}function il(t,g){void 0===g&&(g=fi());var p=ol(t);return(t=t||{}).validate=!("validate"in t)||t.validate,{serialize:function(e){var f,d;d=t.validate,f={3:function(e){p.text(e.value,e.raw)},8:function(e){p.comment(e.value)},7:function(e){p.pi(e.name,e.value)},10:function(e){p.doctype(e.value)},4:function(e){p.cdata(e.value)},11:function(e){if(e=e.firstChild)for(;m(e),e=e.next;);}},p.reset();var m=function(e){var t,n,r,o,i,a,u,s,c,l=f[e.type];if(l)l(e);else{if(t=e.name,n=e.shortEnded,r=e.attributes,d&&r&&1<r.length&&((a=[]).map={},c=g.getElementRule(e.name))){for(u=0,s=c.attributesOrder.length;u<s;u++)(o=c.attributesOrder[u])in r.map&&(i=r.map[o],a.map[o]=i,a.push({name:o,value:i}));for(u=0,s=r.length;u<s;u++)(o=r[u].name)in a.map||(i=r.map[o],a.map[o]=i,a.push({name:o,value:i}));r=a}if(p.start(e.name,r,n),!n){if(e=e.firstChild)for(;m(e),e=e.next;);p.end(t)}}};return 1!==e.type||t.inner?f[11](e):m(e),p.getContent()}}}var al,ul=function(a){var u=ku.fromRangeStart(a),s=ku.fromRangeEnd(a),c=a.commonAncestorContainer;return uc.fromPosition(!1,c,s).map(function(e){return!Ss(u,s,c)&&Ss(u,e,c)?(t=u.container(),n=u.offset(),r=e.container(),o=e.offset(),(i=V.document.createRange()).setStart(t,n),i.setEnd(r,o),i):a;var t,n,r,o,i}).getOr(a)},sl=function(e){return e.collapsed?e:ul(e)},cl=Uo.matchNodeNames("td th"),ll=function(e,t){var n,r,o=e.selection.getRng(),i=o.startContainer,a=o.startOffset;o.collapsed&&(n=i,r=a,Uo.isText(n)&&"\xa0"===n.nodeValue[r-1])&&Uo.isText(i)&&(i.insertData(a-1," "),i.deleteData(a,1),o.setStart(i,a),o.setEnd(i,a),e.selection.setRng(o)),e.selection.setContent(t)},fl=function(e,t,n){var r,o,i,a,u,s,c,l,f,d,m,g=e.selection,p=e.dom;if(/^ | $/.test(t)&&(t=function(e,t){var n,r;n=e.startContainer,r=e.startOffset;var o=function(e){return n[e]&&3===n[e].nodeType};return 3===n.nodeType&&(0<r?t=t.replace(/^&nbsp;/," "):o("previousSibling")||(t=t.replace(/^ /,"&nbsp;")),r<n.length?t=t.replace(/&nbsp;(<br>|)$/," "):o("nextSibling")||(t=t.replace(/(&nbsp;| )(<br>|)$/,"&nbsp;"))),t}(g.getRng(),t)),r=e.parser,m=n.merge,o=il({validate:e.settings.validate},e.schema),d='<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#x200B;</span>',s={content:t,format:"html",selection:!0,paste:n.paste},(s=e.fire("BeforeSetContent",s)).isDefaultPrevented())e.fire("SetContent",{content:s.content,format:"html",selection:!0,paste:n.paste});else{-1===(t=s.content).indexOf("{$caret}")&&(t+="{$caret}"),t=t.replace(/\{\$caret\}/,d);var h,v,y,b,C,x,w=(l=g.getRng()).startContainer||(l.parentElement?l.parentElement():null),N=e.getBody();w===N&&g.isCollapsed()&&p.isBlock(N.firstChild)&&(h=e,(v=N.firstChild)&&!h.schema.getShortEndedElements()[v.nodeName])&&p.isEmpty(N.firstChild)&&((l=p.createRng()).setStart(N.firstChild,0),l.setEnd(N.firstChild,0),g.setRng(l)),g.isCollapsed()||(e.selection.setRng(sl(e.selection.getRng())),e.getDoc().execCommand("Delete",!1,null),y=e.selection.getRng(),b=t,C=y.startContainer,x=y.startOffset,3===C.nodeType&&y.collapsed&&("\xa0"===C.data[x]?(C.deleteData(x,1),/[\u00a0| ]$/.test(b)||(b+=" ")):"\xa0"===C.data[x-1]&&(C.deleteData(x-1,1),/[\u00a0| ]$/.test(b)||(b=" "+b))),t=b);var E,S,T,k={context:(i=g.getNode()).nodeName.toLowerCase(),data:n.data,insert:!0};if(u=r.parse(t,k),!0===n.paste&&Yc(e.schema,u)&&Jc(p,i))return l=Gc(o,p,e.selection.getRng(),u),e.selection.setRng(l),void e.fire("SetContent",s);if(function(e){for(var t=e;t=t.walk();)1===t.type&&t.attr("data-mce-fragment","1")}(u),"mce_marker"===(f=u.lastChild).attr("id"))for(f=(c=f).prev;f;f=f.walk(!0))if(3===f.type||!p.isBlock(f.name)){e.schema.isValidChild(f.parent.name,"span")&&f.parent.insert(c,f,"br"===f.name);break}if(e._selectionOverrides.showBlockCaretContainer(i),k.invalid){for(ll(e,d),i=g.getNode(),a=e.getBody(),9===i.nodeType?i=f=a:f=i;f!==a;)f=(i=f).parentNode;t=i===a?a.innerHTML:p.getOuterHTML(i),t=o.serialize(r.parse(t.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i,function(){return o.serialize(u)}))),i===a?p.setHTML(a,t):p.setOuterHTML(i,t)}else!function(e,t,n){if("all"===n.getAttribute("data-mce-bogus"))n.parentNode.insertBefore(e.dom.createFragment(t),n);else{var r=n.firstChild,o=n.lastChild;!r||r===o&&"BR"===r.nodeName?e.dom.setHTML(n,t):ll(e,t)}}(e,t=o.serialize(u),i);!function(e,t){var n=e.schema.getTextInlineElements(),r=e.dom;if(t){var o=e.getBody(),i=new Zc(r);Xt.each(r.select("*[data-mce-fragment]"),function(e){for(var t=e.parentNode;t&&t!==o;t=t.parentNode)n[e.nodeName.toLowerCase()]&&i.compare(t,e)&&r.remove(e,!0)})}}(e,m),function(n,e){var t,r,o,i,a,u=n.dom,s=n.selection;if(e){if(n.selection.scrollIntoView(e),t=function(e){for(var t=n.getBody();e&&e!==t;e=e.parentNode)if("false"===n.dom.getContentEditable(e))return e;return null}(e))return u.remove(e),s.select(t);var c=u.createRng();(i=e.previousSibling)&&3===i.nodeType?(c.setStart(i,i.nodeValue.length),fe.ie||(a=e.nextSibling)&&3===a.nodeType&&(i.appendData(a.data),a.parentNode.removeChild(a))):(c.setStartBefore(e),c.setEndBefore(e)),r=u.getParent(e,u.isBlock),u.remove(e),r&&u.isEmpty(r)&&(n.$(r).empty(),c.setStart(r,0),c.setEnd(r,0),cl(r)||r.getAttribute("data-mce-fragment")||!(o=function(e){var t=ku.fromRangeStart(e);if(t=Gs(n.getBody()).next(t))return t.toRange()}(c))?u.add(r,u.create("br",{"data-mce-bogus":"1"})):(c=o,u.remove(r))),s.setRng(c)}}(e,p.get("mce_marker")),E=e.getBody(),Xt.each(E.getElementsByTagName("*"),function(e){e.removeAttribute("data-mce-fragment")}),S=e.dom,T=e.selection.getStart(),_.from(S.getParent(T,"td,th")).map(ar.fromDom).each(nl),e.fire("SetContent",s),e.addVisual()}},dl=function(e,t){var n,r,o="string"!=typeof(n=t)?(r=Xt.extend({paste:n.paste,data:{paste:n.paste}},n),{content:n.content,details:r}):{content:n,details:{}};fl(e,o.content,o.details)},ml=/[\u0591-\u07FF\uFB1D-\uFDFF\uFE70-\uFEFC]/,gl=function(e,t,n){var r=e.getParam(t,n);if(-1!==r.indexOf("=")){var o=e.getParam(t,"","hash");return o.hasOwnProperty(e.id)?o[e.id]:n}return r},pl=function(e){return e.getParam("iframe_attrs",{})},hl=function(e){return e.getParam("doctype","<!DOCTYPE html>")},vl=function(e){return e.getParam("document_base_url","")},yl=function(e){return gl(e,"body_id","tinymce")},bl=function(e){return gl(e,"body_class","")},Cl=function(e){return e.getParam("content_security_policy","")},xl=function(e){return e.getParam("br_in_pre",!0)},wl=function(e){if(e.getParam("force_p_newlines",!1))return"p";var t=e.getParam("forced_root_block","p");return!1===t?"":t},Nl=function(e){return e.getParam("forced_root_block_attrs",{})},El=function(e){return e.getParam("br_newline_selector",".mce-toc h2,figcaption,caption")},Sl=function(e){return e.getParam("no_newline_selector","")},Tl=function(e){return e.getParam("keep_styles",!0)},kl=function(e){return e.getParam("end_container_on_empty_block",!1)},_l=function(e){return Xt.explode(e.getParam("font_size_style_values",""))},Al=function(e){return Xt.explode(e.getParam("font_size_classes",""))},Rl=function(e){return e.getParam("images_dataimg_filter",q(!0),"function")},Dl=function(e){return e.getParam("automatic_uploads",!0,"boolean")},Ol=function(e){return e.getParam("images_reuse_filename",!1,"boolean")},Bl=function(e){return e.getParam("images_replace_blob_uris",!0,"boolean")},Pl=function(e){return e.getParam("images_upload_url","","string")},Il=function(e){return e.getParam("images_upload_base_path","","string")},Ll=function(e){return e.getParam("images_upload_credentials",!1,"boolean")},Fl=function(e){return e.getParam("images_upload_handler",null,"function")},Ml=function(e){return e.getParam("content_css_cors",!1,"boolean")},zl=function(e){return e.getParam("inline_boundaries_selector","a[href],code,.mce-annotation","string")},Ul=function(e,t){if(!t)return t;var n=t.container(),r=t.offset();return e?Sa(n)?Uo.isText(n.nextSibling)?ku(n.nextSibling,0):ku.after(n):_a(t)?ku(n,r+1):t:Sa(n)?Uo.isText(n.previousSibling)?ku(n.previousSibling,n.previousSibling.data.length):ku.before(n):Aa(t)?ku(n,r-1):t},jl={isInlineTarget:function(e,t){return Ir(ar.fromDom(t),zl(e))},findRootInline:function(e,t,n){var r,o,i,a=(r=e,o=t,i=n,U(Ei.DOM.getParents(i.container(),"*",o),r));return _.from(a[a.length-1])},isRtl:function(e){return"rtl"===Ei.DOM.getStyle(e,"direction",!0)||(t=e.textContent,ml.test(t));var t},isAtZwsp:function(e){return _a(e)||Aa(e)},normalizePosition:Ul,normalizeForwards:d(Ul,!0),normalizeBackwards:d(Ul,!1),hasSameParentBlock:function(e,t,n){var r=Es(t,e),o=Es(n,e);return r&&r===o}},Vl=function(e,t){return Mr(e,t)?ta(t,function(e){return wo(e)||Eo(e)},(n=e,function(e){return Fr(n,ar.fromDom(e.dom().parentNode))})):_.none();var n},Hl=function(e){var t,n,r;e.dom.isEmpty(e.getBody())&&(e.setContent(""),n=(t=e).getBody(),r=n.firstChild&&t.dom.isBlock(n.firstChild)?n.firstChild:n,t.selection.setCursorLocation(r,0))},ql=function(i,a,u){return nu(uc.firstPositionIn(u),uc.lastPositionIn(u),function(e,t){var n=jl.normalizePosition(!0,e),r=jl.normalizePosition(!1,t),o=jl.normalizePosition(!1,a);return i?uc.nextPosition(u,o).map(function(e){return e.isEqual(r)&&a.isEqual(n)}).getOr(!1):uc.prevPosition(u,o).map(function(e){return e.isEqual(n)&&a.isEqual(r)}).getOr(!1)}).getOr(!0)},$l=function(e,t){var n,r,o,i=ar.fromDom(e),a=ar.fromDom(t);return n=a,r="pre,code",o=d(Fr,i),na(n,r,o).isSome()},Wl=function(e,t){return Va(t)&&!1===(r=e,o=t,Uo.isText(o)&&/^[ \t\r\n]*$/.test(o.data)&&!1===$l(r,o))||(n=t,Uo.isElement(n)&&"A"===n.nodeName&&n.hasAttribute("name"))||Kl(t);var n,r,o},Kl=Uo.hasAttribute("data-mce-bookmark"),Xl=Uo.hasAttribute("data-mce-bogus"),Yl=Uo.hasAttributeValue("data-mce-bogus","all"),Gl=function(e){return function(e){var t,n,r=0;if(Wl(e,e))return!1;if(!(n=e.firstChild))return!0;t=new mo(n,e);do{if(Yl(n))n=t.next(!0);else if(Xl(n))n=t.next();else if(Uo.isBr(n))r++,n=t.next();else{if(Wl(e,n))return!1;n=t.next()}}while(n);return r<=1}(e.dom())},Jl=_r("block","position"),Ql=_r("from","to"),Zl=function(e,t){var n=ar.fromDom(e),r=ar.fromDom(t.container());return Vl(n,r).map(function(e){return Jl(e,t)})},ef=function(o,i,e){var t=Zl(o,ku.fromRangeStart(e)),n=t.bind(function(e){return uc.fromPosition(i,o,e.position()).bind(function(e){return Zl(o,e).map(function(e){return t=o,n=i,r=e,Uo.isBr(r.position().getNode())&&!1===Gl(r.block())?uc.positionIn(!1,r.block().dom()).bind(function(e){return e.isEqual(r.position())?uc.fromPosition(n,t,e).bind(function(e){return Zl(t,e)}):_.some(r)}).getOr(r):r;var t,n,r})})});return nu(t,n,Ql).filter(function(e){return!1===Fr((r=e).from().block(),r.to().block())&&jr((n=e).from().block()).bind(function(t){return jr(n.to().block()).filter(function(e){return Fr(t,e)})}).isSome()&&(t=e,!1===Uo.isContentEditableFalse(t.from().block().dom())&&!1===Uo.isContentEditableFalse(t.to().block().dom()));var t,n,r})},tf=function(e,t,n){return n.collapsed?ef(e,t,n):_.none()},nf=function(e,t,n){return Mr(t,e)?function(e,t){for(var n=D(t)?t:b,r=e.dom(),o=[];null!==r.parentNode&&r.parentNode!==undefined;){var i=r.parentNode,a=ar.fromDom(i);if(o.push(a),!0===n(a))break;r=i}return o}(e,function(e){return n(e)||Fr(e,t)}).slice(0,-1):[]},rf=function(e,t){return nf(e,t,q(!1))},of=rf,af=function(e,t){return[e].concat(rf(e,t))},uf=function(e){var t,n=(t=Wr(e),Y(t,bo).fold(function(){return t},function(e){return t.slice(0,e)}));return z(n,zi),n},sf=function(e,t){var n=af(t,e);return X(n.reverse(),Gl).each(zi)},cf=function(e,t,n,r){if(Gl(n))return tl(n),uc.firstPositionIn(n.dom());0===U(qr(r),function(e){return!Gl(e)}).length&&Gl(t)&&Bi(r,ar.fromTag("br"));var o=uc.prevPosition(n.dom(),ku.before(r.dom()));return z(uf(t),function(e){Bi(r,e)}),sf(e,t),o},lf=function(e,t,n){if(Gl(n))return zi(n),Gl(t)&&tl(t),uc.firstPositionIn(t.dom());var r=uc.lastPositionIn(n.dom());return z(uf(t),function(e){Li(n,e)}),sf(e,t),r},ff=function(e,t){return Mr(t,e)?(n=af(e,t),_.from(n[n.length-1])):_.none();var n},df=function(e,t){uc.positionIn(e,t.dom()).map(function(e){return e.getNode()}).map(ar.fromDom).filter(xo).each(zi)},mf=function(e,t,n){return df(!0,t),df(!1,n),ff(t,n).fold(d(lf,e,t,n),d(cf,e,t,n))},gf=function(e,t,n,r){return t?mf(e,r,n):mf(e,n,r)},pf=function(t,n){var e,r=ar.fromDom(t.getBody());return(e=tf(r.dom(),n,t.selection.getRng()).bind(function(e){return gf(r,n,e.from().block(),e.to().block())})).each(function(e){t.selection.setRng(e.toRange())}),e.isSome()},hf=function(e,t){var n=ar.fromDom(t),r=d(Fr,e);return ea(n,ko,r).isSome()},vf=function(e,t){var n,r,o=uc.prevPosition(e.dom(),ku.fromRangeStart(t)).isNone(),i=uc.nextPosition(e.dom(),ku.fromRangeEnd(t)).isNone();return!(hf(n=e,(r=t).startContainer)||hf(n,r.endContainer))&&o&&i},yf=function(e){var n,r,o,t,i=ar.fromDom(e.getBody()),a=e.selection.getRng();return vf(i,a)?((t=e).setContent(""),t.selection.setCursorLocation(),!0):(n=i,r=e.selection,o=r.getRng(),nu(Vl(n,ar.fromDom(o.startContainer)),Vl(n,ar.fromDom(o.endContainer)),function(e,t){return!1===Fr(e,t)&&(o.deleteContents(),gf(n,!0,e,t).each(function(e){r.setRng(e.toRange())}),!0)}).getOr(!1))},bf=function(e,t){return!e.selection.isCollapsed()&&yf(e)},Cf=function(a){if(!k(a))throw new Error("cases must be an array");if(0===a.length)throw new Error("there must be at least one case");var u=[],n={};return z(a,function(e,r){var t=gr(e);if(1!==t.length)throw new Error("one and only one name per case");var o=t[0],i=e[o];if(n[o]!==undefined)throw new Error("duplicate key detected:"+o);if("cata"===o)throw new Error("cannot have a case named cata (sorry)");if(!k(i))throw new Error("case arguments must be an array");u.push(o),n[o]=function(){var e=arguments.length;if(e!==i.length)throw new Error("Wrong number of arguments to case "+o+". Expected "+i.length+" ("+i+"), got "+e);for(var n=new Array(e),t=0;t<n.length;t++)n[t]=arguments[t];return{fold:function(){if(arguments.length!==a.length)throw new Error("Wrong number of arguments to fold. Expected "+a.length+", got "+arguments.length);return arguments[r].apply(null,n)},match:function(e){var t=gr(e);if(u.length!==t.length)throw new Error("Wrong number of arguments to match. Expected: "+u.join(",")+"\nActual: "+t.join(","));if(!J(u,function(e){return F(t,e)}))throw new Error("Not all branches were specified when using match. Specified: "+t.join(", ")+"\nRequired: "+u.join(", "));return e[o].apply(null,n)},log:function(e){V.console.log(e,{constructors:u,constructor:o,params:n})}}}}),n},xf=function(e){return Ps(e).exists(xo)},wf=function(e,t,n){var r=U(af(ar.fromDom(n.container()),t),bo),o=Z(r).getOr(t);return uc.fromPosition(e,o.dom(),n).filter(xf)},Nf=function(e,t){return Ps(t).exists(xo)||wf(!0,e,t).isSome()},Ef=function(e,t){return(n=t,_.from(n.getNode(!0)).map(ar.fromDom)).exists(xo)||wf(!1,e,t).isSome();var n},Sf=d(wf,!1),Tf=d(wf,!0),kf=(al="\xa0",function(e){return al===e}),_f=function(e){return/^[\r\n\t ]$/.test(e)},Af=function(e){return!_f(e)&&!kf(e)},Rf=function(n,r,o){return _.from(o.container()).filter(Uo.isText).exists(function(e){var t=n?0:-1;return r(e.data.charAt(o.offset()+t))})},Df=d(Rf,!0,_f),Of=d(Rf,!1,_f),Bf=function(e){var t=e.container();return Uo.isText(t)&&0===t.data.length},Pf=function(e,t){var n=Ts(e,t);return Uo.isContentEditableFalse(n)&&!Uo.isBogusAll(n)},If=d(Pf,0),Lf=d(Pf,-1),Ff=function(e,t){return Uo.isTable(Ts(e,t))},Mf=d(Ff,0),zf=d(Ff,-1),Uf=Cf([{remove:["element"]},{moveToElement:["element"]},{moveToPosition:["position"]}]),jf=function(e,t,n,r){var o=r.getNode(!1===t);return Vl(ar.fromDom(e),ar.fromDom(n.getNode())).map(function(e){return Gl(e)?Uf.remove(e.dom()):Uf.moveToElement(o)}).orThunk(function(){return _.some(Uf.moveToElement(o))})},Vf=function(u,s,c){return uc.fromPosition(s,u,c).bind(function(e){return a=e.getNode(),ko(ar.fromDom(a))||Eo(ar.fromDom(a))?_.none():(t=u,o=e,i=function(e){return Co(ar.fromDom(e))&&!Ss(r,o,t)},Os(!(n=s),r=c).fold(function(){return Os(n,o).fold(q(!1),i)},i)?_.none():s&&Uo.isContentEditableFalse(e.getNode())?jf(u,s,c,e):!1===s&&Uo.isContentEditableFalse(e.getNode(!0))?jf(u,s,c,e):s&&Lf(c)?_.some(Uf.moveToPosition(e)):!1===s&&If(c)?_.some(Uf.moveToPosition(e)):_.none());var t,n,r,o,i,a})},Hf=function(r,e,o){return i=e,a=o.getNode(!1===i),u=i?"after":"before",Uo.isElement(a)&&a.getAttribute("data-mce-caret")===u?(t=e,n=o.getNode(!1===e),t&&Uo.isContentEditableFalse(n.nextSibling)?_.some(Uf.moveToElement(n.nextSibling)):!1===t&&Uo.isContentEditableFalse(n.previousSibling)?_.some(Uf.moveToElement(n.previousSibling)):_.none()).fold(function(){return Vf(r,e,o)},_.some):Vf(r,e,o).bind(function(e){return t=r,n=o,e.fold(function(e){return _.some(Uf.remove(e))},function(e){return _.some(Uf.moveToElement(e))},function(e){return Ss(n,e,t)?_.none():_.some(Uf.moveToPosition(e))});var t,n});var t,n,i,a,u},qf=function(e,t,n){if(0!==n){var r,o,i,a=e.data.slice(t,t+n),u=t+n>=e.data.length,s=0===t;e.replaceData(t,n,(o=s,i=u,j((r=a).split(""),function(e,t){return-1!==" \f\n\r\t\x0B".indexOf(t)||"\xa0"===t?e.previousCharIsSpace||""===e.str&&o||e.str.length===r.length-1&&i?{previousCharIsSpace:!1,str:e.str+"\xa0"}:{previousCharIsSpace:!0,str:e.str+" "}:{previousCharIsSpace:!1,str:e.str+t}},{previousCharIsSpace:!1,str:""}).str))}},$f=function(e,t){var n,r=e.data.slice(t),o=r.length-(n=r,n.replace(/^\s+/g,"")).length;return qf(e,t,o)},Wf=function(e,t){return r=e,o=(n=t).container(),i=n.offset(),!1===ku.isTextPosition(n)&&o===r.parentNode&&i>ku.before(r).offset()?ku(t.container(),t.offset()-1):t;var n,r,o,i},Kf=function(e){return Va(e.previousSibling)?_.some((t=e.previousSibling,Uo.isText(t)?ku(t,t.data.length):ku.after(t))):e.previousSibling?uc.lastPositionIn(e.previousSibling):_.none();var t},Xf=function(e){return Va(e.nextSibling)?_.some((t=e.nextSibling,Uo.isText(t)?ku(t,0):ku.before(t))):e.nextSibling?uc.firstPositionIn(e.nextSibling):_.none();var t},Yf=function(r,o){return Kf(o).orThunk(function(){return Xf(o)}).orThunk(function(){return e=r,t=o,n=ku.before(t.previousSibling?t.previousSibling:t.parentNode),uc.prevPosition(e,n).fold(function(){return uc.nextPosition(e,ku.after(t))},_.some);var e,t,n})},Gf=function(n,r){return Xf(r).orThunk(function(){return Kf(r)}).orThunk(function(){return e=n,t=r,uc.nextPosition(e,ku.after(t)).fold(function(){return uc.prevPosition(e,ku.before(t))},_.some);var e,t})},Jf=function(e,t,n){return(r=e,o=t,i=n,r?Gf(o,i):Yf(o,i)).map(d(Wf,n));var r,o,i},Qf=function(t,n,e){e.fold(function(){t.focus()},function(e){t.selection.setRng(e.toRange(),n)})},Zf=function(e,t){return t&&e.schema.getBlockElements().hasOwnProperty(lr(t))},ed=function(e){if(Gl(e)){var t=ar.fromHtml('<br data-mce-bogus="1">');return Mi(e),Li(e,t),_.some(ku.before(t.dom()))}return _.none()},td=function(e,t,l){var n,r,o,i,a=Vr(e).filter(mr),u=Hr(e).filter(mr);return zi(e),(n=a,r=u,o=t,i=function(e,t,n){var r,o,i,a,u=e.dom(),s=t.dom(),c=u.data.length;return o=s,i=l,a=Jn((r=u).data).length,r.appendData(o.data),zi(ar.fromDom(o)),i&&$f(r,a),n.container()===s?ku(u,c):n},n.isSome()&&r.isSome()&&o.isSome()?_.some(i(n.getOrDie(),r.getOrDie(),o.getOrDie())):_.none()).orThunk(function(){return l&&(a.each(function(e){return t=e.dom(),n=e.dom().length,r=t.data.slice(0,n),o=r.length-Jn(r).length,qf(t,n-o,o);var t,n,r,o}),u.each(function(e){return $f(e.dom(),0)})),t})},nd=function(e,t){return n=e.schema.getTextInlineElements(),r=lr(t),pr.call(n,r);var n,r},rd=function(t,n,e,r){void 0===r&&(r=!0);var o,i=Jf(n,t.getBody(),e.dom()),a=ea(e,d(Zf,t),(o=t.getBody(),function(e){return e.dom()===o})),u=td(e,i,nd(t,e));t.dom.isEmpty(t.getBody())?(t.setContent(""),t.selection.setCursorLocation()):a.bind(ed).fold(function(){r&&Qf(t,n,u)},function(e){r&&Qf(t,n,_.some(e))})},od=function(a,u){var e,t,n,r,o,i;return(e=a.getBody(),t=u,n=a.selection.getRng(),r=Ds(t?1:-1,e,n),o=ku.fromRangeStart(r),i=ar.fromDom(e),!1===t&&Lf(o)?_.some(Uf.remove(o.getNode(!0))):t&&If(o)?_.some(Uf.remove(o.getNode())):!1===t&&If(o)&&Ef(i,o)?Sf(i,o).map(function(e){return Uf.remove(e.getNode())}):t&&Lf(o)&&Nf(i,o)?Tf(i,o).map(function(e){return Uf.remove(e.getNode())}):Hf(e,t,o)).map(function(e){return e.fold((o=a,i=u,function(e){return o._selectionOverrides.hideFakeCaret(),rd(o,i,ar.fromDom(e)),!0}),(n=a,r=u,function(e){var t=r?ku.before(e):ku.after(e);return n.selection.setRng(t.toRange()),!0}),(t=a,function(e){return t.selection.setRng(e.toRange()),!0}));var t,n,r,o,i}).getOr(!1)},id=function(e,t){var n,r=e.selection.getNode();return!!Uo.isContentEditableFalse(r)&&(n=ar.fromDom(e.getBody()),z(Ji(n,".mce-offscreen-selection"),zi),rd(e,t,ar.fromDom(e.selection.getNode())),Hl(e),!0)},ad=function(e,t){return e.selection.isCollapsed()?od(e,t):id(e,t)},ud=function(e){var t,n=function(e,t){for(;t&&t!==e;){if(Uo.isContentEditableTrue(t)||Uo.isContentEditableFalse(t))return t;t=t.parentNode}return null}(e.getBody(),e.selection.getNode());return Uo.isContentEditableTrue(n)&&e.dom.isBlock(n)&&e.dom.isEmpty(n)&&(t=e.dom.create("br",{"data-mce-bogus":"1"}),e.dom.setHTML(n,""),n.appendChild(t),e.selection.setRng(ku.before(t).toRange())),!0},sd=Uo.isText,cd=function(e){return sd(e)&&e.data[0]===Ca},ld=function(e){return sd(e)&&e.data[e.data.length-1]===Ca},fd=function(e){return e.ownerDocument.createTextNode(Ca)},dd=function(e,t){return e?function(e){if(sd(e.previousSibling))return ld(e.previousSibling)||e.previousSibling.appendData(Ca),e.previousSibling;if(sd(e))return cd(e)||e.insertData(0,Ca),e;var t=fd(e);return e.parentNode.insertBefore(t,e),t}(t):function(e){if(sd(e.nextSibling))return cd(e.nextSibling)||e.nextSibling.insertData(0,Ca),e.nextSibling;if(sd(e))return ld(e)||e.appendData(Ca),e;var t=fd(e);return e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t),t}(t)},md=d(dd,!0),gd=d(dd,!1),pd=function(e,t){return Uo.isText(e.container())?dd(t,e.container()):dd(t,e.getNode())},hd=function(e,t){var n=t.get();return n&&e.container()===n&&Sa(n)},vd=function(n,e){return e.fold(function(e){us.remove(n.get());var t=md(e);return n.set(t),_.some(ku(t,t.length-1))},function(e){return uc.firstPositionIn(e).map(function(e){if(hd(e,n))return ku(n.get(),1);us.remove(n.get());var t=pd(e,!0);return n.set(t),ku(t,1)})},function(e){return uc.lastPositionIn(e).map(function(e){if(hd(e,n))return ku(n.get(),n.get().length-1);us.remove(n.get());var t=pd(e,!1);return n.set(t),ku(t,t.length-1)})},function(e){us.remove(n.get());var t=gd(e);return n.set(t),_.some(ku(t,1))})},yd=function(e,t){for(var n=0;n<e.length;n++){var r=e[n].apply(null,t);if(r.isSome())return r}return _.none()},bd=Cf([{before:["element"]},{start:["element"]},{end:["element"]},{after:["element"]}]),Cd=function(e,t){var n=Es(t,e);return n||e},xd=function(e,t,n){var r=jl.normalizeForwards(n),o=Cd(t,r.container());return jl.findRootInline(e,o,r).fold(function(){return uc.nextPosition(o,r).bind(d(jl.findRootInline,e,o)).map(function(e){return bd.before(e)})},_.none)},wd=function(e,t){return null===Ju(e,t)},Nd=function(e,t,n){return jl.findRootInline(e,t,n).filter(d(wd,t))},Ed=function(e,t,n){var r=jl.normalizeBackwards(n);return Nd(e,t,r).bind(function(e){return uc.prevPosition(e,r).isNone()?_.some(bd.start(e)):_.none()})},Sd=function(e,t,n){var r=jl.normalizeForwards(n);return Nd(e,t,r).bind(function(e){return uc.nextPosition(e,r).isNone()?_.some(bd.end(e)):_.none()})},Td=function(e,t,n){var r=jl.normalizeBackwards(n),o=Cd(t,r.container());return jl.findRootInline(e,o,r).fold(function(){return uc.prevPosition(o,r).bind(d(jl.findRootInline,e,o)).map(function(e){return bd.after(e)})},_.none)},kd=function(e){return!1===jl.isRtl(Ad(e))},_d=function(e,t,n){return yd([xd,Ed,Sd,Td],[e,t,n]).filter(kd)},Ad=function(e){return e.fold($,$,$,$)},Rd=function(e){return e.fold(q("before"),q("start"),q("end"),q("after"))},Dd=function(e){return e.fold(bd.before,bd.before,bd.after,bd.after)},Od=function(n,e,r,t,o,i){return nu(jl.findRootInline(e,r,t),jl.findRootInline(e,r,o),function(e,t){return e!==t&&jl.hasSameParentBlock(r,e,t)?bd.after(n?e:t):i}).getOr(i)},Bd=function(e,r){return e.fold(q(!0),function(e){return n=r,!(Rd(t=e)===Rd(n)&&Ad(t)===Ad(n));var t,n})},Pd=function(e,t){return e?t.fold(H(_.some,bd.start),_.none,H(_.some,bd.after),_.none):t.fold(_.none,H(_.some,bd.before),_.none,H(_.some,bd.end))},Id=function(a,u,s,c){var e=jl.normalizePosition(a,c),l=_d(u,s,e);return _d(u,s,e).bind(d(Pd,a)).orThunk(function(){return t=a,n=u,r=s,o=l,e=c,i=jl.normalizePosition(t,e),uc.fromPosition(t,r,i).map(d(jl.normalizePosition,t)).fold(function(){return o.map(Dd)},function(e){return _d(n,r,e).map(d(Od,t,n,r,i,e)).filter(d(Bd,o))}).filter(kd);var t,n,r,o,e,i})},Ld=_d,Fd=Id,Md=(d(Id,!1),d(Id,!0),Dd),zd=function(e){return e.fold(bd.start,bd.start,bd.end,bd.end)},Ud=function(e){return D(e.selection.getSel().modify)},jd=function(e,t,n){var r=e?1:-1;return t.setRng(ku(n.container(),n.offset()+r).toRange()),t.getSel().modify("move",e?"forward":"backward","word"),!0},Vd=function(e,t){var n=t.selection.getRng(),r=e?ku.fromRangeEnd(n):ku.fromRangeStart(n);return!!Ud(t)&&(e&&_a(r)?jd(!0,t.selection,r):!(e||!Aa(r))&&jd(!1,t.selection,r))},Hd=function(e,t){var n=e.dom.createRng();n.setStart(t.container(),t.offset()),n.setEnd(t.container(),t.offset()),e.selection.setRng(n)},qd=function(e){return!1!==e.settings.inline_boundaries},$d=function(e,t){e?t.setAttribute("data-mce-selected","inline-boundary"):t.removeAttribute("data-mce-selected")},Wd=function(t,e,n){return vd(e,n).map(function(e){return Hd(t,e),n})},Kd=function(e,t,n){return function(){return!!qd(t)&&Vd(e,t)}},Xd={move:function(a,u,s){return function(){return!!qd(a)&&(t=a,n=u,e=s,r=t.getBody(),o=ku.fromRangeStart(t.selection.getRng()),i=d(jl.isInlineTarget,t),Fd(e,i,r,o).bind(function(e){return Wd(t,n,e)})).isSome();var t,n,e,r,o,i}},moveNextWord:d(Kd,!0),movePrevWord:d(Kd,!1),setupSelectedState:function(a){var u=Vi(null),s=d(jl.isInlineTarget,a);return a.on("NodeChange",function(e){var t,n,r,o,i;qd(a)&&(t=s,n=a.dom,r=e.parents,o=U(n.select('*[data-mce-selected="inline-boundary"]'),t),i=U(r,t),z(Q(o,i),d($d,!1)),z(Q(i,o),d($d,!0)),function(e,t){if(e.selection.isCollapsed()&&!0!==e.composing&&t.get()){var n=ku.fromRangeStart(e.selection.getRng());ku.isTextPosition(n)&&!1===jl.isAtZwsp(n)&&(Hd(e,us.removeAndReposition(t.get(),n)),t.set(null))}}(a,u),function(n,r,o,e){if(r.selection.isCollapsed()){var t=U(e,n);z(t,function(e){var t=ku.fromRangeStart(r.selection.getRng());Ld(n,r.getBody(),t).bind(function(e){return Wd(r,o,e)})})}}(s,a,u,e.parents))}),u},setCaretPosition:Hd},Yd=function(t,n){return function(e){return vd(n,e).map(function(e){return Xd.setCaretPosition(t,e),!0}).getOr(!1)}},Gd=function(r,o,i,a){var u=r.getBody(),s=d(jl.isInlineTarget,r);r.undoManager.ignore(function(){var e,t,n;r.selection.setRng((e=i,t=a,(n=V.document.createRange()).setStart(e.container(),e.offset()),n.setEnd(t.container(),t.offset()),n)),r.execCommand("Delete"),Ld(s,u,ku.fromRangeStart(r.selection.getRng())).map(zd).map(Yd(r,o))}),r.nodeChanged()},Jd=function(n,r,i,o){var e,t,a=(e=n.getBody(),t=o.container(),Es(t,e)||e),u=d(jl.isInlineTarget,n),s=Ld(u,a,o);return s.bind(function(e){return i?e.fold(q(_.some(zd(e))),_.none,q(_.some(Md(e))),_.none):e.fold(_.none,q(_.some(Md(e))),_.none,q(_.some(zd(e))))}).map(Yd(n,r)).getOrThunk(function(){var t=uc.navigate(i,a,o),e=t.bind(function(e){return Ld(u,a,e)});return s.isSome()&&e.isSome()?jl.findRootInline(u,a,o).map(function(e){return o=e,!!nu(uc.firstPositionIn(o),uc.lastPositionIn(o),function(e,t){var n=jl.normalizePosition(!0,e),r=jl.normalizePosition(!1,t);return uc.nextPosition(o,n).map(function(e){return e.isEqual(r)}).getOr(!0)}).getOr(!0)&&(rd(n,i,ar.fromDom(e)),!0);var o}).getOr(!1):e.bind(function(e){return t.map(function(e){return i?Gd(n,r,o,e):Gd(n,r,e,o),!0})}).getOr(!1)})},Qd=function(e,t,n){if(e.selection.isCollapsed()&&!1!==e.settings.inline_boundaries){var r=ku.fromRangeStart(e.selection.getRng());return Jd(e,t,n,r)}return!1},Zd=_r("start","end"),em=_r("rng","table","cells"),tm=Cf([{removeTable:["element"]},{emptyCells:["cells"]}]),nm=function(e,t){return oa(ar.fromDom(e),"td,th",t)},rm=function(e,t){return na(e,"table",t)},om=function(e){return!1===Fr(e.start(),e.end())},im=function(e,n){return rm(e.start(),n).bind(function(t){return rm(e.end(),n).bind(function(e){return Fr(t,e)?_.some(t):_.none()})})},am=function(e){return Ji(e,"td,th")},um=function(r,e){var t=nm(e.startContainer,r),n=nm(e.endContainer,r);return e.collapsed?_.none():nu(t,n,Zd).fold(function(){return t.fold(function(){return n.bind(function(t){return rm(t,r).bind(function(e){return Z(am(e)).map(function(e){return Zd(e,t)})})})},function(t){return rm(t,r).bind(function(e){return ee(am(e)).map(function(e){return Zd(t,e)})})})},function(e){return sm(r,e)?_.none():(n=r,rm((t=e).start(),n).bind(function(e){return ee(am(e)).map(function(e){return Zd(t.start(),e)})}));var t,n})},sm=function(e,t){return im(t,e).isSome()},cm=function(e,t){var n,r,o,i,a=d(Fr,e);return(n=t,r=a,o=nm(n.startContainer,r),i=nm(n.endContainer,r),nu(o,i,Zd).filter(om).filter(function(e){return sm(r,e)}).orThunk(function(){return um(r,n)})).bind(function(e){return im(t=e,a).map(function(e){return em(t,e,am(e))});var t})},lm=function(e,t){return Y(e,function(e){return Fr(e,t)})},fm=function(n){return(r=n,nu(lm(r.cells(),r.rng().start()),lm(r.cells(),r.rng().end()),function(e,t){return r.cells().slice(e,t+1)})).map(function(e){var t=n.cells();return e.length===t.length?tm.removeTable(n.table()):tm.emptyCells(e)});var r},dm=function(e,t){return cm(e,t).bind(fm)},mm=function(e){var t=[];if(e)for(var n=0;n<e.rangeCount;n++)t.push(e.getRangeAt(n));return t},gm=mm,pm=function(e){return G(e,function(e){var t=Qa(e);return t?[ar.fromDom(t)]:[]})},hm=function(e){return 1<mm(e).length},vm=function(e){return U(pm(e),ko)},ym=function(e){return Ji(e,"td[data-mce-selected],th[data-mce-selected]")},bm=function(e,t){var n=ym(t),r=vm(e);return 0<n.length?n:r},Cm=bm,xm=function(e){return bm(gm(e.selection.getSel()),ar.fromDom(e.getBody()))},wm=function(e,t){return z(t,tl),e.selection.setCursorLocation(t[0].dom(),0),!0},Nm=function(e,t){return rd(e,!1,t),!0},Em=function(n,e,r,t){return Tm(e,t).fold(function(){return t=n,dm(e,r).map(function(e){return e.fold(d(Nm,t),d(wm,t))});var t},function(e){return km(n,e)}).getOr(!1)},Sm=function(e,t){return X(af(t,e),ko)},Tm=function(e,t){return X(af(t,e),function(e){return"caption"===lr(e)})},km=function(e,t){return tl(t),e.selection.setCursorLocation(t.dom(),0),_.some(!0)},_m=function(u,s,c,l,f){return uc.navigate(c,u.getBody(),f).bind(function(e){return r=l,o=c,i=f,a=e,uc.firstPositionIn(r.dom()).bind(function(t){return uc.lastPositionIn(r.dom()).map(function(e){return o?i.isEqual(t)&&a.isEqual(e):i.isEqual(e)&&a.isEqual(t)})}).getOr(!0)?km(u,l):(t=l,n=e,Tm(s,ar.fromDom(n.getNode())).map(function(e){return!1===Fr(e,t)}));var t,n,r,o,i,a}).or(_.some(!0))},Am=function(a,u,s,e){var c=ku.fromRangeStart(a.selection.getRng());return Sm(s,e).bind(function(e){return Gl(e)?km(a,e):(t=a,n=s,r=u,o=e,i=c,uc.navigate(r,t.getBody(),i).bind(function(e){return Sm(n,ar.fromDom(e.getNode())).map(function(e){return!1===Fr(e,o)})}));var t,n,r,o,i})},Rm=function(a,u,e){var s=ar.fromDom(a.getBody());return Tm(s,e).fold(function(){return Am(a,u,s,e)},function(e){return t=a,n=u,r=s,o=e,i=ku.fromRangeStart(t.selection.getRng()),Gl(o)?km(t,o):_m(t,r,n,o,i);var t,n,r,o,i}).getOr(!1)},Dm=function(e,t){var n,r,o,i,a,u=ar.fromDom(e.selection.getStart(!0)),s=xm(e);return e.selection.isCollapsed()&&0===s.length?Rm(e,t,u):(n=e,r=u,o=ar.fromDom(n.getBody()),i=n.selection.getRng(),0!==(a=xm(n)).length?wm(n,a):Em(n,o,i,r))},Om=xc.isEq,Bm=function(e,t,n){var r=e.formatter.get(n);if(r)for(var o=0;o<r.length;o++)if(!1===r[o].inherit&&e.dom.is(t,r[o].selector))return!0;return!1},Pm=function(t,e,n,r){var o=t.dom.getRoot();return e!==o&&(e=t.dom.getParent(e,function(e){return!!Bm(t,e,n)||e.parentNode===o||!!Fm(t,e,n,r,!0)}),Fm(t,e,n,r))},Im=function(e,t,n){return!!Om(t,n.inline)||!!Om(t,n.block)||(n.selector?1===t.nodeType&&e.is(t,n.selector):void 0)},Lm=function(e,t,n,r,o,i){var a,u,s,c=n[r];if(n.onmatch)return n.onmatch(t,n,r);if(c)if("undefined"==typeof c.length){for(a in c)if(c.hasOwnProperty(a)){if(u="attributes"===r?e.getAttrib(t,a):xc.getStyle(e,t,a),o&&!u&&!n.exact)return;if((!o||n.exact)&&!Om(u,xc.normalizeStyleValue(e,xc.replaceVars(c[a],i),a)))return}}else for(s=0;s<c.length;s++)if("attributes"===r?e.getAttrib(t,c[s]):xc.getStyle(e,t,c[s]))return n;return n},Fm=function(e,t,n,r,o){var i,a,u,s,c=e.formatter.get(n),l=e.dom;if(c&&t)for(a=0;a<c.length;a++)if(i=c[a],Im(e.dom,t,i)&&Lm(l,t,i,"attributes",o,r)&&Lm(l,t,i,"styles",o,r)){if(s=i.classes)for(u=0;u<s.length;u++)if(!e.dom.hasClass(t,s[u]))return;return i}},Mm={matchNode:Fm,matchName:Im,match:function(e,t,n,r){var o;return r?Pm(e,r,t,n):(r=e.selection.getNode(),!!Pm(e,r,t,n)||!((o=e.selection.getStart())===r||!Pm(e,o,t,n)))},matchAll:function(r,o,i){var e,a=[],u={};return e=r.selection.getStart(),r.dom.getParent(e,function(e){var t,n;for(t=0;t<o.length;t++)n=o[t],!u[n]&&Fm(r,e,n,i)&&(u[n]=!0,a.push(n))},r.dom.getRoot()),a},canApply:function(e,t){var n,r,o,i,a,u=e.formatter.get(t),s=e.dom;if(u)for(n=e.selection.getStart(),r=xc.getParents(s,n),i=u.length-1;0<=i;i--){if(!(a=u[i].selector)||u[i].defaultBlock)return!0;for(o=r.length-1;0<=o;o--)if(s.is(r[o],a))return!0}return!1},matchesUnInheritedFormatSelector:Bm},zm=function(e,t){return e.splitText(t)},Um=function(e){var t=e.startContainer,n=e.startOffset,r=e.endContainer,o=e.endOffset;return t===r&&Uo.isText(t)?0<n&&n<t.nodeValue.length&&(t=(r=zm(t,n)).previousSibling,n<o?(t=r=zm(r,o-=n).previousSibling,o=r.nodeValue.length,n=0):o=0):(Uo.isText(t)&&0<n&&n<t.nodeValue.length&&(t=zm(t,n),n=0),Uo.isText(r)&&0<o&&o<r.nodeValue.length&&(o=(r=zm(r,o).previousSibling).nodeValue.length)),{startContainer:t,startOffset:n,endContainer:r,endOffset:o}},jm=Ca,Vm="_mce_caret",Hm=function(e){return 0<function(e){for(var t=[];e;){if(3===e.nodeType&&e.nodeValue!==jm||1<e.childNodes.length)return[];1===e.nodeType&&t.push(e),e=e.firstChild}return t}(e).length},qm=function(e){var t;if(e)for(e=(t=new mo(e,e)).current();e;e=t.next())if(3===e.nodeType)return e;return null},$m=function(e){var t=ar.fromTag("span");return wr(t,{id:Vm,"data-mce-bogus":"1","data-mce-type":"format-caret"}),e&&Li(t,ar.fromText(jm)),t},Wm=function(e,t,n){void 0===n&&(n=!0);var r,o=e.dom,i=e.selection;if(Hm(t))rd(e,!1,ar.fromDom(t),n);else{var a=i.getRng(),u=o.getParent(t,o.isBlock),s=((r=qm(t))&&r.nodeValue.charAt(0)===jm&&r.deleteData(0,1),r);a.startContainer===s&&0<a.startOffset&&a.setStart(s,a.startOffset-1),a.endContainer===s&&0<a.endOffset&&a.setEnd(s,a.endOffset-1),o.remove(t,!0),u&&o.isEmpty(u)&&tl(ar.fromDom(u)),i.setRng(a)}},Km=function(e,t,n){void 0===n&&(n=!0);var r=e.dom,o=e.selection;if(t)Wm(e,t,n);else if(!(t=Ju(e.getBody(),o.getStart())))for(;t=r.get(Vm);)Wm(e,t,!1)},Xm=function(e,t,n){var r=e.dom,o=r.getParent(n,d(xc.isTextBlock,e));o&&r.isEmpty(o)?n.parentNode.replaceChild(t,n):(el(ar.fromDom(n)),r.isEmpty(n)?n.parentNode.replaceChild(t,n):r.insertAfter(t,n))},Ym=function(e,t){return e.appendChild(t),t},Gm=function(e,t){var n,r,o=(n=function(e,t){return Ym(e,t.cloneNode(!1))},r=t,function(e,t){for(var n=e.length-1;0<=n;n--)t(e[n],n)}(e,function(e){r=n(r,e)}),r);return Ym(o,o.ownerDocument.createTextNode(jm))},Jm=function(i){i.on("mouseup keydown",function(e){var t,n,r,o;t=i,n=e.keyCode,r=t.selection,o=t.getBody(),Km(t,null,!1),8!==n&&46!==n||!r.isCollapsed()||r.getStart().innerHTML!==jm||Km(t,Ju(o,r.getStart())),37!==n&&39!==n||Km(t,Ju(o,r.getStart()))})},Qm=function(e,t){return e.schema.getTextInlineElements().hasOwnProperty(lr(t))&&!Gu(t.dom())&&!Uo.isBogus(t.dom())},Zm=function(e){return 1===Wr(e).length},eg=function(e,t,n,r){var o,i,a,u,s=d(Qm,t),c=W(U(r,s),function(e){return e.dom()});if(0===c.length)rd(t,e,n);else{var l=(o=n.dom(),i=c,a=$m(!1),u=Gm(i,a.dom()),Bi(ar.fromDom(o),a),zi(ar.fromDom(o)),ku(u,0));t.selection.setRng(l.toRange())}},tg=function(r,o){var t,e=ar.fromDom(r.getBody()),n=ar.fromDom(r.selection.getStart()),i=U((t=af(n,e),Y(t,bo).fold(q(t),function(e){return t.slice(0,e)})),Zm);return ee(i).map(function(e){var t,n=ku.fromRangeStart(r.selection.getRng());return!(!ql(o,n,e.dom())||Gu((t=e).dom())&&Hm(t.dom())||(eg(o,r,e,i),0))}).getOr(!1)},ng=function(e,t){return!!e.selection.isCollapsed()&&tg(e,t)},rg=Uo.isContentEditableTrue,og=Uo.isContentEditableFalse,ig=function(e,t,n,r,o){return t._selectionOverrides.showCaret(e,n,r,o)},ag=function(e,t){var n,r;return e.fire("BeforeObjectSelected",{target:t}).isDefaultPrevented()?null:((r=(n=t).ownerDocument.createRange()).selectNode(n),r)},ug=function(e,t,n){var r=Ds(1,e.getBody(),t),o=ku.fromRangeStart(r),i=o.getNode();if(og(i))return ig(1,e,i,!o.isAtEnd(),!1);var a=o.getNode(!0);if(og(a))return ig(1,e,a,!1,!1);var u=e.dom.getParent(o.getNode(),function(e){return og(e)||rg(e)});return og(u)?ig(1,e,u,!1,n):null},sg=function(e,t,n){if(!t||!t.collapsed)return t;var r=ug(e,t,n);return r||t},cg=function(e,t,n,r,o,i){var a,u,s=ig(r,e,i.getNode(!o),o,!0);if(t.collapsed){var c=t.cloneRange();o?c.setEnd(s.startContainer,s.startOffset):c.setStart(s.endContainer,s.endOffset),c.deleteContents()}else t.deleteContents();return e.selection.setRng(s),a=e.dom,u=n,Uo.isText(u)&&0===u.data.length&&a.remove(u),!0},lg=function(e,t){return function(e,t){var n=e.selection.getRng();if(!Uo.isText(n.commonAncestorContainer))return!1;var r=t?Su.Forwards:Su.Backwards,o=Gs(e.getBody()),i=d(Is,o.next),a=d(Is,o.prev),u=t?i:a,s=t?If:Lf,c=Bs(r,e.getBody(),n),l=jl.normalizePosition(t,u(c));if(!l)return!1;if(s(l))return cg(e,n,c.getNode(),r,t,l);var f=u(l);return!!(f&&s(f)&&Ls(l,f))&&cg(e,n,c.getNode(),r,t,f)}(e,t)},fg=function(e,t){e.getDoc().execCommand(t,!1,null)},dg=function(e){ad(e,!1)||lg(e,!1)||Qd(e,!1)||pf(e,!1)||Dm(e)||bf(e,!1)||ng(e,!1)||(fg(e,"Delete"),Hl(e))},mg=function(e){ad(e,!0)||lg(e,!0)||Qd(e,!0)||pf(e,!0)||Dm(e)||bf(e,!0)||ng(e,!0)||fg(e,"ForwardDelete")},gg=function(o,t,e){var n=function(e){return t=o,n=e.dom(),r=kr(n,t),_.from(r).filter(function(e){return 0<e.length});var t,n,r};return ta(ar.fromDom(e),function(e){return n(e).isSome()},function(e){return Fr(ar.fromDom(t),e)}).bind(n)},pg=function(o){return function(r,e){return _.from(e).map(ar.fromDom).filter(dr).bind(function(e){return gg(o,r,e.dom()).or((t=o,n=e.dom(),_.from(Ei.DOM.getStyle(n,t,!0))));var t,n}).getOr("")}},hg={getFontSize:pg("font-size"),getFontFamily:H(function(e){return e.replace(/[\'\"\\]/g,"").replace(/,\s+/g,",")},pg("font-family")),toPt:function(e,t){return/[0-9.]+px$/.test(e)?(n=72*parseInt(e,10)/96,r=t||0,o=Math.pow(10,r),Math.round(n*o)/o+"pt"):e;var n,r,o}},vg=function(e){return uc.firstPositionIn(e.getBody()).map(function(e){var t=e.container();return Uo.isText(t)?t.parentNode:t})},yg=function(o){return _.from(o.selection.getRng()).bind(function(e){var t,n,r=o.getBody();return n=r,(t=e).startContainer===n&&0===t.startOffset?_.none():_.from(o.selection.getStart(!0))})},bg=function(e,t){if(/^[0-9\.]+$/.test(t)){var n=parseInt(t,10);if(1<=n&&n<=7){var r=_l(e),o=Al(e);return o?o[n-1]||t:r[n-1]||t}return t}return t},Cg=function(e,t){return e&&t&&e.startContainer===t.startContainer&&e.startOffset===t.startOffset&&e.endContainer===t.endContainer&&e.endOffset===t.endOffset},xg=function(e,t,n){return null!==function(e,t,n){for(;e&&e!==t;){if(n(e))return e;e=e.parentNode}return null}(e,t,n)},wg=function(e,t,n){return xg(e,t,function(e){return e.nodeName===n})},Ng=function(e){return e&&"TABLE"===e.nodeName},Eg=function(e,t,n){for(var r=new mo(t,e.getParent(t.parentNode,e.isBlock)||e.getRoot());t=r[n?"prev":"next"]();)if(Uo.isBr(t))return!0},Sg=function(e,t,n,r,o){var i,a,u,s,c,l,f=e.getRoot(),d=e.schema.getNonEmptyElements();if(u=e.getParent(o.parentNode,e.isBlock)||f,r&&Uo.isBr(o)&&t&&e.isEmpty(u))return _.some(Eu(o.parentNode,e.nodeIndex(o)));for(i=new mo(o,u);s=i[r?"prev":"next"]();){if("false"===e.getContentEditableParent(s)||(l=f,Ta(c=s)&&!1===xg(c,l,Gu)))return _.none();if(Uo.isText(s)&&0<s.nodeValue.length)return!1===wg(s,f,"A")?_.some(Eu(s,r?s.nodeValue.length:0)):_.none();if(e.isBlock(s)||d[s.nodeName.toLowerCase()])return _.none();a=s}return n&&a?_.some(Eu(a,0)):_.none()},Tg=function(e,t,n,r){var o,i,a,u,s,c,l,f,d,m,g=e.getRoot(),p=!1;if(o=r[(n?"start":"end")+"Container"],i=r[(n?"start":"end")+"Offset"],l=Uo.isElement(o)&&i===o.childNodes.length,s=e.schema.getNonEmptyElements(),c=n,Ta(o))return _.none();if(Uo.isElement(o)&&i>o.childNodes.length-1&&(c=!1),Uo.isDocument(o)&&(o=g,i=0),o===g){if(c&&(u=o.childNodes[0<i?i-1:0])){if(Ta(u))return _.none();if(s[u.nodeName]||Ng(u))return _.none()}if(o.hasChildNodes()){if(i=Math.min(!c&&0<i?i-1:i,o.childNodes.length-1),o=o.childNodes[i],i=Uo.isText(o)&&l?o.data.length:0,!t&&o===g.lastChild&&Ng(o))return _.none();if(function(e,t){for(;t&&t!==e;){if(Uo.isContentEditableFalse(t))return!0;t=t.parentNode}return!1}(g,o)||Ta(o))return _.none();if(o.hasChildNodes()&&!1===Ng(o)){a=new mo(u=o,g);do{if(Uo.isContentEditableFalse(u)||Ta(u)){p=!1;break}if(Uo.isText(u)&&0<u.nodeValue.length){i=c?0:u.nodeValue.length,o=u,p=!0;break}if(s[u.nodeName.toLowerCase()]&&(!(f=u)||!/^(TD|TH|CAPTION)$/.test(f.nodeName))){i=e.nodeIndex(u),o=u.parentNode,c||i++,p=!0;break}}while(u=c?a.next():a.prev())}}}return t&&(Uo.isText(o)&&0===i&&Sg(e,l,t,!0,o).each(function(e){o=e.container(),i=e.offset(),p=!0}),Uo.isElement(o)&&((u=o.childNodes[i])||(u=o.childNodes[i-1]),!u||!Uo.isBr(u)||(m="A",(d=u).previousSibling&&d.previousSibling.nodeName===m)||Eg(e,u,!1)||Eg(e,u,!0)||Sg(e,l,t,!0,u).each(function(e){o=e.container(),i=e.offset(),p=!0}))),c&&!t&&Uo.isText(o)&&i===o.nodeValue.length&&Sg(e,l,t,!1,o).each(function(e){o=e.container(),i=e.offset(),p=!0}),p?_.some(Eu(o,i)):_.none()},kg=function(e,t){var n=t.collapsed,r=t.cloneRange(),o=Eu.fromRangeStart(t);return Tg(e,n,!0,r).each(function(e){n&&Eu.isAbove(o,e)||r.setStart(e.container(),e.offset())}),n||Tg(e,n,!1,r).each(function(e){r.setEnd(e.container(),e.offset())}),n&&r.collapse(!0),Cg(t,r)?_.none():_.some(r)},_g=function(e,t,n){var r=e.create("span",{},"&nbsp;");n.parentNode.insertBefore(r,n),t.scrollIntoView(r),e.remove(r)},Ag=function(e,t,n,r){var o=e.createRng();r?(o.setStartBefore(n),o.setEndBefore(n)):(o.setStartAfter(n),o.setEndAfter(n)),t.setRng(o)},Rg=function(e,t){var n,r,o=e.selection,i=e.dom,a=o.getRng();kg(i,a).each(function(e){a.setStart(e.startContainer,e.startOffset),a.setEnd(e.endContainer,e.endOffset)});var u=a.startOffset,s=a.startContainer;if(1===s.nodeType&&s.hasChildNodes()){var c=u>s.childNodes.length-1;s=s.childNodes[Math.min(u,s.childNodes.length-1)]||s,u=c&&3===s.nodeType?s.nodeValue.length:0}var l=i.getParent(s,i.isBlock),f=l?i.getParent(l.parentNode,i.isBlock):null,d=f?f.nodeName.toUpperCase():"",m=t&&t.ctrlKey;"LI"!==d||m||(l=f),s&&3===s.nodeType&&u>=s.nodeValue.length&&(function(e,t,n){for(var r,o=new mo(t,n),i=e.getNonEmptyElements();r=o.next();)if(i[r.nodeName.toLowerCase()]||0<r.length)return!0}(e.schema,s,l)||(n=i.create("br"),a.insertNode(n),a.setStartAfter(n),a.setEndAfter(n),r=!0)),n=i.create("br"),Mu(i,a,n),_g(i,o,n),Ag(i,o,n,r),e.undoManager.add()},Dg=function(e,t){var n=ar.fromTag("br");Bi(ar.fromDom(t),n),e.undoManager.add()},Og=function(e,t){Bg(e.getBody(),t)||Pi(ar.fromDom(t),ar.fromTag("br"));var n=ar.fromTag("br");Pi(ar.fromDom(t),n),_g(e.dom,e.selection,n.dom()),Ag(e.dom,e.selection,n.dom(),!1),e.undoManager.add()},Bg=function(e,t){return n=ku.after(t),!!Uo.isBr(n.getNode())||uc.nextPosition(e,ku.after(t)).map(function(e){return Uo.isBr(e.getNode())}).getOr(!1);var n},Pg=function(e){return e&&"A"===e.nodeName&&"href"in e},Ig=function(e){return e.fold(q(!1),Pg,Pg,q(!1))},Lg=function(e,t){t.fold(o,d(Dg,e),d(Og,e),o)},Fg=function(e,t){var n,r,o,i=(n=e,r=d(jl.isInlineTarget,n),o=ku.fromRangeStart(n.selection.getRng()),Ld(r,n.getBody(),o).filter(Ig));i.isSome()?i.each(d(Lg,e)):Rg(e,t)},Mg={create:_r("start","soffset","finish","foffset")},zg=Cf([{before:["element"]},{on:["element","offset"]},{after:["element"]}]),Ug=(zg.before,zg.on,zg.after,function(e){return e.fold($,$,$)}),jg=Cf([{domRange:["rng"]},{relative:["startSitu","finishSitu"]},{exact:["start","soffset","finish","foffset"]}]),Vg={domRange:jg.domRange,relative:jg.relative,exact:jg.exact,exactFromRange:function(e){return jg.exact(e.start(),e.soffset(),e.finish(),e.foffset())},getWin:function(e){var t=e.match({domRange:function(e){return ar.fromDom(e.startContainer)},relative:function(e,t){return Ug(e)},exact:function(e,t,n,r){return e}});return Ur(t)},range:Mg.create},Hg=or.detect().browser,qg=function(e,t){var n=mr(t)?Fc(t).length:Wr(t).length+1;return n<e?n:e<0?0:e},$g=function(e){return Vg.range(e.start(),qg(e.soffset(),e.start()),e.finish(),qg(e.foffset(),e.finish()))},Wg=function(e,t){return!Uo.isRestrictedNode(t.dom())&&(Mr(e,t)||Fr(e,t))},Kg=function(t){return function(e){return Wg(t,e.start())&&Wg(t,e.finish())}},Xg=function(e){return!0===e.inline||Hg.isIE()},Yg=function(e){return Vg.range(ar.fromDom(e.startContainer),e.startOffset,ar.fromDom(e.endContainer),e.endOffset)},Gg=function(e){var t=e.getSelection();return(t&&0!==t.rangeCount?_.from(t.getRangeAt(0)):_.none()).map(Yg)},Jg=function(e){var t=Ur(e);return Gg(t.dom()).filter(Kg(e))},Qg=function(e,t){return _.from(t).filter(Kg(e)).map($g)},Zg=function(e){var t=V.document.createRange();try{return t.setStart(e.start().dom(),e.soffset()),t.setEnd(e.finish().dom(),e.foffset()),_.some(t)}catch(n){return _.none()}},ep=function(e){return(e.bookmark?e.bookmark:_.none()).bind(d(Qg,ar.fromDom(e.getBody()))).bind(Zg)},tp=function(e){var t=Xg(e)?Jg(ar.fromDom(e.getBody())):_.none();e.bookmark=t.isSome()?t:e.bookmark},np=function(t){ep(t).each(function(e){t.selection.setRng(e)})},rp=ep,op=function(e){return No(e)||Eo(e)},ip=function(e){return U(W(e.selection.getSelectedBlocks(),ar.fromDom),function(e){return!op(e)&&!jr(e).map(op).getOr(!1)})},ap=function(e,t){var n=e.settings,r=e.dom,o=e.selection,i=e.formatter,a=/[a-z%]+$/i.exec(n.indentation)[0],u=parseInt(n.indentation,10),s=e.getParam("indent_use_margin",!1);e.queryCommandState("InsertUnorderedList")||e.queryCommandState("InsertOrderedList")||n.forced_root_block||r.getParent(o.getNode(),r.isBlock)||i.apply("div"),z(ip(e),function(e){!function(e,t,n,r,o,i){if("false"!==e.getContentEditable(i)){var a=n?"margin":"padding";if(a="TABLE"===i.nodeName?"margin":a,a+="rtl"===e.getStyle(i,"direction",!0)?"Right":"Left","outdent"===t){var u=Math.max(0,parseInt(i.style[a]||0,10)-r);e.setStyle(i,a,u?u+o:"")}else u=parseInt(i.style[a]||0,10)+r+o,e.setStyle(i,a,u)}}(r,t,s,u,a,e.dom())})},up=Xt.each,sp=Xt.extend,cp=Xt.map,lp=Xt.inArray;function fp(s){var o,i,a,t,c={state:{},exec:{},value:{}},n=s.settings;s.on("PreInit",function(){o=s.dom,i=s.selection,n=s.settings,a=s.formatter});var r=function(e){var t;if(!s.quirks.isHidden()&&!s.removed){if(e=e.toLowerCase(),t=c.state[e])return t(e);try{return s.getDoc().queryCommandState(e)}catch(n){}return!1}},e=function(e,n){n=n||"exec",up(e,function(t,e){up(e.toLowerCase().split(","),function(e){c[n][e]=t})})},u=function(e,t,n){e=e.toLowerCase(),c.value[e]=function(){return t.call(n||s)}};sp(this,{execCommand:function(t,n,r,e){var o,i,a=!1;if(!s.removed){if(/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(t)||e&&e.skip_focus?np(s):s.focus(),(e=s.fire("BeforeExecCommand",{command:t,ui:n,value:r})).isDefaultPrevented())return!1;if(i=t.toLowerCase(),o=c.exec[i])return o(i,n,r),s.fire("ExecCommand",{command:t,ui:n,value:r}),!0;if(up(s.plugins,function(e){if(e.execCommand&&e.execCommand(t,n,r))return s.fire("ExecCommand",{command:t,ui:n,value:r}),!(a=!0)}),a)return a;if(s.theme&&s.theme.execCommand&&s.theme.execCommand(t,n,r))return s.fire("ExecCommand",{command:t,ui:n,value:r}),!0;try{a=s.getDoc().execCommand(t,n,r)}catch(u){}return!!a&&(s.fire("ExecCommand",{command:t,ui:n,value:r}),!0)}},queryCommandState:r,queryCommandValue:function(e){var t;if(!s.quirks.isHidden()&&!s.removed){if(e=e.toLowerCase(),t=c.value[e])return t(e);try{return s.getDoc().queryCommandValue(e)}catch(n){}}},queryCommandSupported:function(e){if(e=e.toLowerCase(),c.exec[e])return!0;try{return s.getDoc().queryCommandSupported(e)}catch(t){}return!1},addCommands:e,addCommand:function(e,o,i){e=e.toLowerCase(),c.exec[e]=function(e,t,n,r){return o.call(i||s,t,n,r)}},addQueryStateHandler:function(e,t,n){e=e.toLowerCase(),c.state[e]=function(){return t.call(n||s)}},addQueryValueHandler:u,hasCustomCommand:function(e){return e=e.toLowerCase(),!!c.exec[e]}});var l=function(e,t,n){return t===undefined&&(t=!1),n===undefined&&(n=null),s.getDoc().execCommand(e,t,n)},f=function(e){return a.match(e)},d=function(e,t){a.toggle(e,t?{value:t}:undefined),s.nodeChanged()},m=function(e){t=i.getBookmark(e)},g=function(){i.moveToBookmark(t)};e({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){s.undoManager.add()},"Cut,Copy,Paste":function(e){var t,n=s.getDoc();try{l(e)}catch(o){t=!0}if("paste"!==e||n.queryCommandEnabled(e)||(t=!0),t||!n.queryCommandSupported(e)){var r=s.translate("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.");fe.mac&&(r=r.replace(/Ctrl\+/g,"\u2318+")),s.notificationManager.open({text:r,type:"error"})}},unlink:function(){if(i.isCollapsed()){var e=s.dom.getParent(s.selection.getStart(),"a");e&&s.dom.remove(e,!0)}else a.remove("link")},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,JustifyNone":function(e){var t=e.substring(7);"full"===t&&(t="justify"),up("left,center,right,justify".split(","),function(e){t!==e&&a.remove("align"+e)}),"none"!==t&&d("align"+t)},"InsertUnorderedList,InsertOrderedList":function(e){var t,n;l(e),(t=o.getParent(i.getNode(),"ol,ul"))&&(n=t.parentNode,/^(H[1-6]|P|ADDRESS|PRE)$/.test(n.nodeName)&&(m(),o.split(n,t),g()))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){d(e)},"ForeColor,HiliteColor":function(e,t,n){d(e,n)},FontName:function(e,t,n){var r,o;o=n,(r=s).formatter.toggle("fontname",{value:bg(r,o)}),r.nodeChanged()},FontSize:function(e,t,n){var r,o;o=n,(r=s).formatter.toggle("fontsize",{value:bg(r,o)}),r.nodeChanged()},RemoveFormat:function(e){a.remove(e)},mceBlockQuote:function(){d("blockquote")},FormatBlock:function(e,t,n){return d(n||"p")},mceCleanup:function(){var e=i.getBookmark();s.setContent(s.getContent()),i.moveToBookmark(e)},mceRemoveNode:function(e,t,n){var r=n||i.getNode();r!==s.getBody()&&(m(),s.dom.remove(r,!0),g())},mceSelectNodeDepth:function(e,t,n){var r=0;o.getParent(i.getNode(),function(e){if(1===e.nodeType&&r++===n)return i.select(e),!1},s.getBody())},mceSelectNode:function(e,t,n){i.select(n)},mceInsertContent:function(e,t,n){dl(s,n)},mceInsertRawHTML:function(e,t,n){i.setContent("tiny_mce_marker");var r=s.getContent();s.setContent(r.replace(/tiny_mce_marker/g,function(){return n}))},mceToggleFormat:function(e,t,n){d(n)},mceSetContent:function(e,t,n){s.setContent(n)},"Indent,Outdent":function(e){ap(s,e)},mceRepaint:function(){},InsertHorizontalRule:function(){s.execCommand("mceInsertContent",!1,"<hr />")},mceToggleVisualAid:function(){s.hasVisual=!s.hasVisual,s.addVisual()},mceReplaceContent:function(e,t,n){s.execCommand("mceInsertContent",!1,n.replace(/\{\$selection\}/g,i.getContent({format:"text"})))},mceInsertLink:function(e,t,n){var r;"string"==typeof n&&(n={href:n}),r=o.getParent(i.getNode(),"a"),n.href=n.href.replace(" ","%20"),r&&n.href||a.remove("link"),n.href&&a.apply("link",n,r)},selectAll:function(){var e=o.getParent(i.getStart(),Uo.isContentEditableTrue);if(e){var t=o.createRng();t.selectNodeContents(e),i.setRng(t)}},"delete":function(){dg(s)},forwardDelete:function(){mg(s)},mceNewDocument:function(){s.setContent("")},InsertLineBreak:function(e,t,n){return Fg(s,n),!0}});var p=function(n){return function(){var e=i.isCollapsed()?[o.getParent(i.getNode(),o.isBlock)]:i.getSelectedBlocks(),t=cp(e,function(e){return!!a.matchNode(e,n)});return-1!==lp(t,!0)}};e({JustifyLeft:p("alignleft"),JustifyCenter:p("aligncenter"),JustifyRight:p("alignright"),JustifyFull:p("alignjustify"),"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){return f(e)},mceBlockQuote:function(){return f("blockquote")},Outdent:function(){var e;if(n.inline_styles){if((e=o.getParent(i.getStart(),o.isBlock))&&0<parseInt(e.style.paddingLeft,10))return!0;if((e=o.getParent(i.getEnd(),o.isBlock))&&0<parseInt(e.style.paddingLeft,10))return!0}return r("InsertUnorderedList")||r("InsertOrderedList")||!n.inline_styles&&!!o.getParent(i.getNode(),"BLOCKQUOTE")},"InsertUnorderedList,InsertOrderedList":function(e){var t=o.getParent(i.getNode(),"ul,ol");return t&&("insertunorderedlist"===e&&"UL"===t.tagName||"insertorderedlist"===e&&"OL"===t.tagName)}},"state"),e({Undo:function(){s.undoManager.undo()},Redo:function(){s.undoManager.redo()}}),u("FontName",function(){return yg(t=s).fold(function(){return vg(t).map(function(e){return hg.getFontFamily(t.getBody(),e)}).getOr("")},function(e){return hg.getFontFamily(t.getBody(),e)});var t},this),u("FontSize",function(){return yg(t=s).fold(function(){return vg(t).map(function(e){return hg.getFontSize(t.getBody(),e)}).getOr("")},function(e){return hg.getFontSize(t.getBody(),e)});var t},this)}var dp=Xt.makeMap("focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover draggesture dragdrop drop drag submit compositionstart compositionend compositionupdate touchstart touchmove touchend"," "),mp=function(a){var u,s,c=this,l={},f=function(){return!1},d=function(){return!0};u=(a=a||{}).scope||c,s=a.toggleEvent||f;var r=function(e,t,n,r){var o,i,a;if(!1===t&&(t=f),t)for(t={func:t},r&&Xt.extend(t,r),a=(i=e.toLowerCase().split(" ")).length;a--;)e=i[a],(o=l[e])||(o=l[e]=[],s(e,!0)),n?o.unshift(t):o.push(t);return c},m=function(e,t){var n,r,o,i,a;if(e)for(n=(i=e.toLowerCase().split(" ")).length;n--;){if(e=i[n],r=l[e],!e){for(o in l)s(o,!1),delete l[o];return c}if(r){if(t)for(a=r.length;a--;)r[a].func===t&&(r=r.slice(0,a).concat(r.slice(a+1)),l[e]=r);else r.length=0;r.length||(s(e,!1),delete l[e])}}else{for(e in l)s(e,!1);l={}}return c};c.fire=function(e,t){var n,r,o,i;if(e=e.toLowerCase(),(t=t||{}).type=e,t.target||(t.target=u),t.preventDefault||(t.preventDefault=function(){t.isDefaultPrevented=d},t.stopPropagation=function(){t.isPropagationStopped=d},t.stopImmediatePropagation=function(){t.isImmediatePropagationStopped=d},t.isDefaultPrevented=f,t.isPropagationStopped=f,t.isImmediatePropagationStopped=f),a.beforeFire&&a.beforeFire(t),n=l[e])for(r=0,o=n.length;r<o;r++){if((i=n[r]).once&&m(e,i.func),t.isImmediatePropagationStopped())return t.stopPropagation(),t;if(!1===i.func.call(u,t))return t.preventDefault(),t}return t},c.on=r,c.off=m,c.once=function(e,t,n){return r(e,t,n,{once:!0})},c.has=function(e){return e=e.toLowerCase(),!(!l[e]||0===l[e].length)}};mp.isNative=function(e){return!!dp[e.toLowerCase()]};var gp,pp=function(n){return n._eventDispatcher||(n._eventDispatcher=new mp({scope:n,toggleEvent:function(e,t){mp.isNative(e)&&n.toggleNativeEvent&&n.toggleNativeEvent(e,t)}})),n._eventDispatcher},hp={fire:function(e,t,n){if(this.removed&&"remove"!==e&&"detach"!==e)return t;if(t=pp(this).fire(e,t,n),!1!==n&&this.parent)for(var r=this.parent();r&&!t.isPropagationStopped();)r.fire(e,t,!1),r=r.parent();return t},on:function(e,t,n){return pp(this).on(e,t,n)},off:function(e,t){return pp(this).off(e,t)},once:function(e,t){return pp(this).once(e,t)},hasEventListeners:function(e){return pp(this).has(e)}},vp=function(e,t){return e.fire("PreProcess",t)},yp=function(e,t){return e.fire("PostProcess",t)},bp=function(e){return e.fire("remove")},Cp=function(e){return e.fire("detach")},xp=function(e,t){return e.fire("SwitchMode",{mode:t})},wp=function(e,t,n,r){e.fire("ObjectResizeStart",{target:t,width:n,height:r})},Np=function(e,t,n,r){e.fire("ObjectResized",{target:t,width:n,height:r})},Ep=function(e,t,n){try{e.getDoc().execCommand(t,!1,n)}catch(r){}},Sp=function(e,t,n){var r,o;Yi(e,t)&&!1===n?(o=t,qi(r=e)?r.dom().classList.remove(o):Wi(r,o),Xi(r)):n&&Ki(e,t)},Tp=function(e,t){Sp(ar.fromDom(e.getBody()),"mce-content-readonly",t),t?(e.selection.controlSelection.hideResizeRect(),e.readonly=!0,e.getBody().contentEditable="false"):(e.readonly=!1,e.getBody().contentEditable="true",Ep(e,"StyleWithCSS",!1),Ep(e,"enableInlineTableEditing",!1),Ep(e,"enableObjectResizing",!1),e.focus(),e.nodeChanged())},kp=function(e){return e.readonly?"readonly":"design"},_p=Ei.DOM,Ap=function(e,t){return"selectionchange"===t?e.getDoc():!e.inline&&/^mouse|touch|click|contextmenu|drop|dragover|dragend/.test(t)?e.getDoc().documentElement:e.settings.event_root?(e.eventRoot||(e.eventRoot=_p.select(e.settings.event_root)[0]),e.eventRoot):e.getBody()},Rp=function(e,t,n){var r;(r=e).hidden||r.readonly?!0===e.readonly&&n.preventDefault():e.fire(t,n)},Dp=function(i,a){var e,t;if(i.delegates||(i.delegates={}),!i.delegates[a]&&!i.removed)if(e=Ap(i,a),i.settings.event_root){if(gp||(gp={},i.editorManager.on("removeEditor",function(){var e;if(!i.editorManager.activeEditor&&gp){for(e in gp)i.dom.unbind(Ap(i,e));gp=null}})),gp[a])return;t=function(e){for(var t=e.target,n=i.editorManager.get(),r=n.length;r--;){var o=n[r].getBody();(o===t||_p.isChildOf(t,o))&&Rp(n[r],a,e)}},gp[a]=t,_p.bind(e,a,t)}else t=function(e){Rp(i,a,e)},_p.bind(e,a,t),i.delegates[a]=t},Op={bindPendingEventDelegates:function(){var t=this;Xt.each(t._pendingNativeEvents,function(e){Dp(t,e)})},toggleNativeEvent:function(e,t){var n=this;"focus"!==e&&"blur"!==e&&(t?n.initialized?Dp(n,e):n._pendingNativeEvents?n._pendingNativeEvents.push(e):n._pendingNativeEvents=[e]:n.initialized&&(n.dom.unbind(Ap(n,e),e,n.delegates[e]),delete n.delegates[e]))},unbindAllNativeEvents:function(){var e,t=this,n=t.getBody(),r=t.dom;if(t.delegates){for(e in t.delegates)t.dom.unbind(Ap(t,e),e,t.delegates[e]);delete t.delegates}!t.inline&&n&&r&&(n.onload=null,r.unbind(t.getWin()),r.unbind(t.getDoc())),r&&(r.unbind(n),r.unbind(t.getContainer()))}},Bp=Op=Xt.extend({},hp,Op),Pp=_r("sections","settings"),Ip=or.detect().deviceType.isTouch(),Lp=["lists","autolink","autosave"],Fp={theme:"mobile"},Mp=function(e){var t=k(e)?e.join(" "):e,n=W(S(t)?t.split(" "):[],Gn);return U(n,function(e){return 0<e.length})},zp=function(e,t){return e.sections().hasOwnProperty(t)},Up=function(e,t,n,r){var o,i=Mp(n.forced_plugins),a=Mp(r.plugins),u=e&&zp(t,"mobile")?U(a,d(F,Lp)):a,s=(o=u,[].concat(Mp(i)).concat(Mp(o)));return Xt.extend(r,{plugins:s.join(" ")})},jp=function(e,t,n,r){var o,i,a,u,s,c,l,f,d,m,g,p,h=(o=["mobile"],i=yr(r,function(e,t){return F(o,t)}),Pp(i.t,i.f)),v=Xt.extend(t,n,h.settings(),(m=e,p=(g=h).settings().inline,m&&zp(g,"mobile")&&!p?(c="mobile",l=Fp,f=h.sections(),d=f.hasOwnProperty(c)?f[c]:{},Xt.extend({},l,d)):{}),{validate:!0,content_editable:h.settings().inline,external_plugins:(a=n,u=h.settings(),s=u.external_plugins?u.external_plugins:{},a&&a.external_plugins?Xt.extend({},a.external_plugins,s):s)});return Up(e,h,n,v)},Vp=function(e,t,n){return _.from(t.settings[n]).filter(e)},Hp=function(e,t,n,r){var o,i,a,u=t in e.settings?e.settings[t]:n;return"hash"===r?(a={},"string"==typeof(i=u)?z(0<i.indexOf("=")?i.split(/[;,](?![^=;,]*(?:[;,]|$))/):i.split(","),function(e){var t=e.split("=");1<t.length?a[Xt.trim(t[0])]=Xt.trim(t[1]):a[Xt.trim(t[0])]=Xt.trim(t)}):a=i,a):"string"===r?Vp(S,e,t).getOr(n):"number"===r?Vp(O,e,t).getOr(n):"boolean"===r?Vp(R,e,t).getOr(n):"object"===r?Vp(T,e,t).getOr(n):"array"===r?Vp(k,e,t).getOr(n):"string[]"===r?Vp((o=S,function(e){return k(e)&&J(e,o)}),e,t).getOr(n):"function"===r?Vp(D,e,t).getOr(n):u},qp=Xt.each,$p=Xt.explode,Wp={f1:112,f2:113,f3:114,f4:115,f5:116,f6:117,f7:118,f8:119,f9:120,f10:121,f11:122,f12:123},Kp=Xt.makeMap("alt,ctrl,shift,meta,access");function Xp(i){var a={},r=[],u=function(e){var t,n,r={};for(n in qp($p(e,"+"),function(e){e in Kp?r[e]=!0:/^[0-9]{2,}$/.test(e)?r.keyCode=parseInt(e,10):(r.charCode=e.charCodeAt(0),r.keyCode=Wp[e]||e.toUpperCase().charCodeAt(0))}),t=[r.keyCode],Kp)r[n]?t.push(n):r[n]=!1;return r.id=t.join(","),r.access&&(r.alt=!0,fe.mac?r.ctrl=!0:r.shift=!0),r.meta&&(fe.mac?r.meta=!0:(r.ctrl=!0,r.meta=!1)),r},s=function(e,t,n,r){var o;return(o=Xt.map($p(e,">"),u))[o.length-1]=Xt.extend(o[o.length-1],{func:n,scope:r||i}),Xt.extend(o[0],{desc:i.translate(t),subpatterns:o.slice(1)})},o=function(e,t){return!!t&&t.ctrl===e.ctrlKey&&t.meta===e.metaKey&&t.alt===e.altKey&&t.shift===e.shiftKey&&!!(e.keyCode===t.keyCode||e.charCode&&e.charCode===t.charCode)&&(e.preventDefault(),!0)},c=function(e){return e.func?e.func.call(e.scope):null};i.on("keyup keypress keydown",function(t){var e,n;((n=t).altKey||n.ctrlKey||n.metaKey||"keydown"===(e=t).type&&112<=e.keyCode&&e.keyCode<=123)&&!t.isDefaultPrevented()&&(qp(a,function(e){if(o(t,e))return r=e.subpatterns.slice(0),"keydown"===t.type&&c(e),!0}),o(t,r[0])&&(1===r.length&&"keydown"===t.type&&c(r[0]),r.shift()))}),this.add=function(e,n,r,o){var t;return"string"==typeof(t=r)?r=function(){i.execCommand(t,!1,null)}:Xt.isArray(t)&&(r=function(){i.execCommand(t[0],t[1],t[2])}),qp($p(Xt.trim(e.toLowerCase())),function(e){var t=s(e,n,r,o);a[t.id]=t}),!0},this.remove=function(e){var t=s(e);return!!a[t.id]&&(delete a[t.id],!0)}}var Yp=function(e){var t=zr(e).dom();return e.dom()===t.activeElement},Gp=function(t){return(e=zr(t),n=e!==undefined?e.dom():V.document,_.from(n.activeElement).map(ar.fromDom)).filter(function(e){return t.dom().contains(e.dom())});var e,n},Jp=function(t,e){return(n=e,n.collapsed?_.from(Za(n.startContainer,n.startOffset)).map(ar.fromDom):_.none()).bind(function(e){return To(e)?_.some(e):!1===Mr(t,e)?_.some(t):_.none()});var n},Qp=function(t,e){Jp(ar.fromDom(t.getBody()),e).bind(function(e){return uc.firstPositionIn(e.dom())}).fold(function(){t.selection.normalize()},function(e){return t.selection.setRng(e.toRange())})},Zp=function(e){if(e.setActive)try{e.setActive()}catch(t){e.focus()}else e.focus()},eh=function(e){var t,n=e.getBody();return n&&(t=ar.fromDom(n),Yp(t)||Gp(t).isSome())},th=function(e){return e.inline?eh(e):(t=e).iframeElement&&Yp(ar.fromDom(t.iframeElement));var t},nh=function(e){return e.editorManager.setActive(e)},rh=function(e,t){e.removed||(t?nh(e):function(t){var e=t.selection,n=t.settings.content_editable,r=t.getBody(),o=e.getRng();t.quirks.refreshContentEditable();var i,a,u=(i=t,a=e.getNode(),i.dom.getParent(a,function(e){return"true"===i.dom.getContentEditable(e)}));if(t.$.contains(r,u))return Zp(u),Qp(t,o),nh(t);t.bookmark!==undefined&&!1===th(t)&&rp(t).each(function(e){t.selection.setRng(e),o=e}),n||(fe.opera||Zp(r),t.getWin().focus()),(fe.gecko||n)&&(Zp(r),Qp(t,o)),nh(t)}(e))},oh=th,ih=function(e,t){return t.dom()[e]},ah=function(e,t){return parseInt(Tr(t,e),10)},uh=d(ih,"clientWidth"),sh=d(ih,"clientHeight"),ch=d(ah,"margin-top"),lh=d(ah,"margin-left"),fh=function(e,t,n){var r,o,i,a,u,s,c,l,f,d,m,g=ar.fromDom(e.getBody()),p=e.inline?g:(r=g,ar.fromDom(r.dom().ownerDocument.documentElement)),h=(o=e.inline,a=t,u=n,s=(i=p).dom().getBoundingClientRect(),{x:a-(o?s.left+i.dom().clientLeft+lh(i):0),y:u-(o?s.top+i.dom().clientTop+ch(i):0)});return l=h.x,f=h.y,d=uh(c=p),m=sh(c),0<=l&&0<=f&&l<=d&&f<=m},dh=function(e){var t,n=e.inline?e.getBody():e.getContentAreaContainer();return(t=n,_.from(t).map(ar.fromDom)).map(function(e){return Mr(zr(e),e)}).getOr(!1)};function mh(n){var t,o=[],i=function(){var e,t=n.theme;return t&&t.getNotificationManagerImpl?t.getNotificationManagerImpl():{open:e=function(){throw new Error("Theme did not provide a NotificationManager implementation.")},close:e,reposition:e,getArgs:e}},a=function(){0<o.length&&i().reposition(o)},u=function(t){Y(o,function(e){return e===t}).each(function(e){o.splice(e,1)})},r=function(r){if(!n.removed&&dh(n))return X(o,function(e){return t=i().getArgs(e),n=r,!(t.type!==n.type||t.text!==n.text||t.progressBar||t.timeout||n.progressBar||n.timeout);var t,n}).getOrThunk(function(){n.editorManager.setActive(n);var e,t=i().open(r,function(){u(t),a()});return e=t,o.push(e),a(),t})};return(t=n).on("SkinLoaded",function(){var e=t.settings.service_message;e&&r({text:e,type:"warning",timeout:0,icon:""})}),t.on("ResizeEditor ResizeWindow",function(){he.requestAnimationFrame(a)}),t.on("remove",function(){z(o.slice(),function(e){i().close(e)})}),{open:r,close:function(){_.from(o[0]).each(function(e){i().close(e),u(e),a()})},getNotifications:function(){return o}}}function gh(r){var o=[],i=function(){var e,t=r.theme;return t&&t.getWindowManagerImpl?t.getWindowManagerImpl():{open:e=function(){throw new Error("Theme did not provide a WindowManager implementation.")},alert:e,confirm:e,close:e,getParams:e,setParams:e}},a=function(e,t){return function(){return t?t.apply(e,arguments):undefined}},u=function(e){var t;o.push(e),t=e,r.fire("OpenWindow",{win:t})},s=function(n){Y(o,function(e){return e===n}).each(function(e){var t;o.splice(e,1),t=n,r.fire("CloseWindow",{win:t}),0===o.length&&r.focus()})},e=function(){return _.from(o[o.length-1])};return r.on("remove",function(){z(o.slice(0),function(e){i().close(e)})}),{windows:o,open:function(e,t){r.editorManager.setActive(r),tp(r);var n=i().open(e,t,s);return u(n),n},alert:function(e,t,n){var r=i().alert(e,a(n||this,t),s);u(r)},confirm:function(e,t,n){var r=i().confirm(e,a(n||this,t),s);u(r)},close:function(){e().each(function(e){i().close(e),s(e)})},getParams:function(){return e().map(i().getParams).getOr(null)},setParams:function(t){e().each(function(e){i().setParams(e,t)})},getWindows:function(){return o}}}var ph={},hh="en",vh={setCode:function(e){e&&(hh=e,this.rtl=!!this.data[e]&&"rtl"===this.data[e]._dir)},getCode:function(){return hh},rtl:!1,add:function(e,t){var n=ph[e];for(var r in n||(ph[e]=n={}),t)n[r]=t[r];this.setCode(e)},translate:function(e){var t=ph[hh]||{},n=function(e){return Xt.is(e,"function")?Object.prototype.toString.call(e):r(e)?"":""+e},r=function(e){return""===e||null===e||Xt.is(e,"undefined")},o=function(e){return e=n(e),Xt.hasOwn(t,e)?n(t[e]):e};if(r(e))return"";if(Xt.is(e,"object")&&Xt.hasOwn(e,"raw"))return n(e.raw);if(Xt.is(e,"array")){var i=e.slice(1);e=o(e[0]).replace(/\{([0-9]+)\}/g,function(e,t){return Xt.hasOwn(i,t)?n(i[t]):e})}return o(e).replace(/{context:\w+}$/,"")},data:ph},yh=Oi.PluginManager,bh=function(e,t){var n=function(e,t){for(var n in yh.urls)if(yh.urls[n]+"/plugin"+t+".js"===e)return n;return null}(t,e.suffix);return n?vh.translate(["Failed to load plugin: {0} from url {1}",n,t]):vh.translate(["Failed to load plugin url: {0}",t])},Ch=function(e,t){e.notificationManager.open({type:"error",text:t})},xh=function(e,t){e._skinLoaded?Ch(e,t):e.on("SkinLoaded",function(){Ch(e,t)})},wh=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r=V.window.console;r&&(r.error?r.error.apply(r,arguments):r.log.apply(r,arguments))},Nh={pluginLoadError:function(e,t){xh(e,bh(e,t))},pluginInitError:function(e,t,n){var r=vh.translate(["Failed to initialize plugin: {0}",t]);wh(r,n),xh(e,r)},uploadError:function(e,t){xh(e,vh.translate(["Failed to upload image: {0}",t]))},displayError:xh,initError:wh},Eh=Oi.PluginManager,Sh=Oi.ThemeManager;function Th(){return new(oe.getOrDie("XMLHttpRequest"))}function kh(u,s){var r={},n=function(e,r,o,t){var i,n;(i=Th()).open("POST",s.url),i.withCredentials=s.credentials,i.upload.onprogress=function(e){t(e.loaded/e.total*100)},i.onerror=function(){o("Image upload failed due to a XHR Transport error. Code: "+i.status)},i.onload=function(){var e,t,n;i.status<200||300<=i.status?o("HTTP Error: "+i.status):(e=JSON.parse(i.responseText))&&"string"==typeof e.location?r((t=s.basePath,n=e.location,t?t.replace(/\/$/,"")+"/"+n.replace(/^\//,""):n)):o("Invalid JSON: "+i.responseText)},(n=new V.FormData).append("file",e.blob(),e.filename()),i.send(n)},c=function(e,t){return{url:t,blobInfo:e,status:!0}},l=function(e,t){return{url:"",blobInfo:e,status:!1,error:t}},f=function(e,t){Xt.each(r[e],function(e){e(t)}),delete r[e]},o=function(e,n){return e=Xt.grep(e,function(e){return!u.isUploaded(e.blobUri())}),de.all(Xt.map(e,function(e){return u.isPending(e.blobUri())?(t=e.blobUri(),new de(function(e){r[t]=r[t]||[],r[t].push(e)})):(o=e,i=s.handler,a=n,u.markPending(o.blobUri()),new de(function(t){var n;try{var r=function(){n&&n.close()};i(o,function(e){r(),u.markUploaded(o.blobUri(),e),f(o.blobUri(),c(o,e)),t(c(o,e))},function(e){r(),u.removeFailed(o.blobUri()),f(o.blobUri(),l(o,e)),t(l(o,e))},function(e){e<0||100<e||(n||(n=a()),n.progressBar.value(e))})}catch(e){t(l(o,e.message))}}));var o,i,a,t}))};return!1===D(s.handler)&&(s.handler=n),{upload:function(e,t){return s.url||s.handler!==n?o(e,t):new de(function(e){e([])})}}}var _h=function(e){return oe.getOrDie("atob")(e)},Ah=function(e){var t,n,r=decodeURIComponent(e).split(",");return(n=/data:([^;]+)/.exec(r[0]))&&(t=n[1]),{type:t,data:r[1]}},Rh=function(a){return new de(function(e){var t,n,r,o,i=Ah(a);try{t=_h(i.data)}catch(oE){return void e(new V.Blob([]))}for(o=t.length,n=new(oe.getOrDie("Uint8Array"))(o),r=0;r<n.length;r++)n[r]=t.charCodeAt(r);e(new V.Blob([n],{type:i.type}))})},Dh=function(e){return 0===e.indexOf("blob:")?(i=e,new de(function(e,t){var n=function(){t("Cannot convert "+i+" to Blob. Resource might not exist or is inaccessible.")};try{var r=Th();r.open("GET",i,!0),r.responseType="blob",r.onload=function(){200===this.status?e(this.response):n()},r.onerror=n,r.send()}catch(o){n()}})):0===e.indexOf("data:")?Rh(e):null;var i},Oh=function(n){return new de(function(e){var t=new(oe.getOrDie("FileReader"));t.onloadend=function(){e(t.result)},t.readAsDataURL(n)})},Bh=Ah,Ph=0,Ih=function(e){return(e||"blobid")+Ph++},Lh=function(n,r,o,t){var i,a;0!==r.src.indexOf("blob:")?(i=Bh(r.src).data,(a=n.findFirst(function(e){return e.base64()===i}))?o({image:r,blobInfo:a}):Dh(r.src).then(function(e){a=n.create(Ih(),e,i),n.add(a),o({image:r,blobInfo:a})},function(e){t(e)})):(a=n.getByUri(r.src))?o({image:r,blobInfo:a}):Dh(r.src).then(function(t){Oh(t).then(function(e){i=Bh(e).data,a=n.create(Ih(),t,i),n.add(a),o({image:r,blobInfo:a})})},function(e){t(e)})},Fh=function(e){return e?te(e.getElementsByTagName("img")):[]},Mh=0,zh={uuid:function(e){return e+Mh+++(t=function(){return Math.round(4294967295*Math.random()).toString(36)},"s"+(new Date).getTime().toString(36)+t()+t()+t());var t}};function Uh(u){var n,o,t,e,i,r,a,s,c,l=(n=[],o=function(e){var t,n,r;if(!e.blob||!e.base64)throw new Error("blob and base64 representations of the image are required for BlobInfo to be created");return t=e.id||zh.uuid("blobid"),n=e.name||t,{id:q(t),name:q(n),filename:q(n+"."+(r=e.blob.type,{"image/jpeg":"jpg","image/jpg":"jpg","image/gif":"gif","image/png":"png"}[r.toLowerCase()]||"dat")),blob:q(e.blob),base64:q(e.base64),blobUri:q(e.blobUri||ae.createObjectURL(e.blob)),uri:q(e.uri)}},{create:function(e,t,n,r){if(S(e))return o({id:e,name:r,blob:t,base64:n});if(T(e))return o(e);throw new Error("Unknown input type")},add:function(e){t(e.id())||n.push(e)},get:t=function(t){return e(function(e){return e.id()===t})},getByUri:function(t){return e(function(e){return e.blobUri()===t})},findFirst:e=function(e){return U(n,e)[0]},removeByUri:function(t){n=U(n,function(e){return e.blobUri()!==t||(ae.revokeObjectURL(e.blobUri()),!1)})},destroy:function(){z(n,function(e){ae.revokeObjectURL(e.blobUri())}),n=[]}}),f=(a={},s=function(e,t){return{status:e,resultUri:t}},{hasBlobUri:c=function(e){return e in a},getResultUri:function(e){var t=a[e];return t?t.resultUri:null},isPending:function(e){return!!c(e)&&1===a[e].status},isUploaded:function(e){return!!c(e)&&2===a[e].status},markPending:function(e){a[e]=s(1,null)},markUploaded:function(e,t){a[e]=s(2,t)},removeFailed:function(e){delete a[e]},destroy:function(){a={}}}),d=[],m=function(t){return function(e){return u.selection?t(e):[]}},g=function(e,t,n){for(var r=0;-1!==(r=e.indexOf(t,r))&&(e=e.substring(0,r)+n+e.substr(r+t.length),r+=n.length-t.length+1),-1!==r;);return e},p=function(e,t,n){return e=g(e,'src="'+t+'"','src="'+n+'"'),e=g(e,'data-mce-src="'+t+'"','data-mce-src="'+n+'"')},h=function(t,n){z(u.undoManager.data,function(e){"fragmented"===e.type?e.fragments=W(e.fragments,function(e){return p(e,t,n)}):e.content=p(e.content,t,n)})},v=function(){return u.notificationManager.open({text:u.translate("Image uploading..."),type:"info",timeout:-1,progressBar:!0})},y=function(e,t){l.removeByUri(e.src),h(e.src,t),u.$(e).attr({src:Ol(u)?t+"?"+(new Date).getTime():t,"data-mce-src":u.convertURL(t,"src")})},b=function(n){return i||(i=kh(f,{url:Pl(u),basePath:Il(u),credentials:Ll(u),handler:Fl(u)})),w().then(m(function(r){var e;return e=W(r,function(e){return e.blobInfo}),i.upload(e,v).then(m(function(e){var t=W(e,function(e,t){var n=r[t].image;return e.status&&Bl(u)?y(n,e.url):e.error&&Nh.uploadError(u,e.error),{element:n,status:e.status}});return n&&n(t),t}))}))},C=function(e){if(Dl(u))return b(e)},x=function(t){return!1!==J(d,function(e){return e(t)})&&(0!==t.getAttribute("src").indexOf("data:")||Rl(u)(t))},w=function(){var o,i,a;return r||(o=f,i=l,a={},r={findAll:function(e,n){var t;n||(n=q(!0)),t=U(Fh(e),function(e){var t=e.src;return!!fe.fileApi&&!e.hasAttribute("data-mce-bogus")&&!e.hasAttribute("data-mce-placeholder")&&!(!t||t===fe.transparentSrc)&&(0===t.indexOf("blob:")?!o.isUploaded(t)&&n(e):0===t.indexOf("data:")&&n(e))});var r=W(t,function(n){if(a[n.src])return new de(function(t){a[n.src].then(function(e){if("string"==typeof e)return e;t({image:n,blobInfo:e.blobInfo})})});var e=new de(function(e,t){Lh(i,n,e,t)}).then(function(e){return delete a[e.image.src],e})["catch"](function(e){return delete a[n.src],e});return a[n.src]=e});return de.all(r)}}),r.findAll(u.getBody(),x).then(m(function(e){return e=U(e,function(e){return"string"!=typeof e||(Nh.displayError(u,e),!1)}),z(e,function(e){h(e.image.src,e.blobInfo.blobUri()),e.image.src=e.blobInfo.blobUri(),e.image.removeAttribute("data-mce-src")}),e}))},N=function(e){return e.replace(/src="(blob:[^"]+)"/g,function(e,n){var t=f.getResultUri(n);if(t)return'src="'+t+'"';var r=l.getByUri(n);return r||(r=j(u.editorManager.get(),function(e,t){return e||t.editorUpload&&t.editorUpload.blobCache.getByUri(n)},null)),r?'src="data:'+r.blob().type+";base64,"+r.base64()+'"':e})};return u.on("setContent",function(){Dl(u)?C():w()}),u.on("RawSaveContent",function(e){e.content=N(e.content)}),u.on("getContent",function(e){e.source_view||"raw"===e.format||(e.content=N(e.content))}),u.on("PostRender",function(){u.parser.addNodeFilter("img",function(e){z(e,function(e){var t=e.attr("src");if(!l.getByUri(t)){var n=f.getResultUri(t);n&&e.attr("src",n)}})})}),{blobCache:l,addFilter:function(e){d.push(e)},uploadImages:b,uploadImagesAuto:C,scanForImages:w,destroy:function(){l.destroy(),f.destroy(),r=i=null}}}var jh=function(e,t){return e.hasOwnProperty(t.nodeName)},Vh=function(e,t){if(Uo.isText(t)){if(0===t.nodeValue.length)return!0;if(/^\s+$/.test(t.nodeValue)&&(!t.nextSibling||jh(e,t.nextSibling)))return!0}return!1},Hh=function(e){var t,n,r,o,i,a,u,s,c,l,f,d=e.settings,m=e.dom,g=e.selection,p=e.schema,h=p.getBlockElements(),v=g.getStart(),y=e.getBody();if(f=d.forced_root_block,v&&Uo.isElement(v)&&f&&(l=y.nodeName.toLowerCase(),p.isValidChild(l,f.toLowerCase())&&(b=h,C=y,x=v,!M(of(ar.fromDom(x),ar.fromDom(C)),function(e){return jh(b,e.dom())})))){var b,C,x,w,N;for(n=(t=g.getRng()).startContainer,r=t.startOffset,o=t.endContainer,i=t.endOffset,c=oh(e),v=y.firstChild;v;)if(w=h,N=v,Uo.isText(N)||Uo.isElement(N)&&!jh(w,N)&&!vc(N)){if(Vh(h,v)){v=(u=v).nextSibling,m.remove(u);continue}a||(a=m.create(f,e.settings.forced_root_block_attrs),v.parentNode.insertBefore(a,v),s=!0),v=(u=v).nextSibling,a.appendChild(u)}else a=null,v=v.nextSibling;s&&c&&(t.setStart(n,r),t.setEnd(o,i),g.setRng(t),e.nodeChanged())}},qh=function(e){e.settings.forced_root_block&&e.on("NodeChange",d(Hh,e))},$h=function(t){return Xr(t).fold(q([t]),function(e){return[t].concat($h(e))})},Wh=function(t){return Yr(t).fold(q([t]),function(e){return"br"===lr(e)?Vr(e).map(function(e){return[t].concat(Wh(e))}).getOr([]):[t].concat(Wh(e))})},Kh=function(o,e){return nu((i=e,a=i.startContainer,u=i.startOffset,Uo.isText(a)?0===u?_.some(ar.fromDom(a)):_.none():_.from(a.childNodes[u]).map(ar.fromDom)),(t=e,n=t.endContainer,r=t.endOffset,Uo.isText(n)?r===n.data.length?_.some(ar.fromDom(n)):_.none():_.from(n.childNodes[r-1]).map(ar.fromDom)),function(e,t){var n=X($h(o),d(Fr,e)),r=X(Wh(o),d(Fr,t));return n.isSome()&&r.isSome()}).getOr(!1);var t,n,r,i,a,u},Xh=function(e,t,n,r){var o=n,i=new mo(n,o),a=e.schema.getNonEmptyElements();do{if(3===n.nodeType&&0!==Xt.trim(n.nodeValue).length)return void(r?t.setStart(n,0):t.setEnd(n,n.nodeValue.length));if(a[n.nodeName]&&!/^(TD|TH)$/.test(n.nodeName))return void(r?t.setStartBefore(n):"BR"===n.nodeName?t.setEndBefore(n):t.setEndAfter(n));if(fe.ie&&fe.ie<11&&e.isBlock(n)&&e.isEmpty(n))return void(r?t.setStart(n,0):t.setEnd(n,0))}while(n=r?i.next():i.prev());"BODY"===o.nodeName&&(r?t.setStart(o,0):t.setEnd(o,o.childNodes.length))},Yh=function(e){var t=e.selection.getSel();return t&&0<t.rangeCount};function Gh(i){var r,o=[];"onselectionchange"in i.getDoc()||i.on("NodeChange Click MouseUp KeyUp Focus",function(e){var t,n;n={startContainer:(t=i.selection.getRng()).startContainer,startOffset:t.startOffset,endContainer:t.endContainer,endOffset:t.endOffset},"nodechange"!==e.type&&Cg(n,r)||i.fire("SelectionChange"),r=n}),i.on("contextmenu",function(){i.fire("SelectionChange")}),i.on("SelectionChange",function(){var e=i.selection.getStart(!0);!e||!fe.range&&i.selection.isCollapsed()||Yh(i)&&!function(e){var t,n;if((n=i.$(e).parentsUntil(i.getBody()).add(e)).length===o.length){for(t=n.length;0<=t&&n[t]===o[t];t--);if(-1===t)return o=n,!0}return o=n,!1}(e)&&i.dom.isChildOf(e,i.getBody())&&i.nodeChanged({selectionChange:!0})}),i.on("MouseUp",function(e){!e.isDefaultPrevented()&&Yh(i)&&("IMG"===i.selection.getNode().nodeName?he.setEditorTimeout(i,function(){i.nodeChanged()}):i.nodeChanged())}),this.nodeChanged=function(e){var t,n,r,o=i.selection;i.initialized&&o&&!i.settings.disable_nodechange&&!i.readonly&&(r=i.getBody(),(t=o.getStart(!0)||r).ownerDocument===i.getDoc()&&i.dom.isChildOf(t,r)||(t=r),n=[],i.dom.getParent(t,function(e){if(e===r)return!0;n.push(e)}),(e=e||{}).element=t,e.parents=n,i.fire("NodeChange",e))}}var Jh,Qh,Zh=function(e){var t,n,r,o;return o=e.getBoundingClientRect(),n=(t=e.ownerDocument).documentElement,r=t.defaultView,{top:o.top+r.pageYOffset-n.clientTop,left:o.left+r.pageXOffset-n.clientLeft}},ev=function(e,t){return n=(u=e).inline?Zh(u.getBody()):{left:0,top:0},a=(i=e).getBody(),r=i.inline?{left:a.scrollLeft,top:a.scrollTop}:{left:0,top:0},{pageX:(o=function(e,t){if(t.target.ownerDocument!==e.getDoc()){var n=Zh(e.getContentAreaContainer()),r=(i=(o=e).getBody(),a=o.getDoc().documentElement,u={left:i.scrollLeft,top:i.scrollTop},s={left:i.scrollLeft||a.scrollLeft,top:i.scrollTop||a.scrollTop},o.inline?u:s);return{left:t.pageX-n.left+r.left,top:t.pageY-n.top+r.top}}var o,i,a,u,s;return{left:t.pageX,top:t.pageY}}(e,t)).left-n.left+r.left,pageY:o.top-n.top+r.top};var n,r,o,i,a,u},tv=Uo.isContentEditableFalse,nv=Uo.isContentEditableTrue,rv=function(e){e&&e.parentNode&&e.parentNode.removeChild(e)},ov=function(u,s){return function(e){if(0===e.button){var t=X(s.dom.getParents(e.target),iu(tv,nv)).getOr(null);if(i=s.getBody(),tv(a=t)&&a!==i){var n=s.dom.getPos(t),r=s.getBody(),o=s.getDoc().documentElement;u.element=t,u.screenX=e.screenX,u.screenY=e.screenY,u.maxX=(s.inline?r.scrollWidth:o.offsetWidth)-2,u.maxY=(s.inline?r.scrollHeight:o.offsetHeight)-2,u.relX=e.pageX-n.x,u.relY=e.pageY-n.y,u.width=t.offsetWidth,u.height=t.offsetHeight,u.ghost=function(e,t,n,r){var o=t.cloneNode(!0);e.dom.setStyles(o,{width:n,height:r}),e.dom.setAttrib(o,"data-mce-selected",null);var i=e.dom.create("div",{"class":"mce-drag-container","data-mce-bogus":"all",unselectable:"on",contenteditable:"false"});return e.dom.setStyles(i,{position:"absolute",opacity:.5,overflow:"hidden",border:0,padding:0,margin:0,width:n,height:r}),e.dom.setStyles(o,{margin:0,boxSizing:"border-box"}),i.appendChild(o),i}(s,t,u.width,u.height)}}var i,a}},iv=function(l,f){return function(e){if(l.dragging&&(s=(i=f).selection,c=s.getSel().getRangeAt(0).startContainer,a=3===c.nodeType?c.parentNode:c,u=l.element,a!==u&&!i.dom.isChildOf(a,u)&&!tv(a))){var t=(r=l.element,(o=r.cloneNode(!0)).removeAttribute("data-mce-selected"),o),n=f.fire("drop",{targetClone:t,clientX:e.clientX,clientY:e.clientY});n.isDefaultPrevented()||(t=n.targetClone,f.undoManager.transact(function(){rv(l.element),f.insertContent(f.dom.getOuterHTML(t)),f._selectionOverrides.hideFakeCaret()}))}var r,o,i,a,u,s,c;av(l)}},av=function(e){e.dragging=!1,e.element=null,rv(e.ghost)},uv=function(e){var t,n,r,o,i,a,p,h,v,u,s,c={};t=Ei.DOM,a=V.document,n=ov(c,e),p=c,h=e,v=he.throttle(function(e,t){h._selectionOverrides.hideFakeCaret(),h.selection.placeCaretAt(e,t)},0),r=function(e){var t,n,r,o,i,a,u,s,c,l,f,d,m=Math.max(Math.abs(e.screenX-p.screenX),Math.abs(e.screenY-p.screenY));if(p.element&&!p.dragging&&10<m){if(h.fire("dragstart",{target:p.element}).isDefaultPrevented())return;p.dragging=!0,h.focus()}if(p.dragging){var g=(f=p,{pageX:(d=ev(h,e)).pageX-f.relX,pageY:d.pageY+5});c=p.ghost,l=h.getBody(),c.parentNode!==l&&l.appendChild(c),t=p.ghost,n=g,r=p.width,o=p.height,i=p.maxX,a=p.maxY,s=u=0,t.style.left=n.pageX+"px",t.style.top=n.pageY+"px",n.pageX+r>i&&(u=n.pageX+r-i),n.pageY+o>a&&(s=n.pageY+o-a),t.style.width=r-u+"px",t.style.height=o-s+"px",v(e.clientX,e.clientY)}},o=iv(c,e),u=c,i=function(){u.dragging&&s.fire("dragend"),av(u)},(s=e).on("mousedown",n),e.on("mousemove",r),e.on("mouseup",o),t.bind(a,"mousemove",r),t.bind(a,"mouseup",i),e.on("remove",function(){t.unbind(a,"mousemove",r),t.unbind(a,"mouseup",i)})},sv=function(e){var n;uv(e),(n=e).on("drop",function(e){var t="undefined"!=typeof e.clientX?n.getDoc().elementFromPoint(e.clientX,e.clientY):null;(tv(t)||tv(n.dom.getContentEditableParent(t)))&&e.preventDefault()})},cv=function(e){return j(e,function(e,t){return e.concat(function(t){var e=function(e){return W(e,function(e){return(e=Wa(e)).node=t,e})};if(Uo.isElement(t))return e(t.getClientRects());if(Uo.isText(t)){var n=t.ownerDocument.createRange();return n.setStart(t,0),n.setEnd(t,t.data.length),e(n.getClientRects())}}(t))},[])};(Qh=Jh||(Jh={}))[Qh.Up=-1]="Up",Qh[Qh.Down=1]="Down";var lv=function(o,i,a,e,u,t){var n,s,c=0,l=[],r=function(e){var t,n,r;for(r=cv([e]),-1===o&&(r=r.reverse()),t=0;t<r.length;t++)if(n=r[t],!a(n,s)){if(0<l.length&&i(n,Ht.last(l))&&c++,n.line=c,u(n))return!0;l.push(n)}};return(s=Ht.last(t.getClientRects()))&&(r(n=t.getNode()),function(e,t,n,r){for(;r=Ns(r,e,qa,t);)if(n(r))return}(o,e,r,n)),l},fv=d(lv,Jh.Up,Ya,Ga),dv=d(lv,Jh.Down,Ga,Ya),mv=function(n){return function(e){return t=n,e.line>t;var t}},gv=function(n){return function(e){return t=n,e.line===t;var t}},pv=Uo.isContentEditableFalse,hv=Ns,vv=function(e,t){return Math.abs(e.left-t)},yv=function(e,t){return Math.abs(e.right-t)},bv=function(e,t){return e>=t.left&&e<=t.right},Cv=function(e,o){return Ht.reduce(e,function(e,t){var n,r;return n=Math.min(vv(e,o),yv(e,o)),r=Math.min(vv(t,o),yv(t,o)),bv(o,t)?t:bv(o,e)?e:r===n&&pv(t.node)?t:r<n?t:e})},xv=function(e,t,n,r){for(;r=hv(r,e,qa,t);)if(n(r))return},wv=function(e,t,n){var r,o,i,a,u,s,c,l=cv(U(te(e.getElementsByTagName("*")),ms)),f=U(l,function(e){return n>=e.top&&n<=e.bottom});return(r=Cv(f,t))&&(r=Cv((a=e,c=function(t,e){var n;return n=U(cv([e]),function(e){return!t(e,u)}),s=s.concat(n),0===n.length},(s=[]).push(u=r),xv(Jh.Up,a,d(c,Ya),u.node),xv(Jh.Down,a,d(c,Ga),u.node),s),t))&&ms(r.node)?(i=t,{node:(o=r).node,before:vv(o,i)<yv(o,i)}):null},Nv=function(t,n,e){if(e.collapsed)return!1;if(fe.ie&&fe.ie<=11&&e.startOffset===e.endOffset-1&&e.startContainer===e.endContainer){var r=e.startContainer.childNodes[e.startOffset];if(Uo.isElement(r))return M(r.getClientRects(),function(e){return Ja(e,t,n)})}return M(e.getClientRects(),function(e){return Ja(e,t,n)})},Ev=function(t){var e=ji(function(){if(!t.removed&&t.selection.getRng().collapsed){var e=sg(t,t.selection.getRng(),!1);t.selection.setRng(e)}},0);t.on("focus",function(){e.throttle()}),t.on("blur",function(){e.cancel()})},Sv={BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,END:35,HOME:36,modifierPressed:function(e){return e.shiftKey||e.ctrlKey||e.altKey||this.metaKeyPressed(e)},metaKeyPressed:function(e){return fe.mac?e.metaKey:e.ctrlKey&&!e.altKey}},Tv=Uo.isContentEditableTrue,kv=Uo.isContentEditableFalse,_v=function(e,t){for(var n=e.getBody();t&&t!==n;){if(Tv(t)||kv(t))return t;t=t.parentNode}return null},Av=function(g){var p,e,t,a=g.getBody(),o=fs(g.getBody(),function(e){return g.dom.isBlock(e)},function(){return oh(g)}),h="sel-"+g.dom.uniqueId(),u=function(e){e&&g.selection.setRng(e)},s=function(){return g.selection.getRng()},v=function(e,t,n,r){return void 0===r&&(r=!0),g.fire("ShowCaret",{target:t,direction:e,before:n}).isDefaultPrevented()?null:(r&&g.selection.scrollIntoView(t,-1===e),o.show(n,t))},y=function(e,t){return t=Ds(e,a,t),-1===e?ku.fromRangeStart(t):ku.fromRangeEnd(t)},n=function(e){return Ta(e)||Da(e)||Oa(e)},b=function(e){return n(e.startContainer)||n(e.endContainer)},c=function(e,t){var n,r,o,i,a,u,s,c,l,f,d=g.$,m=g.dom;if(!e)return null;if(e.collapsed){if(!b(e))if(!1===t){if(c=y(-1,e),ms(c.getNode(!0)))return v(-1,c.getNode(!0),!1,!1);if(ms(c.getNode()))return v(-1,c.getNode(),!c.isAtEnd(),!1)}else{if(c=y(1,e),ms(c.getNode()))return v(1,c.getNode(),!c.isAtEnd(),!1);if(ms(c.getNode(!0)))return v(1,c.getNode(!0),!1,!1)}return null}return i=e.startContainer,a=e.startOffset,u=e.endOffset,3===i.nodeType&&0===a&&kv(i.parentNode)&&(i=i.parentNode,a=m.nodeIndex(i),i=i.parentNode),1!==i.nodeType?null:(u===a+1&&i===e.endContainer&&(n=i.childNodes[a]),kv(n)?(l=f=n.cloneNode(!0),(s=g.fire("ObjectSelected",{target:n,targetClone:l})).isDefaultPrevented()?null:(r=ra(ar.fromDom(g.getBody()),"#"+h).fold(function(){return d([])},function(e){return d([e.dom()])}),l=s.targetClone,0===r.length&&(r=d('<div data-mce-bogus="all" class="mce-offscreen-selection"></div>').attr("id",h)).appendTo(g.getBody()),e=g.dom.createRng(),l===f&&fe.ie?(r.empty().append('<p style="font-size: 0" data-mce-bogus="all">\xa0</p>').append(l),e.setStartAfter(r[0].firstChild.firstChild),e.setEndAfter(l)):(r.empty().append("\xa0").append(l).append("\xa0"),e.setStart(r[0].firstChild,1),e.setEnd(r[0].lastChild,0)),r.css({top:m.getPos(n,g.getBody()).y}),r[0].focus(),(o=g.selection.getSel()).removeAllRanges(),o.addRange(e),z(Ji(ar.fromDom(g.getBody()),"*[data-mce-selected]"),function(e){Er(e,"data-mce-selected")}),n.setAttribute("data-mce-selected","1"),p=n,C(),e)):null)},l=function(){p&&(p.removeAttribute("data-mce-selected"),ra(ar.fromDom(g.getBody()),"#"+h).each(zi),p=null),ra(ar.fromDom(g.getBody()),"#"+h).each(zi),p=null},C=function(){o.hide()};return fe.ceFalse&&(function(){g.on("mouseup",function(e){var t=s();t.collapsed&&fh(g,e.clientX,e.clientY)&&u(ug(g,t,!1))}),g.on("click",function(e){var t;(t=_v(g,e.target))&&(kv(t)&&(e.preventDefault(),g.focus()),Tv(t)&&g.dom.isChildOf(t,g.selection.getNode())&&l())}),g.on("blur NewBlock",function(){l()}),g.on("ResizeWindow FullscreenStateChanged",function(){return o.reposition()});var n,r,i=function(e,t){var n,r,o=g.dom.getParent(e,g.dom.isBlock),i=g.dom.getParent(t,g.dom.isBlock);return!(!o||!g.dom.isChildOf(o,i)||!1!==kv(_v(g,o)))||o&&(n=o,r=i,!(g.dom.getParent(n,g.dom.isBlock)===g.dom.getParent(r,g.dom.isBlock)))&&function(e){var t=Gs(e);if(!e.firstChild)return!1;var n=ku.before(e.firstChild),r=t.next(n);return r&&!If(r)&&!Lf(r)}(o)};r=!1,(n=g).on("touchstart",function(){r=!1}),n.on("touchmove",function(){r=!0}),n.on("touchend",function(e){var t=_v(n,e.target);kv(t)&&(r||(e.preventDefault(),c(ag(n,t))))}),g.on("mousedown",function(e){var t,n=e.target;if((n===a||"HTML"===n.nodeName||g.dom.isChildOf(n,a))&&!1!==fh(g,e.clientX,e.clientY))if(t=_v(g,n))kv(t)?(e.preventDefault(),c(ag(g,t))):(l(),Tv(t)&&e.shiftKey||Nv(e.clientX,e.clientY,g.selection.getRng())||(C(),g.selection.placeCaretAt(e.clientX,e.clientY)));else if(!1===ms(n)){l(),C();var r=wv(a,e.clientX,e.clientY);if(r&&!i(e.target,r.node)){e.preventDefault();var o=v(1,r.node,r.before,!1);g.getBody().focus(),u(o)}}}),g.on("keypress",function(e){Sv.modifierPressed(e)||(e.keyCode,kv(g.selection.getNode())&&e.preventDefault())}),g.on("getSelectionRange",function(e){var t=e.range;if(p){if(!p.parentNode)return void(p=null);(t=t.cloneRange()).selectNode(p),e.range=t}}),g.on("setSelectionRange",function(e){var t;(t=c(e.range,e.forward))&&(e.range=t)}),g.on("AfterSetSelectionRange",function(e){var t,n=e.range;b(n)||"mcepastebin"===n.startContainer.parentNode.id||C(),t=n.startContainer.parentNode,g.dom.hasClass(t,"mce-offscreen-selection")||l()}),g.on("copy",function(e){var t,n=e.clipboardData;if(!e.isDefaultPrevented()&&e.clipboardData&&!fe.ie){var r=(t=g.dom.get(h))?t.getElementsByTagName("*")[0]:t;r&&(e.preventDefault(),n.clearData(),n.setData("text/html",r.outerHTML),n.setData("text/plain",r.outerText))}}),sv(g),Ev(g)}(),e=g.contentStyles,t=".mce-content-body",e.push(o.getCss()),e.push(t+" .mce-offscreen-selection {position: absolute;left: -9999999999px;max-width: 1000000px;}"+t+" *[contentEditable=false] {cursor: default;}"+t+" *[contentEditable=true] {cursor: text;}")),{showCaret:v,showBlockCaretContainer:function(e){e.hasAttribute("data-mce-caret")&&(Ba(e),u(s()),g.selection.scrollIntoView(e[0]))},hideFakeCaret:C,destroy:function(){o.destroy(),p=null}}},Rv=function(e,t,n){var r,o,i,a,u=1;for(a=e.getShortEndedElements(),(i=/<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g).lastIndex=r=n;o=i.exec(t);){if(r=i.lastIndex,"/"===o[1])u--;else if(!o[1]){if(o[2]in a)continue;u++}if(0===u)break}return r},Dv=function(e,t){var n=e.exec(t);if(n){var r=n[1],o=n[2];return"string"==typeof r&&"data-mce-bogus"===r.toLowerCase()?o:null}return null};function Ov(z,U){void 0===U&&(U=fi());var e=function(){};!1!==(z=z||{}).fix_self_closing&&(z.fix_self_closing=!0);var j=z.comment?z.comment:e,V=z.cdata?z.cdata:e,H=z.text?z.text:e,q=z.start?z.start:e,$=z.end?z.end:e,W=z.pi?z.pi:e,K=z.doctype?z.doctype:e;return{parse:function(e){var t,n,r,d,o,i,a,m,u,s,g,c,p,l,f,h,v,y,b,C,x,w,N,E,S,T,k,_,A,R=0,D=[],O=0,B=ei.decode,P=Xt.makeMap("src,href,data,background,formaction,poster,xlink:href"),I=/((java|vb)script|mhtml):/i,L=function(e){var t,n;for(t=D.length;t--&&D[t].name!==e;);if(0<=t){for(n=D.length-1;t<=n;n--)(e=D[n]).valid&&$(e.name);D.length=t}},F=function(e,t,n,r,o){var i,a,u,s,c;if(n=(t=t.toLowerCase())in g?t:B(n||r||o||""),p&&!m&&0==(0===(u=t).indexOf("data-")||0===u.indexOf("aria-"))){if(!(i=y[t])&&b){for(a=b.length;a--&&!(i=b[a]).pattern.test(t););-1===a&&(i=null)}if(!i)return;if(i.validValues&&!(n in i.validValues))return}if(P[t]&&!z.allow_script_urls){var l=n.replace(/[\s\u0000-\u001F]+/g,"");try{l=decodeURIComponent(l)}catch(f){l=unescape(l)}if(I.test(l))return;if(c=l,!(s=z).allow_html_data_urls&&(/^data:image\//i.test(c)?!1===s.allow_svg_data_urls&&/^data:image\/svg\+xml/i.test(c):/^data:/i.test(c)))return}m&&(t in P||0===t.indexOf("on"))||(d.map[t]=n,d.push({name:t,value:n}))};for(S=new RegExp("<(?:(?:!--([\\w\\W]*?)--!?>)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)>)|(?:([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g"),T=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g,s=U.getShortEndedElements(),E=z.self_closing_elements||U.getSelfClosingElements(),g=U.getBoolAttrs(),p=z.validate,u=z.remove_internals,A=z.fix_self_closing,k=U.getSpecialElements(),N=e+">";t=S.exec(N);){if(R<t.index&&H(B(e.substr(R,t.index-R))),n=t[6])":"===(n=n.toLowerCase()).charAt(0)&&(n=n.substr(1)),L(n);else if(n=t[7]){if(t.index+t[0].length>e.length){H(B(e.substr(t.index))),R=t.index+t[0].length;continue}":"===(n=n.toLowerCase()).charAt(0)&&(n=n.substr(1)),c=n in s,A&&E[n]&&0<D.length&&D[D.length-1].name===n&&L(n);var M=Dv(T,t[8]);if(null!==M){if("all"===M){R=Rv(U,e,S.lastIndex),S.lastIndex=R;continue}f=!1}if(!p||(l=U.getElementRule(n))){if(f=!0,p&&(y=l.attributes,b=l.attributePatterns),(v=t[8])?((m=-1!==v.indexOf("data-mce-type"))&&u&&(f=!1),(d=[]).map={},v.replace(T,F)):(d=[]).map={},p&&!m){if(C=l.attributesRequired,x=l.attributesDefault,w=l.attributesForced,l.removeEmptyAttrs&&!d.length&&(f=!1),w)for(o=w.length;o--;)a=(h=w[o]).name,"{$uid}"===(_=h.value)&&(_="mce_"+O++),d.map[a]=_,d.push({name:a,value:_});if(x)for(o=x.length;o--;)(a=(h=x[o]).name)in d.map||("{$uid}"===(_=h.value)&&(_="mce_"+O++),d.map[a]=_,d.push({name:a,value:_}));if(C){for(o=C.length;o--&&!(C[o]in d.map););-1===o&&(f=!1)}if(h=d.map["data-mce-bogus"]){if("all"===h){R=Rv(U,e,S.lastIndex),S.lastIndex=R;continue}f=!1}}f&&q(n,d,c)}else f=!1;if(r=k[n]){r.lastIndex=R=t.index+t[0].length,(t=r.exec(e))?(f&&(i=e.substr(R,t.index-R)),R=t.index+t[0].length):(i=e.substr(R),R=e.length),f&&(0<i.length&&H(i,!0),$(n)),S.lastIndex=R;continue}c||(v&&v.indexOf("/")===v.length-1?f&&$(n):D.push({name:n,valid:f}))}else(n=t[1])?(">"===n.charAt(0)&&(n=" "+n),z.allow_conditional_comments||"[if"!==n.substr(0,3).toLowerCase()||(n=" "+n),j(n)):(n=t[2])?V(n.replace(/<!--|--!?>/g,"")):(n=t[3])?K(n):(n=t[4])&&W(n,t[5]);R=t.index+t[0].length}for(R<e.length&&H(B(e.substr(R))),o=D.length-1;0<=o;o--)(n=D[o]).valid&&$(n.name)}}}(Ov||(Ov={})).findEndTag=Rv;var Bv=Ov,Pv=function(e,t){var n,r,o,i,a,u,s,c,l=t,f=/<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g,d=e.schema;for(u=e.getTempAttrs(),s=l,c=new RegExp(["\\s?("+u.join("|")+')="[^"]+"'].join("|"),"gi"),l=s.replace(c,""),a=d.getShortEndedElements();i=f.exec(l);)r=f.lastIndex,o=i[0].length,n=a[i[1]]?r:Bv.findEndTag(d,l,r),l=l.substring(0,r-o)+l.substring(n),f.lastIndex=r-o;return xa(l)},Iv={trimExternal:Pv,trimInternal:Pv},Lv=0,Fv=2,Mv=1,zv=function(g,p){var e=g.length+p.length+2,h=new Array(e),v=new Array(e),c=function(e,t,n,r,o){var i=l(e,t,n,r);if(null===i||i.start===t&&i.diag===t-r||i.end===e&&i.diag===e-n)for(var a=e,u=n;a<t||u<r;)a<t&&u<r&&g[a]===p[u]?(o.push([0,g[a]]),++a,++u):r-n<t-e?(o.push([2,g[a]]),++a):(o.push([1,p[u]]),++u);else{c(e,i.start,n,i.start-i.diag,o);for(var s=i.start;s<i.end;++s)o.push([0,g[s]]);c(i.end,t,i.end-i.diag,r,o)}},y=function(e,t,n,r){for(var o=e;o-t<r&&o<n&&g[o]===p[o-t];)++o;return{start:e,end:o,diag:t}},l=function(e,t,n,r){var o=t-e,i=r-n;if(0===o||0===i)return null;var a,u,s,c,l,f=o-i,d=i+o,m=(d%2==0?d:d+1)/2;for(h[1+m]=e,v[1+m]=t+1,a=0;a<=m;++a){for(u=-a;u<=a;u+=2){for(s=u+m,u===-a||u!==a&&h[s-1]<h[s+1]?h[s]=h[s+1]:h[s]=h[s-1]+1,l=(c=h[s])-e+n-u;c<t&&l<r&&g[c]===p[l];)h[s]=++c,++l;if(f%2!=0&&f-a<=u&&u<=f+a&&v[s-f]<=h[s])return y(v[s-f],u+e-n,t,r)}for(u=f-a;u<=f+a;u+=2){for(s=u+m-f,u===f-a||u!==f+a&&v[s+1]<=v[s-1]?v[s]=v[s+1]-1:v[s]=v[s-1],l=(c=v[s]-1)-e+n-u;e<=c&&n<=l&&g[c]===p[l];)v[s]=c--,l--;if(f%2==0&&-a<=u&&u<=a&&v[s]<=h[s+f])return y(v[s],u+e-n,t,r)}}},t=[];return c(0,g.length,0,p.length,t),t},Uv=function(e){return Uo.isElement(e)?e.outerHTML:Uo.isText(e)?ei.encodeRaw(e.data,!1):Uo.isComment(e)?"\x3c!--"+e.data+"--\x3e":""},jv=function(e,t,n){var r=function(e){var t,n,r;for(r=V.document.createElement("div"),t=V.document.createDocumentFragment(),e&&(r.innerHTML=e);n=r.firstChild;)t.appendChild(n);return t}(t);if(e.hasChildNodes()&&n<e.childNodes.length){var o=e.childNodes[n];o.parentNode.insertBefore(r,o)}else e.appendChild(r)},Vv=function(e){return U(W(te(e.childNodes),Uv),function(e){return 0<e.length})},Hv=function(e,t){var n,r,o,i=W(te(t.childNodes),Uv);return n=zv(i,e),r=t,o=0,z(n,function(e){e[0]===Lv?o++:e[0]===Mv?(jv(r,e[1],o),o++):e[0]===Fv&&function(e,t){if(e.hasChildNodes()&&t<e.childNodes.length){var n=e.childNodes[t];n.parentNode.removeChild(n)}}(r,o)}),t},qv=Vi(_.none()),$v=function(e){return{type:"fragmented",fragments:e,content:"",bookmark:null,beforeBookmark:null}},Wv=function(e){return{type:"complete",fragments:null,content:e,bookmark:null,beforeBookmark:null}},Kv=function(e){return"fragmented"===e.type?e.fragments.join(""):e.content},Xv=function(e){var t=ar.fromTag("body",qv.get().getOrThunk(function(){var e=V.document.implementation.createHTMLDocument("undo");return qv.set(_.some(e)),e}));return va(t,Kv(e)),z(Ji(t,"*[data-mce-bogus]"),Ui),t.dom().innerHTML},Yv=function(n){var e,t,r;return e=Vv(n.getBody()),-1!==(t=(r=G(e,function(e){var t=Iv.trimInternal(n.serializer,e);return 0<t.length?[t]:[]})).join("")).indexOf("</iframe>")?$v(r):Wv(t)},Gv=function(e,t,n){"fragmented"===t.type?Hv(t.fragments,e.getBody()):e.setContent(t.content,{format:"raw"}),e.selection.moveToBookmark(n?t.beforeBookmark:t.bookmark)},Jv=function(e,t){return!(!e||!t)&&(r=t,Kv(e)===Kv(r)||(n=t,Xv(e)===Xv(n)));var n,r};function Qv(u){var s,r,o=this,c=0,l=[],t=0,f=function(){return 0===t},i=function(e){f()&&(o.typing=e)},d=function(e){u.setDirty(e)},a=function(e){i(!1),o.add({},e)},n=function(){o.typing&&(i(!1),o.add())};return u.on("init",function(){o.add()}),u.on("BeforeExecCommand",function(e){var t=e.command;"Undo"!==t&&"Redo"!==t&&"mceRepaint"!==t&&(n(),o.beforeChange())}),u.on("ExecCommand",function(e){var t=e.command;"Undo"!==t&&"Redo"!==t&&"mceRepaint"!==t&&a(e)}),u.on("ObjectResizeStart Cut",function(){o.beforeChange()}),u.on("SaveContent ObjectResized blur",a),u.on("DragEnd",a),u.on("KeyUp",function(e){var t=e.keyCode;e.isDefaultPrevented()||((33<=t&&t<=36||37<=t&&t<=40||45===t||e.ctrlKey)&&(a(),u.nodeChanged()),46!==t&&8!==t||u.nodeChanged(),r&&o.typing&&!1===Jv(Yv(u),l[0])&&(!1===u.isDirty()&&(d(!0),u.fire("change",{level:l[0],lastLevel:null})),u.fire("TypingUndo"),r=!1,u.nodeChanged()))}),u.on("KeyDown",function(e){var t=e.keyCode;if(!e.isDefaultPrevented())if(33<=t&&t<=36||37<=t&&t<=40||45===t)o.typing&&a(e);else{var n=e.ctrlKey&&!e.altKey||e.metaKey;!(t<16||20<t)||224===t||91===t||o.typing||n||(o.beforeChange(),i(!0),o.add({},e),r=!0)}}),u.on("MouseDown",function(e){o.typing&&a(e)}),u.on("input",function(e){var t;e.inputType&&("insertReplacementText"===e.inputType||"insertText"===(t=e).inputType&&null===t.data)&&a(e)}),u.addShortcut("meta+z","","Undo"),u.addShortcut("meta+y,meta+shift+z","","Redo"),u.on("AddUndo Undo Redo ClearUndos",function(e){e.isDefaultPrevented()||u.nodeChanged()}),o={data:l,typing:!1,beforeChange:function(){f()&&(s=Xu.getUndoBookmark(u.selection))},add:function(e,t){var n,r,o,i=u.settings;if(o=Yv(u),e=e||{},e=Xt.extend(e,o),!1===f()||u.removed)return null;if(r=l[c],u.fire("BeforeAddUndo",{level:e,lastLevel:r,originalEvent:t}).isDefaultPrevented())return null;if(r&&Jv(r,e))return null;if(l[c]&&(l[c].beforeBookmark=s),i.custom_undo_redo_levels&&l.length>i.custom_undo_redo_levels){for(n=0;n<l.length-1;n++)l[n]=l[n+1];l.length--,c=l.length}e.bookmark=Xu.getUndoBookmark(u.selection),c<l.length-1&&(l.length=c+1),l.push(e),c=l.length-1;var a={level:e,lastLevel:r,originalEvent:t};return u.fire("AddUndo",a),0<c&&(d(!0),u.fire("change",a)),e},undo:function(){var e;return o.typing&&(o.add(),o.typing=!1,i(!1)),0<c&&(e=l[--c],Gv(u,e,!0),d(!0),u.fire("undo",{level:e})),e},redo:function(){var e;return c<l.length-1&&(e=l[++c],Gv(u,e,!1),d(!0),u.fire("redo",{level:e})),e},clear:function(){l=[],c=0,o.typing=!1,o.data=l,u.fire("ClearUndos")},hasUndo:function(){return 0<c||o.typing&&l[0]&&!Jv(Yv(u),l[0])},hasRedo:function(){return c<l.length-1&&!o.typing},transact:function(e){return n(),o.beforeChange(),o.ignore(e),o.add()},ignore:function(e){try{t++,e()}finally{t--}},extra:function(e,t){var n,r;o.transact(e)&&(r=l[c].bookmark,n=l[c-1],Gv(u,n,!0),o.transact(t)&&(l[c-1].beforeBookmark=r))}}}var Zv,ey,ty={},ny=Ht.filter,ry=Ht.each;ey=function(e){var t,n,r=e.selection.getRng();t=Uo.matchNodeNames("pre"),r.collapsed||(n=e.selection.getSelectedBlocks(),ry(ny(ny(n,t),function(e){return t(e.previousSibling)&&-1!==Ht.indexOf(n,e.previousSibling)}),function(e){var t,n;t=e.previousSibling,gn(n=e).remove(),gn(t).append("<br><br>").append(n.childNodes)}))},ty[Zv="pre"]||(ty[Zv]=[]),ty[Zv].push(ey);var oy=function(e,t){ry(ty[e],function(e){e(t)})},iy=/^(src|href|style)$/,ay=Xt.each,uy=xc.isEq,sy=function(e,t,n){return e.isChildOf(t,n)&&t!==n&&!e.isBlock(n)},cy=function(e,t,n){var r,o,i;return r=t[n?"startContainer":"endContainer"],o=t[n?"startOffset":"endOffset"],Uo.isElement(r)&&(i=r.childNodes.length-1,!n&&o&&o--,r=r.childNodes[i<o?i:o]),Uo.isText(r)&&n&&o>=r.nodeValue.length&&(r=new mo(r,e.getBody()).next()||r),Uo.isText(r)&&!n&&0===o&&(r=new mo(r,e.getBody()).prev()||r),r},ly=function(e,t,n,r){var o=e.create(n,r);return t.parentNode.insertBefore(o,t),o.appendChild(t),o},fy=function(e,t,n,r,o){var i=ar.fromDom(t),a=ar.fromDom(e.create(r,o)),u=n?$r(i):qr(i);return Fi(a,u),n?(Bi(i,a),Ii(a,i)):(Pi(i,a),Li(a,i)),a.dom()},dy=function(e,t,n,r){return!(t=xc.getNonWhiteSpaceSibling(t,n,r))||"BR"===t.nodeName||e.isBlock(t)},my=function(e,n,r,o,i){var t,a,u,s,c,l,f,d,m,g,p,h,v,y,b=e.dom;if(c=b,!(uy(l=o,(f=n).inline)||uy(l,f.block)||(f.selector?Uo.isElement(l)&&c.is(l,f.selector):void 0)||(s=o,n.links&&"A"===s.tagName)))return!1;if("all"!==n.remove)for(ay(n.styles,function(e,t){e=xc.normalizeStyleValue(b,xc.replaceVars(e,r),t),"number"==typeof t&&(t=e,i=0),(n.remove_similar||!i||uy(xc.getStyle(b,i,t),e))&&b.setStyle(o,t,""),u=1}),u&&""===b.getAttrib(o,"style")&&(o.removeAttribute("style"),o.removeAttribute("data-mce-style")),ay(n.attributes,function(e,t){var n;if(e=xc.replaceVars(e,r),"number"==typeof t&&(t=e,i=0),!i||uy(b.getAttrib(i,t),e)){if("class"===t&&(e=b.getAttrib(o,t))&&(n="",ay(e.split(/\s+/),function(e){/mce\-\w+/.test(e)&&(n+=(n?" ":"")+e)}),n))return void b.setAttrib(o,t,n);"class"===t&&o.removeAttribute("className"),iy.test(t)&&o.removeAttribute("data-mce-"+t),o.removeAttribute(t)}}),ay(n.classes,function(e){e=xc.replaceVars(e,r),i&&!b.hasClass(i,e)||b.removeClass(o,e)}),a=b.getAttribs(o),t=0;t<a.length;t++){var C=a[t].nodeName;if(0!==C.indexOf("_")&&0!==C.indexOf("data-"))return!1}return"none"!==n.remove?(d=e,g=n,h=(m=o).parentNode,v=d.dom,y=d.settings.forced_root_block,g.block&&(y?h===v.getRoot()&&(g.list_block&&uy(m,g.list_block)||ay(Xt.grep(m.childNodes),function(e){xc.isValid(d,y,e.nodeName.toLowerCase())?p?p.appendChild(e):(p=ly(v,e,y),v.setAttribs(p,d.settings.forced_root_block_attrs)):p=0})):v.isBlock(m)&&!v.isBlock(h)&&(dy(v,m,!1)||dy(v,m.firstChild,!0,1)||m.insertBefore(v.create("br"),m.firstChild),dy(v,m,!0)||dy(v,m.lastChild,!1,1)||m.appendChild(v.create("br")))),g.selector&&g.inline&&!uy(g.inline,m)||v.remove(m,1),!0):void 0},gy=my,py=function(s,c,l,e,f){var t,n,d=s.formatter.get(c),m=d[0],a=!0,u=s.dom,r=s.selection,i=function(e){var n,t,r,o,i,a,u=(n=s,t=e,r=c,o=l,i=f,ay(xc.getParents(n.dom,t.parentNode).reverse(),function(e){var t;a||"_start"===e.id||"_end"===e.id||(t=Mm.matchNode(n,e,r,o,i))&&!1!==t.split&&(a=e)}),a);return function(e,t,n,r,o,i,a,u){var s,c,l,f,d,m,g=e.dom;if(n){for(m=n.parentNode,s=r.parentNode;s&&s!==m;s=s.parentNode){for(c=g.clone(s,!1),d=0;d<t.length;d++)if(my(e,t[d],u,c,c)){c=0;break}c&&(l&&c.appendChild(l),f||(f=c),l=c)}!i||a.mixed&&g.isBlock(n)||(r=g.split(n,r)),l&&(o.parentNode.insertBefore(l,o),f.appendChild(o))}return r}(s,d,u,e,e,!0,m,l)},g=function(e){var t,n,r,o,i;if(Uo.isElement(e)&&u.getContentEditable(e)&&(o=a,a="true"===u.getContentEditable(e),i=!0),t=Xt.grep(e.childNodes),a&&!i)for(n=0,r=d.length;n<r&&!my(s,d[n],l,e,e);n++);if(m.deep&&t.length){for(n=0,r=t.length;n<r;n++)g(t[n]);i&&(a=o)}},p=function(e){var t,n=u.get(e?"_start":"_end"),r=n[e?"firstChild":"lastChild"];return vc(t=r)&&Uo.isElement(t)&&("_start"===t.id||"_end"===t.id)&&(r=r[e?"firstChild":"lastChild"]),Uo.isText(r)&&0===r.data.length&&(r=e?n.previousSibling||n.nextSibling:n.nextSibling||n.previousSibling),u.remove(n,!0),r},o=function(e){var t,n,r=e.commonAncestorContainer;if(e=Bc(s,e,d,!0),m.split){if(e=Um(e),(t=cy(s,e,!0))!==(n=cy(s,e))){if(/^(TR|TH|TD)$/.test(t.nodeName)&&t.firstChild&&(t="TR"===t.nodeName?t.firstChild.firstChild||t:t.firstChild||t),r&&/^T(HEAD|BODY|FOOT|R)$/.test(r.nodeName)&&/^(TH|TD)$/.test(n.nodeName)&&n.firstChild&&(n=n.firstChild||n),sy(u,t,n)){var o=_.from(t.firstChild).getOr(t);return i(fy(u,o,!0,"span",{id:"_start","data-mce-type":"bookmark"})),void p(!0)}if(sy(u,n,t))return o=_.from(n.lastChild).getOr(n),i(fy(u,o,!1,"span",{id:"_end","data-mce-type":"bookmark"})),void p(!1);t=ly(u,t,"span",{id:"_start","data-mce-type":"bookmark"}),n=ly(u,n,"span",{id:"_end","data-mce-type":"bookmark"}),i(t),i(n),t=p(!0),n=p()}else t=n=i(t);e.startContainer=t.parentNode?t.parentNode:t,e.startOffset=u.nodeIndex(t),e.endContainer=n.parentNode?n.parentNode:n,e.endOffset=u.nodeIndex(n)+1}Ic(u,e,function(e){ay(e,function(e){g(e),Uo.isElement(e)&&"underline"===s.dom.getStyle(e,"text-decoration")&&e.parentNode&&"underline"===xc.getTextDecoration(u,e.parentNode)&&my(s,{deep:!1,exact:!0,inline:"span",styles:{textDecoration:"underline"}},null,e)})})};if(e)e.nodeType?((n=u.createRng()).setStartBefore(e),n.setEndAfter(e),o(n)):o(e);else if("false"!==u.getContentEditable(r.getNode()))r.isCollapsed()&&m.inline&&!u.select("td[data-mce-selected],th[data-mce-selected]").length?function(e,t,n,r){var o,i,a,u,s,c,l,f=e.dom,d=e.selection,m=[],g=d.getRng();for(o=g.startContainer,i=g.startOffset,3===(s=o).nodeType&&(i!==o.nodeValue.length&&(u=!0),s=s.parentNode);s;){if(Mm.matchNode(e,s,t,n,r)){c=s;break}s.nextSibling&&(u=!0),m.push(s),s=s.parentNode}if(c)if(u){a=d.getBookmark(),g.collapse(!0);var p=Bc(e,g,e.formatter.get(t),!0);p=Um(p),e.formatter.remove(t,n,p),d.moveToBookmark(a)}else{l=Ju(e.getBody(),c);var h=$m(!1).dom(),v=Gm(m,h);Xm(e,h,l||c),Wm(e,l,!1),d.setCursorLocation(v,1),f.isEmpty(c)&&f.remove(c)}}(s,c,l,f):(t=Xu.getPersistentBookmark(s.selection,!0),o(r.getRng()),r.moveToBookmark(t),m.inline&&Mm.match(s,c,l,r.getStart())&&xc.moveStart(u,r,r.getRng()),s.nodeChanged());else{e=r.getNode();for(var h=0,v=d.length;h<v&&(!d[h].ceFalseOverride||!my(s,d[h],l,e,e));h++);}},hy=Xt.each,vy=function(e){return e&&1===e.nodeType&&!vc(e)&&!Gu(e)&&!Uo.isBogus(e)},yy=function(e,t){var n;for(n=e;n;n=n[t]){if(3===n.nodeType&&0!==n.nodeValue.length)return e;if(1===n.nodeType&&!vc(n))return n}return e},by=function(e,t,n){var r,o,i=new Zc(e);if(t&&n&&(t=yy(t,"previousSibling"),n=yy(n,"nextSibling"),i.compare(t,n))){for(r=t.nextSibling;r&&r!==n;)r=(o=r).nextSibling,t.appendChild(o);return e.remove(n),Xt.each(Xt.grep(n.childNodes),function(e){t.appendChild(e)}),t}return n},Cy=function(e,t,n){hy(e.childNodes,function(e){vy(e)&&(t(e)&&n(e),e.hasChildNodes()&&Cy(e,t,n))})},xy=function(n,e){return d(function(e,t){return!(!t||!xc.getStyle(n,t,e))},e)},wy=function(r,e,t){return d(function(e,t,n){r.setStyle(n,e,t),""===n.getAttribute("style")&&n.removeAttribute("style"),Ny(r,n)},e,t)},Ny=function(e,t){"SPAN"===t.nodeName&&0===e.getAttribs(t).length&&e.remove(t,!0)},Ey=function(e,t){var n;1===t.nodeType&&t.parentNode&&1===t.parentNode.nodeType&&(n=xc.getTextDecoration(e,t.parentNode),e.getStyle(t,"color")&&n?e.setStyle(t,"text-decoration",n):e.getStyle(t,"text-decoration")===n&&e.setStyle(t,"text-decoration",null))},Sy=function(n,e,r,o){hy(e,function(t){hy(n.dom.select(t.inline,o),function(e){vy(e)&&gy(n,t,r,e,t.exact?e:null)}),function(r,e,t){if(e.clear_child_styles){var n=e.links?"*:not(a)":"*";hy(r.select(n,t),function(n){vy(n)&&hy(e.styles,function(e,t){r.setStyle(n,t,"")})})}}(n.dom,t,o)})},Ty=function(e,t,n,r){(t.styles.color||t.styles.textDecoration)&&(Xt.walk(r,d(Ey,e),"childNodes"),Ey(e,r))},ky=function(e,t,n,r){t.styles&&t.styles.backgroundColor&&Cy(r,xy(e,"fontSize"),wy(e,"backgroundColor",xc.replaceVars(t.styles.backgroundColor,n)))},_y=function(e,t,n,r){"sub"!==t.inline&&"sup"!==t.inline||(Cy(r,xy(e,"fontSize"),wy(e,"fontSize","")),e.remove(e.select("sup"===t.inline?"sub":"sup",r),!0))},Ay=function(e,t,n,r){r&&!1!==t.merge_siblings&&(r=by(e,xc.getNonWhiteSpaceSibling(r),r),r=by(e,r,xc.getNonWhiteSpaceSibling(r,!0)))},Ry=function(t,n,r,o,i){Mm.matchNode(t,i.parentNode,r,o)&&gy(t,n,o,i)||n.merge_with_parents&&t.dom.getParent(i.parentNode,function(e){if(Mm.matchNode(t,e,r,o))return gy(t,n,o,i),!0})},Dy=Xt.each,Oy=function(g,p,h,r){var e,t,v=g.formatter.get(p),y=v[0],o=!r&&g.selection.isCollapsed(),i=g.dom,n=g.selection,b=function(n,e){if(e=e||y,n){if(e.onformat&&e.onformat(n,e,h,r),Dy(e.styles,function(e,t){i.setStyle(n,t,xc.replaceVars(e,h))}),e.styles){var t=i.getAttrib(n,"style");t&&n.setAttribute("data-mce-style",t)}Dy(e.attributes,function(e,t){i.setAttrib(n,t,xc.replaceVars(e,h))}),Dy(e.classes,function(e){e=xc.replaceVars(e,h),i.hasClass(n,e)||i.addClass(n,e)})}},C=function(e,t){var n=!1;return!!y.selector&&(Dy(e,function(e){if(!("collapsed"in e&&e.collapsed!==o))return i.is(t,e.selector)&&!Gu(t)?(b(t,e),!(n=!0)):void 0}),n)},a=function(s,e,t,c){var l,f,d=[],m=!0;l=y.inline||y.block,f=s.create(l),b(f),Ic(s,e,function(e){var a,u=function(e){var t,n,r,o;if(o=m,t=e.nodeName.toLowerCase(),n=e.parentNode.nodeName.toLowerCase(),1===e.nodeType&&s.getContentEditable(e)&&(o=m,m="true"===s.getContentEditable(e),r=!0),xc.isEq(t,"br"))return a=0,void(y.block&&s.remove(e));if(y.wrapper&&Mm.matchNode(g,e,p,h))a=0;else{if(m&&!r&&y.block&&!y.wrapper&&xc.isTextBlock(g,t)&&xc.isValid(g,n,l))return e=s.rename(e,l),b(e),d.push(e),void(a=0);if(y.selector){var i=C(v,e);if(!y.inline||i)return void(a=0)}!m||r||!xc.isValid(g,l,t)||!xc.isValid(g,n,l)||!c&&3===e.nodeType&&1===e.nodeValue.length&&65279===e.nodeValue.charCodeAt(0)||Gu(e)||y.inline&&s.isBlock(e)?(a=0,Dy(Xt.grep(e.childNodes),u),r&&(m=o),a=0):(a||(a=s.clone(f,!1),e.parentNode.insertBefore(a,e),d.push(a)),a.appendChild(e))}};Dy(e,u)}),!0===y.links&&Dy(d,function(e){var t=function(e){"A"===e.nodeName&&b(e,y),Dy(Xt.grep(e.childNodes),t)};t(e)}),Dy(d,function(e){var t,n,r,o,i,a=function(e){var n=!1;return Dy(e.childNodes,function(e){if((t=e)&&1===t.nodeType&&!vc(t)&&!Gu(t)&&!Uo.isBogus(t))return n=e,!1;var t}),n};n=0,Dy(e.childNodes,function(e){xc.isWhiteSpaceNode(e)||vc(e)||n++}),t=n,!(1<d.length)&&s.isBlock(e)||0!==t?(y.inline||y.wrapper)&&(y.exact||1!==t||((o=a(r=e))&&!vc(o)&&Mm.matchName(s,o,y)&&(i=s.clone(o,!1),b(i),s.replace(i,r,!0),s.remove(o,1)),e=i||r),Sy(g,v,h,e),Ry(g,y,p,h,e),ky(s,y,h,e),_y(s,y,h,e),Ay(s,y,h,e)):s.remove(e,1)})};if("false"!==i.getContentEditable(n.getNode())){if(y){if(r)r.nodeType?C(v,r)||((t=i.createRng()).setStartBefore(r),t.setEndAfter(r),a(i,Bc(g,t,v),0,!0)):a(i,r,0,!0);else if(o&&y.inline&&!i.select("td[data-mce-selected],th[data-mce-selected]").length)!function(e,t,n){var r,o,i,a,u,s,c=e.selection;a=(r=c.getRng(!0)).startOffset,s=r.startContainer.nodeValue,(o=Ju(e.getBody(),c.getStart()))&&(i=qm(o));var l,f,d=/[^\s\u00a0\u00ad\u200b\ufeff]/;s&&0<a&&a<s.length&&d.test(s.charAt(a))&&d.test(s.charAt(a-1))?(u=c.getBookmark(),r.collapse(!0),r=Bc(e,r,e.formatter.get(t)),r=Um(r),e.formatter.apply(t,n,r),c.moveToBookmark(u)):(o&&i.nodeValue===jm||(l=e.getDoc(),f=$m(!0).dom(),i=(o=l.importNode(f,!0)).firstChild,r.insertNode(o),a=1),e.formatter.apply(t,n,o),c.setCursorLocation(i,a))}(g,p,h);else{var u=g.selection.getNode();g.settings.forced_root_block||!v[0].defaultBlock||i.getParent(u,i.isBlock)||Oy(g,v[0].defaultBlock),g.selection.setRng(sl(g.selection.getRng())),e=Xu.getPersistentBookmark(g.selection,!0),a(i,Bc(g,n.getRng(),v)),y.styles&&Ty(i,y,h,u),n.moveToBookmark(e),xc.moveStart(i,n,n.getRng()),g.nodeChanged()}oy(p,g)}}else{r=n.getNode();for(var s=0,c=v.length;s<c;s++)if(v[s].ceFalseOverride&&i.is(r,v[s].selector))return void b(r,v[s])}},By={applyFormat:Oy},Py=Xt.each,Iy=function(e,t,n,r,o){var i,a,u,s,c,l,f,d;null===t.get()&&(a=e,u={},(i=t).set({}),a.on("NodeChange",function(n){var r=xc.getParents(a.dom,n.element),o={};r=Xt.grep(r,function(e){return 1===e.nodeType&&!e.getAttribute("data-mce-bogus")}),Py(i.get(),function(e,n){Py(r,function(t){return a.formatter.matchNode(t,n,{},e.similar)?(u[n]||(Py(e,function(e){e(!0,{node:t,format:n,parents:r})}),u[n]=e),o[n]=e,!1):!Mm.matchesUnInheritedFormatSelector(a,t,n)&&void 0})}),Py(u,function(e,t){o[t]||(delete u[t],Py(e,function(e){e(!1,{node:n.element,format:t,parents:r})}))})})),c=n,l=r,f=o,d=(s=t).get(),Py(c.split(","),function(e){d[e]||(d[e]=[],d[e].similar=f),d[e].push(l)}),s.set(d)},Ly={get:function(r){var t={valigntop:[{selector:"td,th",styles:{verticalAlign:"top"}}],valignmiddle:[{selector:"td,th",styles:{verticalAlign:"middle"}}],valignbottom:[{selector:"td,th",styles:{verticalAlign:"bottom"}}],alignleft:[{selector:"figure.image",collapsed:!1,classes:"align-left",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"left"},inherit:!1,preview:!1,defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"left"},preview:"font-family font-size"}],aligncenter:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"center"},inherit:!1,preview:"font-family font-size",defaultBlock:"div"},{selector:"figure.image",collapsed:!1,classes:"align-center",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"img",collapsed:!1,styles:{display:"block",marginLeft:"auto",marginRight:"auto"},preview:!1},{selector:"table",collapsed:!1,styles:{marginLeft:"auto",marginRight:"auto"},preview:"font-family font-size"}],alignright:[{selector:"figure.image",collapsed:!1,classes:"align-right",ceFalseOverride:!0,preview:"font-family font-size"},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"right"},inherit:!1,preview:"font-family font-size",defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"right"},preview:"font-family font-size"}],alignjustify:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"justify"},inherit:!1,defaultBlock:"div",preview:"font-family font-size"}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:!0},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:!0},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},links:!0,remove_similar:!0,clear_child_styles:!0},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},links:!0,remove_similar:!0,clear_child_styles:!0},fontname:{inline:"span",toggle:!1,styles:{fontFamily:"%value"},clear_child_styles:!0},fontsize:{inline:"span",toggle:!1,styles:{fontSize:"%value"},clear_child_styles:!0},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},code:{inline:"code"},link:{inline:"a",selector:"a",remove:"all",split:!0,deep:!0,onmatch:function(){return!0},onformat:function(n,e,t){Xt.each(t,function(e,t){r.setAttrib(n,t,e)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins",remove:"all",split:!0,expand:!1,block_expand:!0,deep:!0},{selector:"span",attributes:["style","class"],remove:"empty",split:!0,expand:!1,deep:!0},{selector:"*",attributes:["style","class"],split:!1,expand:!1,deep:!0}]};return Xt.each("p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp".split(/\s/),function(e){t[e]={block:e,remove:"all"}}),t}},Fy=Xt.each,My=Ei.DOM,zy=function(e,t){var n,o,r,m=t&&t.schema||fi({}),g=function(e){var t,n,r;return o="string"==typeof e?{name:e,classes:[],attrs:{}}:e,t=My.create(o.name),n=t,(r=o).classes.length&&My.addClass(n,r.classes.join(" ")),My.setAttribs(n,r.attrs),t},p=function(n,e,t){var r,o,i,a,u,s,c,l,f=0<e.length&&e[0],d=f&&f.name;if(u=d,s="string"!=typeof(a=n)?a.nodeName.toLowerCase():a,c=m.getElementRule(s),i=!(!(l=c&&c.parentsRequired)||!l.length)&&(u&&-1!==Xt.inArray(l,u)?u:l[0]))d===i?(o=e[0],e=e.slice(1)):o=i;else if(f)o=e[0],e=e.slice(1);else if(!t)return n;return o&&(r=g(o)).appendChild(n),t&&(r||(r=My.create("div")).appendChild(n),Xt.each(t,function(e){var t=g(e);r.insertBefore(t,n)})),p(r,e,o&&o.siblings)};return e&&e.length?(o=e[0],n=g(o),(r=My.create("div")).appendChild(p(n,e.slice(1),o.siblings)),r):""},Uy=function(e){var t,a={classes:[],attrs:{}};return"*"!==(e=a.selector=Xt.trim(e))&&(t=e.replace(/(?:([#\.]|::?)([\w\-]+)|(\[)([^\]]+)\]?)/g,function(e,t,n,r,o){switch(t){case"#":a.attrs.id=n;break;case".":a.classes.push(n);break;case":":-1!==Xt.inArray("checked disabled enabled read-only required".split(" "),n)&&(a.attrs[n]=n)}if("["===r){var i=o.match(/([\w\-]+)(?:\=\"([^\"]+))?/);i&&(a.attrs[i[1]]=i[2])}return""})),a.name=t||"div",a},jy=function(e){return e&&"string"==typeof e?(e=(e=e.split(/\s*,\s*/)[0]).replace(/\s*(~\+|~|\+|>)\s*/g,"$1"),Xt.map(e.split(/(?:>|\s+(?![^\[\]]+\]))/),function(e){var t=Xt.map(e.split(/(?:~\+|~|\+)/),Uy),n=t.pop();return t.length&&(n.siblings=t),n}).reverse()):[]},Vy=function(n,e){var t,r,o,i,a,u,s="";if(!1===(u=n.settings.preview_styles))return"";"string"!=typeof u&&(u="font-family font-size font-weight font-style text-decoration text-transform color background-color border border-radius outline text-shadow");var c=function(e){return e.replace(/%(\w+)/g,"")};if("string"==typeof e){if(!(e=n.formatter.get(e)))return;e=e[0]}return"preview"in e&&!1===(u=e.preview)?"":(t=e.block||e.inline||"span",(i=jy(e.selector)).length?(i[0].name||(i[0].name=t),t=e.selector,r=zy(i,n)):r=zy([t],n),o=My.select(t,r)[0]||r.firstChild,Fy(e.styles,function(e,t){(e=c(e))&&My.setStyle(o,t,e)}),Fy(e.attributes,function(e,t){(e=c(e))&&My.setAttrib(o,t,e)}),Fy(e.classes,function(e){e=c(e),My.hasClass(o,e)||My.addClass(o,e)}),n.fire("PreviewFormats"),My.setStyles(r,{position:"absolute",left:-65535}),n.getBody().appendChild(r),a=My.getStyle(n.getBody(),"fontSize",!0),a=/px$/.test(a)?parseInt(a,10):0,Fy(u.split(" "),function(e){var t=My.getStyle(o,e,!0);if(!("background-color"===e&&/transparent|rgba\s*\([^)]+,\s*0\)/.test(t)&&(t=My.getStyle(n.getBody(),e,!0),"#ffffff"===My.toHex(t).toLowerCase())||"color"===e&&"#000000"===My.toHex(t).toLowerCase())){if("font-size"===e&&/em|%$/.test(t)){if(0===a)return;t=parseFloat(t)/(/%$/.test(t)?100:1)*a+"px"}"border"===e&&t&&(s+="padding:0 2px;"),s+=e+":"+t+";"}}),n.fire("AfterPreviewFormats"),My.remove(r),s)},Hy=function(e,t,n,r,o){var i=t.get(n);!Mm.match(e,n,r,o)||"toggle"in i[0]&&!i[0].toggle?By.applyFormat(e,n,r,o):py(e,n,r,o)},qy=function(e){e.addShortcut("meta+b","","Bold"),e.addShortcut("meta+i","","Italic"),e.addShortcut("meta+u","","Underline");for(var t=1;t<=6;t++)e.addShortcut("access+"+t,"",["FormatBlock",!1,"h"+t]);e.addShortcut("access+7","",["FormatBlock",!1,"p"]),e.addShortcut("access+8","",["FormatBlock",!1,"div"]),e.addShortcut("access+9","",["FormatBlock",!1,"address"])};function $y(e){var t,n,r,o=(t=e,n={},(r=function(e,t){e&&("string"!=typeof e?Xt.each(e,function(e,t){r(t,e)}):(t=t.length?t:[t],Xt.each(t,function(e){"undefined"==typeof e.deep&&(e.deep=!e.selector),"undefined"==typeof e.split&&(e.split=!e.selector||e.inline),"undefined"==typeof e.remove&&e.selector&&!e.inline&&(e.remove="none"),e.selector&&e.inline&&(e.mixed=!0,e.block_expand=!0),"string"==typeof e.classes&&(e.classes=e.classes.split(/\s+/))}),n[e]=t))})(Ly.get(t.dom)),r(t.settings.formats),{get:function(e){return e?n[e]:n},register:r,unregister:function(e){return e&&n[e]&&delete n[e],n}}),i=Vi(null);return qy(e),Jm(e),{get:o.get,register:o.register,unregister:o.unregister,apply:d(By.applyFormat,e),remove:d(py,e),toggle:d(Hy,e,o),match:d(Mm.match,e),matchAll:d(Mm.matchAll,e),matchNode:d(Mm.matchNode,e),canApply:d(Mm.canApply,e),formatChanged:d(Iy,e,i),getCssText:d(Vy,e)}}var Wy,Ky=Object.prototype.hasOwnProperty,Xy=(Wy=function(e,t){return t},function(){for(var e=new Array(arguments.length),t=0;t<e.length;t++)e[t]=arguments[t];if(0===e.length)throw new Error("Can't merge zero objects");for(var n={},r=0;r<e.length;r++){var o=e[r];for(var i in o)Ky.call(o,i)&&(n[i]=Wy(n[i],o[i]))}return n}),Yy={register:function(t,s,c){t.addAttributeFilter("data-mce-tabindex",function(e,t){for(var n,r=e.length;r--;)(n=e[r]).attr("tabindex",n.attributes.map["data-mce-tabindex"]),n.attr(t,null)}),t.addAttributeFilter("src,href,style",function(e,t){for(var n,r,o=e.length,i="data-mce-"+t,a=s.url_converter,u=s.url_converter_scope;o--;)(r=(n=e[o]).attributes.map[i])!==undefined?(n.attr(t,0<r.length?r:null),n.attr(i,null)):(r=n.attributes.map[t],"style"===t?r=c.serializeStyle(c.parseStyle(r),n.name):a&&(r=a.call(u,r,t,n.name)),n.attr(t,0<r.length?r:null))}),t.addAttributeFilter("class",function(e){for(var t,n,r=e.length;r--;)(n=(t=e[r]).attr("class"))&&(n=t.attr("class").replace(/(?:^|\s)mce-item-\w+(?!\S)/g,""),t.attr("class",0<n.length?n:null))}),t.addAttributeFilter("data-mce-type",function(e,t,n){for(var r,o=e.length;o--;)"bookmark"!==(r=e[o]).attributes.map["data-mce-type"]||n.cleanup||(_.from(r.firstChild).exists(function(e){return!ba(e.value)})?r.unwrap():r.remove())}),t.addNodeFilter("noscript",function(e){for(var t,n=e.length;n--;)(t=e[n].firstChild)&&(t.value=ei.decode(t.value))}),t.addNodeFilter("script,style",function(e,t){for(var n,r,o,i=e.length,a=function(e){return e.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi,"").replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")};i--;)r=(n=e[i]).firstChild?n.firstChild.value:"","script"===t?((o=n.attr("type"))&&n.attr("type","mce-no/type"===o?null:o.replace(/^mce\-/,"")),"xhtml"===s.element_format&&0<r.length&&(n.firstChild.value="// <![CDATA[\n"+a(r)+"\n// ]]>")):"xhtml"===s.element_format&&0<r.length&&(n.firstChild.value="\x3c!--\n"+a(r)+"\n--\x3e")}),t.addNodeFilter("#comment",function(e){for(var t,n=e.length;n--;)0===(t=e[n]).value.indexOf("[CDATA[")?(t.name="#cdata",t.type=4,t.value=t.value.replace(/^\[CDATA\[|\]\]$/g,"")):0===t.value.indexOf("mce:protected ")&&(t.name="#text",t.type=3,t.raw=!0,t.value=unescape(t.value).substr(14))}),t.addNodeFilter("xml:namespace,input",function(e,t){for(var n,r=e.length;r--;)7===(n=e[r]).type?n.remove():1===n.type&&("input"!==t||"type"in n.attributes.map||n.attr("type","text"))}),t.addAttributeFilter("data-mce-type",function(e){z(e,function(e){"format-caret"===e.attr("data-mce-type")&&(e.isEmpty(t.schema.getNonEmptyElements())?e.remove():e.unwrap())})}),t.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style,data-mce-selected,data-mce-expando,data-mce-type,data-mce-resize",function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)})},trimTrailingBr:function(e){var t,n,r=function(e){return e&&"br"===e.name};r(t=e.lastChild)&&r(n=t.prev)&&(t.remove(),n.remove())}},Gy={process:function(e,t,n){return f=n,(l=e)&&l.hasEventListeners("PreProcess")&&!f.no_events?(o=t,i=n,c=(r=e).dom,o=o.cloneNode(!0),(a=V.document.implementation).createHTMLDocument&&(u=a.createHTMLDocument(""),Xt.each("BODY"===o.nodeName?o.childNodes:[o],function(e){u.body.appendChild(u.importNode(e,!0))}),o="BODY"!==o.nodeName?u.body.firstChild:u.body,s=c.doc,c.doc=u),vp(r,Xy(i,{node:o})),s&&(c.doc=s),o):t;var r,o,i,a,u,s,c,l,f}},Jy=function(e,a,u){e.addNodeFilter("font",function(e){z(e,function(e){var t,n=a.parse(e.attr("style")),r=e.attr("color"),o=e.attr("face"),i=e.attr("size");r&&(n.color=r),o&&(n["font-family"]=o),i&&(n["font-size"]=u[parseInt(e.attr("size"),10)-1]),e.name="span",e.attr("style",a.serialize(n)),t=e,z(["color","face","size"],function(e){t.attr(e,null)})})})},Qy=function(e,t){var n,r=mi();t.convert_fonts_to_spans&&Jy(e,r,Xt.explode(t.font_size_legacy_values)),n=r,e.addNodeFilter("strike",function(e){z(e,function(e){var t=n.parse(e.attr("style"));t["text-decoration"]="line-through",e.name="span",e.attr("style",n.serialize(t))})})},Zy={register:function(e,t){t.inline_styles&&Qy(e,t)}},eb=/^[ \t\r\n]*$/,tb={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11},nb=function(e,t,n){var r,o,i=n?"lastChild":"firstChild",a=n?"prev":"next";if(e[i])return e[i];if(e!==t){if(r=e[a])return r;for(o=e.parent;o&&o!==t;o=o.parent)if(r=o[a])return r}},rb=function(){function a(e,t){this.name=e,1===(this.type=t)&&(this.attributes=[],this.attributes.map={})}return a.create=function(e,t){var n,r;if(n=new a(e,tb[e]||1),t)for(r in t)n.attr(r,t[r]);return n},a.prototype.replace=function(e){return e.parent&&e.remove(),this.insert(e,this),this.remove(),this},a.prototype.attr=function(e,t){var n,r;if("string"!=typeof e){for(r in e)this.attr(r,e[r]);return this}if(n=this.attributes){if(t!==undefined){if(null===t){if(e in n.map)for(delete n.map[e],r=n.length;r--;)if(n[r].name===e)return n=n.splice(r,1),this;return this}if(e in n.map){for(r=n.length;r--;)if(n[r].name===e){n[r].value=t;break}}else n.push({name:e,value:t});return n.map[e]=t,this}return n.map[e]}},a.prototype.clone=function(){var e,t,n,r,o,i=new a(this.name,this.type);if(n=this.attributes){for((o=[]).map={},e=0,t=n.length;e<t;e++)"id"!==(r=n[e]).name&&(o[o.length]={name:r.name,value:r.value},o.map[r.name]=r.value);i.attributes=o}return i.value=this.value,i.shortEnded=this.shortEnded,i},a.prototype.wrap=function(e){return this.parent.insert(e,this),e.append(this),this},a.prototype.unwrap=function(){var e,t;for(e=this.firstChild;e;)t=e.next,this.insert(e,this,!0),e=t;this.remove()},a.prototype.remove=function(){var e=this.parent,t=this.next,n=this.prev;return e&&(e.firstChild===this?(e.firstChild=t)&&(t.prev=null):n.next=t,e.lastChild===this?(e.lastChild=n)&&(n.next=null):t.prev=n,this.parent=this.next=this.prev=null),this},a.prototype.append=function(e){var t;return e.parent&&e.remove(),(t=this.lastChild)?((t.next=e).prev=t,this.lastChild=e):this.lastChild=this.firstChild=e,e.parent=this,e},a.prototype.insert=function(e,t,n){var r;return e.parent&&e.remove(),r=t.parent||this,n?(t===r.firstChild?r.firstChild=e:t.prev.next=e,e.prev=t.prev,(e.next=t).prev=e):(t===r.lastChild?r.lastChild=e:t.next.prev=e,e.next=t.next,(e.prev=t).next=e),e.parent=r,e},a.prototype.getAll=function(e){var t,n=[];for(t=this.firstChild;t;t=nb(t,this))t.name===e&&n.push(t);return n},a.prototype.empty=function(){var e,t,n;if(this.firstChild){for(e=[],n=this.firstChild;n;n=nb(n,this))e.push(n);for(t=e.length;t--;)(n=e[t]).parent=n.firstChild=n.lastChild=n.next=n.prev=null}return this.firstChild=this.lastChild=null,this},a.prototype.isEmpty=function(e,t,n){var r,o,i=this.firstChild;if(t=t||{},i)do{if(1===i.type){if(i.attributes.map["data-mce-bogus"])continue;if(e[i.name])return!1;for(r=i.attributes.length;r--;)if("name"===(o=i.attributes[r].name)||0===o.indexOf("data-mce-bookmark"))return!1}if(8===i.type)return!1;if(3===i.type&&!eb.test(i.value))return!1;if(3===i.type&&i.parent&&t[i.parent.name]&&eb.test(i.value))return!1;if(n&&n(i))return!1}while(i=nb(i,this));return!0},a.prototype.walk=function(e){return nb(this,null,e)},a}(),ob=function(e,t,n,r){(e.padd_empty_with_br||t.insert)&&n[r.name]?r.empty().append(new rb("br",1)).shortEnded=!0:r.empty().append(new rb("#text",3)).value="\xa0"},ib=function(e){return ab(e,"#text")&&"\xa0"===e.firstChild.value},ab=function(e,t){return e&&e.firstChild&&e.firstChild===e.lastChild&&e.firstChild.name===t},ub=function(r,e,t,n){return n.isEmpty(e,t,function(e){return t=e,(n=r.getElementRule(t.name))&&n.paddEmpty;var t,n})},sb=function(e,t){return e&&(t[e.name]||"br"===e.name)},cb=function(e,p){var h=e.schema;p.remove_trailing_brs&&e.addNodeFilter("br",function(e,t,n){var r,o,i,a,u,s,c,l,f=e.length,d=Xt.extend({},h.getBlockElements()),m=h.getNonEmptyElements(),g=h.getNonEmptyElements();for(d.body=1,r=0;r<f;r++)if(i=(o=e[r]).parent,d[o.parent.name]&&o===i.lastChild){for(u=o.prev;u;){if("span"!==(s=u.name)||"bookmark"!==u.attr("data-mce-type")){if("br"!==s)break;if("br"===s){o=null;break}}u=u.prev}o&&(o.remove(),ub(h,m,g,i)&&(c=h.getElementRule(i.name))&&(c.removeEmpty?i.remove():c.paddEmpty&&ob(p,n,d,i)))}else{for(a=o;i&&i.firstChild===a&&i.lastChild===a&&!d[(a=i).name];)i=i.parent;a===i&&!0!==p.padd_empty_with_br&&((l=new rb("#text",3)).value="\xa0",o.replace(l))}}),e.addAttributeFilter("href",function(e){var t,n,r,o=e.length;if(!p.allow_unsafe_link_target)for(;o--;)"a"===(t=e[o]).name&&"_blank"===t.attr("target")&&t.attr("rel",(n=t.attr("rel"),r=n?Xt.trim(n):"",/\b(noopener)\b/g.test(r)?r:r.split(" ").filter(function(e){return 0<e.length}).concat(["noopener"]).sort().join(" ")))}),p.allow_html_in_named_anchor||e.addAttributeFilter("id,name",function(e){for(var t,n,r,o,i=e.length;i--;)if("a"===(o=e[i]).name&&o.firstChild&&!o.attr("href"))for(r=o.parent,t=o.lastChild;n=t.prev,r.insert(t,o),t=n;);}),p.fix_list_elements&&e.addNodeFilter("ul,ol",function(e){for(var t,n,r=e.length;r--;)if("ul"===(n=(t=e[r]).parent).name||"ol"===n.name)if(t.prev&&"li"===t.prev.name)t.prev.append(t);else{var o=new rb("li",1);o.attr("style","list-style-type: none"),t.wrap(o)}}),p.validate&&h.getValidClasses()&&e.addAttributeFilter("class",function(e){for(var t,n,r,o,i,a,u,s=e.length,c=h.getValidClasses();s--;){for(n=(t=e[s]).attr("class").split(" "),i="",r=0;r<n.length;r++)o=n[r],u=!1,(a=c["*"])&&a[o]&&(u=!0),a=c[t.name],!u&&a&&a[o]&&(u=!0),u&&(i&&(i+=" "),i+=o);i.length||(i=null),t.attr("class",i)}})},lb=Xt.makeMap,fb=Xt.each,db=Xt.explode,mb=Xt.extend;function gb(T,k){void 0===k&&(k=fi());var _={},A=[],R={},D={};(T=T||{}).validate=!("validate"in T)||T.validate,T.root_name=T.root_name||"body";var O=function(e){var t,n,r;(n=e.name)in _&&((r=R[n])?r.push(e):R[n]=[e]),t=A.length;for(;t--;)(n=A[t].name)in e.attributes.map&&((r=D[n])?r.push(e):D[n]=[e]);return e},e={schema:k,addAttributeFilter:function(e,n){fb(db(e),function(e){var t;for(t=0;t<A.length;t++)if(A[t].name===e)return void A[t].callbacks.push(n);A.push({name:e,callbacks:[n]})})},getAttributeFilters:function(){return[].concat(A)},addNodeFilter:function(e,n){fb(db(e),function(e){var t=_[e];t||(_[e]=t=[]),t.push(n)})},getNodeFilters:function(){var e=[];for(var t in _)_.hasOwnProperty(t)&&e.push({name:t,callbacks:_[t]});return e},filterNode:O,parse:function(e,a){var t,n,r,o,i,u,s,c,l,f,d,m=[];a=a||{},R={},D={},l=mb(lb("script,style,head,html,body,title,meta,param"),k.getBlockElements());var g=k.getNonEmptyElements(),p=k.children,h=T.validate,v="forced_root_block"in a?a.forced_root_block:T.forced_root_block,y=k.getWhiteSpaceElements(),b=/^[ \t\r\n]+/,C=/[ \t\r\n]+$/,x=/[ \t\r\n]+/g,w=/^[ \t\r\n]+$/;f=y.hasOwnProperty(a.context)||y.hasOwnProperty(T.root_name);var N=function(e,t){var n,r=new rb(e,t);return e in _&&((n=R[e])?n.push(r):R[e]=[r]),r},E=function(e){var t,n,r,o,i=k.getBlockElements();for(t=e.prev;t&&3===t.type;){if(0<(r=t.value.replace(C,"")).length)return void(t.value=r);if(n=t.next){if(3===n.type&&n.value.length){t=t.prev;continue}if(!i[n.name]&&"script"!==n.name&&"style"!==n.name){t=t.prev;continue}}o=t.prev,t.remove(),t=o}};t=Bv({validate:h,allow_script_urls:T.allow_script_urls,allow_conditional_comments:T.allow_conditional_comments,self_closing_elements:function(e){var t,n={};for(t in e)"li"!==t&&"p"!==t&&(n[t]=e[t]);return n}(k.getSelfClosingElements()),cdata:function(e){d.append(N("#cdata",4)).value=e},text:function(e,t){var n;f||(e=e.replace(x," "),sb(d.lastChild,l)&&(e=e.replace(b,""))),0!==e.length&&((n=N("#text",3)).raw=!!t,d.append(n).value=e)},comment:function(e){d.append(N("#comment",8)).value=e},pi:function(e,t){d.append(N(e,7)).value=t,E(d)},doctype:function(e){d.append(N("#doctype",10)).value=e,E(d)},start:function(e,t,n){var r,o,i,a,u;if(i=h?k.getElementRule(e):{}){for((r=N(i.outputName||e,1)).attributes=t,r.shortEnded=n,d.append(r),(u=p[d.name])&&p[r.name]&&!u[r.name]&&m.push(r),o=A.length;o--;)(a=A[o].name)in t.map&&((s=D[a])?s.push(r):D[a]=[r]);l[e]&&E(r),n||(d=r),!f&&y[e]&&(f=!0)}},end:function(e){var t,n,r,o,i;if(n=h?k.getElementRule(e):{}){if(l[e]&&!f){if((t=d.firstChild)&&3===t.type)if(0<(r=t.value.replace(b,"")).length)t.value=r,t=t.next;else for(o=t.next,t.remove(),t=o;t&&3===t.type;)r=t.value,o=t.next,(0===r.length||w.test(r))&&(t.remove(),t=o),t=o;if((t=d.lastChild)&&3===t.type)if(0<(r=t.value.replace(C,"")).length)t.value=r,t=t.prev;else for(o=t.prev,t.remove(),t=o;t&&3===t.type;)r=t.value,o=t.prev,(0===r.length||w.test(r))&&(t.remove(),t=o),t=o}if(f&&y[e]&&(f=!1),n.removeEmpty&&ub(k,g,y,d)&&!d.attributes.map.name&&!d.attr("id"))return i=d.parent,l[d.name]?d.empty().remove():d.unwrap(),void(d=i);n.paddEmpty&&(ib(d)||ub(k,g,y,d))&&ob(T,a,l,d),d=d.parent}}},k);var S=d=new rb(a.context||T.root_name,11);if(t.parse(e),h&&m.length&&(a.context?a.invalid=!0:function(e){var t,n,r,o,i,a,u,s,c,l,f,d,m,g,p,h;for(d=lb("tr,td,th,tbody,thead,tfoot,table"),l=k.getNonEmptyElements(),f=k.getWhiteSpaceElements(),m=k.getTextBlockElements(),g=k.getSpecialElements(),t=0;t<e.length;t++)if((n=e[t]).parent&&!n.fixed)if(m[n.name]&&"li"===n.parent.name){for(p=n.next;p&&m[p.name];)p.name="li",p.fixed=!0,n.parent.insert(p,n.parent),p=p.next;n.unwrap(n)}else{for(o=[n],r=n.parent;r&&!k.isValidChild(r.name,n.name)&&!d[r.name];r=r.parent)o.push(r);if(r&&1<o.length){for(o.reverse(),i=a=O(o[0].clone()),c=0;c<o.length-1;c++){for(k.isValidChild(a.name,o[c].name)?(u=O(o[c].clone()),a.append(u)):u=a,s=o[c].firstChild;s&&s!==o[c+1];)h=s.next,u.append(s),s=h;a=u}ub(k,l,f,i)?r.insert(n,o[0],!0):(r.insert(i,o[0],!0),r.insert(n,i)),r=o[0],(ub(k,l,f,r)||ab(r,"br"))&&r.empty().remove()}else if(n.parent){if("li"===n.name){if((p=n.prev)&&("ul"===p.name||"ul"===p.name)){p.append(n);continue}if((p=n.next)&&("ul"===p.name||"ul"===p.name)){p.insert(n,p.firstChild,!0);continue}n.wrap(O(new rb("ul",1)));continue}k.isValidChild(n.parent.name,"div")&&k.isValidChild("div",n.name)?n.wrap(O(new rb("div",1))):g[n.name]?n.empty().remove():n.unwrap()}}}(m)),v&&("body"===S.name||a.isRootContent)&&function(){var e,t,n=S.firstChild,r=function(e){e&&((n=e.firstChild)&&3===n.type&&(n.value=n.value.replace(b,"")),(n=e.lastChild)&&3===n.type&&(n.value=n.value.replace(C,"")))};if(k.isValidChild(S.name,v.toLowerCase())){for(;n;)e=n.next,3===n.type||1===n.type&&"p"!==n.name&&!l[n.name]&&!n.attr("data-mce-type")?(t||((t=N(v,1)).attr(T.forced_root_block_attrs),S.insert(t,n)),t.append(n)):(r(t),t=null),n=e;r(t)}}(),!a.invalid){for(c in R){for(s=_[c],i=(n=R[c]).length;i--;)n[i].parent||n.splice(i,1);for(r=0,o=s.length;r<o;r++)s[r](n,c,a)}for(r=0,o=A.length;r<o;r++)if((s=A[r]).name in D){for(i=(n=D[s.name]).length;i--;)n[i].parent||n.splice(i,1);for(i=0,u=s.callbacks.length;i<u;i++)s.callbacks[i](n,s.name,a)}}return S}};return cb(e,T),Zy.register(e,T),e}var pb=function(e,t,n){-1===Xt.inArray(t,n)&&(e.addAttributeFilter(n,function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)}),t.push(n))},hb=function(e,t,n){var r=xa(n.getInner?t.innerHTML:e.getOuterHTML(t));return n.selection||_o(ar.fromDom(t))?r:Xt.trim(r)},vb=function(e,t,n){var r=n.selection?Xy({forced_root_block:!1},n):n,o=e.parse(t,r);return Yy.trimTrailingBr(o),o},yb=function(e,t,n,r,o){var i,a,u,s,c=(i=r,il(t,n).serialize(i));return a=e,s=c,!(u=o).no_events&&a?yp(a,Xy(u,{content:s})).content:s};function bb(e,t){var a,u,s,c,l,n,r=(a=e,n=["data-mce-selected"],s=(u=t)&&u.dom?u.dom:Ei.DOM,c=u&&u.schema?u.schema:fi(a),a.entity_encoding=a.entity_encoding||"named",a.remove_trailing_brs=!("remove_trailing_brs"in a)||a.remove_trailing_brs,l=gb(a,c),Yy.register(l,a,s),{schema:c,addNodeFilter:l.addNodeFilter,addAttributeFilter:l.addAttributeFilter,serialize:function(e,t){var n=Xy({format:"html"},t||{}),r=Gy.process(u,e,n),o=hb(s,r,n),i=vb(l,o,n);return"tree"===n.format?i:yb(u,a,c,i,n)},addRules:function(e){c.addValidElements(e)},setRules:function(e){c.setValidElements(e)},addTempAttr:d(pb,l,n),getTempAttrs:function(){return n}});return{schema:r.schema,addNodeFilter:r.addNodeFilter,addAttributeFilter:r.addAttributeFilter,serialize:r.serialize,addRules:r.addRules,setRules:r.setRules,addTempAttr:r.addTempAttr,getTempAttrs:r.getTempAttrs}}function Cb(e){return{getBookmark:d(pc,e),moveToBookmark:d(hc,e)}}(Cb||(Cb={})).isBookmarkNode=vc;var xb,wb,Nb=Cb,Eb=Uo.isContentEditableFalse,Sb=Uo.isContentEditableTrue,Tb=function(r,a){var u,s,c,l,f,d,m,g,p,h,v,y,i,b,C,x,w,N=a.dom,E=Xt.each,S=a.getDoc(),T=V.document,k=Math.abs,_=Math.round,A=a.getBody();l={nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};var e=".mce-content-body";a.contentStyles.push(e+" div.mce-resizehandle {position: absolute;border: 1px solid black;box-sizing: content-box;background: #FFF;width: 7px;height: 7px;z-index: 10000}"+e+" .mce-resizehandle:hover {background: #000}"+e+" img[data-mce-selected],"+e+" hr[data-mce-selected] {outline: 1px solid black;resize: none}"+e+" .mce-clonedresizable {position: absolute;"+(fe.gecko?"":"outline: 1px dashed black;")+"opacity: .5;filter: alpha(opacity=50);z-index: 10000}"+e+" .mce-resize-helper {background: #555;background: rgba(0,0,0,0.75);border-radius: 3px;border: 1px;color: white;display: none;font-family: sans-serif;font-size: 12px;white-space: nowrap;line-height: 14px;margin: 5px 10px;padding: 5px;position: absolute;z-index: 10001}");var R=function(e){return e&&("IMG"===e.nodeName||a.dom.is(e,"figure.image"))},n=function(e){var t,n,r=e.target;t=e,n=a.selection.getRng(),!R(t.target)||Nv(t.clientX,t.clientY,n)||e.isDefaultPrevented()||a.selection.select(r)},D=function(e){return a.dom.is(e,"figure.image")?e.querySelector("img"):e},O=function(e){var t=a.settings.object_resizing;return!1!==t&&!fe.iOS&&("string"!=typeof t&&(t="table,img,figure.image,div"),"false"!==e.getAttribute("data-mce-resize")&&e!==a.getBody()&&Ir(ar.fromDom(e),t))},B=function(e){var t,n,r,o;t=e.screenX-d,n=e.screenY-m,b=t*f[2]+h,C=n*f[3]+v,b=b<5?5:b,C=C<5?5:C,(R(u)&&!1!==a.settings.resize_img_proportional?!Sv.modifierPressed(e):Sv.modifierPressed(e)||R(u)&&f[2]*f[3]!=0)&&(k(t)>k(n)?(C=_(b*y),b=_(C/y)):(b=_(C/y),C=_(b*y))),N.setStyles(D(s),{width:b,height:C}),r=0<(r=f.startPos.x+t)?r:0,o=0<(o=f.startPos.y+n)?o:0,N.setStyles(c,{left:r,top:o,display:"block"}),c.innerHTML=b+" &times; "+C,f[2]<0&&s.clientWidth<=b&&N.setStyle(s,"left",g+(h-b)),f[3]<0&&s.clientHeight<=C&&N.setStyle(s,"top",p+(v-C)),(t=A.scrollWidth-x)+(n=A.scrollHeight-w)!=0&&N.setStyles(c,{left:r-t,top:o-n}),i||(wp(a,u,h,v),i=!0)},P=function(){i=!1;var e=function(e,t){t&&(u.style[e]||!a.schema.isValid(u.nodeName.toLowerCase(),e)?N.setStyle(D(u),e,t):N.setAttrib(D(u),e,t))};e("width",b),e("height",C),N.unbind(S,"mousemove",B),N.unbind(S,"mouseup",P),T!==S&&(N.unbind(T,"mousemove",B),N.unbind(T,"mouseup",P)),N.remove(s),N.remove(c),o(u),Np(a,u,b,C),N.setAttrib(u,"style",N.getAttrib(u,"style")),a.nodeChanged()},o=function(e){var t,r,o,n,i;I(),M(),t=N.getPos(e,A),g=t.x,p=t.y,i=e.getBoundingClientRect(),r=i.width||i.right-i.left,o=i.height||i.bottom-i.top,u!==e&&(u=e,b=C=0),n=a.fire("ObjectSelected",{target:e}),O(e)&&!n.isDefaultPrevented()?E(l,function(n,e){var t;(t=N.get("mceResizeHandle"+e))&&N.remove(t),t=N.add(A,"div",{id:"mceResizeHandle"+e,"data-mce-bogus":"all","class":"mce-resizehandle",unselectable:!0,style:"cursor:"+e+"-resize; margin:0; padding:0"}),11===fe.ie&&(t.contentEditable=!1),N.bind(t,"mousedown",function(e){var t;e.stopImmediatePropagation(),e.preventDefault(),d=(t=e).screenX,m=t.screenY,h=D(u).clientWidth,v=D(u).clientHeight,y=v/h,(f=n).startPos={x:r*n[0]+g,y:o*n[1]+p},x=A.scrollWidth,w=A.scrollHeight,s=u.cloneNode(!0),N.addClass(s,"mce-clonedresizable"),N.setAttrib(s,"data-mce-bogus","all"),s.contentEditable=!1,s.unSelectabe=!0,N.setStyles(s,{left:g,top:p,margin:0}),s.removeAttribute("data-mce-selected"),A.appendChild(s),N.bind(S,"mousemove",B),N.bind(S,"mouseup",P),T!==S&&(N.bind(T,"mousemove",B),N.bind(T,"mouseup",P)),c=N.add(A,"div",{"class":"mce-resize-helper","data-mce-bogus":"all"},h+" &times; "+v)}),n.elm=t,N.setStyles(t,{left:r*n[0]+g-t.offsetWidth/2,top:o*n[1]+p-t.offsetHeight/2})}):I(),u.setAttribute("data-mce-selected","1")},I=function(){var e,t;for(e in M(),u&&u.removeAttribute("data-mce-selected"),l)(t=N.get("mceResizeHandle"+e))&&(N.unbind(t),N.remove(t))},L=function(e){var t,n=function(e,t){if(e)do{if(e===t)return!0}while(e=e.parentNode)};i||a.removed||(E(N.select("img[data-mce-selected],hr[data-mce-selected]"),function(e){e.removeAttribute("data-mce-selected")}),t="mousedown"===e.type?e.target:r.getNode(),n(t=N.$(t).closest("table,img,figure.image,hr")[0],A)&&(z(),n(r.getStart(!0),t)&&n(r.getEnd(!0),t))?o(t):I())},F=function(e){return Eb(function(e,t){for(;t&&t!==e;){if(Sb(t)||Eb(t))return t;t=t.parentNode}return null}(a.getBody(),e))},M=function(){for(var e in l){var t=l[e];t.elm&&(N.unbind(t.elm),delete t.elm)}},z=function(){try{a.getDoc().execCommand("enableObjectResizing",!1,!1)}catch(e){}};return a.on("init",function(){z(),fe.ie&&11<=fe.ie&&(a.on("mousedown click",function(e){var t=e.target,n=t.nodeName;i||!/^(TABLE|IMG|HR)$/.test(n)||F(t)||(2!==e.button&&a.selection.select(t,"TABLE"===n),"mousedown"===e.type&&a.nodeChanged())}),a.dom.bind(A,"mscontrolselect",function(e){var t=function(e){he.setEditorTimeout(a,function(){a.selection.select(e)})};if(F(e.target))return e.preventDefault(),void t(e.target);/^(TABLE|IMG|HR)$/.test(e.target.nodeName)&&(e.preventDefault(),"IMG"===e.target.tagName&&t(e.target))}));var t=he.throttle(function(e){a.composing||L(e)});a.on("nodechange ResizeEditor ResizeWindow drop FullscreenStateChanged",t),a.on("keyup compositionend",function(e){u&&"TABLE"===u.nodeName&&t(e)}),a.on("hide blur",I),a.on("contextmenu",n)}),a.on("remove",M),{isResizable:O,showResizeRect:o,hideResizeRect:I,updateResizeRect:L,destroy:function(){u=s=null}}},kb=function(e){for(var t=0,n=0,r=e;r&&r.nodeType;)t+=r.offsetLeft||0,n+=r.offsetTop||0,r=r.offsetParent;return{x:t,y:n}},_b=function(e,t,n){var r,o,i,a,u,s=e.dom,c=s.getRoot(),l=0;if(u={elm:t,alignToTop:n},e.fire("scrollIntoView",u),!u.isDefaultPrevented()&&Uo.isElement(t)){if(!1===n&&(l=t.offsetHeight),"BODY"!==c.nodeName){var f=e.selection.getScrollContainer();if(f)return r=kb(t).y-kb(f).y+l,a=f.clientHeight,void((r<(i=f.scrollTop)||i+a<r+25)&&(f.scrollTop=r<i?r:r-a+25))}o=s.getViewPort(e.getWin()),r=s.getPos(t).y+l,i=o.y,a=o.h,(r<o.y||i+a<r+25)&&e.getWin().scrollTo(0,r<i?r:r-a+25)}},Ab=function(d,e){Z(Eu.fromRangeStart(e).getClientRects()).each(function(e){var t,n,r,o,i,a,u,s,c,l=function(e){if(e.inline)return e.getBody().getBoundingClientRect();var t=e.getWin();return{left:0,right:t.innerWidth,top:0,bottom:t.innerHeight,width:t.innerWidth,height:t.innerHeight}}(d),f={x:(i=t=l,a=n=e,a.left>i.left&&a.right<i.right?0:a.left<i.left?a.left-i.left:a.right-i.right),y:(r=t,o=n,o.top>r.top&&o.bottom<r.bottom?0:o.top<r.top?o.top-r.top:o.bottom-r.bottom)};s=0!==f.x?0<f.x?f.x+4:f.x-4:0,c=0!==f.y?0<f.y?f.y+4:f.y-4:0,(u=d).inline?(u.getBody().scrollLeft+=s,u.getBody().scrollTop+=c):u.getWin().scrollBy(s,c)})},Rb=function(e){return Uo.isContentEditableTrue(e)||Uo.isContentEditableFalse(e)},Db=function(e,t,n){var r,o,i,a,u,s=n;if(s.caretPositionFromPoint)(o=s.caretPositionFromPoint(e,t))&&((r=n.createRange()).setStart(o.offsetNode,o.offset),r.collapse(!0));else if(n.caretRangeFromPoint)r=n.caretRangeFromPoint(e,t);else if(s.body.createTextRange){r=s.body.createTextRange();try{r.moveToPoint(e,t),r.collapse(!0)}catch(c){r=function(e,n,t){var r,o,i;if(r=t.elementFromPoint(e,n),o=t.body.createTextRange(),r&&"HTML"!==r.tagName||(r=t.body),o.moveToElementText(r),0<(i=(i=Xt.toArray(o.getClientRects())).sort(function(e,t){return(e=Math.abs(Math.max(e.top-n,e.bottom-n)))-(t=Math.abs(Math.max(t.top-n,t.bottom-n)))})).length){n=(i[0].bottom+i[0].top)/2;try{return o.moveToPoint(e,n),o.collapse(!0),o}catch(a){}}return null}(e,t,n)}return i=r,a=n.body,u=i&&i.parentElement?i.parentElement():null,Uo.isContentEditableFalse(function(e,t,n){for(;e&&e!==t;){if(n(e))return e;e=e.parentNode}return null}(u,a,Rb))?null:i}return r},Ob=function(n,e){return W(e,function(e){var t=n.fire("GetSelectionRange",{range:e});return t.range!==e?t.range:e})},Bb=function(e,t){var n=(t||V.document).createDocumentFragment();return z(e,function(e){n.appendChild(e.dom())}),ar.fromDom(n)},Pb=_r("element","width","rows"),Ib=_r("element","cells"),Lb=_r("x","y"),Fb=function(e,t){var n=parseInt(Nr(e,t),10);return isNaN(n)?1:n},Mb=function(e){return j(e,function(e,t){return t.cells().length>e?t.cells().length:e},0)},zb=function(e,t){for(var n=e.rows(),r=0;r<n.length;r++)for(var o=n[r].cells(),i=0;i<o.length;i++)if(Fr(o[i],t))return _.some(Lb(i,r));return _.none()},Ub=function(e,t,n,r,o){for(var i=[],a=e.rows(),u=n;u<=o;u++){var s=a[u].cells(),c=t<r?s.slice(t,r+1):s.slice(r,t+1);i.push(Ib(a[u].element(),c))}return i},jb=function(e){var o=Pb(pa(e),0,[]);return z(Ji(e,"tr"),function(n,r){z(Ji(n,"td,th"),function(e,t){!function(e,t,n,r,o){for(var i=Fb(o,"rowspan"),a=Fb(o,"colspan"),u=e.rows(),s=n;s<n+i;s++){u[s]||(u[s]=Ib(ha(r),[]));for(var c=t;c<t+a;c++)u[s].cells()[c]=s===n&&c===t?o:pa(o)}}(o,function(e,t,n){for(;r=t,o=n,i=void 0,((i=e.rows())[o]?i[o].cells():[])[r];)t++;var r,o,i;return t}(o,t,r),r,n,e)})}),Pb(o.element(),Mb(o.rows()),o.rows())},Vb=function(e){return n=W((t=e).rows(),function(e){var t=W(e.cells(),function(e){var t=ha(e);return Er(t,"colspan"),Er(t,"rowspan"),t}),n=pa(e.element());return Fi(n,t),n}),r=pa(t.element()),o=ar.fromTag("tbody"),Fi(o,n),Li(r,o),r;var t,n,r,o},Hb=function(l,e,t){return zb(l,e).bind(function(c){return zb(l,t).map(function(e){return t=l,r=e,o=(n=c).x(),i=n.y(),a=r.x(),u=r.y(),s=i<u?Ub(t,o,i,a,u):Ub(t,o,u,a,i),Pb(t.element(),Mb(s),s);var t,n,r,o,i,a,u,s})})},qb=function(n,t){return X(n,function(e){return"li"===lr(e)&&Kh(e,t)}).fold(q([]),function(e){return(t=n,X(t,function(e){return"ul"===lr(e)||"ol"===lr(e)})).map(function(e){return[ar.fromTag("li"),ar.fromTag(lr(e))]}).getOr([]);var t})},$b=function(e,t){var n,r=ar.fromDom(t.commonAncestorContainer),o=af(r,e),i=U(o,function(e){return Co(e)||yo(e)}),a=qb(o,t),u=i.concat(a.length?a:Eo(n=r)?jr(n).filter(No).fold(q([]),function(e){return[n,e]}):No(n)?[n]:[]);return W(u,pa)},Wb=function(){return Bb([])},Kb=function(e,t){return n=ar.fromDom(t.cloneContents()),r=$b(e,t),o=j(r,function(e,t){return Li(t,e),t},n),0<r.length?Bb([o]):o;var n,r,o},Xb=function(e,o){return(t=e,n=o[0],na(n,"table",d(Fr,t))).bind(function(e){var t=o[0],n=o[o.length-1],r=jb(e);return Hb(r,t,n).map(function(e){return Bb([Vb(e)])})}).getOrThunk(Wb);var t,n},Yb=function(e,t){var n,r,o=Cm(t,e);return 0<o.length?Xb(e,o):(n=e,0<(r=t).length&&r[0].collapsed?Wb():Kb(n,r[0]))},Gb=function(e,t){if(void 0===t&&(t={}),t.get=!0,t.format=t.format||"html",t.selection=!0,(t=e.fire("BeforeGetContent",t)).isDefaultPrevented())return e.fire("GetContent",t),t.content;if("text"===t.format)return c=e,_.from(c.selection.getRng()).map(function(e){var t=c.dom.add(c.getBody(),"div",{"data-mce-bogus":"all",style:"overflow: hidden; opacity: 0;"},e.cloneContents()),n=xa(t.innerText);return c.dom.remove(t),n}).getOr("");t.getInner=!0;var n,r,o,i,a,u,s,c,l=(r=t,i=(n=e).selection.getRng(),a=n.dom.create("body"),u=n.selection.getSel(),s=Ob(n,gm(u)),(o=r.contextual?Yb(ar.fromDom(n.getBody()),s).dom():i.cloneContents())&&a.appendChild(o),n.selection.serializer.serialize(a,r));return"tree"===t.format?l:(t.content=e.selection.isCollapsed()?"":l,e.fire("GetContent",t),t.content)},Jb=function(e,t,n){var r,o,i,a=e.selection.getRng(),u=e.getDoc();if((n=n||{format:"html"}).set=!0,n.selection=!0,n.content=t,n.no_events||!(n=e.fire("BeforeSetContent",n)).isDefaultPrevented()){if(t=n.content,a.insertNode){t+='<span id="__caret">_</span>',a.startContainer===u&&a.endContainer===u?u.body.innerHTML=t:(a.deleteContents(),0===u.body.childNodes.length?u.body.innerHTML=t:a.createContextualFragment?a.insertNode(a.createContextualFragment(t)):(o=u.createDocumentFragment(),i=u.createElement("div"),o.appendChild(i),i.outerHTML=t,a.insertNode(o))),r=e.dom.get("__caret"),(a=u.createRange()).setStartBefore(r),a.setEndBefore(r),e.selection.setRng(a),e.dom.remove("__caret");try{e.selection.setRng(a)}catch(s){}}else a.item&&(u.execCommand("Delete",!1,null),a=e.getRng()),/^\s+/.test(t)?(a.pasteHTML('<span id="__mce_tmp">_</span>'+t),e.dom.remove("__mce_tmp")):a.pasteHTML(t);n.no_events||e.fire("SetContent",n)}else e.fire("SetContent",n)},Qb=function(e,t,n,r,o){var i=n?t.startContainer:t.endContainer,a=n?t.startOffset:t.endOffset;return _.from(i).map(ar.fromDom).map(function(e){return r&&t.collapsed?e:Kr(e,o(e,a)).getOr(e)}).bind(function(e){return dr(e)?_.some(e):jr(e)}).map(function(e){return e.dom()}).getOr(e)},Zb=function(e,t,n){return Qb(e,t,!0,n,function(e,t){return Math.min(e.dom().childNodes.length,t)})},eC=function(e,t,n){return Qb(e,t,!1,n,function(e,t){return 0<t?t-1:t})},tC=function(e,t){for(var n=e;e&&Uo.isText(e)&&0===e.length;)e=t?e.nextSibling:e.previousSibling;return e||n},nC=Xt.each,rC=function(e){return!!e.select},oC=function(e){return!(!e||!e.ownerDocument)&&Mr(ar.fromDom(e.ownerDocument),ar.fromDom(e))},iC=function(u,s,e,c){var n,t,l,f,a,r=function(e,t){return Jb(c,e,t)},o=function(e){var t=m();t.collapse(!!e),i(t)},d=function(){return s.getSelection?s.getSelection():s.document.selection},m=function(){var e,t,n,r,o=function(e,t,n){try{return t.compareBoundaryPoints(e,n)}catch(r){return-1}};if(!s)return null;if(null==(r=s.document))return null;if(c.bookmark!==undefined&&!1===oh(c)){var i=rp(c);if(i.isSome())return i.map(function(e){return Ob(c,[e])[0]}).getOr(r.createRange())}try{(e=d())&&!Uo.isRestrictedNode(e.anchorNode)&&(t=0<e.rangeCount?e.getRangeAt(0):e.createRange?e.createRange():r.createRange())}catch(a){}return(t=Ob(c,[t])[0])||(t=r.createRange?r.createRange():r.body.createTextRange()),t.setStart&&9===t.startContainer.nodeType&&t.collapsed&&(n=u.getRoot(),t.setStart(n,0),t.setEnd(n,0)),l&&f&&(0===o(t.START_TO_START,t,l)&&0===o(t.END_TO_END,t,l)?t=f:f=l=null),t},i=function(e,t){var n,r;if((o=e)&&(rC(o)||oC(o.startContainer)&&oC(o.endContainer))){var o,i=rC(e)?e:null;if(i){f=null;try{i.select()}catch(a){}}else{if(n=d(),e=c.fire("SetSelectionRange",{range:e,forward:t}).range,n){f=e;try{n.removeAllRanges(),n.addRange(e)}catch(a){}!1===t&&n.extend&&(n.collapse(e.endContainer,e.endOffset),n.extend(e.startContainer,e.startOffset)),l=0<n.rangeCount?n.getRangeAt(0):null}e.collapsed||e.startContainer!==e.endContainer||!n.setBaseAndExtent||fe.ie||e.endOffset-e.startOffset<2&&e.startContainer.hasChildNodes()&&(r=e.startContainer.childNodes[e.startOffset])&&"IMG"===r.tagName&&(n.setBaseAndExtent(e.startContainer,e.startOffset,e.endContainer,e.endOffset),n.anchorNode===e.startContainer&&n.focusNode===e.endContainer||n.setBaseAndExtent(r,0,r,1)),c.fire("AfterSetSelectionRange",{range:e,forward:t})}}},g=function(){var e,t,n=d();return!(n&&n.anchorNode&&n.focusNode)||((e=u.createRng()).setStart(n.anchorNode,n.anchorOffset),e.collapse(!0),(t=u.createRng()).setStart(n.focusNode,n.focusOffset),t.collapse(!0),e.compareBoundaryPoints(e.START_TO_START,t)<=0)},p={bookmarkManager:null,controlSelection:null,dom:u,win:s,serializer:e,editor:c,collapse:o,setCursorLocation:function(e,t){var n=u.createRng();e?(n.setStart(e,t),n.setEnd(e,t),i(n),o(!1)):(Xh(u,n,c.getBody(),!0),i(n))},getContent:function(e){return Gb(c,e)},setContent:r,getBookmark:function(e,t){return n.getBookmark(e,t)},moveToBookmark:function(e){return n.moveToBookmark(e)},select:function(e,t){var r,n,o;return(r=u,n=e,o=t,_.from(n).map(function(e){var t=r.nodeIndex(e),n=r.createRng();return n.setStart(e.parentNode,t),n.setEnd(e.parentNode,t+1),o&&(Xh(r,n,e,!0),Xh(r,n,e,!1)),n})).each(i),e},isCollapsed:function(){var e=m(),t=d();return!(!e||e.item)&&(e.compareEndPoints?0===e.compareEndPoints("StartToEnd",e):!t||e.collapsed)},isForward:g,setNode:function(e){return r(u.getOuterHTML(e)),e},getNode:function(){return e=c.getBody(),(t=m())?(r=t.startContainer,o=t.endContainer,i=t.startOffset,a=t.endOffset,n=t.commonAncestorContainer,!t.collapsed&&(r===o&&a-i<2&&r.hasChildNodes()&&(n=r.childNodes[i]),3===r.nodeType&&3===o.nodeType&&(r=r.length===i?tC(r.nextSibling,!0):r.parentNode,o=0===a?tC(o.previousSibling,!1):o.parentNode,r&&r===o))?r:n&&3===n.nodeType?n.parentNode:n):e;var e,t,n,r,o,i,a},getSel:d,setRng:i,getRng:m,getStart:function(e){return Zb(c.getBody(),m(),e)},getEnd:function(e){return eC(c.getBody(),m(),e)},getSelectedBlocks:function(e,t){return function(e,t,n,r){var o,i,a=[];if(i=e.getRoot(),n=e.getParent(n||Zb(i,t,t.collapsed),e.isBlock),r=e.getParent(r||eC(i,t,t.collapsed),e.isBlock),n&&n!==i&&a.push(n),n&&r&&n!==r)for(var u=new mo(o=n,i);(o=u.next())&&o!==r;)e.isBlock(o)&&a.push(o);return r&&n!==r&&r!==i&&a.push(r),a}(u,m(),e,t)},normalize:function(){var e=m(),t=d();if(!hm(t)&&Yh(c)){var n=kg(u,e);return n.each(function(e){i(e,g())}),n.getOr(e)}return e},selectorChanged:function(e,t){var i;return a||(a={},i={},c.on("NodeChange",function(e){var n=e.element,r=u.getParents(n,null,u.getRoot()),o={};nC(a,function(e,n){nC(r,function(t){if(u.is(t,n))return i[n]||(nC(e,function(e){e(!0,{node:t,selector:n,parents:r})}),i[n]=e),o[n]=e,!1})}),nC(i,function(e,t){o[t]||(delete i[t],nC(e,function(e){e(!1,{node:n,selector:t,parents:r})}))})})),a[e]||(a[e]=[]),a[e].push(t),p},getScrollContainer:function(){for(var e,t=u.getRoot();t&&"BODY"!==t.nodeName;){if(t.scrollHeight>t.clientHeight){e=t;break}t=t.parentNode}return e},scrollIntoView:function(e,t){return _b(c,e,t)},placeCaretAt:function(e,t){return i(Db(e,t,c.getDoc()))},getBoundingClientRect:function(){var e=m();return e.collapsed?ku.fromRangeStart(e).getClientRects()[0]:e.getBoundingClientRect()},destroy:function(){s=l=f=null,t.destroy()}};return n=Nb(p),t=Tb(p,c),p.bookmarkManager=n,p.controlSelection=t,p};(wb=xb||(xb={}))[wb.Br=0]="Br",wb[wb.Block=1]="Block",wb[wb.Wrap=2]="Wrap",wb[wb.Eol=3]="Eol";var aC=function(e,t){return e===Su.Backwards?t.reverse():t},uC=function(e,t,n,r){for(var o,i,a,u,s,c,l=Gs(n),f=r,d=[];f&&(s=l,c=f,o=t===Su.Forwards?s.next(c):s.prev(c));){if(Uo.isBr(o.getNode(!1)))return t===Su.Forwards?{positions:aC(t,d).concat([o]),breakType:xb.Br,breakAt:_.some(o)}:{positions:aC(t,d),breakType:xb.Br,breakAt:_.some(o)};if(o.isVisible()){if(e(f,o)){var m=(i=t,a=f,u=o,Uo.isBr(u.getNode(i===Su.Forwards))?xb.Br:!1===Ss(a,u)?xb.Block:xb.Wrap);return{positions:aC(t,d),breakType:m,breakAt:_.some(o)}}d.push(o),f=o}else f=o}return{positions:aC(t,d),breakType:xb.Eol,breakAt:_.none()}},sC=function(n,r,o,e){return r(o,e).breakAt.map(function(e){var t=r(o,e).positions;return n===Su.Backwards?t.concat(e):[e].concat(t)}).getOr([])},cC=function(e,i){return j(e,function(e,o){return e.fold(function(){return _.some(o)},function(r){return nu(Z(r.getClientRects()),Z(o.getClientRects()),function(e,t){var n=Math.abs(i-e.left);return Math.abs(i-t.left)<=n?o:r}).or(e)})},_.none())},lC=function(t,e){return Z(e.getClientRects()).bind(function(e){return cC(t,e.left)})},fC=d(uC,Eu.isAbove,-1),dC=d(uC,Eu.isBelow,1),mC=d(sC,-1,fC),gC=d(sC,1,dC),pC=Uo.isContentEditableFalse,hC=Qa,vC=function(e,t,n,r){var o=e===Su.Forwards,i=o?If:Lf;if(!r.collapsed){var a=hC(r);if(pC(a))return ig(e,t,a,e===Su.Backwards,!0)}var u=Ea(r.startContainer),s=Bs(e,t.getBody(),r);if(i(s))return ag(t,s.getNode(!o));var c=jl.normalizePosition(o,n(s));if(!c)return u?r:null;if(i(c))return ig(e,t,c.getNode(!o),o,!0);var l=n(c);return l&&i(l)&&Ls(c,l)?ig(e,t,l.getNode(!o),o,!0):u?sg(t,c.toRange(),!0):null},yC=function(e,t,n,r){var o,i,a,u,s,c,l,f,d;if(d=hC(r),o=Bs(e,t.getBody(),r),i=n(t.getBody(),mv(1),o),a=U(i,gv(1)),s=Ht.last(o.getClientRects()),(If(o)||Mf(o))&&(d=o.getNode()),(Lf(o)||zf(o))&&(d=o.getNode(!0)),!s)return null;if(c=s.left,(u=Cv(a,c))&&pC(u.node))return l=Math.abs(c-u.left),f=Math.abs(c-u.right),ig(e,t,u.node,l<f,!0);if(d){var m=function(e,t,n,r){var o,i,a,u,s,c,l=Gs(t),f=[],d=0,m=function(e){return Ht.last(e.getClientRects())};1===e?(o=l.next,i=Ga,a=Ya,u=ku.after(r)):(o=l.prev,i=Ya,a=Ga,u=ku.before(r)),c=m(u);do{if(u.isVisible()&&!a(s=m(u),c)){if(0<f.length&&i(s,Ht.last(f))&&d++,(s=Wa(s)).position=u,s.line=d,n(s))return f;f.push(s)}}while(u=o(u));return f}(e,t.getBody(),mv(1),d);if(u=Cv(U(m,gv(1)),c))return sg(t,u.position.toRange(),!0);if(u=Ht.last(U(m,gv(0))))return sg(t,u.position.toRange(),!0)}},bC=function(e,t,n){var r,o,i,a,u=Gs(e.getBody()),s=d(Is,u.next),c=d(Is,u.prev);if(n.collapsed&&e.settings.forced_root_block){if(!(r=e.dom.getParent(n.startContainer,"PRE")))return;(1===t?s(ku.fromRangeStart(n)):c(ku.fromRangeStart(n)))||(a=(i=e).dom.create(wl(i)),(!fe.ie||11<=fe.ie)&&(a.innerHTML='<br data-mce-bogus="1">'),o=a,1===t?e.$(r).after(o):e.$(r).before(o),e.selection.select(o,!0),e.selection.collapse())}},CC=function(l,f){return function(){var e,t,n,r,o,i,a,u,s,c=(t=f,r=Gs((e=l).getBody()),o=d(Is,r.next),i=d(Is,r.prev),a=t?Su.Forwards:Su.Backwards,u=t?o:i,s=e.selection.getRng(),(n=vC(a,e,u,s))?n:(n=bC(e,a,s))||null);return!!c&&(l.selection.setRng(c),!0)}},xC=function(u,s){return function(){var e,t,n,r,o,i,a=(r=(t=s)?1:-1,o=t?dv:fv,i=(e=u).selection.getRng(),(n=yC(r,e,o,i))?n:(n=bC(e,r,i))||null);return!!a&&(u.selection.setRng(a),!0)}},wC=function(r,o){return function(){var t,e=o?ku.fromRangeEnd(r.selection.getRng()):ku.fromRangeStart(r.selection.getRng()),n=o?dC(r.getBody(),e):fC(r.getBody(),e);return(o?ee(n.positions):Z(n.positions)).filter((t=o,function(e){return t?Lf(e):If(e)})).fold(q(!1),function(e){return r.selection.setRng(e.toRange()),!0})}},NC=function(e,t,n,r,o){var i,a,u,s,c=Ji(ar.fromDom(n),"td,th,caption").map(function(e){return e.dom()}),l=U((i=e,G(c,function(e){var t,n,r=(t=Wa(e.getBoundingClientRect()),n=-1,{left:t.left-n,top:t.top-n,right:t.right+2*n,bottom:t.bottom+2*n,width:t.width+n,height:t.height+n});return[{x:r.left,y:i(r),cell:e},{x:r.right,y:i(r),cell:e}]})),function(e){return t(e,o)});return(a=l,u=r,s=o,j(a,function(e,r){return e.fold(function(){return _.some(r)},function(e){var t=Math.sqrt(Math.abs(e.x-u)+Math.abs(e.y-s)),n=Math.sqrt(Math.abs(r.x-u)+Math.abs(r.y-s));return _.some(n<t?r:e)})},_.none())).map(function(e){return e.cell})},EC=d(NC,function(e){return e.bottom},function(e,t){return e.y<t}),SC=d(NC,function(e){return e.top},function(e,t){return e.y>t}),TC=function(t,n){return Z(n.getClientRects()).bind(function(e){return EC(t,e.left,e.top)}).bind(function(e){return lC((t=e,uc.lastPositionIn(t).map(function(e){return fC(t,e).positions.concat(e)}).getOr([])),n);var t})},kC=function(t,n){return ee(n.getClientRects()).bind(function(e){return SC(t,e.left,e.top)}).bind(function(e){return lC((t=e,uc.firstPositionIn(t).map(function(e){return[e].concat(dC(t,e).positions)}).getOr([])),n);var t})},_C=function(e,t){e.selection.setRng(t),Ab(e,t)},AC=function(e,t,n){var r,o,i,a,u=e(t,n);return(a=u).breakType===xb.Wrap&&0===a.positions.length||!Uo.isBr(n.getNode())&&(i=u).breakType===xb.Br&&1===i.positions.length?(r=e,o=t,!u.breakAt.map(function(e){return r(o,e).breakAt.isSome()}).getOr(!1)):u.breakAt.isNone()},RC=d(AC,fC),DC=d(AC,dC),OC=function(e,t,n,r){var o,i,a,u,s=e.selection.getRng(),c=t?1:-1;if(ds()&&(o=t,i=s,a=n,u=ku.fromRangeStart(i),uc.positionIn(!o,a).map(function(e){return e.isEqual(u)}).getOr(!1))){var l=ig(c,e,n,!t,!0);return _C(e,l),!0}return!1},BC=function(e,t){var n=t.getNode(e);return Uo.isElement(n)&&"TABLE"===n.nodeName?_.some(n):_.none()},PC=function(u,s,c){var e=BC(!!s,c),t=!1===s;e.fold(function(){return _C(u,c.toRange())},function(a){return uc.positionIn(t,u.getBody()).filter(function(e){return e.isEqual(c)}).fold(function(){return _C(u,c.toRange())},function(e){return n=s,o=a,t=c,void((i=wl(r=u))?r.undoManager.transact(function(){var e=ar.fromTag(i);wr(e,Nl(r)),Li(e,ar.fromTag("br")),n?Pi(ar.fromDom(o),e):Bi(ar.fromDom(o),e);var t=r.dom.createRng();t.setStart(e.dom(),0),t.setEnd(e.dom(),0),_C(r,t)}):_C(r,t.toRange()));var n,r,o,t,i})})},IC=function(e,t,n,r){var o,i,a,u,s,c,l=e.selection.getRng(),f=ku.fromRangeStart(l),d=e.getBody();if(!t&&RC(r,f)){var m=(u=d,TC(s=n,c=f).orThunk(function(){return Z(c.getClientRects()).bind(function(e){return cC(mC(u,ku.before(s)),e.left)})}).getOr(ku.before(s)));return PC(e,t,m),!0}return!(!t||!DC(r,f))&&(o=d,m=kC(i=n,a=f).orThunk(function(){return Z(a.getClientRects()).bind(function(e){return cC(gC(o,ku.after(i)),e.left)})}).getOr(ku.after(i)),PC(e,t,m),!0)},LC=function(t,n){return function(){return _.from(t.dom.getParent(t.selection.getNode(),"td,th")).bind(function(e){return _.from(t.dom.getParent(e,"table")).map(function(e){return OC(t,n,e)})}).getOr(!1)}},FC=function(n,r){return function(){return _.from(n.dom.getParent(n.selection.getNode(),"td,th")).bind(function(t){return _.from(n.dom.getParent(t,"table")).map(function(e){return IC(n,r,e,t)})}).getOr(!1)}},MC=function(e){return F(["figcaption"],lr(e))},zC=function(e){var t=V.document.createRange();return t.setStartBefore(e.dom()),t.setEndBefore(e.dom()),t},UC=function(e,t,n){n?Li(e,t):Ii(e,t)},jC=function(e,t,n,r){return""===t?(l=e,f=r,d=ar.fromTag("br"),UC(l,d,f),zC(d)):(o=e,i=r,a=t,u=n,s=ar.fromTag(a),c=ar.fromTag("br"),wr(s,u),Li(s,c),UC(o,s,i),zC(c));var o,i,a,u,s,c,l,f,d},VC=function(e,t,n){return t?(o=e.dom(),dC(o,n).breakAt.isNone()):(r=e.dom(),fC(r,n).breakAt.isNone());var r,o},HC=function(t,n){var e,r,o,i=ar.fromDom(t.getBody()),a=ku.fromRangeStart(t.selection.getRng()),u=wl(t),s=Nl(t);return(e=a,r=i,o=d(Fr,r),ta(ar.fromDom(e.container()),bo,o).filter(MC)).exists(function(){if(VC(i,n,a)){var e=jC(i,u,s,n);return t.selection.setRng(e),!0}return!1})},qC=function(e,t){return function(){return!!e.selection.isCollapsed()&&HC(e,t)}},$C=function(e,r){return G(W(e,function(e){return Xy({shiftKey:!1,altKey:!1,ctrlKey:!1,metaKey:!1,keyCode:0,action:o},e)}),function(e){return t=e,(n=r).keyCode===t.keyCode&&n.shiftKey===t.shiftKey&&n.altKey===t.altKey&&n.ctrlKey===t.ctrlKey&&n.metaKey===t.metaKey?[e]:[];var t,n})},WC=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r=Array.prototype.slice.call(arguments,1);return function(){return e.apply(null,r)}},KC=function(e,t){return X($C(e,t),function(e){return e.action()})},XC=function(i,a){i.on("keydown",function(e){var t,n,r,o;!1===e.isDefaultPrevented()&&(t=i,n=a,r=e,o=or.detect().os,KC([{keyCode:Sv.RIGHT,action:CC(t,!0)},{keyCode:Sv.LEFT,action:CC(t,!1)},{keyCode:Sv.UP,action:xC(t,!1)},{keyCode:Sv.DOWN,action:xC(t,!0)},{keyCode:Sv.RIGHT,action:LC(t,!0)},{keyCode:Sv.LEFT,action:LC(t,!1)},{keyCode:Sv.UP,action:FC(t,!1)},{keyCode:Sv.DOWN,action:FC(t,!0)},{keyCode:Sv.RIGHT,action:Xd.move(t,n,!0)},{keyCode:Sv.LEFT,action:Xd.move(t,n,!1)},{keyCode:Sv.RIGHT,ctrlKey:!o.isOSX(),altKey:o.isOSX(),action:Xd.moveNextWord(t,n)},{keyCode:Sv.LEFT,ctrlKey:!o.isOSX(),altKey:o.isOSX(),action:Xd.movePrevWord(t,n)},{keyCode:Sv.UP,action:qC(t,!1)},{keyCode:Sv.DOWN,action:qC(t,!0)}],r).each(function(e){r.preventDefault()}))})},YC=function(o,i){o.on("keydown",function(e){var t,n,r;!1===e.isDefaultPrevented()&&(t=o,n=i,r=e,KC([{keyCode:Sv.BACKSPACE,action:WC(ad,t,!1)},{keyCode:Sv.DELETE,action:WC(ad,t,!0)},{keyCode:Sv.BACKSPACE,action:WC(lg,t,!1)},{keyCode:Sv.DELETE,action:WC(lg,t,!0)},{keyCode:Sv.BACKSPACE,action:WC(Qd,t,n,!1)},{keyCode:Sv.DELETE,action:WC(Qd,t,n,!0)},{keyCode:Sv.BACKSPACE,action:WC(Dm,t,!1)},{keyCode:Sv.DELETE,action:WC(Dm,t,!0)},{keyCode:Sv.BACKSPACE,action:WC(bf,t,!1)},{keyCode:Sv.DELETE,action:WC(bf,t,!0)},{keyCode:Sv.BACKSPACE,action:WC(pf,t,!1)},{keyCode:Sv.DELETE,action:WC(pf,t,!0)},{keyCode:Sv.BACKSPACE,action:WC(ng,t,!1)},{keyCode:Sv.DELETE,action:WC(ng,t,!0)}],r).each(function(e){r.preventDefault()}))}),o.on("keyup",function(e){var t,n;!1===e.isDefaultPrevented()&&(t=o,n=e,KC([{keyCode:Sv.BACKSPACE,action:WC(ud,t)},{keyCode:Sv.DELETE,action:WC(ud,t)}],n))})},GC=function(e){return _.from(e.dom.getParent(e.selection.getStart(!0),e.dom.isBlock))},JC=function(e,t){var n,r,o,i=t,a=e.dom,u=e.schema.getMoveCaretBeforeOnEnterElements();if(t){if(/^(LI|DT|DD)$/.test(t.nodeName)){var s=function(e){for(;e;){if(1===e.nodeType||3===e.nodeType&&e.data&&/[\r\n\s]/.test(e.data))return e;e=e.nextSibling}}(t.firstChild);s&&/^(UL|OL|DL)$/.test(s.nodeName)&&t.insertBefore(a.doc.createTextNode("\xa0"),t.firstChild)}if(o=a.createRng(),t.normalize(),t.hasChildNodes()){for(n=new mo(t,t);r=n.current();){if(Uo.isText(r)){o.setStart(r,0),o.setEnd(r,0);break}if(u[r.nodeName.toLowerCase()]){o.setStartBefore(r),o.setEndBefore(r);break}i=r,r=n.next()}r||(o.setStart(i,0),o.setEnd(i,0))}else Uo.isBr(t)?t.nextSibling&&a.isBlock(t.nextSibling)?(o.setStartBefore(t),o.setEndBefore(t)):(o.setStartAfter(t),o.setEndAfter(t)):(o.setStart(t,0),o.setEnd(t,0));e.selection.setRng(o),a.remove(void 0),e.selection.scrollIntoView(t)}},QC=function(e,t){var n,r,o=e.getRoot();for(n=t;n!==o&&"false"!==e.getContentEditable(n);)"true"===e.getContentEditable(n)&&(r=n),n=n.parentNode;return n!==o?r:o},ZC=GC,ex=function(e){return GC(e).fold(q(""),function(e){return e.nodeName.toUpperCase()})},tx=function(e){return GC(e).filter(function(e){return Eo(ar.fromDom(e))}).isSome()},nx=function(e,t){return e&&e.parentNode&&e.parentNode.nodeName===t},rx=function(e){return e&&/^(OL|UL|LI)$/.test(e.nodeName)},ox=function(e){var t=e.parentNode;return/^(LI|DT|DD)$/.test(t.nodeName)?t:e},ix=function(e,t,n){for(var r=e[n?"firstChild":"lastChild"];r&&!Uo.isElement(r);)r=r[n?"nextSibling":"previousSibling"];return r===t},ax=function(e,t,n,r,o){var i=e.dom,a=e.selection.getRng();if(n!==e.getBody()){var u;rx(u=n)&&rx(u.parentNode)&&(o="LI");var s,c,l=o?t(o):i.create("BR");if(ix(n,r,!0)&&ix(n,r,!1))nx(n,"LI")?i.insertAfter(l,ox(n)):i.replace(l,n);else if(ix(n,r,!0))nx(n,"LI")?(i.insertAfter(l,ox(n)),l.appendChild(i.doc.createTextNode(" ")),l.appendChild(n)):n.parentNode.insertBefore(l,n);else if(ix(n,r,!1))i.insertAfter(l,ox(n));else{n=ox(n);var f=a.cloneRange();f.setStartAfter(r),f.setEndAfter(n);var d=f.extractContents();"LI"===o&&(c="LI",(s=d).firstChild&&s.firstChild.nodeName===c)?(l=d.firstChild,i.insertAfter(d,n)):(i.insertAfter(d,n),i.insertAfter(l,n))}i.remove(r),JC(e,l)}},ux=function(e){e.innerHTML='<br data-mce-bogus="1">'},sx=function(e,t){return e.nodeName===t||e.previousSibling&&e.previousSibling.nodeName===t},cx=function(e,t){return t&&e.isBlock(t)&&!/^(TD|TH|CAPTION|FORM)$/.test(t.nodeName)&&!/^(fixed|absolute)/i.test(t.style.position)&&"true"!==e.getContentEditable(t)},lx=function(e,t,n){return!1===Uo.isText(t)?n:e?1===n&&t.data.charAt(n-1)===Ca?0:n:n===t.data.length-1&&t.data.charAt(n)===Ca?t.data.length:n},fx=function(e,t){var n,r,o=e.getRoot();for(n=t;n!==o&&"false"!==e.getContentEditable(n);)"true"===e.getContentEditable(n)&&(r=n),n=n.parentNode;return n!==o?r:o},dx=function(o,i,e){_.from(e.style).map(o.dom.parseStyle).each(function(e){var t=function(e){var t={},n=e.dom();if(br(n))for(var r=0;r<n.style.length;r++){var o=n.style.item(r);t[o]=n.style[o]}return t}(ar.fromDom(i)),n=da(da({},t),e);o.dom.setStyles(i,n)});var t=_.from(e["class"]).map(function(e){return e.split(/\s+/)}),n=_.from(i.className).map(function(e){return U(e.split(/\s+/),function(e){return""!==e})});nu(t,n,function(t,e){var n=U(e,function(e){return!F(t,e)}),r=function(){for(var e=0,t=0,n=arguments.length;t<n;t++)e+=arguments[t].length;var r=Array(e),o=0;for(t=0;t<n;t++)for(var i=arguments[t],a=0,u=i.length;a<u;a++,o++)r[o]=i[a];return r}(t,n);o.dom.setAttrib(i,"class",r.join(" "))});var r=["style","class"],a=yr(e,function(e,t){return!F(r,t)}).t;o.dom.setAttribs(i,a)},mx=function(e,t){var n=wl(e);if(n&&n.toLowerCase()===t.tagName.toLowerCase()){var r=Nl(e);dx(e,t,r)}},gx=function(a,e){var t,u,s,i,c,n,r,o,l,f,d,m,g,p,h,v,y,b,C,x=a.dom,w=a.schema,N=w.getNonEmptyElements(),E=a.selection.getRng(),S=function(e){var t,n,r,o=s,i=w.getTextInlineElements();if(r=t=e||"TABLE"===f||"HR"===f?x.create(e||m):c.cloneNode(!1),!1===Tl(a))x.setAttrib(t,"style",null),x.setAttrib(t,"class",null);else do{if(i[o.nodeName]){if(Gu(o)||vc(o))continue;n=o.cloneNode(!1),x.setAttrib(n,"id",""),t.hasChildNodes()?n.appendChild(t.firstChild):r=n,t.appendChild(n)}}while((o=o.parentNode)&&o!==u);return mx(a,t),ux(r),t},T=function(e){var t,n,r,o;if(o=lx(e,s,i),Uo.isText(s)&&(e?0<o:o<s.nodeValue.length))return!1;if(s.parentNode===c&&g&&!e)return!0;if(e&&Uo.isElement(s)&&s===c.firstChild)return!0;if(sx(s,"TABLE")||sx(s,"HR"))return g&&!e||!g&&e;for(t=new mo(s,c),Uo.isText(s)&&(e&&0===o?t.prev():e||o!==s.nodeValue.length||t.next());n=t.current();){if(Uo.isElement(n)){if(!n.getAttribute("data-mce-bogus")&&(r=n.nodeName.toLowerCase(),N[r]&&"br"!==r))return!1}else if(Uo.isText(n)&&!/^[ \t\r\n]*$/.test(n.nodeValue))return!1;e?t.prev():t.next()}return!0},k=function(){r=/^(H[1-6]|PRE|FIGURE)$/.test(f)&&"HGROUP"!==d?S(m):S(),kl(a)&&cx(x,l)&&x.isEmpty(c)?r=x.split(l,c):x.insertAfter(r,c),JC(a,r)};kg(x,E).each(function(e){E.setStart(e.startContainer,e.startOffset),E.setEnd(e.endContainer,e.endOffset)}),s=E.startContainer,i=E.startOffset,m=wl(a),n=e.shiftKey,Uo.isElement(s)&&s.hasChildNodes()&&(g=i>s.childNodes.length-1,s=s.childNodes[Math.min(i,s.childNodes.length-1)]||s,i=g&&Uo.isText(s)?s.nodeValue.length:0),(u=fx(x,s))&&((m&&!n||!m&&n)&&(s=function(e,t,n,r,o){var i,a,u,s,c,l,f,d=t||"P",m=e.dom,g=fx(m,r);if(!(a=m.getParent(r,m.isBlock))||!cx(m,a)){if(l=(a=a||g)===e.getBody()||(f=a)&&/^(TD|TH|CAPTION)$/.test(f.nodeName)?a.nodeName.toLowerCase():a.parentNode.nodeName.toLowerCase(),!a.hasChildNodes())return i=m.create(d),mx(e,i),a.appendChild(i),n.setStart(i,0),n.setEnd(i,0),i;for(s=r;s.parentNode!==a;)s=s.parentNode;for(;s&&!m.isBlock(s);)s=(u=s).previousSibling;if(u&&e.schema.isValidChild(l,d.toLowerCase())){for(i=m.create(d),mx(e,i),u.parentNode.insertBefore(i,u),s=u;s&&!m.isBlock(s);)c=s.nextSibling,i.appendChild(s),s=c;n.setStart(r,o),n.setEnd(r,o)}}return r}(a,m,E,s,i)),c=x.getParent(s,x.isBlock),l=c?x.getParent(c.parentNode,x.isBlock):null,f=c?c.nodeName.toUpperCase():"","LI"!==(d=l?l.nodeName.toUpperCase():"")||e.ctrlKey||(l=(c=l).parentNode,f=d),/^(LI|DT|DD)$/.test(f)&&x.isEmpty(c)?ax(a,S,l,c,m):m&&c===a.getBody()||(m=m||"P",Ea(c)?(r=Ba(c),x.isEmpty(c)&&ux(c),mx(a,r),JC(a,r)):T()?k():T(!0)?(r=c.parentNode.insertBefore(S(),c),JC(a,sx(c,"HR")?r:c)):((t=(b=E,C=b.cloneRange(),C.setStart(b.startContainer,lx(!0,b.startContainer,b.startOffset)),C.setEnd(b.endContainer,lx(!1,b.endContainer,b.endOffset)),C).cloneRange()).setEndAfter(c),o=t.extractContents(),y=o,z(Gi(ar.fromDom(y),mr),function(e){var t=e.dom();t.nodeValue=xa(t.nodeValue)}),function(e){for(;Uo.isText(e)&&(e.nodeValue=e.nodeValue.replace(/^[\r\n]+/,"")),e=e.firstChild;);}(o),r=o.firstChild,x.insertAfter(o,c),function(e,t,n){var r,o=n,i=[];if(o){for(;o=o.firstChild;){if(e.isBlock(o))return;Uo.isElement(o)&&!t[o.nodeName.toLowerCase()]&&i.push(o)}for(r=i.length;r--;)!(o=i[r]).hasChildNodes()||o.firstChild===o.lastChild&&""===o.firstChild.nodeValue?e.remove(o):(a=e,(u=o)&&"A"===u.nodeName&&a.isEmpty(u)&&e.remove(o));var a,u}}(x,N,r),p=x,(h=c).normalize(),(v=h.lastChild)&&!/^(left|right)$/gi.test(p.getStyle(v,"float",!0))||p.add(h,"br"),x.isEmpty(c)&&ux(c),r.normalize(),x.isEmpty(r)?(x.remove(r),k()):(mx(a,r),JC(a,r))),x.setAttrib(r,"id",""),a.fire("NewBlock",{newBlock:r})))},px=function(e,t){return ZC(e).filter(function(e){return 0<t.length&&Ir(ar.fromDom(e),t)}).isSome()},hx=function(e){return px(e,El(e))},vx=function(e){return px(e,Sl(e))},yx=Cf([{br:[]},{block:[]},{none:[]}]),bx=function(e,t){return vx(e)},Cx=function(n){return function(e,t){return""===wl(e)===n}},xx=function(n){return function(e,t){return tx(e)===n}},wx=function(n,r){return function(e,t){return ex(e)===n.toUpperCase()===r}},Nx=function(e){return wx("pre",e)},Ex=function(n){return function(e,t){return xl(e)===n}},Sx=function(e,t){return hx(e)},Tx=function(e,t){return t},kx=function(e){var t=wl(e),n=QC(e.dom,e.selection.getStart());return n&&e.schema.isValidChild(n.nodeName,t||"P")},_x=function(e,t){return function(n,r){return j(e,function(e,t){return e&&t(n,r)},!0)?_.some(t):_.none()}},Ax=function(e,t){return yd([_x([bx],yx.none()),_x([wx("summary",!0)],yx.br()),_x([Nx(!0),Ex(!1),Tx],yx.br()),_x([Nx(!0),Ex(!1)],yx.block()),_x([Nx(!0),Ex(!0),Tx],yx.block()),_x([Nx(!0),Ex(!0)],yx.br()),_x([xx(!0),Tx],yx.br()),_x([xx(!0)],yx.block()),_x([Cx(!0),Tx,kx],yx.block()),_x([Cx(!0)],yx.br()),_x([Sx],yx.br()),_x([Cx(!1),Tx],yx.br()),_x([kx],yx.block())],[e,t.shiftKey]).getOr(yx.none())},Rx=function(e,t){Ax(e,t).fold(function(){Fg(e,t)},function(){gx(e,t)},o)},Dx=function(o){o.on("keydown",function(e){var t,n,r;e.keyCode===Sv.ENTER&&(t=o,(n=e).isDefaultPrevented()||(n.preventDefault(),(r=t.undoManager).typing&&(r.typing=!1,r.add()),t.undoManager.transact(function(){!1===t.selection.isCollapsed()&&t.execCommand("Delete"),Rx(t,n)})))})},Ox=function(n,r){var e=r.container(),t=r.offset();return Uo.isText(e)?(e.insertData(t,n),_.some(Eu(e,t+n.length))):Ps(r).map(function(e){var t=ar.fromText(n);return r.isAtEnd()?Pi(e,t):Bi(e,t),Eu(t.dom(),n.length)})},Bx=d(Ox,"\xa0"),Px=d(Ox," "),Ix=function(e,t,n){return uc.navigateIgnore(e,t,n,Bf)},Lx=function(e,t){return X(af(ar.fromDom(t.container()),e),bo)},Fx=function(e,n,r){return Ix(e,n.dom(),r).forall(function(t){return Lx(n,r).fold(function(){return!1===Ss(t,r,n.dom())},function(e){return!1===Ss(t,r,n.dom())&&Mr(e,ar.fromDom(t.container()))})})},Mx=function(t,n,r){return Lx(n,r).fold(function(){return Ix(t,n.dom(),r).forall(function(e){return!1===Ss(e,r,n.dom())})},function(e){return Ix(t,e.dom(),r).isNone()})},zx=d(Mx,!1),Ux=d(Mx,!0),jx=d(Fx,!1),Vx=d(Fx,!0),Hx=function(e){return Eu.isTextPosition(e)&&!e.isAtStart()&&!e.isAtEnd()},qx=function(e,t){var n=U(af(ar.fromDom(t.container()),e),bo);return Z(n).getOr(e)},$x=function(e,t){return Hx(t)?Of(t):Of(t)||uc.prevPosition(qx(e,t).dom(),t).exists(Of)},Wx=function(e,t){return Hx(t)?Df(t):Df(t)||uc.nextPosition(qx(e,t).dom(),t).exists(Df)},Kx=function(e){return Ps(e).bind(function(e){return ta(e,dr)}).exists(function(e){return t=Tr(e,"white-space"),F(["pre","pre-wrap"],t);var t})},Xx=function(e,t){return o=e,i=t,uc.prevPosition(o.dom(),i).isNone()||(n=e,r=t,uc.nextPosition(n.dom(),r).isNone())||zx(e,t)||Ux(e,t)||Ef(e,t)||Nf(e,t);var n,r,o,i},Yx=function(e,t){var n,r,o,i=(r=(n=t).container(),o=n.offset(),Uo.isText(r)&&o<r.data.length?Eu(r,o+1):n);return!Kx(i)&&(Ux(e,i)||Vx(e,i)||Nf(e,i)||Wx(e,i))},Gx=function(e,t){return n=e,!Kx(r=t)&&(zx(n,r)||jx(n,r)||Ef(n,r)||$x(n,r))||Yx(e,t);var n,r},Jx=function(e,t){return kf(e.charAt(t))},Qx=function(e){var t=e.container();return Uo.isText(t)&&Yn(t.data,"\xa0")},Zx=function(e){var n,t=e.data,r=(n=t.split(""),W(n,function(e,t){return kf(e)&&0<t&&t<n.length-1&&Af(n[t-1])&&Af(n[t+1])?" ":e}).join(""));return r!==t&&(e.data=r,!0)},ew=function(l,e){return _.some(e).filter(Qx).bind(function(e){var t,n,r,o,i,a,u,s,c=e.container();return i=l,u=(a=c).data,s=Eu(a,0),Jx(u,0)&&!Gx(i,s)&&(a.data=" "+u.slice(1),1)||Zx(c)||(t=l,r=(n=c).data,o=Eu(n,r.length-1),Jx(r,r.length-1)&&!Gx(t,o)&&(n.data=r.slice(0,-1)+" ",1))?_.some(e):_.none()})},tw=function(t){var e=ar.fromDom(t.getBody());t.selection.isCollapsed()&&ew(e,Eu.fromRangeStart(t.selection.getRng())).each(function(e){t.selection.setRng(e.toRange())})},nw=function(r,o){return function(e){return t=r,!Kx(n=e)&&(Xx(t,n)||$x(t,n)||Wx(t,n))?Bx(o):Px(o);var t,n}},rw=function(e){var t,n,r=ku.fromRangeStart(e.selection.getRng()),o=ar.fromDom(e.getBody());if(e.selection.isCollapsed()){var i=d(jl.isInlineTarget,e),a=ku.fromRangeStart(e.selection.getRng());return Ld(i,e.getBody(),a).bind((n=o,function(e){return e.fold(function(e){return uc.prevPosition(n.dom(),ku.before(e))},function(e){return uc.firstPositionIn(e)},function(e){return uc.lastPositionIn(e)},function(e){return uc.nextPosition(n.dom(),ku.after(e))})})).bind(nw(o,r)).exists((t=e,function(e){return t.selection.setRng(e.toRange()),t.nodeChanged(),!0}))}return!1},ow=function(r){r.on("keydown",function(e){var t,n;!1===e.isDefaultPrevented()&&(t=r,n=e,KC([{keyCode:Sv.SPACEBAR,action:WC(rw,t)}],n).each(function(e){n.preventDefault()}))})},iw=function(e,t){var n;t.hasAttribute("data-mce-caret")&&(Ba(t),(n=e).selection.setRng(n.selection.getRng()),e.selection.scrollIntoView(t))},aw=function(e,t){var n,r=(n=e,ra(ar.fromDom(n.getBody()),"*[data-mce-caret]").fold(q(null),function(e){return e.dom()}));if(r)return"compositionstart"===t.type?(t.preventDefault(),t.stopPropagation(),void iw(e,r)):void(ka(r)&&(iw(e,r),e.undoManager.add()))},uw=function(e){e.on("keyup compositionstart",d(aw,e))},sw=or.detect().browser,cw=function(t){var e,n;e=t,n=ji(function(){e.composing||tw(e)},0),sw.isIE()&&(e.on("keypress",function(e){n.throttle()}),e.on("remove",function(e){n.cancel()})),t.on("input",function(e){!1===e.isComposing&&tw(t)})},lw=function(r){r.on("keydown",function(e){var t,n;!1===e.isDefaultPrevented()&&(t=r,n=e,KC([{keyCode:Sv.END,action:wC(t,!0)},{keyCode:Sv.HOME,action:wC(t,!1)}],n).each(function(e){n.preventDefault()}))})},fw=function(e){var t=Xd.setupSelectedState(e);uw(e),XC(e,t),YC(e,t),Dx(e),ow(e),cw(e),lw(e)};function dw(u){var s,n,r,o=Xt.each,c=Sv.BACKSPACE,l=Sv.DELETE,f=u.dom,d=u.selection,e=u.settings,t=u.parser,i=fe.gecko,a=fe.ie,m=fe.webkit,g="data:text/mce-internal,",p=a?"Text":"URL",h=function(e,t){try{u.getDoc().execCommand(e,!1,t)}catch(n){}},v=function(e){return e.isDefaultPrevented()},y=function(){u.shortcuts.add("meta+a",null,"SelectAll")},b=function(){u.on("keydown",function(e){if(!v(e)&&e.keyCode===c&&d.isCollapsed()&&0===d.getRng().startOffset){var t=d.getNode().previousSibling;if(t&&t.nodeName&&"table"===t.nodeName.toLowerCase())return e.preventDefault(),!1}})},C=function(){u.inline||(u.contentStyles.push("body {min-height: 150px}"),u.on("click",function(e){var t;if("HTML"===e.target.nodeName){if(11<fe.ie)return void u.getBody().focus();t=u.selection.getRng(),u.getBody().focus(),u.selection.setRng(t),u.selection.normalize(),u.nodeChanged()}}))};return u.on("keydown",function(e){var t,n,r,o,i;if(!v(e)&&e.keyCode===Sv.BACKSPACE&&(n=(t=d.getRng()).startContainer,r=t.startOffset,o=f.getRoot(),i=n,t.collapsed&&0===r)){for(;i&&i.parentNode&&i.parentNode.firstChild===i&&i.parentNode!==o;)i=i.parentNode;"BLOCKQUOTE"===i.tagName&&(u.formatter.toggle("blockquote",null,i),(t=f.createRng()).setStart(n,0),t.setEnd(n,0),d.setRng(t))}}),s=function(e){var t=f.create("body"),n=e.cloneContents();return t.appendChild(n),d.serializer.serialize(t,{format:"html"})},u.on("keydown",function(e){var t,n,r,o,i,a=e.keyCode;if(!v(e)&&(a===l||a===c)){if(t=u.selection.isCollapsed(),n=u.getBody(),t&&!f.isEmpty(n))return;if(!t&&(r=u.selection.getRng(),o=s(r),(i=f.createRng()).selectNode(u.getBody()),o!==s(i)))return;e.preventDefault(),u.setContent(""),n.firstChild&&f.isBlock(n.firstChild)?u.selection.setCursorLocation(n.firstChild,0):u.selection.setCursorLocation(n,0),u.nodeChanged()}}),fe.windowsPhone||u.on("keyup focusin mouseup",function(e){Sv.modifierPressed(e)||d.normalize()},!0),m&&(u.settings.content_editable||f.bind(u.getDoc(),"mousedown mouseup",function(e){var t;if(e.target===u.getDoc().documentElement)if(t=d.getRng(),u.getBody().focus(),"mousedown"===e.type){if(Ta(t.startContainer))return;d.placeCaretAt(e.clientX,e.clientY)}else d.setRng(t)}),u.on("click",function(e){var t=e.target;/^(IMG|HR)$/.test(t.nodeName)&&"false"!==f.getContentEditableParent(t)&&(e.preventDefault(),u.selection.select(t),u.nodeChanged()),"A"===t.nodeName&&f.hasClass(t,"mce-item-anchor")&&(e.preventDefault(),d.select(t))}),e.forced_root_block&&u.on("init",function(){h("DefaultParagraphSeparator",e.forced_root_block)}),u.on("init",function(){u.dom.bind(u.getBody(),"submit",function(e){e.preventDefault()})}),b(),t.addNodeFilter("br",function(e){for(var t=e.length;t--;)"Apple-interchange-newline"===e[t].attr("class")&&e[t].remove()}),fe.iOS?(u.inline||u.on("keydown",function(){V.document.activeElement===V.document.body&&u.getWin().focus()}),C(),u.on("click",function(e){var t=e.target;do{if("A"===t.tagName)return void e.preventDefault()}while(t=t.parentNode)}),u.contentStyles.push(".mce-content-body {-webkit-touch-callout: none}")):y()),11<=fe.ie&&(C(),b()),fe.ie&&(y(),h("AutoUrlDetect",!1),u.on("dragstart",function(e){var t,n,r;(t=e).dataTransfer&&(u.selection.isCollapsed()&&"IMG"===t.target.tagName&&d.select(t.target),0<(n=u.selection.getContent()).length&&(r=g+escape(u.id)+","+escape(n),t.dataTransfer.setData(p,r)))}),u.on("drop",function(e){if(!v(e)){var t=(i=e).dataTransfer&&(a=i.dataTransfer.getData(p))&&0<=a.indexOf(g)?(a=a.substr(g.length).split(","),{id:unescape(a[0]),html:unescape(a[1])}):null;if(t&&t.id!==u.id){e.preventDefault();var n=Db(e.x,e.y,u.getDoc());d.setRng(n),r=t.html,o=!0,u.queryCommandSupported("mceInsertClipboardContent")?u.execCommand("mceInsertClipboardContent",!1,{content:r,internal:o}):u.execCommand("mceInsertContent",!1,r)}}var r,o,i,a})),i&&(u.on("keydown",function(e){if(!v(e)&&e.keyCode===c){if(!u.getBody().getElementsByTagName("hr").length)return;if(d.isCollapsed()&&0===d.getRng().startOffset){var t=d.getNode(),n=t.previousSibling;if("HR"===t.nodeName)return f.remove(t),void e.preventDefault();n&&n.nodeName&&"hr"===n.nodeName.toLowerCase()&&(f.remove(n),e.preventDefault())}}}),V.Range.prototype.getClientRects||u.on("mousedown",function(e){if(!v(e)&&"HTML"===e.target.nodeName){var t=u.getBody();t.blur(),he.setEditorTimeout(u,function(){t.focus()})}}),n=function(){var e=f.getAttribs(d.getStart().cloneNode(!1));return function(){var t=d.getStart();t!==u.getBody()&&(f.setAttrib(t,"style",null),o(e,function(e){t.setAttributeNode(e.cloneNode(!0))}))}},r=function(){return!d.isCollapsed()&&f.getParent(d.getStart(),f.isBlock)!==f.getParent(d.getEnd(),f.isBlock)},u.on("keypress",function(e){var t;if(!v(e)&&(8===e.keyCode||46===e.keyCode)&&r())return t=n(),u.getDoc().execCommand("delete",!1,null),t(),e.preventDefault(),!1}),f.bind(u.getDoc(),"cut",function(e){var t;!v(e)&&r()&&(t=n(),he.setEditorTimeout(u,function(){t()}))}),e.readonly||u.on("BeforeExecCommand MouseDown",function(){h("StyleWithCSS",!1),h("enableInlineTableEditing",!1),e.object_resizing||h("enableObjectResizing",!1)}),u.on("SetContent ExecCommand",function(e){"setcontent"!==e.type&&"mceInsertLink"!==e.command||o(f.select("a"),function(e){var t=e.parentNode,n=f.getRoot();if(t.lastChild===e){for(;t&&!f.isBlock(t);){if(t.parentNode.lastChild!==t||t===n)return;t=t.parentNode}f.add(t,"br",{"data-mce-bogus":1})}})}),u.contentStyles.push("img:-moz-broken {-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}"),fe.mac&&u.on("keydown",function(e){!Sv.metaKeyPressed(e)||e.shiftKey||37!==e.keyCode&&39!==e.keyCode||(e.preventDefault(),u.selection.getSel().modify("move",37===e.keyCode?"backward":"forward","lineboundary"))}),b()),{refreshContentEditable:function(){},isHidden:function(){var e;return!i||u.removed?0:!(e=u.selection.getSel())||!e.rangeCount||0===e.rangeCount}}}var mw=function(e){return Uo.isElement(e)&&wo(ar.fromDom(e))},gw=function(t){t.on("click",function(e){3<=e.detail&&function(e){var t=e.selection.getRng(),n=Eu.fromRangeStart(t),r=Eu.fromRangeEnd(t);if(Eu.isElementPosition(n)){var o=n.container();mw(o)&&uc.firstPositionIn(o).each(function(e){return t.setStart(e.container(),e.offset())})}Eu.isElementPosition(r)&&(o=n.container(),mw(o)&&uc.lastPositionIn(o).each(function(e){return t.setEnd(e.container(),e.offset())})),e.selection.setRng(sl(t))}(t)})},pw=function(e){var t,n;(t=e).on("click",function(e){t.dom.getParent(e.target,"details")&&e.preventDefault()}),(n=e).parser.addNodeFilter("details",function(e){z(e,function(e){e.attr("data-mce-open",e.attr("open")),e.attr("open","open")})}),n.serializer.addNodeFilter("details",function(e){z(e,function(e){var t=e.attr("data-mce-open");e.attr("open",S(t)?t:null),e.attr("data-mce-open",null)})})},hw=Ei.DOM,vw=function(e){var t;e.bindPendingEventDelegates(),e.initialized=!0,e.fire("init"),e.focus(!0),e.nodeChanged({initial:!0}),e.execCallback("init_instance_callback",e),(t=e).settings.auto_focus&&he.setEditorTimeout(t,function(){var e;(e=!0===t.settings.auto_focus?t:t.editorManager.get(t.settings.auto_focus)).destroyed||e.focus()},100)},yw=function(t,e){var n,r,u,o,i,a,s,c,l,f=t.settings,d=t.getElement(),m=t.getDoc();f.inline||(t.getElement().style.visibility=t.orgVisibility),e||f.content_editable||(m.open(),m.write(t.iframeHTML),m.close()),f.content_editable&&(t.on("remove",function(){var e=this.getBody();hw.removeClass(e,"mce-content-body"),hw.removeClass(e,"mce-edit-focus"),hw.setAttrib(e,"contentEditable",null)}),hw.addClass(d,"mce-content-body"),t.contentDocument=m=f.content_document||V.document,t.contentWindow=f.content_window||V.window,t.bodyElement=d,f.content_document=f.content_window=null,f.root_name=d.nodeName.toLowerCase()),(n=t.getBody()).disabled=!0,t.readonly=f.readonly,t.readonly||(t.inline&&"static"===hw.getStyle(n,"position",!0)&&(n.style.position="relative"),n.contentEditable=t.getParam("content_editable_state",!0)),n.disabled=!1,t.editorUpload=Uh(t),t.schema=fi(f),t.dom=Ei(m,{keep_values:!0,url_converter:t.convertURL,url_converter_scope:t,hex_colors:f.force_hex_style_colors,class_filter:f.class_filter,update_styles:!0,root_element:t.inline?t.getBody():null,collect:f.content_editable,schema:t.schema,contentCssCors:Ml(t),onSetAttrib:function(e){t.fire("SetAttrib",e)}}),t.parser=((o=gb((u=t).settings,u.schema)).addAttributeFilter("src,href,style,tabindex",function(e,t){for(var n,r,o,i=e.length,a=u.dom;i--;)if(r=(n=e[i]).attr(t),o="data-mce-"+t,!n.attributes.map[o]){if(0===r.indexOf("data:")||0===r.indexOf("blob:"))continue;"style"===t?((r=a.serializeStyle(a.parseStyle(r),n.name)).length||(r=null),n.attr(o,r),n.attr(t,r)):"tabindex"===t?(n.attr(o,r),n.attr(t,null)):n.attr(o,u.convertURL(r,t,n.name))}}),o.addNodeFilter("script",function(e){for(var t,n,r=e.length;r--;)0!==(n=(t=e[r]).attr("type")||"no/type").indexOf("mce-")&&t.attr("type","mce-"+n)}),o.addNodeFilter("#cdata",function(e){for(var t,n=e.length;n--;)(t=e[n]).type=8,t.name="#comment",t.value="[CDATA["+t.value+"]]"}),o.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(e){for(var t,n=e.length,r=u.schema.getNonEmptyElements();n--;)(t=e[n]).isEmpty(r)&&0===t.getAll("br").length&&(t.append(new rb("br",1)).shortEnded=!0)}),o),t.serializer=bb(f,t),t.selection=iC(t.dom,t.getWin(),t.serializer,t),t.annotator=Vc(t),t.formatter=$y(t),t.undoManager=Qv(t),t._nodeChangeDispatcher=new Gh(t),t._selectionOverrides=Av(t),pw(t),gw(t),fw(t),qh(t),t.fire("PreInit"),f.browser_spellcheck||f.gecko_spellcheck||(m.body.spellcheck=!1,hw.setAttrib(n,"spellcheck","false")),t.quirks=dw(t),t.fire("PostRender"),f.directionality&&(n.dir=f.directionality),f.nowrap&&(n.style.whiteSpace="nowrap"),f.protect&&t.on("BeforeSetContent",function(t){Xt.each(f.protect,function(e){t.content=t.content.replace(e,function(e){return"\x3c!--mce:protected "+escape(e)+"--\x3e"})})}),t.on("SetContent",function(){t.addVisual(t.getBody())}),t.load({initial:!0,format:"html"}),t.startContent=t.getContent({format:"raw"}),t.on("compositionstart compositionend",function(e){t.composing="compositionstart"===e.type}),0<t.contentStyles.length&&(r="",Xt.each(t.contentStyles,function(e){r+=e+"\r\n"}),t.dom.addStyle(r)),(i=t,i.inline?hw.styleSheetLoader:i.dom.styleSheetLoader).loadAll(t.contentCSS,function(e){vw(t)},function(e){vw(t)}),f.content_style&&(a=t,s=f.content_style,c=ar.fromDom(a.getDoc().head),l=ar.fromTag("style"),xr(l,"type","text/css"),Li(l,ar.fromText(s)),Li(c,l))},bw=Ei.DOM,Cw=function(e,t){var n,r,o,i,a,u,s,c=e.editorManager.translate("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),l=(n=e.id,r=c,o=t.height,i=pl(e),s=ar.fromTag("iframe"),wr(s,i),wr(s,{id:n+"_ifr",frameBorder:"0",allowTransparency:"true",title:r}),Sr(s,{width:"100%",height:(a=o,u="number"==typeof a?a+"px":a,u||""),display:"block"}),s).dom();l.onload=function(){l.onload=null,e.fire("load")};var f,d,m,g,p=function(e,t){if(V.document.domain!==V.window.location.hostname&&fe.ie&&fe.ie<12){var n=zh.uuid("mce");e[n]=function(){yw(e)};var r='javascript:(function(){document.open();document.domain="'+V.document.domain+'";var ed = window.parent.tinymce.get("'+e.id+'");document.write(ed.iframeHTML);document.close();ed.'+n+"(true);})()";return bw.setAttrib(t,"src",r),!0}return!1}(e,l);return e.contentAreaContainer=t.iframeContainer,e.iframeElement=l,e.iframeHTML=(g=hl(f=e)+"<html><head>",vl(f)!==f.documentBaseUrl&&(g+='<base href="'+f.documentBaseURI.getURI()+'" />'),g+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />',d=yl(f),m=bl(f),Cl(f)&&(g+='<meta http-equiv="Content-Security-Policy" content="'+Cl(f)+'" />'),g+='</head><body id="'+d+'" class="mce-content-body '+m+'" data-id="'+f.id+'"><br></body></html>'),bw.add(t.iframeContainer,l),p},xw=function(e,t){var n=Cw(e,t);t.editorContainer&&(bw.get(t.editorContainer).style.display=e.orgDisplay,e.hidden=bw.isHidden(t.editorContainer)),e.getElement().style.display="none",bw.setAttrib(e.id,"aria-hidden","true"),n||yw(e)},ww=Ei.DOM,Nw=function(t,n,e){var r=Eh.get(e),o=Eh.urls[e]||t.documentBaseUrl.replace(/\/$/,"");if(e=Xt.trim(e),r&&-1===Xt.inArray(n,e)){if(Xt.each(Eh.dependencies(e),function(e){Nw(t,n,e)}),t.plugins[e])return;try{var i=new r(t,o,t.$);(t.plugins[e]=i).init&&(i.init(t,o),n.push(e))}catch(oE){Nh.pluginInitError(t,e,oE)}}},Ew=function(e){return e.replace(/^\-/,"")},Sw=function(e){return{editorContainer:e,iframeContainer:e}},Tw=function(e){var t,n,r=e.getElement();return e.inline?Sw(null):(t=r,n=ww.create("div"),ww.insertAfter(n,t),Sw(n))},kw=function(e){var t,n,r,o,i,a,u,s,c,l,f,d=e.settings,m=e.getElement();return e.orgDisplay=m.style.display,S(d.theme)?(l=(o=e).settings,f=o.getElement(),i=l.width||ww.getStyle(f,"width")||"100%",a=l.height||ww.getStyle(f,"height")||f.offsetHeight,u=l.min_height||100,(s=/^[0-9\.]+(|px)$/i).test(""+i)&&(i=Math.max(parseInt(i,10),100)),s.test(""+a)&&(a=Math.max(parseInt(a,10),u)),c=o.theme.renderUI({targetNode:f,width:i,height:a,deltaWidth:l.delta_width,deltaHeight:l.delta_height}),l.content_editable||(a=(c.iframeHeight||a)+("number"==typeof a?c.deltaHeight||0:""))<u&&(a=u),c.height=a,c):D(d.theme)?(r=(t=e).getElement(),(n=t.settings.theme(t,r)).editorContainer.nodeType&&(n.editorContainer.id=n.editorContainer.id||t.id+"_parent"),n.iframeContainer&&n.iframeContainer.nodeType&&(n.iframeContainer.id=n.iframeContainer.id||t.id+"_iframecontainer"),n.height=n.iframeHeight?n.iframeHeight:r.offsetHeight,n):Tw(e)},_w=function(t){var e,n,r,o,i,a,u=t.settings,s=t.getElement();return t.rtl=u.rtl_ui||t.editorManager.i18n.rtl,t.editorManager.i18n.setCode(u.language),u.aria_label=u.aria_label||ww.getAttrib(s,"aria-label",t.getLang("aria.rich_text_area")),t.fire("ScriptsLoaded"),o=(n=t).settings.theme,S(o)?(n.settings.theme=Ew(o),r=Sh.get(o),n.theme=new r(n,Sh.urls[o]),n.theme.init&&n.theme.init(n,Sh.urls[o]||n.documentBaseUrl.replace(/\/$/,""),n.$)):n.theme={},i=t,a=[],Xt.each(i.settings.plugins.split(/[ ,]/),function(e){Nw(i,a,Ew(e))}),e=kw(t),t.editorContainer=e.editorContainer?e.editorContainer:null,u.content_css&&Xt.each(Xt.explode(u.content_css),function(e){t.contentCSS.push(t.documentBaseURI.toAbsolute(e))}),u.content_editable?yw(t):xw(t,e)},Aw=Ei.DOM,Rw=function(e){return"-"===e.charAt(0)},Dw=function(i,a){var u=Ai.ScriptLoader;!function(e,t,n,r){var o=t.settings,i=o.theme;if(S(i)){if(!Rw(i)&&!Sh.urls.hasOwnProperty(i)){var a=o.theme_url;a?Sh.load(i,t.documentBaseURI.toAbsolute(a)):Sh.load(i,"themes/"+i+"/theme"+n+".js")}e.loadQueue(function(){Sh.waitFor(i,r)})}else r()}(u,i,a,function(){var e,t,n,r,o;e=u,(n=(t=i).settings).language&&"en"!==n.language&&!n.language_url&&(n.language_url=t.editorManager.baseURL+"/langs/"+n.language+".js"),n.language_url&&!t.editorManager.i18n.data[n.language]&&e.add(n.language_url),r=i.settings,o=a,Xt.isArray(r.plugins)&&(r.plugins=r.plugins.join(" ")),Xt.each(r.external_plugins,function(e,t){Eh.load(t,e),r.plugins+=" "+t}),Xt.each(r.plugins.split(/[ ,]/),function(e){if((e=Xt.trim(e))&&!Eh.urls[e])if(Rw(e)){e=e.substr(1,e.length);var t=Eh.dependencies(e);Xt.each(t,function(e){var t={prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"};e=Eh.createUrl(t,e),Eh.load(e.resource,e)})}else Eh.load(e,{prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"})}),u.loadQueue(function(){i.removed||_w(i)},i,function(e){Nh.pluginLoadError(i,e[0]),i.removed||_w(i)})})},Ow=function(t){var e=t.settings,n=t.id,r=function(){Aw.unbind(V.window,"ready",r),t.render()};if(Se.Event.domLoaded){if(t.getElement()&&fe.contentEditable){e.inline?t.inline=!0:(t.orgVisibility=t.getElement().style.visibility,t.getElement().style.visibility="hidden");var o=t.getElement().form||Aw.getParent(n,"form");o&&(t.formElement=o,e.hidden_input&&!/TEXTAREA|INPUT/i.test(t.getElement().nodeName)&&(Aw.insertAfter(Aw.create("input",{type:"hidden",name:n}),n),t.hasHiddenInput=!0),t.formEventDelegate=function(e){t.fire(e.type,e)},Aw.bind(o,"submit reset",t.formEventDelegate),t.on("reset",function(){t.setContent(t.startContent,{format:"raw"})}),!e.submit_patch||o.submit.nodeType||o.submit.length||o._mceOldSubmit||(o._mceOldSubmit=o.submit,o.submit=function(){return t.editorManager.triggerSave(),t.setDirty(!1),o._mceOldSubmit(o)})),t.windowManager=gh(t),t.notificationManager=mh(t),"xml"===e.encoding&&t.on("GetContent",function(e){e.save&&(e.content=Aw.encode(e.content))}),e.add_form_submit_trigger&&t.on("submit",function(){t.initialized&&t.save()}),e.add_unload_trigger&&(t._beforeUnload=function(){!t.initialized||t.destroyed||t.isHidden()||t.save({format:"raw",no_events:!0,set_dirty:!1})},t.editorManager.on("BeforeUnload",t._beforeUnload)),t.editorManager.add(t),Dw(t,t.suffix)}}else Aw.bind(V.window,"ready",r)},Bw=function(e,t,n){var r=e.sidebars?e.sidebars:[];r.push({name:t,settings:n}),e.sidebars=r},Pw=Xt.each,Iw=Xt.trim,Lw="source protocol authority userInfo user password host port relative path directory file query anchor".split(" "),Fw={ftp:21,http:80,https:443,mailto:25},Mw=function(r,e){var t,n,o=this;if(r=Iw(r),t=(e=o.settings=e||{}).base_uri,/^([\w\-]+):([^\/]{2})/i.test(r)||/^\s*#/.test(r))o.source=r;else{var i=0===r.indexOf("//");0!==r.indexOf("/")||i||(r=(t&&t.protocol||"http")+"://mce_host"+r),/^[\w\-]*:?\/\//.test(r)||(n=e.base_uri?e.base_uri.path:new Mw(V.document.location.href).directory,""==e.base_uri.protocol?r="//mce_host"+o.toAbsPath(n,r):(r=/([^#?]*)([#?]?.*)/.exec(r),r=(t&&t.protocol||"http")+"://mce_host"+o.toAbsPath(n,r[1])+r[2])),r=r.replace(/@@/g,"(mce_at)"),r=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(r),Pw(Lw,function(e,t){var n=r[t];n&&(n=n.replace(/\(mce_at\)/g,"@@")),o[e]=n}),t&&(o.protocol||(o.protocol=t.protocol),o.userInfo||(o.userInfo=t.userInfo),o.port||"mce_host"!==o.host||(o.port=t.port),o.host&&"mce_host"!==o.host||(o.host=t.host),o.source=""),i&&(o.protocol="")}};Mw.prototype={setPath:function(e){e=/^(.*?)\/?(\w+)?$/.exec(e),this.path=e[0],this.directory=e[1],this.file=e[2],this.source="",this.getURI()},toRelative:function(e){var t;if("./"===e)return e;if("mce_host"!==(e=new Mw(e,{base_uri:this})).host&&this.host!==e.host&&e.host||this.port!==e.port||this.protocol!==e.protocol&&""!==e.protocol)return e.getURI();var n=this.getURI(),r=e.getURI();return n===r||"/"===n.charAt(n.length-1)&&n.substr(0,n.length-1)===r?n:(t=this.toRelPath(this.path,e.path),e.query&&(t+="?"+e.query),e.anchor&&(t+="#"+e.anchor),t)},toAbsolute:function(e,t){return(e=new Mw(e,{base_uri:this})).getURI(t&&this.isSameOrigin(e))},isSameOrigin:function(e){if(this.host==e.host&&this.protocol==e.protocol){if(this.port==e.port)return!0;var t=Fw[this.protocol];if(t&&(this.port||t)==(e.port||t))return!0}return!1},toRelPath:function(e,t){var n,r,o,i=0,a="";if(e=(e=e.substring(0,e.lastIndexOf("/"))).split("/"),n=t.split("/"),e.length>=n.length)for(r=0,o=e.length;r<o;r++)if(r>=n.length||e[r]!==n[r]){i=r+1;break}if(e.length<n.length)for(r=0,o=n.length;r<o;r++)if(r>=e.length||e[r]!==n[r]){i=r+1;break}if(1===i)return t;for(r=0,o=e.length-(i-1);r<o;r++)a+="../";for(r=i-1,o=n.length;r<o;r++)a+=r!==i-1?"/"+n[r]:n[r];return a},toAbsPath:function(e,t){var n,r,o,i=0,a=[];for(r=/\/$/.test(t)?"/":"",e=e.split("/"),t=t.split("/"),Pw(e,function(e){e&&a.push(e)}),e=a,n=t.length-1,a=[];0<=n;n--)0!==t[n].length&&"."!==t[n]&&(".."!==t[n]?0<i?i--:a.push(t[n]):i++);return 0!==(o=(n=e.length-i)<=0?a.reverse().join("/"):e.slice(0,n).join("/")+"/"+a.reverse().join("/")).indexOf("/")&&(o="/"+o),r&&o.lastIndexOf("/")!==o.length-1&&(o+=r),o},getURI:function(e){var t,n=this;return n.source&&!e||(t="",e||(n.protocol?t+=n.protocol+"://":t+="//",n.userInfo&&(t+=n.userInfo+"@"),n.host&&(t+=n.host),n.port&&(t+=":"+n.port)),n.path&&(t+=n.path),n.query&&(t+="?"+n.query),n.anchor&&(t+="#"+n.anchor),n.source=t),n.source}},Mw.parseDataUri=function(e){var t,n;return e=decodeURIComponent(e).split(","),(n=/data:([^;]+)/.exec(e[0]))&&(t=n[1]),{type:t,data:e[1]}},Mw.getDocumentBaseUrl=function(e){var t;return t=0!==e.protocol.indexOf("http")&&"file:"!==e.protocol?e.href:e.protocol+"//"+e.host+e.pathname,/^[^:]+:\/\/\/?[^\/]+\//.test(t)&&(t=t.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(t)||(t+="/")),t};var zw=function(e,t,n){var r,o,i,a,u;if(t.format=t.format?t.format:"html",t.get=!0,t.getInner=!0,t.no_events||e.fire("BeforeGetContent",t),"raw"===t.format)r=Xt.trim(Iv.trimExternal(e.serializer,n.innerHTML));else if("text"===t.format)r=xa(n.innerText||n.textContent);else{if("tree"===t.format)return e.serializer.serialize(n,t);i=(o=e).serializer.serialize(n,t),a=wl(o),u=new RegExp("^(<"+a+"[^>]*>(&nbsp;|&#160;|\\s|\xa0|<br \\/>|)<\\/"+a+">[\r\n]*|<br \\/>[\r\n]*)$"),r=i.replace(u,"")}return"text"===t.format||_o(ar.fromDom(n))?t.content=r:t.content=Xt.trim(r),t.no_events||e.fire("GetContent",t),t.content},Uw=function(e,t){t(e),e.firstChild&&Uw(e.firstChild,t),e.next&&Uw(e.next,t)},jw=function(e,t,n){var r=function(e,n,t){var r={},o={},i=[];for(var a in t.firstChild&&Uw(t.firstChild,function(t){z(e,function(e){e.name===t.name&&(r[e.name]?r[e.name].nodes.push(t):r[e.name]={filter:e,nodes:[t]})}),z(n,function(e){"string"==typeof t.attr(e.name)&&(o[e.name]?o[e.name].nodes.push(t):o[e.name]={filter:e,nodes:[t]})})}),r)r.hasOwnProperty(a)&&i.push(r[a]);for(var a in o)o.hasOwnProperty(a)&&i.push(o[a]);return i}(e,t,n);z(r,function(t){z(t.filter.callbacks,function(e){e(t.nodes,t.filter.name,{})})})},Vw=function(e){return e instanceof rb},Hw=function(e,t){var r;e.dom.setHTML(e.getBody(),t),oh(r=e)&&uc.firstPositionIn(r.getBody()).each(function(e){var t=e.getNode(),n=Uo.isTable(t)?uc.firstPositionIn(t).getOr(e):e;r.selection.setRng(n.toRange())})},qw=function(u,s,c){return void 0===c&&(c={}),c.format=c.format?c.format:"html",c.set=!0,c.content=Vw(s)?"":s,Vw(s)||c.no_events||(u.fire("BeforeSetContent",c),s=c.content),_.from(u.getBody()).fold(q(s),function(e){return Vw(s)?function(e,t,n,r){jw(e.parser.getNodeFilters(),e.parser.getAttributeFilters(),n);var o=il({validate:e.validate},e.schema).serialize(n);return r.content=_o(ar.fromDom(t))?o:Xt.trim(o),Hw(e,r.content),r.no_events||e.fire("SetContent",r),n}(u,e,s,c):(t=u,n=e,o=c,0===(r=s).length||/^\s+$/.test(r)?(a='<br data-mce-bogus="1">',"TABLE"===n.nodeName?r="<tr><td>"+a+"</td></tr>":/^(UL|OL)$/.test(n.nodeName)&&(r="<li>"+a+"</li>"),(i=wl(t))&&t.schema.isValidChild(n.nodeName.toLowerCase(),i.toLowerCase())?(r=a,r=t.dom.createHTML(i,t.settings.forced_root_block_attrs,r)):r||(r='<br data-mce-bogus="1">'),Hw(t,r),t.fire("SetContent",o)):("raw"!==o.format&&(r=il({validate:t.validate},t.schema).serialize(t.parser.parse(r,{isRootContent:!0,insert:!0}))),o.content=_o(ar.fromDom(n))?r:Xt.trim(r),Hw(t,o.content),o.no_events||t.fire("SetContent",o)),o.content);var t,n,r,o,i,a})},$w=Ei.DOM,Ww=function(e){return _.from(e).each(function(e){return e.destroy()})},Kw=function(e){if(!e.removed){var t=e._selectionOverrides,n=e.editorUpload,r=e.getBody(),o=e.getElement();r&&e.save({is_removing:!0}),e.removed=!0,e.unbindAllNativeEvents(),e.hasHiddenInput&&o&&$w.remove(o.nextSibling),bp(e),e.editorManager.remove(e),!e.inline&&r&&(i=e,$w.setStyle(i.id,"display",i.orgDisplay)),Cp(e),$w.remove(e.getContainer()),Ww(t),Ww(n),e.destroy()}var i},Xw=function(e,t){var n,r,o,i=e.selection,a=e.dom;e.destroyed||(t||e.removed?(t||(e.editorManager.off("beforeunload",e._beforeUnload),e.theme&&e.theme.destroy&&e.theme.destroy(),Ww(i),Ww(a)),(r=(n=e).formElement)&&(r._mceOldSubmit&&(r.submit=r._mceOldSubmit,r._mceOldSubmit=null),$w.unbind(r,"submit reset",n.formEventDelegate)),(o=e).contentAreaContainer=o.formElement=o.container=o.editorContainer=null,o.bodyElement=o.contentDocument=o.contentWindow=null,o.iframeElement=o.targetElm=null,o.selection&&(o.selection=o.selection.win=o.selection.dom=o.selection.dom.doc=null),e.destroyed=!0):e.remove())},Yw=Ei.DOM,Gw=Xt.extend,Jw=Xt.each,Qw=Xt.resolve,Zw=fe.ie,eN=function(e,t,n){var r,o,i,a,u,s,c,l=this,f=l.documentBaseUrl=n.documentBaseURL,d=n.baseURI;r=l,o=e,i=f,a=n.defaultSettings,u=t,c={id:o,theme:"modern",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:i,add_form_submit_trigger:!0,submit_patch:!0,add_unload_trigger:!0,convert_urls:!0,relative_urls:!0,remove_script_host:!0,object_resizing:!0,doctype:"<!DOCTYPE html>",visual:!0,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",forced_root_block:"p",hidden_input:!0,render_ui:!0,indentation:"40px",inline_styles:!0,convert_fonts_to_spans:!0,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",entity_encoding:"named",url_converter:(s=r).convertURL,url_converter_scope:s,ie7_compat:!0},t=jp(Ip,c,a,u),l.settings=t,Oi.language=t.language||"en",Oi.languageLoad=t.language_load,Oi.baseURL=n.baseURL,l.id=e,l.setDirty(!1),l.plugins={},l.documentBaseURI=new Mw(t.document_base_url,{base_uri:d}),l.baseURI=d,l.contentCSS=[],l.contentStyles=[],l.shortcuts=new Xp(l),l.loadedCSS={},l.editorCommands=new fp(l),l.suffix=n.suffix,l.editorManager=n,l.inline=t.inline,l.buttons={},l.menuItems={},t.cache_suffix&&(fe.cacheSuffix=t.cache_suffix.replace(/^[\?\&]+/,"")),!1===t.override_viewport&&(fe.overrideViewPort=!1),n.fire("SetupEditor",{editor:l}),l.execCallback("setup",l),l.$=gn.overrideDefaults(function(){return{context:l.inline?l.getBody():l.getDoc(),element:l.getBody()}})};Gw(eN.prototype={render:function(){Ow(this)},focus:function(e){rh(this,e)},hasFocus:function(){return oh(this)},execCallback:function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r,o=this.settings[e];if(o)return this.callbackLookup&&(r=this.callbackLookup[e])&&(o=r.func,r=r.scope),"string"==typeof o&&(r=(r=o.replace(/\.\w+$/,""))?Qw(r):0,o=Qw(o),this.callbackLookup=this.callbackLookup||{},this.callbackLookup[e]={func:o,scope:r}),o.apply(r||this,Array.prototype.slice.call(arguments,1))},translate:function(e){if(e&&Xt.is(e,"string")){var n=this.settings.language||"en",r=this.editorManager.i18n;e=r.data[n+"."+e]||e.replace(/\{\#([^\}]+)\}/g,function(e,t){return r.data[n+"."+t]||"{#"+t+"}"})}return this.editorManager.translate(e)},getLang:function(e,t){return this.editorManager.i18n.data[(this.settings.language||"en")+"."+e]||(t!==undefined?t:"{#"+e+"}")},getParam:function(e,t,n){return Hp(this,e,t,n)},nodeChanged:function(e){this._nodeChangeDispatcher.nodeChanged(e)},addButton:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),t.stateSelector&&"undefined"==typeof t.active&&(t.active=!1),t.text||t.icon||(t.icon=e),t.tooltip=t.tooltip||t.title,n.buttons[e]=t},addSidebar:function(e,t){return Bw(this,e,t)},addMenuItem:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),n.menuItems[e]=t},addContextToolbar:function(e,t){var n,r=this;r.contextToolbars=r.contextToolbars||[],"string"==typeof e&&(n=e,e=function(e){return r.dom.is(e,n)}),r.contextToolbars.push({id:zh.uuid("mcet"),predicate:e,items:t})},addCommand:function(e,t,n){this.editorCommands.addCommand(e,t,n)},addQueryStateHandler:function(e,t,n){this.editorCommands.addQueryStateHandler(e,t,n)},addQueryValueHandler:function(e,t,n){this.editorCommands.addQueryValueHandler(e,t,n)},addShortcut:function(e,t,n,r){this.shortcuts.add(e,t,n,r)},execCommand:function(e,t,n,r){return this.editorCommands.execCommand(e,t,n,r)},queryCommandState:function(e){return this.editorCommands.queryCommandState(e)},queryCommandValue:function(e){return this.editorCommands.queryCommandValue(e)},queryCommandSupported:function(e){return this.editorCommands.queryCommandSupported(e)},show:function(){this.hidden&&(this.hidden=!1,this.inline?this.getBody().contentEditable=!0:(Yw.show(this.getContainer()),Yw.hide(this.id)),this.load(),this.fire("show"))},hide:function(){var e=this,t=e.getDoc();e.hidden||(Zw&&t&&!e.inline&&t.execCommand("SelectAll"),e.save(),e.inline?(e.getBody().contentEditable=!1,e===e.editorManager.focusedEditor&&(e.editorManager.focusedEditor=null)):(Yw.hide(e.getContainer()),Yw.setStyle(e.id,"display",e.orgDisplay)),e.hidden=!0,e.fire("hide"))},isHidden:function(){return!!this.hidden},setProgressState:function(e,t){this.fire("ProgressState",{state:e,time:t})},load:function(e){var t,n=this.getElement();return this.removed?"":n?((e=e||{}).load=!0,t=this.setContent(n.value!==undefined?n.value:n.innerHTML,e),e.element=n,e.no_events||this.fire("LoadContent",e),e.element=n=null,t):void 0},save:function(e){var t,n,r=this,o=r.getElement();if(o&&r.initialized&&!r.removed)return(e=e||{}).save=!0,e.element=o,e.content=r.getContent(e),e.no_events||r.fire("SaveContent",e),"raw"===e.format&&r.fire("RawSaveContent",e),t=e.content,/TEXTAREA|INPUT/i.test(o.nodeName)?o.value=t:(!e.is_removing&&r.inline||(o.innerHTML=t),(n=Yw.getParent(r.id,"form"))&&Jw(n.elements,function(e){if(e.name===r.id)return e.value=t,!1})),e.element=o=null,!1!==e.set_dirty&&r.setDirty(!1),t},setContent:function(e,t){return qw(this,e,t)},getContent:function(e){return t=this,void 0===(n=e)&&(n={}),_.from(t.getBody()).fold(q("tree"===n.format?new rb("body",11):""),function(e){return zw(t,n,e)});var t,n},insertContent:function(e,t){t&&(e=Gw({content:e},t)),this.execCommand("mceInsertContent",!1,e)},isDirty:function(){return!this.isNotDirty},setDirty:function(e){var t=!this.isNotDirty;this.isNotDirty=!e,e&&e!==t&&this.fire("dirty")},setMode:function(e){var t,n;(n=e)!==kp(t=this)&&(t.initialized?Tp(t,"readonly"===n):t.on("init",function(){Tp(t,"readonly"===n)}),xp(t,n))},getContainer:function(){return this.container||(this.container=Yw.get(this.editorContainer||this.id+"_parent")),this.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return this.targetElm||(this.targetElm=Yw.get(this.id)),this.targetElm},getWin:function(){var e;return this.contentWindow||(e=this.iframeElement)&&(this.contentWindow=e.contentWindow),this.contentWindow},getDoc:function(){var e;return this.contentDocument||(e=this.getWin())&&(this.contentDocument=e.document),this.contentDocument},getBody:function(){var e=this.getDoc();return this.bodyElement||(e?e.body:null)},convertURL:function(e,t,n){var r=this.settings;return r.urlconverter_callback?this.execCallback("urlconverter_callback",e,n,!0,t):!r.convert_urls||n&&"LINK"===n.nodeName||0===e.indexOf("file:")||0===e.length?e:r.relative_urls?this.documentBaseURI.toRelative(e):e=this.documentBaseURI.toAbsolute(e,r.remove_script_host)},addVisual:function(e){var n,r=this,o=r.settings,i=r.dom;e=e||r.getBody(),r.hasVisual===undefined&&(r.hasVisual=o.visual),Jw(i.select("table,a",e),function(e){var t;switch(e.nodeName){case"TABLE":return n=o.visual_table_class||"mce-item-table",void((t=i.getAttrib(e,"border"))&&"0"!==t||!r.hasVisual?i.removeClass(e,n):i.addClass(e,n));case"A":return void(i.getAttrib(e,"href")||(t=i.getAttrib(e,"name")||e.id,n=o.visual_anchor_class||"mce-item-anchor",t&&r.hasVisual?i.addClass(e,n):i.removeClass(e,n)))}}),r.fire("VisualAid",{element:e,hasVisual:r.hasVisual})},remove:function(){Kw(this)},destroy:function(e){Xw(this,e)},uploadImages:function(e){return this.editorUpload.uploadImages(e)},_scanForImages:function(){return this.editorUpload.scanForImages()}},Bp);var tN,nN,rN,oN={isEditorUIElement:function(e){return-1!==e.className.toString().indexOf("mce-")}},iN=function(n,e){var t,r;or.detect().browser.isIE()?(r=n).on("focusout",function(){tp(r)}):(t=e,n.on("mouseup touchend",function(e){t.throttle()})),n.on("keyup nodechange",function(e){var t;"nodechange"===(t=e).type&&t.selectionChange||tp(n)})},aN=function(e){var t,n,r,o=ji(function(){tp(e)},0);e.inline&&(t=e,n=o,r=function(){n.throttle()},Ei.DOM.bind(V.document,"mouseup",r),t.on("remove",function(){Ei.DOM.unbind(V.document,"mouseup",r)})),e.on("init",function(){iN(e,o)}),e.on("remove",function(){o.cancel()})},uN=Ei.DOM,sN=function(e){return oN.isEditorUIElement(e)},cN=function(t,e){var n=t?t.settings.custom_ui_selector:"";return null!==uN.getParent(e,function(e){return sN(e)||!!n&&t.dom.is(e,n)})},lN=function(r,e){var t=e.editor;aN(t),t.on("focusin",function(){var e=r.focusedEditor;e!==this&&(e&&e.fire("blur",{focusedEditor:this}),r.setActive(this),(r.focusedEditor=this).fire("focus",{blurredEditor:e}),this.focus(!0))}),t.on("focusout",function(){var t=this;he.setEditorTimeout(t,function(){var e=r.focusedEditor;cN(t,function(){try{return V.document.activeElement}catch(e){return V.document.body}}())||e!==t||(t.fire("blur",{focusedEditor:null}),r.focusedEditor=null)})}),tN||(tN=function(e){var t,n=r.activeEditor;t=e.target,n&&t.ownerDocument===V.document&&(t===V.document.body||cN(n,t)||r.focusedEditor!==n||(n.fire("blur",{focusedEditor:null}),r.focusedEditor=null))},uN.bind(V.document,"focusin",tN))},fN=function(e,t){e.focusedEditor===t.editor&&(e.focusedEditor=null),e.activeEditor||(uN.unbind(V.document,"focusin",tN),tN=null)},dN=function(e){e.on("AddEditor",d(lN,e)),e.on("RemoveEditor",d(fN,e))},mN=Ei.DOM,gN=Xt.explode,pN=Xt.each,hN=Xt.extend,vN=0,yN=!1,bN=[],CN=[],xN=function(t){var n=t.type;pN(rN.get(),function(e){switch(n){case"scroll":e.fire("ScrollWindow",t);break;case"resize":e.fire("ResizeWindow",t)}})},wN=function(e){e!==yN&&(e?gn(window).on("resize scroll",xN):gn(window).off("resize scroll",xN),yN=e)},NN=function(t){var e=CN;delete bN[t.id];for(var n=0;n<bN.length;n++)if(bN[n]===t){bN.splice(n,1);break}return CN=U(CN,function(e){return t!==e}),rN.activeEditor===t&&(rN.activeEditor=0<CN.length?CN[0]:null),rN.focusedEditor===t&&(rN.focusedEditor=null),e.length!==CN.length};hN(rN={defaultSettings:{},$:gn,majorVersion:"4",minorVersion:"9.9",releaseDate:"2020-03-25",editors:bN,i18n:vh,activeEditor:null,settings:{},setup:function(){var e,t,n="";t=Mw.getDocumentBaseUrl(V.document.location),/^[^:]+:\/\/\/?[^\/]+\//.test(t)&&(t=t.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(t)||(t+="/"));var r=window.tinymce||window.tinyMCEPreInit;if(r)e=r.base||r.baseURL,n=r.suffix;else{for(var o=V.document.getElementsByTagName("script"),i=0;i<o.length;i++){var a;if(""!==(a=o[i].src||"")){var u=a.substring(a.lastIndexOf("/"));if(/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(a)){-1!==u.indexOf(".min")&&(n=".min"),e=a.substring(0,a.lastIndexOf("/"));break}}}!e&&V.document.currentScript&&(-1!==(a=V.document.currentScript.src).indexOf(".min")&&(n=".min"),e=a.substring(0,a.lastIndexOf("/")))}this.baseURL=new Mw(t).toAbsolute(e),this.documentBaseURL=t,this.baseURI=new Mw(this.baseURL),this.suffix=n,dN(this)},overrideDefaults:function(e){var t,n;(t=e.base_url)&&(this.baseURL=new Mw(this.documentBaseURL).toAbsolute(t.replace(/\/+$/,"")),this.baseURI=new Mw(this.baseURL)),n=e.suffix,e.suffix&&(this.suffix=n);var r=(this.defaultSettings=e).plugin_base_urls;for(var o in r)Oi.PluginManager.urls[o]=r[o]},init:function(r){var n,u,s=this;u=Xt.makeMap("area base basefont br col frame hr img input isindex link meta param embed source wbr track colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu"," ");var c=function(e){var t=e.id;return t||(t=(t=e.name)&&!mN.get(t)?e.name:mN.uniqueId(),e.setAttribute("id",t)),t},l=function(e,t){return t.constructor===RegExp?t.test(e.className):mN.hasClass(e,t)},f=function(e){n=e},e=function(){var o,i=0,a=[],n=function(e,t,n){var r=new eN(e,t,s);a.push(r),r.on("init",function(){++i===o.length&&f(a)}),r.targetElm=r.targetElm||n,r.render()};mN.unbind(window,"ready",e),function(e){var t=r[e];t&&t.apply(s,Array.prototype.slice.call(arguments,2))}("onpageload"),o=gn.unique(function(t){var e,n=[];if(fe.ie&&fe.ie<11)return Nh.initError("TinyMCE does not support the browser you are using. For a list of supported browsers please see: https://www.tinymce.com/docs/get-started/system-requirements/"),[];if(t.types)return pN(t.types,function(e){n=n.concat(mN.select(e.selector))}),n;if(t.selector)return mN.select(t.selector);if(t.target)return[t.target];switch(t.mode){case"exact":0<(e=t.elements||"").length&&pN(gN(e),function(t){var e;(e=mN.get(t))?n.push(e):pN(V.document.forms,function(e){pN(e.elements,function(e){e.name===t&&(t="mce_editor_"+vN++,mN.setAttrib(e,"id",t),n.push(e))})})});break;case"textareas":case"specific_textareas":pN(mN.select("textarea"),function(e){t.editor_deselector&&l(e,t.editor_deselector)||t.editor_selector&&!l(e,t.editor_selector)||n.push(e)})}return n}(r)),r.types?pN(r.types,function(t){Xt.each(o,function(e){return!mN.is(e,t.selector)||(n(c(e),hN({},r,t),e),!1)})}):(Xt.each(o,function(e){var t;(t=s.get(e.id))&&t.initialized&&!(t.getContainer()||t.getBody()).parentNode&&(NN(t),t.unbindAllNativeEvents(),t.destroy(!0),t.removed=!0,t=null)}),0===(o=Xt.grep(o,function(e){return!s.get(e.id)})).length?f([]):pN(o,function(e){var t;t=e,r.inline&&t.tagName.toLowerCase()in u?Nh.initError("Could not initialize inline editor on invalid inline target element",e):n(c(e),r,e)}))};return s.settings=r,mN.bind(window,"ready",e),new de(function(t){n?t(n):f=function(e){t(e)}})},get:function(t){return 0===arguments.length?CN.slice(0):S(t)?X(CN,function(e){return e.id===t}).getOr(null):O(t)&&CN[t]?CN[t]:null},add:function(e){var t=this;return bN[e.id]===e||(null===t.get(e.id)&&("length"!==e.id&&(bN[e.id]=e),bN.push(e),CN.push(e)),wN(!0),t.activeEditor=e,t.fire("AddEditor",{editor:e}),nN||(nN=function(){t.fire("BeforeUnload")},mN.bind(window,"beforeunload",nN))),e},createEditor:function(e,t){return this.add(new eN(e,t,this))},remove:function(e){var t,n,r=this;if(e){if(!S(e))return n=e,A(r.get(n.id))?null:(NN(n)&&r.fire("RemoveEditor",{editor:n}),0===CN.length&&mN.unbind(window,"beforeunload",nN),n.remove(),wN(0<CN.length),n);pN(mN.select(e),function(e){(n=r.get(e.id))&&r.remove(n)})}else for(t=CN.length-1;0<=t;t--)r.remove(CN[t])},execCommand:function(e,t,n){var r=this.get(n);switch(e){case"mceAddEditor":return this.get(n)||new eN(n,this.settings,this).render(),!0;case"mceRemoveEditor":return r&&r.remove(),!0;case"mceToggleEditor":return r?r.isHidden()?r.show():r.hide():this.execCommand("mceAddEditor",0,n),!0}return!!this.activeEditor&&this.activeEditor.execCommand(e,t,n)},triggerSave:function(){pN(CN,function(e){e.save()})},addI18n:function(e,t){vh.add(e,t)},translate:function(e){return vh.translate(e)},setActive:function(e){var t=this.activeEditor;this.activeEditor!==e&&(t&&t.fire("deactivate",{relatedTarget:e}),e.fire("activate",{relatedTarget:t})),this.activeEditor=e}},hp),rN.setup();var EN,SN=rN;function TN(n){return{walk:function(e,t){return Ic(n,e,t)},split:Um,normalize:function(t){return kg(n,t).fold(q(!1),function(e){return t.setStart(e.startContainer,e.startOffset),t.setEnd(e.endContainer,e.endOffset),!0})}}}(EN=TN||(TN={})).compareRanges=Cg,EN.getCaretRangeFromPoint=Db,EN.getSelectedNode=Qa,EN.getNode=Za;var kN,_N,AN=TN,RN=Math.min,DN=Math.max,ON=Math.round,BN=function(e,t,n){var r,o,i,a,u,s;return r=t.x,o=t.y,i=e.w,a=e.h,u=t.w,s=t.h,"b"===(n=(n||"").split(""))[0]&&(o+=s),"r"===n[1]&&(r+=u),"c"===n[0]&&(o+=ON(s/2)),"c"===n[1]&&(r+=ON(u/2)),"b"===n[3]&&(o-=a),"r"===n[4]&&(r-=i),"c"===n[3]&&(o-=ON(a/2)),"c"===n[4]&&(r-=ON(i/2)),PN(r,o,i,a)},PN=function(e,t,n,r){return{x:e,y:t,w:n,h:r}},IN={inflate:function(e,t,n){return PN(e.x-t,e.y-n,e.w+2*t,e.h+2*n)},relativePosition:BN,findBestRelativePosition:function(e,t,n,r){var o,i;for(i=0;i<r.length;i++)if((o=BN(e,t,r[i])).x>=n.x&&o.x+o.w<=n.w+n.x&&o.y>=n.y&&o.y+o.h<=n.h+n.y)return r[i];return null},intersect:function(e,t){var n,r,o,i;return n=DN(e.x,t.x),r=DN(e.y,t.y),o=RN(e.x+e.w,t.x+t.w),i=RN(e.y+e.h,t.y+t.h),o-n<0||i-r<0?null:PN(n,r,o-n,i-r)},clamp:function(e,t,n){var r,o,i,a,u,s,c,l,f,d;return u=e.x,s=e.y,c=e.x+e.w,l=e.y+e.h,f=t.x+t.w,d=t.y+t.h,r=DN(0,t.x-u),o=DN(0,t.y-s),i=DN(0,c-f),a=DN(0,l-d),u+=r,s+=o,n&&(c+=r,l+=o,u-=i,s-=a),PN(u,s,(c-=i)-u,(l-=a)-s)},create:PN,fromClientRect:function(e){return PN(e.left,e.top,e.width,e.height)}},LN={},FN={add:function(e,t){LN[e.toLowerCase()]=t},has:function(e){return!!LN[e.toLowerCase()]},get:function(e){var t=e.toLowerCase(),n=LN.hasOwnProperty(t)?LN[t]:null;if(null===n)throw new Error("Could not find module for type: "+e);return n},create:function(e,t){var n;if("string"==typeof e?(t=t||{}).type=e:e=(t=e).type,e=e.toLowerCase(),!(n=LN[e]))throw new Error("Could not find control by type: "+e);return(n=new n(t)).type=e,n}},MN=Xt.each,zN=Xt.extend,UN=function(){};UN.extend=kN=function(n){var e,t,r,o=this.prototype,i=function(){var e,t,n;if(!_N&&(this.init&&this.init.apply(this,arguments),t=this.Mixins))for(e=t.length;e--;)(n=t[e]).init&&n.init.apply(this,arguments)},a=function(){return this},u=function(n,r){return function(){var e,t=this._super;return this._super=o[n],e=r.apply(this,arguments),this._super=t,e}};for(t in _N=!0,e=new this,_N=!1,n.Mixins&&(MN(n.Mixins,function(e){for(var t in e)"init"!==t&&(n[t]=e[t])}),o.Mixins&&(n.Mixins=o.Mixins.concat(n.Mixins))),n.Methods&&MN(n.Methods.split(","),function(e){n[e]=a}),n.Properties&&MN(n.Properties.split(","),function(e){var t="_"+e;n[e]=function(e){return e!==undefined?(this[t]=e,this):this[t]}}),n.Statics&&MN(n.Statics,function(e,t){i[t]=e}),n.Defaults&&o.Defaults&&(n.Defaults=zN({},o.Defaults,n.Defaults)),n)"function"==typeof(r=n[t])&&o[t]?e[t]=u(t,r):e[t]=r;return i.prototype=e,(i.constructor=i).extend=kN,i};var jN=Math.min,VN=Math.max,HN=Math.round,qN=function(e,n){var r,o,t,i;if(n=n||'"',null===e)return"null";if("string"==(t=typeof e))return o="\bb\tt\nn\ff\rr\"\"''\\\\",n+e.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(e,t){return'"'===n&&"'"===e?e:(r=o.indexOf(t))+1?"\\"+o.charAt(r+1):(e=t.charCodeAt().toString(16),"\\u"+"0000".substring(e.length)+e)})+n;if("object"===t){if(e.hasOwnProperty&&"[object Array]"===Object.prototype.toString.call(e)){for(r=0,o="[";r<e.length;r++)o+=(0<r?",":"")+qN(e[r],n);return o+"]"}for(i in o="{",e)e.hasOwnProperty(i)&&(o+="function"!=typeof e[i]?(1<o.length?","+n:n)+i+n+":"+qN(e[i],n):"");return o+"}"}return""+e},$N={serialize:qN,parse:function(e){try{return JSON.parse(e)}catch(t){}}},WN={callbacks:{},count:0,send:function(t){var n=this,r=Ei.DOM,o=t.count!==undefined?t.count:n.count,i="tinymce_jsonp_"+o;n.callbacks[o]=function(e){r.remove(i),delete n.callbacks[o],t.callback(e)},r.add(r.doc.body,"script",{id:i,src:t.url,type:"text/javascript"}),n.count++}},KN={send:function(e){var t,n=0,r=function(){!e.async||4===t.readyState||1e4<n++?(e.success&&n<1e4&&200===t.status?e.success.call(e.success_scope,""+t.responseText,t,e):e.error&&e.error.call(e.error_scope,1e4<n?"TIMED_OUT":"GENERAL",t,e),t=null):setTimeout(r,10)};if(e.scope=e.scope||this,e.success_scope=e.success_scope||e.scope,e.error_scope=e.error_scope||e.scope,e.async=!1!==e.async,e.data=e.data||"",KN.fire("beforeInitialize",{settings:e}),t=Th()){if(t.overrideMimeType&&t.overrideMimeType(e.content_type),t.open(e.type||(e.data?"POST":"GET"),e.url,e.async),e.crossDomain&&(t.withCredentials=!0),e.content_type&&t.setRequestHeader("Content-Type",e.content_type),e.requestheaders&&Xt.each(e.requestheaders,function(e){t.setRequestHeader(e.key,e.value)}),t.setRequestHeader("X-Requested-With","XMLHttpRequest"),(t=KN.fire("beforeSend",{xhr:t,settings:e}).xhr).send(e.data),!e.async)return r();setTimeout(r,10)}}};Xt.extend(KN,hp);var XN,YN,GN,JN,QN=Xt.extend,ZN=function(e){this.settings=QN({},e),this.count=0};ZN.sendRPC=function(e){return(new ZN).send(e)},ZN.prototype={send:function(n){var r=n.error,o=n.success;(n=QN(this.settings,n)).success=function(e,t){void 0===(e=$N.parse(e))&&(e={error:"JSON Parse error."}),e.error?r.call(n.error_scope||n.scope,e.error,t):o.call(n.success_scope||n.scope,e.result)},n.error=function(e,t){r&&r.call(n.error_scope||n.scope,e,t)},n.data=$N.serialize({id:n.id||"c"+this.count++,method:n.method,params:n.params}),n.content_type="application/json",KN.send(n)}};try{XN=V.window.localStorage}catch(oE){YN={},GN=[],JN={getItem:function(e){var t=YN[e];return t||null},setItem:function(e,t){GN.push(e),YN[e]=String(t)},key:function(e){return GN[e]},removeItem:function(t){GN=GN.filter(function(e){return e===t}),delete YN[t]},clear:function(){GN=[],YN={}},length:0},Object.defineProperty(JN,"length",{get:function(){return GN.length},configurable:!1,enumerable:!1}),XN=JN}var eE,tE=SN,nE={geom:{Rect:IN},util:{Promise:de,Delay:he,Tools:Xt,VK:Sv,URI:Mw,Class:UN,EventDispatcher:mp,Observable:hp,I18n:vh,XHR:KN,JSON:$N,JSONRequest:ZN,JSONP:WN,LocalStorage:XN,Color:function(e){var n={},u=0,s=0,c=0,t=function(e){var t;return"object"==typeof e?"r"in e?(u=e.r,s=e.g,c=e.b):"v"in e&&function(e,t,n){var r,o,i,a;if(e=(parseInt(e,10)||0)%360,t=parseInt(t,10)/100,n=parseInt(n,10)/100,t=VN(0,jN(t,1)),n=VN(0,jN(n,1)),0!==t){switch(r=e/60,i=(o=n*t)*(1-Math.abs(r%2-1)),a=n-o,Math.floor(r)){case 0:u=o,s=i,c=0;break;case 1:u=i,s=o,c=0;break;case 2:u=0,s=o,c=i;break;case 3:u=0,s=i,c=o;break;case 4:u=i,s=0,c=o;break;case 5:u=o,s=0,c=i;break;default:u=s=c=0}u=HN(255*(u+a)),s=HN(255*(s+a)),c=HN(255*(c+a))}else u=s=c=HN(255*n)}(e.h,e.s,e.v):(t=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(e))?(u=parseInt(t[1],10),s=parseInt(t[2],10),c=parseInt(t[3],10)):(t=/#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(e))?(u=parseInt(t[1],16),s=parseInt(t[2],16),c=parseInt(t[3],16)):(t=/#([0-F])([0-F])([0-F])/gi.exec(e))&&(u=parseInt(t[1]+t[1],16),s=parseInt(t[2]+t[2],16),c=parseInt(t[3]+t[3],16)),u=u<0?0:255<u?255:u,s=s<0?0:255<s?255:s,c=c<0?0:255<c?255:c,n};return e&&t(e),n.toRgb=function(){return{r:u,g:s,b:c}},n.toHsv=function(){return e=u,t=s,n=c,o=0,(i=jN(e/=255,jN(t/=255,n/=255)))===(a=VN(e,VN(t,n)))?{h:0,s:0,v:100*(o=i)}:(r=(a-i)/a,{h:HN(60*((e===i?3:n===i?1:5)-(e===i?t-n:n===i?e-t:n-e)/((o=a)-i))),s:HN(100*r),v:HN(100*o)});var e,t,n,r,o,i,a},n.toHex=function(){var e=function(e){return 1<(e=parseInt(e,10).toString(16)).length?e:"0"+e};return"#"+e(u)+e(s)+e(c)},n.parse=t,n}},dom:{EventUtils:Se,Sizzle:St,DomQuery:gn,TreeWalker:mo,DOMUtils:Ei,ScriptLoader:Ai,RangeUtils:AN,Serializer:bb,ControlSelection:Tb,BookmarkManager:Nb,Selection:iC,Event:Se.Event},html:{Styles:mi,Entities:ei,Node:rb,Schema:fi,SaxParser:Bv,DomParser:gb,Writer:ol,Serializer:il},ui:{Factory:FN},Env:fe,AddOnManager:Oi,Annotator:Vc,Formatter:$y,UndoManager:Qv,EditorCommands:fp,WindowManager:gh,NotificationManager:mh,EditorObservable:Bp,Shortcuts:Xp,Editor:eN,FocusManager:oN,EditorManager:SN,DOM:Ei.DOM,ScriptLoader:Ai.ScriptLoader,PluginManager:Oi.PluginManager,ThemeManager:Oi.ThemeManager,trim:Xt.trim,isArray:Xt.isArray,is:Xt.is,toArray:Xt.toArray,makeMap:Xt.makeMap,each:Xt.each,map:Xt.map,grep:Xt.grep,inArray:Xt.inArray,extend:Xt.extend,create:Xt.create,walk:Xt.walk,createNS:Xt.createNS,resolve:Xt.resolve,explode:Xt.explode,_addCacheSuffix:Xt._addCacheSuffix,isOpera:fe.opera,isWebKit:fe.webkit,isIE:fe.ie,isGecko:fe.gecko,isMac:fe.mac},rE=tE=Xt.extend(tE,nE);eE=rE,window.tinymce=eE,window.tinyMCE=eE,function(e){if("object"==typeof module)try{module.exports=e}catch(t){}}(rE)}(window);
\ No newline at end of file
index ca90be3053af821b307e8b77bd72548f846056fa..fd61a62c7611d72701f84277e02388371d65f3a7 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -2,16 +2,19 @@
 
 [![GitHub release](https://img.shields.io/github/release/BookStackApp/BookStack.svg)](https://github.com/BookStackApp/BookStack/releases/latest)
 [![license](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/BookStackApp/BookStack/blob/master/LICENSE)
+[![Crowdin](https://badges.crowdin.net/bookstack/localized.svg)](https://crowdin.com/project/bookstack)
 [![Build Status](https://github.com/BookStackApp/BookStack/workflows/phpunit/badge.svg)](https://github.com/BookStackApp/BookStack/actions)
 [![Discord](https://img.shields.io/static/v1?label=Chat&message=Discord&color=738adb&logo=discord)](https://discord.gg/ztkBqR2)
 
-A platform for storing and organising information and documentation. General information and documentation for BookStack can be found at https://www.bookstackapp.com/.
+A platform for storing and organising information and documentation. Details for BookStack can be found on the official website at https://www.bookstackapp.com/.
 
 * [Installation Instructions](https://www.bookstackapp.com/docs/admin/installation)
 * [Documentation](https://www.bookstackapp.com/docs)
 * [Demo Instance](https://demo.bookstackapp.com)
     * [Admin Login](https://demo.bookstackapp.com/[email protected]&password=password)
 * [BookStack Blog](https://www.bookstackapp.com/blog)
+* [Issue List](https://github.com/BookStackApp/BookStack/issues)
+* [Discord Chat](https://discord.gg/ztkBqR2)
 
 ## 📚 Project Definition
 
@@ -25,7 +28,7 @@ In regards to development philosophy, BookStack has a relaxed, open & positive a
 
 Below is a high-level road map view for BookStack to provide a sense of direction of where the project is going. This can change at any point and does not reflect many features and improvements that will also be included as part of the journey along this road map. For more granular detail of what will be included in upcoming releases you can review the project milestones as defined in the "Release Process" section below.
 
-- **Platform REST API** *(In Design)*
+- **Platform REST API** *(Base Implemented, In review and roll-out)*
     - *A REST API covering, at minimum, control of core content models (Books, Chapters, Pages) for automation and platform extension.*
 - **Editor Alignment & Review**
     - *Review the page editors with goal of achieving increased interoperability & feature parity while also considering collaborative editing potential.*
@@ -40,15 +43,15 @@ BookStack releases are each assigned a version number, such as "v0.25.2", in the
 
 Each BookStack release will have a [milestone](https://github.com/BookStackApp/BookStack/milestones) created with issues & pull requests assigned to it to define what will be in that release. Milestones are built up then worked through until complete at which point, after some testing and documentation updates, the release will be deployed. 
 
-For feature releases, and some patch releases, the release will be accompanied by a post on the [BookStack blog](https://www.bookstackapp.com/blog/) which will provide additional detail on features, changes & updates otherwise the [GitHub release page](https://github.com/BookStackApp/BookStack/releases) will show a list of changes. You can sign up to be alerted to new BookStack blogs posts (once per week maximum) [at this link](http://eepurl.com/cmmq5j).
+For feature releases, and some patch releases, the release will be accompanied by a post on the [BookStack blog](https://www.bookstackapp.com/blog/) which will provide additional detail on features, changes & updates otherwise the [GitHub release page](https://github.com/BookStackApp/BookStack/releases) will show a list of changes. You can sign up to be alerted to new BookStack blogs posts (once per week maximum) [at this link](https://updates.bookstackapp.com/signup/bookstack-news-and-updates).
 
 ## 🛠️ Development & Testing
 
 All development on BookStack is currently done on the master branch. When it's time for a release the master branch is merged into release with built & minified CSS & JS then tagged at its version. Here are the current development requirements:
 
-* [Node.js](https://nodejs.org/en/) v10.0+
+* [Node.js](https://nodejs.org/en/) v12.0+
 
-SASS is used to help the CSS development and the JavaScript is run through babel to allow for writing ES6 code. This is done using webpack. To run the build task you can use the following commands:
+This project uses SASS for CSS development and this is built, along with the JavaScript, using a range of npm scripts. The below npm commands can be used to install the dependencies & run the build tasks:
 
 ``` bash
 # Install NPM Dependencies
@@ -77,7 +80,7 @@ Once done you can run `php vendor/bin/phpunit` in the application root directory
 
 ### 📜 Code Standards
 
-PHP code within BookStack is generally to [PSR-2](http://www.php-fig.org/psr/psr-2/) standards. From the BookStack root folder you can run `./vendor/bin/phpcs` to check code is formatted correctly and `./vendor/bin/phpcbf` to auto-fix non-PSR-2 code.
+PHP code within BookStack is generally to [PSR-2](http://www.php-fig.org/psr/psr-2/) standards. From the BookStack root folder you can run `./vendor/bin/phpcs` to check code is formatted correctly and `./vendor/bin/phpcbf` to auto-fix non-PSR-2 code. Please don't auto-fix code unless it's related to changes you've made otherwise you'll likely cause git conflicts.
 
 ### 🐋 Development using Docker
 
@@ -90,12 +93,11 @@ To get started, make sure you meet the following requirements:
 
 If all the conditions are met, you can proceed with the following steps:
 
-1. Install PHP/Composer dependencies with **`docker-compose run app composer install`** (first time can take a while because the image has to be built).
-2. **Copy `.env.example` to `.env`** and change `APP_KEY` to a random 32 char string.
-3. Make sure **port 8080 is unused** *or else* change `DEV_PORT` to a free port on your host.
-4. **Run `chgrp -R docker storage`**. The development container will chown the `storage` directory to the `www-data` user inside the container so BookStack can write to it. You need to change the group to your host's `docker` group here to not lose access to the `storage` directory.
-5. **Run `docker-compose up`** and wait until all database migrations have been done.
-6. You can now login with `[email protected]` and `password` as password on `localhost:8080` (or another port if specified).
+1. **Copy `.env.example` to `.env`**, change `APP_KEY` to a random 32 char string and set `APP_ENV` to `local`.
+2. Make sure **port 8080 is unused** *or else* change `DEV_PORT` to a free port on your host.
+3. **Run `chgrp -R docker storage`**. The development container will chown the `storage` directory to the `www-data` user inside the container so BookStack can write to it. You need to change the group to your host's `docker` group here to not lose access to the `storage` directory.
+4. **Run `docker-compose up`** and wait until the image is built and all database migrations have been done.
+5. You can now login with `[email protected]` and `password` as password on `localhost:8080` (or another port if specified).
 
 If needed, You'll be able to run any artisan commands via docker-compose like so:
 
@@ -107,28 +109,19 @@ The docker-compose setup runs an instance of [MailHog](https://github.com/mailho
 
 ## 🌎 Translations
 
-All text strings can be found in the `resources/lang` folder where each language option has its own folder. To add a new language you should copy the `en` folder to an new folder (eg. `fr` for french) then go through and translate all text strings in those files, leaving the keys and file-names intact. If a language string is missing then the `en` translation will be used. To show the language option in the user preferences language drop-down you will need to add your language to the options found at the bottom of the `resources/lang/en/settings.php` file. A system-wide language can also be set in the `.env` file like so: `APP_LANG=en`.
+Translations for text within BookStack is managed through the [BookStack project on Crowdin](https://crowdin.com/project/bookstack). Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time. Crowdin is the preferred way to provide translations, otherwise the raw translations files can be found within the `resources/lang` path.
 
-You will also need to add the language to the `locales` array in the `config/app.php` file.
+If you'd like a new language to be added to Crowdin, for you to be able to provide translations for, please [open a new issue here](https://github.com/BookStackApp/BookStack/issues/new?template=language_request.md).
 
-There is a script available which compares translation content to `en` files to see what items are missing or redundant. This can be ran like so from your BookStack install folder:
-
-```bash
-# Syntax
-php resources/lang/check.php <lang>
-
-# Examples
-php resources/lang/check.php fr
-php resources/lang/check.php pt_BR
-```
-
-Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time.
+Please note, translations in BookStack are provided to the "Crowdin Global Translation Memory" which helps BookStack and other projects with finding translations. If you are not happy with contributing to this then providing translations to BookStack, even manually via GitHub, is not advised.
 
 ## 🎁 Contributing, Issues & Pull Requests
 
 Feel free to create issues to request new features or to report bugs & problems. Just please follow the template given when creating the issue.
 
-Pull requests are welcome. Unless a small tweak or language update, It may be best to open the pull request early or create an issue for your intended change to discuss how it will fit in to the project and plan out the merge. Pull requests should be created from the `master` branch since they will be merged back into `master` once done. Please do not build from or request a merge into the `release` branch as this is only for publishing releases. If you are looking to alter CSS or JavaScript content please edit the source files found in `resources/assets`. Any CSS or JS files within `public` are built from these source files and therefore should not be edited directly.
+Pull requests are welcome. Unless a small tweak or language update, It may be best to open the pull request early or create an issue for your intended change to discuss how it will fit in to the project and plan out the merge. Just because a feature request exists, or is tagged, does not mean that feature would be accepted into the core project.
+
+Pull requests should be created from the `master` branch since they will be merged back into `master` once done. Please do not build from or request a merge into the `release` branch as this is only for publishing releases. If you are looking to alter CSS or JavaScript content please edit the source files found in `resources/assets`. Any CSS or JS files within `public` are built from these source files and therefore should not be edited directly.
 
 The project's code of conduct [can be found here](https://github.com/BookStackApp/BookStack/blob/master/.github/CODE_OF_CONDUCT.md).
 
@@ -136,7 +129,7 @@ The project's code of conduct [can be found here](https://github.com/BookStackAp
 
 Security information for administering a BookStack instance can be found on the [documentation site here](https://www.bookstackapp.com/docs/admin/security/).
 
-If you'd like to be notified of new potential security concerns you can [sign-up to the BookStack security mailing list](http://eepurl.com/glIh8z).
+If you'd like to be notified of new potential security concerns you can [sign-up to the BookStack security mailing list](https://updates.bookstackapp.com/signup/bookstack-security-updates).
 
 If you would like to report a security concern in a more confidential manner than via a GitHub issue, You can directly email the lead maintainer [ssddanbrown](https://github.com/ssddanbrown). You will need to login to be able to see the email address on the [GitHub profile page](https://github.com/ssddanbrown). Alternatively you can send a DM via twitter to [@ssddanbrown](https://twitter.com/ssddanbrown).
 
@@ -156,13 +149,14 @@ The BookStack source is provided under the MIT License. The libraries used by, a
 
 The great people that have worked to build and improve BookStack can [be seen here](https://github.com/BookStackApp/BookStack/graphs/contributors).
 
+The wonderful people that have provided translations, either through GitHub or via Crowdin [can be seen here](https://github.com/BookStackApp/BookStack/blob/master/.github/translators.txt).
+
 These are the great open-source projects used to help build BookStack:
 
 * [Laravel](http://laravel.com/)
 * [TinyMCE](https://www.tinymce.com/)
 * [CodeMirror](https://codemirror.net)
-* [Vue.js](http://vuejs.org/)
-* [Sortable](https://github.com/SortableJS/Sortable) & [Vue.Draggable](https://github.com/SortableJS/Vue.Draggable)
+* [Sortable](https://github.com/SortableJS/Sortable)
 * [Google Material Icons](https://material.io/icons/)
 * [Dropzone.js](http://www.dropzonejs.com/)
 * [clipboard.js](https://clipboardjs.com/)
@@ -173,5 +167,5 @@ These are the great open-source projects used to help build BookStack:
     * [Snappy (WKHTML2PDF)](https://github.com/barryvdh/laravel-snappy)
     * [Laravel IDE helper](https://github.com/barryvdh/laravel-ide-helper)
 * [WKHTMLtoPDF](http://wkhtmltopdf.org/index.html)
-* [Draw.io](https://github.com/jgraph/drawio)
-* [Laravel Stats](https://github.com/stefanzweifel/laravel-stats)
\ No newline at end of file
+* [diagrams.net](https://github.com/jgraph/drawio)
+* [OneLogin's SAML PHP Toolkit](https://github.com/onelogin/php-saml)
diff --git a/resources/icons/dark-mode.svg b/resources/icons/dark-mode.svg
new file mode 100644 (file)
index 0000000..8b00d72
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 2c-1.82 0-3.53.5-5 1.35C7.99 5.08 10 8.3 10 12s-2.01 6.92-5 8.65C6.47 21.5 8.18 22 10 22c5.52 0 10-4.48 10-10S15.52 2 10 2z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/fullscreen.svg b/resources/icons/fullscreen.svg
new file mode 100644 (file)
index 0000000..d00b574
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/light-mode.svg b/resources/icons/light-mode.svg
new file mode 100644 (file)
index 0000000..cf2961d
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/saml2.svg b/resources/icons/saml2.svg
new file mode 100644 (file)
index 0000000..a9a2994
--- /dev/null
@@ -0,0 +1,4 @@
+<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+    <path d="M0 0h24v24H0z" fill="none"/>
+    <path d="M12.65 10C11.83 7.67 9.61 6 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6c2.61 0 4.83-1.67 5.65-4H17v4h4v-4h2v-4H12.65zM7 14c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/>
+</svg>
\ No newline at end of file
diff --git a/resources/js/components/add-remove-rows.js b/resources/js/components/add-remove-rows.js
new file mode 100644 (file)
index 0000000..9a5f019
--- /dev/null
@@ -0,0 +1,54 @@
+import {onChildEvent} from "../services/dom";
+import {uniqueId} from "../services/util";
+
+/**
+ * AddRemoveRows
+ * Allows easy row add/remove controls onto a table.
+ * Needs a model row to use when adding a new row.
+ * @extends {Component}
+ */
+class AddRemoveRows {
+    setup() {
+        this.modelRow = this.$refs.model;
+        this.addButton = this.$refs.add;
+        this.removeSelector = this.$opts.removeSelector;
+        this.rowSelector = this.$opts.rowSelector;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.addButton.addEventListener('click', this.add.bind(this));
+
+        onChildEvent(this.$el, this.removeSelector, 'click', (e) => {
+            const row = e.target.closest(this.rowSelector);
+            row.remove();
+        });
+    }
+
+    // For external use
+    add() {
+        const clone = this.modelRow.cloneNode(true);
+        clone.classList.remove('hidden');
+        this.setClonedInputNames(clone);
+        this.modelRow.parentNode.insertBefore(clone, this.modelRow);
+        window.components.init(clone);
+    }
+
+    /**
+     * Update the HTML names of a clone to be unique if required.
+     * Names can use placeholder values. For exmaple, a model row
+     * may have name="tags[randrowid][name]".
+     * These are the available placeholder values:
+     * - randrowid - An random string ID, applied the same across the row.
+     * @param {HTMLElement} clone
+     */
+    setClonedInputNames(clone) {
+        const rowId = uniqueId();
+        const randRowIdElems = clone.querySelectorAll(`[name*="randrowid"]`);
+        for (const elem of randRowIdElems) {
+            elem.name = elem.name.split('randrowid').join(rowId);
+        }
+    }
+}
+
+export default AddRemoveRows;
\ No newline at end of file
diff --git a/resources/js/components/ajax-delete-row.js b/resources/js/components/ajax-delete-row.js
new file mode 100644 (file)
index 0000000..2feb3d5
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * AjaxDelete
+ * @extends {Component}
+ */
+import {onSelect} from "../services/dom";
+
+class AjaxDeleteRow {
+    setup() {
+        this.row = this.$el;
+        this.url = this.$opts.url;
+        this.deleteButtons = this.$manyRefs.delete;
+
+        onSelect(this.deleteButtons, this.runDelete.bind(this));
+    }
+
+    runDelete() {
+        this.row.style.opacity = '0.7';
+        this.row.style.pointerEvents = 'none';
+
+        window.$http.delete(this.url).then(resp => {
+            if (typeof resp.data === 'object' && resp.data.message) {
+                window.$events.emit('success', resp.data.message);
+            }
+            this.row.remove();
+        }).catch(err => {
+            this.row.style.opacity = null;
+            this.row.style.pointerEvents = null;
+        });
+    }
+}
+
+export default AjaxDeleteRow;
\ No newline at end of file
diff --git a/resources/js/components/ajax-form.js b/resources/js/components/ajax-form.js
new file mode 100644 (file)
index 0000000..91029d0
--- /dev/null
@@ -0,0 +1,82 @@
+import {onEnterPress, onSelect} from "../services/dom";
+
+/**
+ * Ajax Form
+ * Will handle button clicks or input enter press events and submit
+ * the data over ajax. Will always expect a partial HTML view to be returned.
+ * Fires an 'ajax-form-success' event when submitted successfully.
+ *
+ * Will handle a real form if that's what the component is added to
+ * otherwise will act as a fake form element.
+ *
+ * @extends {Component}
+ */
+class AjaxForm {
+    setup() {
+        this.container = this.$el;
+        this.responseContainer = this.container;
+        this.url = this.$opts.url;
+        this.method = this.$opts.method || 'post';
+        this.successMessage = this.$opts.successMessage;
+        this.submitButtons = this.$manyRefs.submit || [];
+
+        if (this.$opts.responseContainer) {
+            this.responseContainer = this.container.closest(this.$opts.responseContainer);
+        }
+
+        this.setupListeners();
+    }
+
+    setupListeners() {
+
+        if (this.container.tagName === 'FORM') {
+            this.container.addEventListener('submit', this.submitRealForm.bind(this));
+            return;
+        }
+
+        onEnterPress(this.container, event => {
+            this.submitFakeForm();
+            event.preventDefault();
+        });
+
+        this.submitButtons.forEach(button => onSelect(button, this.submitFakeForm.bind(this)));
+    }
+
+    submitFakeForm() {
+        const fd = new FormData();
+        const inputs = this.container.querySelectorAll(`[name]`);
+        for (const input of inputs) {
+            fd.append(input.getAttribute('name'), input.value);
+        }
+        this.submit(fd);
+    }
+
+    submitRealForm(event) {
+        event.preventDefault();
+        const fd = new FormData(this.container);
+        this.submit(fd);
+    }
+
+    async submit(formData) {
+        this.responseContainer.style.opacity = '0.7';
+        this.responseContainer.style.pointerEvents = 'none';
+
+        try {
+            const resp = await window.$http[this.method.toLowerCase()](this.url, formData);
+            this.$emit('success', {formData});
+            this.responseContainer.innerHTML = resp.data;
+            if (this.successMessage) {
+                window.$events.emit('success', this.successMessage);
+            }
+        } catch (err) {
+            this.responseContainer.innerHTML = err.data;
+        }
+
+        window.components.init(this.responseContainer);
+        this.responseContainer.style.opacity = null;
+        this.responseContainer.style.pointerEvents = null;
+    }
+
+}
+
+export default AjaxForm;
\ No newline at end of file
diff --git a/resources/js/components/attachments.js b/resources/js/components/attachments.js
new file mode 100644 (file)
index 0000000..6dcfe9f
--- /dev/null
@@ -0,0 +1,79 @@
+/**
+ * Attachments
+ * @extends {Component}
+ */
+import {showLoading} from "../services/dom";
+
+class Attachments {
+
+    setup() {
+        this.container = this.$el;
+        this.pageId = this.$opts.pageId;
+        this.editContainer = this.$refs.editContainer;
+        this.listContainer = this.$refs.listContainer;
+        this.mainTabs = this.$refs.mainTabs;
+        this.list = this.$refs.list;
+
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        const reloadListBound = this.reloadList.bind(this);
+        this.container.addEventListener('dropzone-success', reloadListBound);
+        this.container.addEventListener('ajax-form-success', reloadListBound);
+
+        this.container.addEventListener('sortable-list-sort', event => {
+            this.updateOrder(event.detail.ids);
+        });
+
+        this.container.addEventListener('event-emit-select-edit', event => {
+            this.startEdit(event.detail.id);
+        });
+
+        this.container.addEventListener('event-emit-select-edit-back', event => {
+            this.stopEdit();
+        });
+
+        this.container.addEventListener('event-emit-select-insert', event => {
+            const insertContent = event.target.closest('[data-drag-content]').getAttribute('data-drag-content');
+            const contentTypes = JSON.parse(insertContent);
+            window.$events.emit('editor::insert', {
+                html: contentTypes['text/html'],
+                markdown: contentTypes['text/plain'],
+            });
+        });
+    }
+
+    reloadList() {
+        this.stopEdit();
+        this.mainTabs.components.tabs.show('items');
+        window.$http.get(`/attachments/get/page/${this.pageId}`).then(resp => {
+            this.list.innerHTML = resp.data;
+            window.components.init(this.list);
+        });
+    }
+
+    updateOrder(idOrder) {
+        window.$http.put(`/attachments/sort/page/${this.pageId}`, {order: idOrder}).then(resp => {
+            window.$events.emit('success', resp.data.message);
+        });
+    }
+
+    async startEdit(id) {
+        this.editContainer.classList.remove('hidden');
+        this.listContainer.classList.add('hidden');
+
+        showLoading(this.editContainer);
+        const resp = await window.$http.get(`/attachments/edit/${id}`);
+        this.editContainer.innerHTML = resp.data;
+        window.components.init(this.editContainer);
+    }
+
+    stopEdit() {
+        this.editContainer.classList.add('hidden');
+        this.listContainer.classList.remove('hidden');
+    }
+
+}
+
+export default Attachments;
\ No newline at end of file
diff --git a/resources/js/components/auto-suggest.js b/resources/js/components/auto-suggest.js
new file mode 100644 (file)
index 0000000..68de49b
--- /dev/null
@@ -0,0 +1,152 @@
+import {escapeHtml} from "../services/util";
+import {onChildEvent} from "../services/dom";
+
+const ajaxCache = {};
+
+/**
+ * AutoSuggest
+ * @extends {Component}
+ */
+class AutoSuggest {
+    setup() {
+        this.parent = this.$el.parentElement;
+        this.container = this.$el;
+        this.type = this.$opts.type;
+        this.url = this.$opts.url;
+        this.input = this.$refs.input;
+        this.list = this.$refs.list;
+
+        this.lastPopulated = 0;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.input.addEventListener('input', this.requestSuggestions.bind(this));
+        this.input.addEventListener('focus', this.requestSuggestions.bind(this));
+        this.input.addEventListener('keydown', event => {
+            if (event.key === 'Tab') {
+                this.hideSuggestions();
+            }
+        });
+
+        this.input.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this));
+        this.container.addEventListener('keydown', this.containerKeyDown.bind(this));
+
+        onChildEvent(this.list, 'button', 'click', (event, el) => {
+            this.selectSuggestion(el.textContent);
+        });
+        onChildEvent(this.list, 'button', 'keydown', (event, el) => {
+            if (event.key === 'Enter') {
+                this.selectSuggestion(el.textContent);
+            }
+        });
+
+    }
+
+    selectSuggestion(value) {
+        this.input.value = value;
+        this.lastPopulated = Date.now();
+        this.input.focus();
+        this.input.dispatchEvent(new Event('input', {bubbles: true}));
+        this.input.dispatchEvent(new Event('change', {bubbles: true}));
+        this.hideSuggestions();
+    }
+
+    containerKeyDown(event) {
+        if (event.key === 'Enter') event.preventDefault();
+        if (this.list.classList.contains('hidden')) return;
+
+        // Down arrow
+        if (event.key === 'ArrowDown') {
+            this.moveFocus(true);
+            event.preventDefault();
+        }
+        // Up Arrow
+        else if (event.key === 'ArrowUp') {
+            this.moveFocus(false);
+            event.preventDefault();
+        }
+        // Escape key
+        else if (event.key === 'Escape') {
+            this.hideSuggestions();
+            event.preventDefault();
+        }
+    }
+
+    moveFocus(forward = true) {
+        const focusables = Array.from(this.container.querySelectorAll('input,button'));
+        const index = focusables.indexOf(document.activeElement);
+        const newFocus = focusables[index + (forward ? 1 : -1)];
+        if (newFocus) {
+            newFocus.focus()
+        }
+    }
+
+    async requestSuggestions() {
+        if (Date.now() - this.lastPopulated < 50) {
+            return;
+        }
+
+        const nameFilter = this.getNameFilterIfNeeded();
+        const search = this.input.value.slice(0, 3).toLowerCase();
+        const suggestions = await this.loadSuggestions(search, nameFilter);
+        let toShow = suggestions.slice(0, 6);
+        if (search.length > 0) {
+            toShow = suggestions.filter(val => {
+                return val.toLowerCase().includes(search);
+            }).slice(0, 6);
+        }
+
+        this.displaySuggestions(toShow);
+    }
+
+    getNameFilterIfNeeded() {
+        if (this.type !== 'value') return null;
+        return this.parent.querySelector('input').value;
+    }
+
+    /**
+     * @param {String} search
+     * @param {String|null} nameFilter
+     * @returns {Promise<Object|String|*>}
+     */
+    async loadSuggestions(search, nameFilter = null) {
+        const params = {search, name: nameFilter};
+        const cacheKey = `${this.url}:${JSON.stringify(params)}`;
+
+        if (ajaxCache[cacheKey]) {
+            return ajaxCache[cacheKey];
+        }
+
+        const resp = await window.$http.get(this.url, params);
+        ajaxCache[cacheKey] = resp.data;
+        return resp.data;
+    }
+
+    /**
+     * @param {String[]} suggestions
+     */
+    displaySuggestions(suggestions) {
+        if (suggestions.length === 0) {
+            return this.hideSuggestions();
+        }
+
+        this.list.innerHTML = suggestions.map(value => `<li><button type="button">${escapeHtml(value)}</button></li>`).join('');
+        this.list.style.display = 'block';
+        for (const button of this.list.querySelectorAll('button')) {
+            button.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this));
+        }
+    }
+
+    hideSuggestions() {
+        this.list.style.display = 'none';
+    }
+
+    hideSuggestionsIfFocusedLost(event) {
+        if (!this.container.contains(event.relatedTarget)) {
+            this.hideSuggestions();
+        }
+    }
+}
+
+export default AutoSuggest;
\ No newline at end of file
index da2b28d8e2a79fb0f4da1bf81d73bf5ca8c17f90..2b94ca4a7a19a68ff82b31345efbc52fc28dc56e 100644 (file)
@@ -134,6 +134,9 @@ class BookSort {
                 onSort: this.updateMapInput.bind(this),
                 dragClass: 'bg-white',
                 ghostClass: 'primary-background-light',
+                multiDrag: true,
+                multiDragKey: 'CTRL',
+                selectedClass: 'sortable-selected',
             });
         }
     }
diff --git a/resources/js/components/breadcrumb-listing.js b/resources/js/components/breadcrumb-listing.js
deleted file mode 100644 (file)
index 7f4344b..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-class BreadcrumbListing {
-
-    constructor(elem) {
-        this.elem = elem;
-        this.searchInput = elem.querySelector('input');
-        this.loadingElem = elem.querySelector('.loading-container');
-        this.entityListElem = elem.querySelector('.breadcrumb-listing-entity-list');
-
-        // this.loadingElem.style.display = 'none';
-        const entityDescriptor = elem.getAttribute('breadcrumb-listing').split(':');
-        this.entityType = entityDescriptor[0];
-        this.entityId = Number(entityDescriptor[1]);
-
-        this.elem.addEventListener('show', this.onShow.bind(this));
-        this.searchInput.addEventListener('input', this.onSearch.bind(this));
-    }
-
-    onShow() {
-        this.loadEntityView();
-    }
-
-    onSearch() {
-        const input = this.searchInput.value.toLowerCase().trim();
-        const listItems = this.entityListElem.querySelectorAll('.entity-list-item');
-        for (let listItem of listItems) {
-            const match = !input || listItem.textContent.toLowerCase().includes(input);
-            listItem.style.display = match ? 'flex' : 'none';
-            listItem.classList.toggle('hidden', !match);
-        }
-    }
-
-    loadEntityView() {
-        this.toggleLoading(true);
-
-        const params = {
-            'entity_id': this.entityId,
-            'entity_type': this.entityType,
-        };
-
-        window.$http.get('/search/entity/siblings', params).then(resp => {
-            this.entityListElem.innerHTML = resp.data;
-        }).catch(err => {
-            console.error(err);
-        }).then(() => {
-            this.toggleLoading(false);
-            this.onSearch();
-        });
-    }
-
-    toggleLoading(show = false) {
-        this.loadingElem.style.display = show ? 'block' : 'none';
-    }
-
-}
-
-export default BreadcrumbListing;
\ No newline at end of file
diff --git a/resources/js/components/code-editor.js b/resources/js/components/code-editor.js
new file mode 100644 (file)
index 0000000..2e3506e
--- /dev/null
@@ -0,0 +1,117 @@
+import Code from "../services/code";
+import {onChildEvent, onEnterPress, onSelect} from "../services/dom";
+
+/**
+ * Code Editor
+ * @extends {Component}
+ */
+class CodeEditor {
+
+    setup() {
+        this.container = this.$refs.container;
+        this.popup = this.$el;
+        this.editorInput = this.$refs.editor;
+        this.languageLinks = this.$manyRefs.languageLink;
+        this.saveButton = this.$refs.saveButton;
+        this.languageInput = this.$refs.languageInput;
+        this.historyDropDown = this.$refs.historyDropDown;
+        this.historyList = this.$refs.historyList;
+
+        this.callback = null;
+        this.editor = null;
+        this.history = {};
+        this.historyKey = 'code_history';
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.container.addEventListener('keydown', event => {
+            if (event.ctrlKey && event.key === 'Enter') {
+                this.save();
+            }
+        });
+
+        onSelect(this.languageLinks, event => {
+            const language = event.target.dataset.lang;
+            this.languageInput.value = language;
+            this.updateEditorMode(language);
+        });
+
+        onEnterPress(this.languageInput, e => this.save());
+        onSelect(this.saveButton, e => this.save());
+
+        onChildEvent(this.historyList, 'button', 'click', (event, elem) => {
+            event.preventDefault();
+            const historyTime = elem.dataset.time;
+            if (this.editor) {
+                this.editor.setValue(this.history[historyTime]);
+            }
+        });
+    }
+
+    save() {
+        if (this.callback) {
+            this.callback(this.editor.getValue(), this.languageInput.value);
+        }
+        this.hide();
+    }
+
+    open(code, language, callback) {
+        this.languageInput.value = language;
+        this.callback = callback;
+
+        this.show();
+        this.updateEditorMode(language);
+
+        Code.setContent(this.editor, code);
+    }
+
+    show() {
+        if (!this.editor) {
+            this.editor = Code.popupEditor(this.editorInput, this.languageInput.value);
+        }
+        this.loadHistory();
+        this.popup.components.popup.show(() => {
+            Code.updateLayout(this.editor);
+            this.editor.focus();
+        }, () => {
+            this.addHistory()
+        });
+    }
+
+    hide() {
+        this.popup.components.popup.hide();
+        this.addHistory();
+    }
+
+    updateEditorMode(language) {
+        Code.setMode(this.editor, language, this.editor.getValue());
+    }
+
+    loadHistory() {
+        this.history = JSON.parse(window.sessionStorage.getItem(this.historyKey) || '{}');
+        const historyKeys = Object.keys(this.history).reverse();
+        this.historyDropDown.classList.toggle('hidden', historyKeys.length === 0);
+        this.historyList.innerHTML = historyKeys.map(key => {
+             const localTime = (new Date(parseInt(key))).toLocaleTimeString();
+             return `<li><button type="button" data-time="${key}">${localTime}</button></li>`;
+        }).join('');
+    }
+
+    addHistory() {
+        if (!this.editor) return;
+        const code = this.editor.getValue();
+        if (!code) return;
+
+        // Stop if we'd be storing the same as the last item
+        const lastHistoryKey = Object.keys(this.history).pop();
+        if (this.history[lastHistoryKey] === code) return;
+
+        this.history[String(Date.now())] = code;
+        const historyString = JSON.stringify(this.history);
+        window.sessionStorage.setItem(this.historyKey, historyString);
+    }
+
+}
+
+export default CodeEditor;
\ No newline at end of file
diff --git a/resources/js/components/code-highlighter.js b/resources/js/components/code-highlighter.js
new file mode 100644 (file)
index 0000000..db61128
--- /dev/null
@@ -0,0 +1,10 @@
+import Code from "../services/code"
+class CodeHighlighter {
+
+    constructor(elem) {
+        Code.highlightWithin(elem);
+    }
+
+}
+
+export default CodeHighlighter;
\ No newline at end of file
index 464f394c1e7e42a8d1dc568c94568d53f1498819..544f91008c7d13eaeec88a9f119ebfb47e839a85 100644 (file)
@@ -12,8 +12,8 @@ class Collapsible {
         this.content = elem.querySelector('[collapsible-content]');
 
         if (!this.trigger) return;
-
         this.trigger.addEventListener('click', this.toggle.bind(this));
+        this.openIfContainsError();
     }
 
     open() {
@@ -36,6 +36,13 @@ class Collapsible {
         }
     }
 
+    openIfContainsError() {
+        const error = this.content.querySelector('.text-neg.text-small');
+        if (error) {
+            this.open();
+        }
+    }
+
 }
 
 export default Collapsible;
\ No newline at end of file
diff --git a/resources/js/components/details-highlighter.js b/resources/js/components/details-highlighter.js
new file mode 100644 (file)
index 0000000..18c5165
--- /dev/null
@@ -0,0 +1,18 @@
+import Code from "../services/code"
+class DetailsHighlighter {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.dealtWith = false;
+        elem.addEventListener('toggle', this.onToggle.bind(this));
+    }
+
+    onToggle() {
+        if (this.dealtWith) return;
+
+        Code.highlightWithin(this.elem);
+        this.dealtWith = true;
+    }
+}
+
+export default DetailsHighlighter;
\ No newline at end of file
diff --git a/resources/js/components/dropdown-search.js b/resources/js/components/dropdown-search.js
new file mode 100644 (file)
index 0000000..8c81aae
--- /dev/null
@@ -0,0 +1,79 @@
+import {debounce} from "../services/util";
+
+class DropdownSearch {
+
+    setup() {
+        this.elem = this.$el;
+        this.searchInput = this.$refs.searchInput;
+        this.loadingElem = this.$refs.loading;
+        this.listContainerElem = this.$refs.listContainer;
+
+        this.localSearchSelector = this.$opts.localSearchSelector;
+        this.url = this.$opts.url;
+
+        this.elem.addEventListener('show', this.onShow.bind(this));
+        this.searchInput.addEventListener('input', this.onSearch.bind(this));
+
+        this.runAjaxSearch = debounce(this.runAjaxSearch, 300, false);
+    }
+
+    onShow() {
+        this.loadList();
+    }
+
+    onSearch() {
+        const input = this.searchInput.value.toLowerCase().trim();
+        if (this.localSearchSelector) {
+            this.runLocalSearch(input);
+        } else {
+            this.toggleLoading(true);
+            this.runAjaxSearch(input);
+        }
+    }
+
+    runAjaxSearch(searchTerm) {
+        this.loadList(searchTerm);
+    }
+
+    runLocalSearch(searchTerm) {
+        const listItems = this.listContainerElem.querySelectorAll(this.localSearchSelector);
+        for (let listItem of listItems) {
+            const match = !searchTerm || listItem.textContent.toLowerCase().includes(searchTerm);
+            listItem.style.display = match ? 'flex' : 'none';
+            listItem.classList.toggle('hidden', !match);
+        }
+    }
+
+    async loadList(searchTerm = '') {
+        this.listContainerElem.innerHTML = '';
+        this.toggleLoading(true);
+
+        try {
+            const resp = await window.$http.get(this.getAjaxUrl(searchTerm));
+            this.listContainerElem.innerHTML = resp.data;
+        } catch (err) {
+            console.error(err);
+        }
+
+        this.toggleLoading(false);
+        if (this.localSearchSelector) {
+            this.onSearch();
+        }
+    }
+
+    getAjaxUrl(searchTerm = null) {
+        if (!searchTerm) {
+            return this.url;
+        }
+
+        const joiner = this.url.includes('?') ? '&' : '?';
+        return `${this.url}${joiner}search=${encodeURIComponent(searchTerm)}`;
+    }
+
+    toggleLoading(show = false) {
+        this.loadingElem.style.display = show ? 'block' : 'none';
+    }
+
+}
+
+export default DropdownSearch;
\ No newline at end of file
index 4de1e239b93b3747bd7474ad188d7684b62dfb9e..22402d483902b2148918150e1d371611dd1abf9b 100644 (file)
@@ -3,17 +3,21 @@ import {onSelect} from "../services/dom";
 /**
  * Dropdown
  * Provides some simple logic to create simple dropdown menus.
+ * @extends {Component}
  */
 class DropDown {
 
-    constructor(elem) {
-        this.container = elem;
-        this.menu = elem.querySelector('.dropdown-menu, [dropdown-menu]');
-        this.moveMenu = elem.hasAttribute('dropdown-move-menu');
-        this.toggle = elem.querySelector('[dropdown-toggle]');
+    setup() {
+        this.container = this.$el;
+        this.menu = this.$refs.menu;
+        this.toggle = this.$refs.toggle;
+        this.moveMenu = this.$opts.moveMenu;
+
+        this.direction = (document.dir === 'rtl') ? 'right' : 'left';
         this.body = document.body;
         this.showing = false;
         this.setupListeners();
+        this.hide = this.hide.bind(this);
     }
 
     show(event = null) {
@@ -28,7 +32,11 @@ class DropDown {
             this.rect = this.menu.getBoundingClientRect();
             this.body.appendChild(this.menu);
             this.menu.style.position = 'fixed';
-            this.menu.style.left = `${this.rect.left}px`;
+            if (this.direction === 'right') {
+                this.menu.style.right = `${(this.rect.right - this.rect.width)}px`;
+            } else {
+                this.menu.style.left = `${this.rect.left}px`;
+            }
             this.menu.style.top = `${this.rect.top}px`;
             this.menu.style.width = `${this.rect.width}px`;
         }
@@ -67,7 +75,7 @@ class DropDown {
         this.toggle.setAttribute('aria-expanded', 'false');
         if (this.moveMenu) {
             this.menu.style.position = '';
-            this.menu.style.left = '';
+            this.menu.style[this.direction] = '';
             this.menu.style.top = '';
             this.menu.style.width = '';
             this.container.appendChild(this.menu);
diff --git a/resources/js/components/dropzone.js b/resources/js/components/dropzone.js
new file mode 100644 (file)
index 0000000..e7273df
--- /dev/null
@@ -0,0 +1,77 @@
+import DropZoneLib from "dropzone";
+import {fadeOut} from "../services/animations";
+
+/**
+ * Dropzone
+ * @extends {Component}
+ */
+class Dropzone {
+    setup() {
+        this.container = this.$el;
+        this.url = this.$opts.url;
+        this.successMessage = this.$opts.successMessage;
+        this.removeMessage = this.$opts.removeMessage;
+        this.uploadLimitMessage = this.$opts.uploadLimitMessage;
+        this.timeoutMessage = this.$opts.timeoutMessage;
+
+        const _this = this;
+        this.dz = new DropZoneLib(this.container, {
+            addRemoveLinks: true,
+            dictRemoveFile: this.removeMessage,
+            timeout: Number(window.uploadTimeout) || 60000,
+            maxFilesize: Number(window.uploadLimit) || 256,
+            url: this.url,
+            withCredentials: true,
+            init() {
+                this.dz = this;
+                this.dz.on('sending', _this.onSending.bind(_this));
+                this.dz.on('success', _this.onSuccess.bind(_this));
+                this.dz.on('error', _this.onError.bind(_this));
+            }
+        });
+    }
+
+    onSending(file, xhr, data) {
+
+        const token = window.document.querySelector('meta[name=token]').getAttribute('content');
+        data.append('_token', token);
+
+        xhr.ontimeout = (e) => {
+            this.dz.emit('complete', file);
+            this.dz.emit('error', file, this.timeoutMessage);
+        }
+    }
+
+    onSuccess(file, data) {
+        this.$emit('success', {file, data});
+
+        if (this.successMessage) {
+            window.$events.emit('success', this.successMessage);
+        }
+
+        fadeOut(file.previewElement, 800, () => {
+            this.dz.removeFile(file);
+        });
+    }
+
+    onError(file, errorMessage, xhr) {
+        this.$emit('error', {file, errorMessage, xhr});
+
+        const setMessage = (message) => {
+            const messsageEl = file.previewElement.querySelector('[data-dz-errormessage]');
+            messsageEl.textContent = message;
+        }
+
+        if (xhr && xhr.status === 413) {
+            setMessage(this.uploadLimitMessage);
+        } else if (errorMessage.file) {
+            setMessage(errorMessage.file);
+        }
+    }
+
+    removeAll() {
+        this.dz.removeAllFiles(true);
+    }
+}
+
+export default Dropzone;
\ No newline at end of file
diff --git a/resources/js/components/entity-search.js b/resources/js/components/entity-search.js
new file mode 100644 (file)
index 0000000..8b1861e
--- /dev/null
@@ -0,0 +1,59 @@
+import {onSelect} from "../services/dom";
+
+/**
+ * Class EntitySearch
+ * @extends {Component}
+ */
+class EntitySearch {
+    setup() {
+        this.entityId = this.$opts.entityId;
+        this.entityType = this.$opts.entityType;
+
+        this.contentView = this.$refs.contentView;
+        this.searchView = this.$refs.searchView;
+        this.searchResults = this.$refs.searchResults;
+        this.searchInput = this.$refs.searchInput;
+        this.searchForm = this.$refs.searchForm;
+        this.clearButton = this.$refs.clearButton;
+        this.loadingBlock = this.$refs.loadingBlock;
+
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.searchInput.addEventListener('change', this.runSearch.bind(this));
+        this.searchForm.addEventListener('submit', e => {
+            e.preventDefault();
+            this.runSearch();
+        });
+
+        onSelect(this.clearButton, this.clearSearch.bind(this));
+    }
+
+    runSearch() {
+        const term = this.searchInput.value.trim();
+        if (term.length === 0) {
+            return this.clearSearch();
+        }
+
+        this.searchView.classList.remove('hidden');
+        this.contentView.classList.add('hidden');
+        this.loadingBlock.classList.remove('hidden');
+
+        const url = window.baseUrl(`/search/${this.entityType}/${this.entityId}`);
+        window.$http.get(url, {term}).then(resp => {
+            this.searchResults.innerHTML = resp.data;
+        }).catch(console.error).then(() => {
+            this.loadingBlock.classList.add('hidden');
+        });
+    }
+
+    clearSearch() {
+        this.searchView.classList.add('hidden');
+        this.contentView.classList.remove('hidden');
+        this.loadingBlock.classList.add('hidden');
+        this.searchInput.value = '';
+    }
+}
+
+export default EntitySearch;
\ No newline at end of file
index 147f7b58353785e55995a1fef5af134a49cf5ff3..0104eace7065373983792a525c501746616bc2c9 100644 (file)
@@ -1,27 +1,29 @@
-
+/**
+ * Entity Selector Popup
+ * @extends {Component}
+ */
 class EntitySelectorPopup {
 
-    constructor(elem) {
-        this.elem = elem;
+    setup() {
+        this.elem = this.$el;
+        this.selectButton = this.$refs.select;
         window.EntitySelectorPopup = this;
 
         this.callback = null;
         this.selection = null;
 
-        this.selectButton = elem.querySelector('.entity-link-selector-confirm');
         this.selectButton.addEventListener('click', this.onSelectButtonClick.bind(this));
-
         window.$events.listen('entity-select-change', this.onSelectionChange.bind(this));
         window.$events.listen('entity-select-confirm', this.onSelectionConfirm.bind(this));
     }
 
     show(callback) {
         this.callback = callback;
-        this.elem.components.overlay.show();
+        this.elem.components.popup.show();
     }
 
     hide() {
-        this.elem.components.overlay.hide();
+        this.elem.components.popup.hide();
     }
 
     onSelectButtonClick() {
diff --git a/resources/js/components/event-emit-select.js b/resources/js/components/event-emit-select.js
new file mode 100644 (file)
index 0000000..cf02158
--- /dev/null
@@ -0,0 +1,29 @@
+import {onSelect} from "../services/dom";
+
+/**
+ * EventEmitSelect
+ * Component will simply emit an event when selected.
+ *
+ * Has one required option: "name".
+ * A name of "hello" will emit a component DOM event of
+ * "event-emit-select-name"
+ *
+ * All options will be set as the "detail" of the event with
+ * their values included.
+ *
+ * @extends {Component}
+ */
+class EventEmitSelect {
+    setup() {
+        this.container = this.$el;
+        this.name = this.$opts.name;
+
+
+        onSelect(this.$el, () => {
+            this.$emit(this.name, this.$opts);
+        });
+    }
+
+}
+
+export default EventEmitSelect;
\ No newline at end of file
diff --git a/resources/js/components/image-manager.js b/resources/js/components/image-manager.js
new file mode 100644 (file)
index 0000000..c974ab1
--- /dev/null
@@ -0,0 +1,208 @@
+import {onChildEvent, onSelect, removeLoading, showLoading} from "../services/dom";
+
+/**
+ * ImageManager
+ * @extends {Component}
+ */
+class ImageManager {
+
+    setup() {
+
+        // Options
+        this.uploadedTo = this.$opts.uploadedTo;
+
+        // Element References
+        this.container = this.$el;
+        this.popupEl = this.$refs.popup;
+        this.searchForm = this.$refs.searchForm;
+        this.searchInput = this.$refs.searchInput;
+        this.cancelSearch = this.$refs.cancelSearch;
+        this.listContainer = this.$refs.listContainer;
+        this.filterTabs = this.$manyRefs.filterTabs;
+        this.selectButton = this.$refs.selectButton;
+        this.formContainer = this.$refs.formContainer;
+        this.dropzoneContainer = this.$refs.dropzoneContainer;
+
+        // Instance data
+        this.type = 'gallery';
+        this.lastSelected = {};
+        this.lastSelectedTime = 0;
+        this.callback = null;
+        this.resetState = () => {
+            this.hasData = false;
+            this.page = 1;
+            this.filter = 'all';
+        };
+        this.resetState();
+
+        this.setupListeners();
+
+        window.ImageManager = this;
+    }
+
+    setupListeners() {
+        onSelect(this.filterTabs, e => {
+            this.resetAll();
+            this.filter = e.target.dataset.filter;
+            this.setActiveFilterTab(this.filter);
+            this.loadGallery();
+        });
+
+        this.searchForm.addEventListener('submit', event => {
+            this.resetListView();
+            this.loadGallery();
+            event.preventDefault();
+        });
+
+        onSelect(this.cancelSearch, event => {
+            this.resetListView();
+            this.resetSearchView();
+            this.loadGallery();
+            this.cancelSearch.classList.remove('active');
+        });
+
+        this.searchInput.addEventListener('input', event => {
+            this.cancelSearch.classList.toggle('active', this.searchInput.value.trim());
+        });
+
+        onChildEvent(this.listContainer, '.load-more', 'click', async event => {
+            showLoading(event.target);
+            this.page++;
+            await this.loadGallery();
+            event.target.remove();
+        });
+
+        this.listContainer.addEventListener('event-emit-select-image', this.onImageSelectEvent.bind(this));
+
+        onSelect(this.selectButton, () => {
+            if (this.callback) {
+                this.callback(this.lastSelected);
+            }
+            this.hide();
+        });
+
+        onChildEvent(this.formContainer, '#image-manager-delete', 'click', event => {
+            if (this.lastSelected) {
+                this.loadImageEditForm(this.lastSelected.id, true);
+            }
+        });
+
+        this.formContainer.addEventListener('ajax-form-success', this.refreshGallery.bind(this));
+        this.container.addEventListener('dropzone-success', this.refreshGallery.bind(this));
+    }
+
+    show(callback, type = 'gallery') {
+        this.resetAll();
+
+        this.callback = callback;
+        this.type = type;
+        this.popupEl.components.popup.show();
+        this.dropzoneContainer.classList.toggle('hidden', type !== 'gallery');
+
+        if (!this.hasData) {
+            this.loadGallery();
+            this.hasData = true;
+        }
+    }
+
+    hide() {
+        this.popupEl.components.popup.hide();
+    }
+
+    async loadGallery() {
+        const params = {
+            page: this.page,
+            search: this.searchInput.value || null,
+            uploaded_to: this.uploadedTo,
+            filter_type: this.filter === 'all' ? null : this.filter,
+        };
+
+        const {data: html} = await window.$http.get(`images/${this.type}`, params);
+        this.addReturnedHtmlElementsToList(html);
+        removeLoading(this.listContainer);
+    }
+
+    addReturnedHtmlElementsToList(html) {
+        const el = document.createElement('div');
+        el.innerHTML = html;
+        window.components.init(el);
+        for (const child of [...el.children]) {
+            this.listContainer.appendChild(child);
+        }
+    }
+
+    setActiveFilterTab(filterName) {
+        this.filterTabs.forEach(t => t.classList.remove('selected'));
+        const activeTab = this.filterTabs.find(t => t.dataset.filter === filterName);
+        if (activeTab) {
+            activeTab.classList.add('selected');
+        }
+    }
+
+    resetAll() {
+        this.resetState();
+        this.resetListView();
+        this.resetSearchView();
+        this.resetEditForm();
+        this.setActiveFilterTab('all');
+        this.selectButton.classList.add('hidden');
+    }
+
+    resetSearchView() {
+        this.searchInput.value = '';
+    }
+
+    resetEditForm() {
+        this.formContainer.innerHTML = '';
+    }
+
+    resetListView() {
+        showLoading(this.listContainer);
+        this.page = 1;
+    }
+
+    refreshGallery() {
+        this.resetListView();
+        this.loadGallery();
+    }
+
+    onImageSelectEvent(event) {
+        const image = JSON.parse(event.detail.data);
+        const isDblClick = ((image && image.id === this.lastSelected.id)
+            && Date.now() - this.lastSelectedTime < 400);
+        const alreadySelected = event.target.classList.contains('selected');
+        [...this.listContainer.querySelectorAll('.selected')].forEach(el => {
+            el.classList.remove('selected');
+        });
+
+        if (!alreadySelected) {
+            event.target.classList.add('selected');
+            this.loadImageEditForm(image.id);
+        } else {
+            this.resetEditForm();
+        }
+        this.selectButton.classList.toggle('hidden', alreadySelected);
+
+        if (isDblClick && this.callback) {
+            this.callback(image);
+            this.hide();
+        }
+
+        this.lastSelected = image;
+        this.lastSelectedTime = Date.now();
+    }
+
+    async loadImageEditForm(imageId, requestDelete = false) {
+        if (!requestDelete) {
+            this.formContainer.innerHTML = '';
+        }
+
+        const params = requestDelete ? {delete: true} : {};
+        const {data: formHtml} = await window.$http.get(`/images/edit/${imageId}`, params);
+        this.formContainer.innerHTML = formHtml;
+        window.components.init(this.formContainer);
+    }
+
+}
+
+export default ImageManager;
\ No newline at end of file
index 14cf08ae2da41014e4703c57bfae6f79c4dc1c9e..91ccdaf3aa6f23fca3a923283634c75eb42a0265 100644 (file)
-import dropdown from "./dropdown";
-import overlay from "./overlay";
-import backToTop from "./back-to-top";
-import notification from "./notification";
-import chapterToggle from "./chapter-toggle";
-import expandToggle from "./expand-toggle";
-import entitySelectorPopup from "./entity-selector-popup";
-import entitySelector from "./entity-selector";
-import sidebar from "./sidebar";
-import pagePicker from "./page-picker";
-import pageComments from "./page-comments";
-import wysiwygEditor from "./wysiwyg-editor";
-import markdownEditor from "./markdown-editor";
-import editorToolbox from "./editor-toolbox";
-import imagePicker from "./image-picker";
-import collapsible from "./collapsible";
-import toggleSwitch from "./toggle-switch";
-import pageDisplay from "./page-display";
-import shelfSort from "./shelf-sort";
-import homepageControl from "./homepage-control";
-import headerMobileToggle from "./header-mobile-toggle";
-import listSortControl from "./list-sort-control";
-import triLayout from "./tri-layout";
-import breadcrumbListing from "./breadcrumb-listing";
-import permissionsTable from "./permissions-table";
-import customCheckbox from "./custom-checkbox";
-import bookSort from "./book-sort";
-import settingAppColorPicker from "./setting-app-color-picker";
-import entityPermissionsEditor from "./entity-permissions-editor";
-import templateManager from "./template-manager";
-import newUserPassword from "./new-user-password";
+import addRemoveRows from "./add-remove-rows.js"
+import ajaxDeleteRow from "./ajax-delete-row.js"
+import ajaxForm from "./ajax-form.js"
+import attachments from "./attachments.js"
+import autoSuggest from "./auto-suggest.js"
+import backToTop from "./back-to-top.js"
+import bookSort from "./book-sort.js"
+import chapterToggle from "./chapter-toggle.js"
+import codeEditor from "./code-editor.js"
+import codeHighlighter from "./code-highlighter.js"
+import collapsible from "./collapsible.js"
+import customCheckbox from "./custom-checkbox.js"
+import detailsHighlighter from "./details-highlighter.js"
+import dropdown from "./dropdown.js"
+import dropdownSearch from "./dropdown-search.js"
+import dropzone from "./dropzone.js"
+import editorToolbox from "./editor-toolbox.js"
+import entityPermissionsEditor from "./entity-permissions-editor.js"
+import entitySearch from "./entity-search.js"
+import entitySelector from "./entity-selector.js"
+import entitySelectorPopup from "./entity-selector-popup.js"
+import eventEmitSelect from "./event-emit-select.js"
+import expandToggle from "./expand-toggle.js"
+import headerMobileToggle from "./header-mobile-toggle.js"
+import homepageControl from "./homepage-control.js"
+import imageManager from "./image-manager.js"
+import imagePicker from "./image-picker.js"
+import index from "./index.js"
+import listSortControl from "./list-sort-control.js"
+import markdownEditor from "./markdown-editor.js"
+import newUserPassword from "./new-user-password.js"
+import notification from "./notification.js"
+import optionalInput from "./optional-input.js"
+import pageComments from "./page-comments.js"
+import pageDisplay from "./page-display.js"
+import pageEditor from "./page-editor.js"
+import pagePicker from "./page-picker.js"
+import permissionsTable from "./permissions-table.js"
+import popup from "./popup.js"
+import settingAppColorPicker from "./setting-app-color-picker.js"
+import settingColorPicker from "./setting-color-picker.js"
+import shelfSort from "./shelf-sort.js"
+import sidebar from "./sidebar.js"
+import sortableList from "./sortable-list.js"
+import submitOnChange from "./submit-on-change.js"
+import tabs from "./tabs.js"
+import tagManager from "./tag-manager.js"
+import templateManager from "./template-manager.js"
+import toggleSwitch from "./toggle-switch.js"
+import triLayout from "./tri-layout.js"
+import userSelect from "./user-select.js"
+import wysiwygEditor from "./wysiwyg-editor.js"
 
 const componentMapping = {
-    'dropdown': dropdown,
-    'overlay': overlay,
-    'back-to-top': backToTop,
-    'notification': notification,
-    'chapter-toggle': chapterToggle,
-    'expand-toggle': expandToggle,
-    'entity-selector-popup': entitySelectorPopup,
-    'entity-selector': entitySelector,
-    'sidebar': sidebar,
-    'page-picker': pagePicker,
-    'page-comments': pageComments,
-    'wysiwyg-editor': wysiwygEditor,
-    'markdown-editor': markdownEditor,
-    'editor-toolbox': editorToolbox,
-    'image-picker': imagePicker,
-    'collapsible': collapsible,
-    'toggle-switch': toggleSwitch,
-    'page-display': pageDisplay,
-    'shelf-sort': shelfSort,
-    'homepage-control': homepageControl,
-    'header-mobile-toggle': headerMobileToggle,
-    'list-sort-control': listSortControl,
-    'tri-layout': triLayout,
-    'breadcrumb-listing': breadcrumbListing,
-    'permissions-table': permissionsTable,
-    'custom-checkbox': customCheckbox,
-    'book-sort': bookSort,
-    'setting-app-color-picker': settingAppColorPicker,
-    'entity-permissions-editor': entityPermissionsEditor,
-    'template-manager': templateManager,
-    'new-user-password': newUserPassword,
+    "add-remove-rows": addRemoveRows,
+    "ajax-delete-row": ajaxDeleteRow,
+    "ajax-form": ajaxForm,
+    "attachments": attachments,
+    "auto-suggest": autoSuggest,
+    "back-to-top": backToTop,
+    "book-sort": bookSort,
+    "chapter-toggle": chapterToggle,
+    "code-editor": codeEditor,
+    "code-highlighter": codeHighlighter,
+    "collapsible": collapsible,
+    "custom-checkbox": customCheckbox,
+    "details-highlighter": detailsHighlighter,
+    "dropdown": dropdown,
+    "dropdown-search": dropdownSearch,
+    "dropzone": dropzone,
+    "editor-toolbox": editorToolbox,
+    "entity-permissions-editor": entityPermissionsEditor,
+    "entity-search": entitySearch,
+    "entity-selector": entitySelector,
+    "entity-selector-popup": entitySelectorPopup,
+    "event-emit-select": eventEmitSelect,
+    "expand-toggle": expandToggle,
+    "header-mobile-toggle": headerMobileToggle,
+    "homepage-control": homepageControl,
+    "image-manager": imageManager,
+    "image-picker": imagePicker,
+    "index": index,
+    "list-sort-control": listSortControl,
+    "markdown-editor": markdownEditor,
+    "new-user-password": newUserPassword,
+    "notification": notification,
+    "optional-input": optionalInput,
+    "page-comments": pageComments,
+    "page-display": pageDisplay,
+    "page-editor": pageEditor,
+    "page-picker": pagePicker,
+    "permissions-table": permissionsTable,
+    "popup": popup,
+    "setting-app-color-picker": settingAppColorPicker,
+    "setting-color-picker": settingColorPicker,
+    "shelf-sort": shelfSort,
+    "sidebar": sidebar,
+    "sortable-list": sortableList,
+    "submit-on-change": submitOnChange,
+    "tabs": tabs,
+    "tag-manager": tagManager,
+    "template-manager": templateManager,
+    "toggle-switch": toggleSwitch,
+    "tri-layout": triLayout,
+    "user-select": userSelect,
+    "wysiwyg-editor": wysiwygEditor,
 };
 
 window.components = {};
 
-const componentNames = Object.keys(componentMapping);
-
 /**
  * Initialize components of the given name within the given element.
  * @param {String} componentName
  * @param {HTMLElement|Document} parentElement
  */
-function initComponent(componentName, parentElement) {
-    let elems = parentElement.querySelectorAll(`[${componentName}]`);
-    if (elems.length === 0) return;
-
-    let component = componentMapping[componentName];
-    if (typeof window.components[componentName] === "undefined") window.components[componentName] = [];
+function searchForComponentInParent(componentName, parentElement) {
+    const elems = parentElement.querySelectorAll(`[${componentName}]`);
     for (let j = 0, jLen = elems.length; j < jLen; j++) {
-        let instance = new component(elems[j]);
-        if (typeof elems[j].components === 'undefined') elems[j].components = {};
-        elems[j].components[componentName] = instance;
-        window.components[componentName].push(instance);
+        initComponent(componentName, elems[j]);
+    }
+}
+
+/**
+ * Initialize a component instance on the given dom element.
+ * @param {String} name
+ * @param {Element} element
+ */
+function initComponent(name, element) {
+    const componentModel = componentMapping[name];
+    if (componentModel === undefined) return;
+
+    // Create our component instance
+    let instance;
+    try {
+        instance = new componentModel(element);
+        instance.$el = element;
+        const allRefs = parseRefs(name, element);
+        instance.$refs = allRefs.refs;
+        instance.$manyRefs = allRefs.manyRefs;
+        instance.$opts = parseOpts(name, element);
+        instance.$emit = (eventName, data = {}) => {
+            data.from = instance;
+            const event = new CustomEvent(`${name}-${eventName}`, {
+                bubbles: true,
+                detail: data
+            });
+            instance.$el.dispatchEvent(event);
+        };
+        if (typeof instance.setup === 'function') {
+            instance.setup();
+        }
+    } catch (e) {
+        console.error('Failed to create component', e, name, element);
+    }
+
+
+    // Add to global listing
+    if (typeof window.components[name] === "undefined") {
+        window.components[name] = [];
+    }
+    window.components[name].push(instance);
+
+    // Add to element listing
+    if (typeof element.components === 'undefined') {
+        element.components = {};
+    }
+    element.components[name] = instance;
+}
+
+/**
+ * Parse out the element references within the given element
+ * for the given component name.
+ * @param {String} name
+ * @param {Element} element
+ */
+function parseRefs(name, element) {
+    const refs = {};
+    const manyRefs = {};
+
+    const prefix = `${name}@`
+    const selector = `[refs*="${prefix}"]`;
+    const refElems = [...element.querySelectorAll(selector)];
+    if (element.matches(selector)) {
+        refElems.push(element);
+    }
+
+    for (const el of refElems) {
+        const refNames = el.getAttribute('refs')
+            .split(' ')
+            .filter(str => str.startsWith(prefix))
+            .map(str => str.replace(prefix, ''))
+            .map(kebabToCamel);
+        for (const ref of refNames) {
+            refs[ref] = el;
+            if (typeof manyRefs[ref] === 'undefined') {
+                manyRefs[ref] = [];
+            }
+            manyRefs[ref].push(el);
+        }
     }
+    return {refs, manyRefs};
+}
+
+/**
+ * Parse out the element component options.
+ * @param {String} name
+ * @param {Element} element
+ * @return {Object<String, String>}
+ */
+function parseOpts(name, element) {
+    const opts = {};
+    const prefix = `option:${name}:`;
+    for (const {name, value} of element.attributes) {
+        if (name.startsWith(prefix)) {
+            const optName = name.replace(prefix, '');
+            opts[kebabToCamel(optName)] = value || '';
+        }
+    }
+    return opts;
+}
+
+/**
+ * Convert a kebab-case string to camelCase
+ * @param {String} kebab
+ * @returns {string}
+ */
+function kebabToCamel(kebab) {
+    const ucFirst = (word) => word.slice(0,1).toUpperCase() + word.slice(1);
+    const words = kebab.split('-');
+    return words[0] + words.slice(1).map(ucFirst).join('');
 }
 
 /**
@@ -93,11 +236,33 @@ function initComponent(componentName, parentElement) {
  */
 function initAll(parentElement) {
     if (typeof parentElement === 'undefined') parentElement = document;
-    for (let i = 0, len = componentNames.length; i < len; i++) {
-        initComponent(componentNames[i], parentElement);
+
+    // Old attribute system
+    for (const componentName of Object.keys(componentMapping)) {
+        searchForComponentInParent(componentName, parentElement);
+    }
+
+    // New component system
+    const componentElems = parentElement.querySelectorAll(`[component],[components]`);
+
+    for (const el of componentElems) {
+        const componentNames = `${el.getAttribute('component') || ''} ${(el.getAttribute('components'))}`.toLowerCase().split(' ').filter(Boolean);
+        for (const name of componentNames) {
+            initComponent(name, el);
+        }
     }
 }
 
 window.components.init = initAll;
+window.components.first = (name) => (window.components[name] || [null])[0];
 
 export default initAll;
+
+/**
+ * @typedef Component
+ * @property {HTMLElement} $el
+ * @property {Object<String, HTMLElement>} $refs
+ * @property {Object<String, HTMLElement[]>} $manyRefs
+ * @property {Object<String, String>} $opts
+ * @property {function(string, Object)} $emit
+ */
\ No newline at end of file
index de256a8466a727add893f5370167f3db4ef34320..bd107f2bf7a00f53ed3404a27c95bcf3c1c7d1c3 100644 (file)
@@ -1,18 +1,19 @@
 import MarkdownIt from "markdown-it";
 import mdTasksLists from 'markdown-it-task-lists';
 import code from '../services/code';
+import Clipboard from "../services/clipboard";
 import {debounce} from "../services/util";
 
 import DrawIO from "../services/drawio";
 
 class MarkdownEditor {
 
-    constructor(elem) {
-        this.elem = elem;
+    setup() {
+        this.elem = this.$el;
 
-        const pageEditor = document.getElementById('page-editor');
-        this.pageId = pageEditor.getAttribute('page-id');
-        this.textDirection = pageEditor.getAttribute('text-direction');
+        this.pageId = this.$opts.pageId;
+        this.textDirection = this.$opts.textDirection;
+        this.imageUploadErrorText = this.$opts.imageUploadErrorText;
 
         this.markdown = new MarkdownIt({html: true});
         this.markdown.use(mdTasksLists, {label: true});
@@ -26,12 +27,18 @@ class MarkdownEditor {
 
         this.onMarkdownScroll = this.onMarkdownScroll.bind(this);
 
-        this.display.addEventListener('load', () => {
+        const displayLoad = () => {
             this.displayDoc = this.display.contentDocument;
             this.init();
-        });
+        };
+
+        if (this.display.contentDocument.readyState === 'complete') {
+            displayLoad();
+        } else {
+            this.display.addEventListener('load', displayLoad.bind(this));
+        }
 
-        window.$events.emitPublic(elem, 'editor-markdown::setup', {
+        window.$events.emitPublic(this.elem, 'editor-markdown::setup', {
             markdownIt: this.markdown,
             displayEl: this.display,
             codeMirrorInstance: this.cm,
@@ -75,6 +82,7 @@ class MarkdownEditor {
                 return;
             }
             if (action === 'insertDrawing') this.actionStartDrawing();
+            if (action === 'fullscreen') this.actionFullScreen();
         });
 
         // Mobile section toggling
@@ -125,7 +133,13 @@ class MarkdownEditor {
 
     loadStylesIntoDisplay() {
         if (this.displayStylesLoaded) return;
-        this.displayDoc.documentElement.className = 'markdown-editor-display';
+        this.displayDoc.documentElement.classList.add('markdown-editor-display');
+        // Set display to be dark mode if parent is
+
+        if (document.documentElement.classList.contains('dark-mode')) {
+            this.displayDoc.documentElement.style.backgroundColor = '#222';
+            this.displayDoc.documentElement.classList.add('dark-mode');
+        }
 
         this.displayDoc.head.innerHTML = '';
         const styles = document.head.querySelectorAll('style,link[rel=stylesheet]');
@@ -215,20 +229,16 @@ class MarkdownEditor {
 
         // Handle image paste
         cm.on('paste', (cm, event) => {
-            const clipboardItems = event.clipboardData.items;
-            if (!event.clipboardData || !clipboardItems) return;
+            const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
 
-            // Don't handle if clipboard includes text content
-            for (let clipboardItem of clipboardItems) {
-                if (clipboardItem.type.includes('text/')) {
-                    return;
-                }
+            // Don't handle the event ourselves if no items exist of contains table-looking data
+            if (!clipboard.hasItems() || clipboard.containsTabularData()) {
+                return;
             }
 
-            for (let clipboardItem of clipboardItems) {
-                if (clipboardItem.type.includes("image")) {
-                    uploadImage(clipboardItem.getAsFile());
-                }
+            const images = clipboard.getImages();
+            for (const image of images) {
+                uploadImage(image);
             }
         });
 
@@ -246,13 +256,15 @@ class MarkdownEditor {
                 });
             }
 
-            if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
+            const clipboard = new Clipboard(event.dataTransfer);
+            if (clipboard.hasItems() && clipboard.getImages().length > 0) {
                 const cursorPos = cm.coordsChar({left: event.pageX, top: event.pageY});
                 cm.setCursor(cursorPos);
                 event.stopPropagation();
                 event.preventDefault();
-                for (let i = 0; i < event.dataTransfer.files.length; i++) {
-                    uploadImage(event.dataTransfer.files[i]);
+                const images = clipboard.getImages();
+                for (const image of images) {
+                    uploadImage(image);
                 }
             }
 
@@ -362,7 +374,7 @@ class MarkdownEditor {
                 const newContent = `[![${selectedText}](${resp.data.thumbs.display})](${resp.data.url})`;
                 replaceContent(placeHolderText, newContent);
             }).catch(err => {
-                window.$events.emit('error', trans('errors.image_upload_error'));
+                window.$events.emit('error', context.imageUploadErrorText);
                 replaceContent(placeHolderText, selectedText);
                 console.log(err);
             });
@@ -411,22 +423,28 @@ class MarkdownEditor {
         });
     }
 
+    getDrawioUrl() {
+        const drawioUrlElem = document.querySelector('[drawio-url]');
+        return drawioUrlElem ? drawioUrlElem.getAttribute('drawio-url') : false;
+    }
+
     // Show draw.io if enabled and handle save.
     actionStartDrawing() {
-        if (document.querySelector('[drawio-enabled]').getAttribute('drawio-enabled') !== 'true') return;
-        let cursorPos = this.cm.getCursor('from');
+        const url = this.getDrawioUrl();
+        if (!url) return;
 
-        DrawIO.show(() => {
+        const cursorPos = this.cm.getCursor('from');
+
+        DrawIO.show(url,() => {
             return Promise.resolve('');
         }, (pngData) => {
-            // let id = "image-" + Math.random().toString(16).slice(2);
-            // let loadingImage = window.baseUrl('/loading.gif');
-            let data = {
+
+            const data = {
                 image: pngData,
-                uploaded_to: Number(document.getElementById('page-editor').getAttribute('page-id'))
+                uploaded_to: Number(this.pageId),
             };
 
-            window.$http.post(window.baseUrl('/images/drawio'), data).then(resp => {
+            window.$http.post("/images/drawio", data).then(resp => {
                 this.insertDrawing(resp.data, cursorPos);
                 DrawIO.close();
             }).catch(err => {
@@ -445,24 +463,24 @@ class MarkdownEditor {
 
     // Show draw.io if enabled and handle save.
     actionEditDrawing(imgContainer) {
-        const drawingDisabled = document.querySelector('[drawio-enabled]').getAttribute('drawio-enabled') !== 'true';
-        if (drawingDisabled) {
+        const drawioUrl = this.getDrawioUrl();
+        if (!drawioUrl) {
             return;
         }
 
         const cursorPos = this.cm.getCursor('from');
         const drawingId = imgContainer.getAttribute('drawio-diagram');
 
-        DrawIO.show(() => {
+        DrawIO.show(drawioUrl, () => {
             return DrawIO.load(drawingId);
         }, (pngData) => {
 
             let data = {
                 image: pngData,
-                uploaded_to: Number(document.getElementById('page-editor').getAttribute('page-id'))
+                uploaded_to: Number(this.pageId),
             };
 
-            window.$http.post(window.baseUrl(`/images/drawio`), data).then(resp => {
+            window.$http.post("/images/drawio", data).then(resp => {
                 let newText = `<div drawio-diagram="${resp.data.id}"><img src="${resp.data.url}"></div>`;
                 let newContent = this.cm.getValue().split('\n').map(line => {
                     if (line.indexOf(`drawio-diagram="${drawingId}"`) !== -1) {
@@ -475,12 +493,19 @@ class MarkdownEditor {
                 this.cm.focus();
                 DrawIO.close();
             }).catch(err => {
-                window.$events.emit('error', trans('errors.image_upload_error'));
+                window.$events.emit('error', this.imageUploadErrorText);
                 console.log(err);
             });
         });
     }
 
+    // Make the editor full screen
+    actionFullScreen() {
+        const alreadyFullscreen = this.elem.classList.contains('fullscreen');
+        this.elem.classList.toggle('fullscreen', !alreadyFullscreen);
+        document.body.classList.toggle('markdown-fullscreen', !alreadyFullscreen);
+    }
+
     // Scroll to a specified text
     scrollToText(searchText) {
         if (!searchText) {
@@ -538,6 +563,17 @@ class MarkdownEditor {
             const prependLineCount = markdown.split('\n').length;
             this.cm.setCursor(cursorPos.line + prependLineCount, cursorPos.ch);
         });
+
+        // Insert editor content at the current location
+        window.$events.listen('editor::insert', (eventContent) => {
+            const markdown = getContentToInsert(eventContent);
+            this.cm.replaceSelection(markdown);
+        });
+
+        // Focus on editor
+        window.$events.listen('editor::focus', () => {
+            this.cm.focus();
+        });
     }
 }
 
index f7edb08aa1327fda20c76728f2fb8add65119b60..35bab4ea656b1c052e875fac2bcdf0e4ef27d268 100644 (file)
@@ -28,7 +28,11 @@ class Notification {
             this.elem.classList.add('showing');
         }, 1);
 
-        if (this.autohide) setTimeout(this.hide.bind(this), 2000);
+        if (this.autohide) {
+            const words = textToShow.split(' ').length;
+            const timeToShow = Math.max(2000, 1000 + (250 * words));
+            setTimeout(this.hide.bind(this), timeToShow);
+        }
     }
 
     hide() {
diff --git a/resources/js/components/optional-input.js b/resources/js/components/optional-input.js
new file mode 100644 (file)
index 0000000..eab58e4
--- /dev/null
@@ -0,0 +1,28 @@
+import {onSelect} from "../services/dom";
+
+class OptionalInput {
+    setup() {
+        this.removeButton = this.$refs.remove;
+        this.showButton = this.$refs.show;
+        this.input = this.$refs.input;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        onSelect(this.removeButton, () => {
+            this.input.value = '';
+            this.input.classList.add('hidden');
+            this.removeButton.classList.add('hidden');
+            this.showButton.classList.remove('hidden');
+        });
+
+        onSelect(this.showButton, () => {
+            this.input.classList.remove('hidden');
+            this.removeButton.classList.remove('hidden');
+            this.showButton.classList.add('hidden');
+        });
+    }
+
+}
+
+export default OptionalInput;
\ No newline at end of file
diff --git a/resources/js/components/overlay.js b/resources/js/components/overlay.js
deleted file mode 100644 (file)
index ad6a010..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-
-class Overlay {
-
-    constructor(elem) {
-        this.container = elem;
-        elem.addEventListener('click', event => {
-             if (event.target === elem) return this.hide();
-        });
-
-        window.addEventListener('keyup', event => {
-            if (event.key === 'Escape') {
-                this.hide();
-            }
-        });
-
-        let closeButtons = elem.querySelectorAll('.popup-header-close');
-        for (let i=0; i < closeButtons.length; i++) {
-            closeButtons[i].addEventListener('click', this.hide.bind(this));
-        }
-    }
-
-    hide() { this.toggle(false); }
-    show() { this.toggle(true); }
-
-    toggle(show = true) {
-        let start = Date.now();
-        let duration = 240;
-
-        function setOpacity() {
-            let elapsedTime = (Date.now() - start);
-            let targetOpacity = show ? (elapsedTime / duration) : 1-(elapsedTime / duration);
-            this.container.style.opacity = targetOpacity;
-            if (elapsedTime > duration) {
-                this.container.style.display = show ? 'flex' : 'none';
-                if (show) {
-                    this.focusOnBody();
-                }
-                this.container.style.opacity = '';
-            } else {
-                requestAnimationFrame(setOpacity.bind(this));
-            }
-        }
-
-        requestAnimationFrame(setOpacity.bind(this));
-    }
-
-    focusOnBody() {
-        const body = this.container.querySelector('.popup-body');
-        if (body) {
-            body.focus();
-        }
-    }
-
-}
-
-export default Overlay;
\ No newline at end of file
index 5d8d169589804a5c92fd355281353bdcdaf6ed88..c86eead1b865bd8bdaa8184f44bd4ab55a961d7b 100644 (file)
@@ -1,19 +1,31 @@
-import MarkdownIt from "markdown-it";
 import {scrollAndHighlightElement} from "../services/util";
 
-const md = new MarkdownIt({ html: false });
-
+/**
+ * @extends {Component}
+ */
 class PageComments {
 
-    constructor(elem) {
-        this.elem = elem;
-        this.pageId = Number(elem.getAttribute('page-id'));
+    setup() {
+        this.elem = this.$el;
+        this.pageId = Number(this.$opts.pageId);
+
+        // Element references
+        this.container = this.$refs.commentContainer;
+        this.formContainer = this.$refs.formContainer;
+        this.commentCountBar = this.$refs.commentCountBar;
+        this.addButtonContainer = this.$refs.addButtonContainer;
+        this.replyToRow = this.$refs.replyToRow;
+
+        // Translations
+        this.updatedText = this.$opts.updatedText;
+        this.deletedText = this.$opts.deletedText;
+        this.createdText = this.$opts.createdText;
+        this.countText = this.$opts.countText;
+
+        // Internal State
         this.editingComment = null;
         this.parentId = null;
 
-        this.container = elem.querySelector('[comment-container]');
-        this.formContainer = elem.querySelector('[comment-form-container]');
-
         if (this.formContainer) {
             this.form = this.formContainer.querySelector('form');
             this.formInput = this.form.querySelector('textarea');
@@ -35,13 +47,14 @@ class PageComments {
         if (actionElem === null) return;
         event.preventDefault();
 
-        let action = actionElem.getAttribute('action');
-        if (action === 'edit') this.editComment(actionElem.closest('[comment]'));
+        const action = actionElem.getAttribute('action');
+        const comment = actionElem.closest('[comment]');
+        if (action === 'edit') this.editComment(comment);
         if (action === 'closeUpdateForm') this.closeUpdateForm();
-        if (action === 'delete') this.deleteComment(actionElem.closest('[comment]'));
+        if (action === 'delete') this.deleteComment(comment);
         if (action === 'addComment') this.showForm();
         if (action === 'hideForm') this.hideForm();
-        if (action === 'reply') this.setReply(actionElem.closest('[comment]'));
+        if (action === 'reply') this.setReply(comment);
         if (action === 'remove-reply-to') this.removeReplyTo();
     }
 
@@ -68,19 +81,19 @@ class PageComments {
         let text = form.querySelector('textarea').value;
         let reqData = {
             text: text,
-            html: md.render(text),
             parent_id: this.parentId || null,
         };
         this.showLoading(form);
         let commentId = this.editingComment.getAttribute('comment');
-        window.$http.put(window.baseUrl(`/ajax/comment/${commentId}`), reqData).then(resp => {
+        window.$http.put(`/comment/${commentId}`, reqData).then(resp => {
             let newComment = document.createElement('div');
             newComment.innerHTML = resp.data;
             this.editingComment.innerHTML = newComment.children[0].innerHTML;
-            window.$events.emit('success', window.trans('entities.comment_updated_success'));
+            window.$events.success(this.updatedText);
             window.components.init(this.editingComment);
             this.closeUpdateForm();
             this.editingComment = null;
+        }).catch(window.$events.showValidationErrors).then(() => {
             this.hideLoading(form);
         });
     }
@@ -88,9 +101,9 @@ class PageComments {
     deleteComment(commentElem) {
         let id = commentElem.getAttribute('comment');
         this.showLoading(commentElem.querySelector('[comment-content]'));
-        window.$http.delete(window.baseUrl(`/ajax/comment/${id}`)).then(resp => {
+        window.$http.delete(`/comment/${id}`).then(resp => {
             commentElem.parentNode.removeChild(commentElem);
-            window.$events.emit('success', window.trans('entities.comment_deleted_success'));
+            window.$events.success(this.deletedText);
             this.updateCount();
             this.hideForm();
         });
@@ -102,25 +115,27 @@ class PageComments {
         let text = this.formInput.value;
         let reqData = {
             text: text,
-            html: md.render(text),
             parent_id: this.parentId || null,
         };
         this.showLoading(this.form);
-        window.$http.post(window.baseUrl(`/ajax/page/${this.pageId}/comment`), reqData).then(resp => {
+        window.$http.post(`/comment/${this.pageId}`, reqData).then(resp => {
             let newComment = document.createElement('div');
             newComment.innerHTML = resp.data;
             let newElem = newComment.children[0];
             this.container.appendChild(newElem);
             window.components.init(newElem);
-            window.$events.emit('success', window.trans('entities.comment_created_success'));
+            window.$events.success(this.createdText);
             this.resetForm();
             this.updateCount();
+        }).catch(err => {
+            window.$events.showValidationErrors(err);
+            this.hideLoading(this.form);
         });
     }
 
     updateCount() {
         let count = this.container.children.length;
-        this.elem.querySelector('[comments-title]').textContent = window.trans_choice('entities.comment_count', count, {count});
+        this.elem.querySelector('[comments-title]').textContent = window.trans_plural(this.countText, count, {count});
     }
 
     resetForm() {
@@ -134,7 +149,7 @@ class PageComments {
     showForm() {
         this.formContainer.style.display = 'block';
         this.formContainer.parentNode.style.display = 'block';
-        this.elem.querySelector('[comment-add-button-container]').style.display = 'none';
+        this.addButtonContainer.style.display = 'none';
         this.formInput.focus();
         this.formInput.scrollIntoView({behavior: "smooth"});
     }
@@ -142,14 +157,12 @@ class PageComments {
     hideForm() {
         this.formContainer.style.display = 'none';
         this.formContainer.parentNode.style.display = 'none';
-        const addButtonContainer = this.elem.querySelector('[comment-add-button-container]');
         if (this.getCommentCount() > 0) {
-            this.elem.appendChild(addButtonContainer)
+            this.elem.appendChild(this.addButtonContainer)
         } else {
-            const countBar = this.elem.querySelector('[comment-count-bar]');
-            countBar.appendChild(addButtonContainer);
+            this.commentCountBar.appendChild(this.addButtonContainer);
         }
-        addButtonContainer.style.display = 'block';
+        this.addButtonContainer.style.display = 'block';
     }
 
     getCommentCount() {
@@ -159,29 +172,29 @@ class PageComments {
     setReply(commentElem) {
         this.showForm();
         this.parentId = Number(commentElem.getAttribute('local-id'));
-        this.elem.querySelector('[comment-form-reply-to]').style.display = 'block';
-        let replyLink = this.elem.querySelector('[comment-form-reply-to] a');
+        this.replyToRow.style.display = 'block';
+        const replyLink = this.replyToRow.querySelector('a');
         replyLink.textContent = `#${this.parentId}`;
         replyLink.href = `#comment${this.parentId}`;
     }
 
     removeReplyTo() {
         this.parentId = null;
-        this.elem.querySelector('[comment-form-reply-to]').style.display = 'none';
+        this.replyToRow.style.display = 'none';
     }
 
     showLoading(formElem) {
-        let groups = formElem.querySelectorAll('.form-group');
-        for (let i = 0, len = groups.length; i < len; i++) {
-            groups[i].style.display = 'none';
+        const groups = formElem.querySelectorAll('.form-group');
+        for (let group of groups) {
+            group.style.display = 'none';
         }
         formElem.querySelector('.form-group.loading').style.display = 'block';
     }
 
     hideLoading(formElem) {
-        let groups = formElem.querySelectorAll('.form-group');
-        for (let i = 0, len = groups.length; i < len; i++) {
-            groups[i].style.display = 'block';
+        const groups = formElem.querySelectorAll('.form-group');
+        for (let group of groups) {
+            group.style.display = 'block';
         }
         formElem.querySelector('.form-group.loading').style.display = 'none';
     }
diff --git a/resources/js/components/page-editor.js b/resources/js/components/page-editor.js
new file mode 100644 (file)
index 0000000..f66e23b
--- /dev/null
@@ -0,0 +1,183 @@
+import * as Dates from "../services/dates";
+import {onSelect} from "../services/dom";
+
+/**
+ * Page Editor
+ * @extends {Component}
+ */
+class PageEditor {
+    setup() {
+        // Options
+        this.draftsEnabled = this.$opts.draftsEnabled === 'true';
+        this.editorType = this.$opts.editorType;
+        this.pageId = Number(this.$opts.pageId);
+        this.isNewDraft = this.$opts.pageNewDraft === 'true';
+        this.hasDefaultTitle = this.$opts.hasDefaultTitle || false;
+
+        // Elements
+        this.container = this.$el;
+        this.titleElem = this.$refs.titleContainer.querySelector('input');
+        this.saveDraftButton = this.$refs.saveDraft;
+        this.discardDraftButton = this.$refs.discardDraft;
+        this.discardDraftWrap = this.$refs.discardDraftWrap;
+        this.draftDisplay = this.$refs.draftDisplay;
+        this.draftDisplayIcon = this.$refs.draftDisplayIcon;
+        this.changelogInput = this.$refs.changelogInput;
+        this.changelogDisplay = this.$refs.changelogDisplay;
+
+        // Translations
+        this.draftText = this.$opts.draftText;
+        this.autosaveFailText = this.$opts.autosaveFailText;
+        this.editingPageText = this.$opts.editingPageText;
+        this.draftDiscardedText = this.$opts.draftDiscardedText;
+        this.setChangelogText = this.$opts.setChangelogText;
+
+        // State data
+        this.editorHTML = '';
+        this.editorMarkdown = '';
+        this.autoSave = {
+            interval: null,
+            frequency: 30000,
+            last: 0,
+        };
+
+        if (this.pageId !== 0 && this.draftsEnabled) {
+            window.setTimeout(() => {
+                this.startAutoSave();
+            }, 1000);
+        }
+        this.draftDisplay.innerHTML = this.draftText;
+
+        this.setupListeners();
+        this.setInitialFocus();
+    }
+
+    setupListeners() {
+        // Listen to save events from editor
+        window.$events.listen('editor-save-draft', this.saveDraft.bind(this));
+        window.$events.listen('editor-save-page', this.savePage.bind(this));
+
+        // Listen to content changes from the editor
+        window.$events.listen('editor-html-change', html => {
+            this.editorHTML = html;
+        });
+        window.$events.listen('editor-markdown-change', markdown => {
+            this.editorMarkdown = markdown;
+        });
+
+        // Changelog controls
+        this.changelogInput.addEventListener('change', this.updateChangelogDisplay.bind(this));
+
+        // Draft Controls
+        onSelect(this.saveDraftButton, this.saveDraft.bind(this));
+        onSelect(this.discardDraftButton, this.discardDraft.bind(this));
+    }
+
+    setInitialFocus() {
+        if (this.hasDefaultTitle) {
+            return this.titleElem.select();
+        }
+
+        window.setTimeout(() => {
+            window.$events.emit('editor::focus', '');
+        }, 500);
+    }
+
+    startAutoSave() {
+        let lastContent = this.titleElem.value.trim() + '::' + this.editorHTML;
+        this.autoSaveInterval = window.setInterval(() => {
+            // Stop if manually saved recently to prevent bombarding the server
+            let savedRecently = (Date.now() - this.autoSave.last < (this.autoSave.frequency)/2);
+            if (savedRecently) return;
+            const newContent = this.titleElem.value.trim() + '::' + this.editorHTML;
+            if (newContent !== lastContent) {
+                lastContent = newContent;
+                this.saveDraft();
+            }
+
+        }, this.autoSave.frequency);
+    }
+
+    savePage() {
+        this.container.closest('form').submit();
+    }
+
+    async saveDraft() {
+        const data = {
+            name: this.titleElem.value.trim(),
+            html: this.editorHTML,
+        };
+
+        if (this.editorType === 'markdown') {
+            data.markdown = this.editorMarkdown;
+        }
+
+        try {
+            const resp = await window.$http.put(`/ajax/page/${this.pageId}/save-draft`, data);
+            if (!this.isNewDraft) {
+                this.toggleDiscardDraftVisibility(true);
+            }
+            this.draftNotifyChange(`${resp.data.message} ${Dates.utcTimeStampToLocalTime(resp.data.timestamp)}`);
+            this.autoSave.last = Date.now();
+        } catch (err) {
+            // Save the editor content in LocalStorage as a last resort, just in case.
+            try {
+                const saveKey = `draft-save-fail-${(new Date()).toISOString()}`;
+                window.localStorage.setItem(saveKey, JSON.stringify(data));
+            } catch (err) {}
+
+            window.$events.emit('error', this.autosaveFailText);
+        }
+
+    }
+
+    draftNotifyChange(text) {
+        this.draftDisplay.innerText = text;
+        this.draftDisplayIcon.classList.add('visible');
+        window.setTimeout(() => {
+            this.draftDisplayIcon.classList.remove('visible');
+        }, 2000);
+    }
+
+    async discardDraft() {
+        let response;
+        try {
+            response = await window.$http.get(`/ajax/page/${this.pageId}`);
+        } catch (e) {
+            return console.error(e);
+        }
+
+        if (this.autoSave.interval) {
+            window.clearInterval(this.autoSave.interval);
+        }
+
+        this.draftDisplay.innerText = this.editingPageText;
+        this.toggleDiscardDraftVisibility(false);
+        window.$events.emit('editor-html-update', response.data.html || '');
+        window.$events.emit('editor-markdown-update', response.data.markdown || response.data.html);
+
+        this.titleElem.value = response.data.name;
+        window.setTimeout(() => {
+            this.startAutoSave();
+        }, 1000);
+        window.$events.emit('success', this.draftDiscardedText);
+
+    }
+
+    updateChangelogDisplay() {
+        let summary = this.changelogInput.value.trim();
+        if (summary.length === 0) {
+            summary = this.setChangelogText;
+        } else if (summary.length > 16) {
+            summary = summary.slice(0, 16) + '...';
+        }
+        this.changelogDisplay.innerText = summary;
+    }
+
+    toggleDiscardDraftVisibility(show) {
+        this.discardDraftWrap.classList.toggle('hidden', !show);
+    }
+
+}
+
+export default PageEditor;
\ No newline at end of file
diff --git a/resources/js/components/popup.js b/resources/js/components/popup.js
new file mode 100644 (file)
index 0000000..13cf69d
--- /dev/null
@@ -0,0 +1,61 @@
+import {fadeIn, fadeOut} from "../services/animations";
+import {onSelect} from "../services/dom";
+
+/**
+ * Popup window that will contain other content.
+ * This component provides the show/hide functionality
+ * with the ability for popup@hide child references to close this.
+ * @extends {Component}
+ */
+class Popup {
+
+    setup() {
+        this.container = this.$el;
+        this.hideButtons = this.$manyRefs.hide || [];
+
+        this.onkeyup = null;
+        this.onHide = null;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        let lastMouseDownTarget = null;
+        this.container.addEventListener('mousedown', event => {
+            lastMouseDownTarget = event.target;
+        });
+
+        this.container.addEventListener('click', event => {
+            if (event.target === this.container && lastMouseDownTarget === this.container) {
+                return this.hide();
+            }
+        });
+
+        onSelect(this.hideButtons, e => this.hide());
+    }
+
+    hide(onComplete = null) {
+        fadeOut(this.container, 240, onComplete);
+        if (this.onkeyup) {
+            window.removeEventListener('keyup', this.onkeyup);
+            this.onkeyup = null;
+        }
+        if (this.onHide) {
+            this.onHide();
+        }
+    }
+
+    show(onComplete = null, onHide = null) {
+        fadeIn(this.container, 240, onComplete);
+
+        this.onkeyup = (event) => {
+            if (event.key === 'Escape') {
+                this.hide();
+            }
+        };
+        window.addEventListener('keyup', this.onkeyup);
+        this.onHide = onHide;
+    }
+
+}
+
+export default Popup;
\ No newline at end of file
index 6c0c0b31dcecb86fbf39fea7c6dbecb496d1822f..ee894c9325c5c5fc0bef05f684a2e6e8032f3661 100644 (file)
@@ -6,11 +6,16 @@ class SettingAppColorPicker {
         this.colorInput = elem.querySelector('input[type=color]');
         this.lightColorInput = elem.querySelector('input[name="setting-app-color-light"]');
         this.resetButton = elem.querySelector('[setting-app-color-picker-reset]');
+        this.defaultButton = elem.querySelector('[setting-app-color-picker-default]');
 
         this.colorInput.addEventListener('change', this.updateColor.bind(this));
         this.colorInput.addEventListener('input', this.updateColor.bind(this));
         this.resetButton.addEventListener('click', event => {
-            this.colorInput.value = '#206ea7';
+            this.colorInput.value = this.colorInput.dataset.current;
+            this.updateColor();
+        });
+        this.defaultButton.addEventListener('click', event => {
+            this.colorInput.value = this.colorInput.dataset.default;
             this.updateColor();
         });
     }
@@ -53,4 +58,4 @@ class SettingAppColorPicker {
 
 }
 
-export default SettingAppColorPicker;
\ No newline at end of file
+export default SettingAppColorPicker;
diff --git a/resources/js/components/setting-color-picker.js b/resources/js/components/setting-color-picker.js
new file mode 100644 (file)
index 0000000..4d8ce0f
--- /dev/null
@@ -0,0 +1,18 @@
+
+class SettingColorPicker {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.colorInput = elem.querySelector('input[type=color]');
+        this.resetButton = elem.querySelector('[setting-color-picker-reset]');
+        this.defaultButton = elem.querySelector('[setting-color-picker-default]');
+        this.resetButton.addEventListener('click', event => {
+            this.colorInput.value = this.colorInput.dataset.current;
+        });
+        this.defaultButton.addEventListener('click', event => {
+          this.colorInput.value = this.colorInput.dataset.default;
+        });
+    }
+}
+
+export default SettingColorPicker;
diff --git a/resources/js/components/sortable-list.js b/resources/js/components/sortable-list.js
new file mode 100644 (file)
index 0000000..0af0e11
--- /dev/null
@@ -0,0 +1,39 @@
+import Sortable from "sortablejs";
+
+/**
+ * SortableList
+ *
+ * Can have data set on the dragged items by setting a 'data-drag-content' attribute.
+ * This attribute must contain JSON where the keys are content types and the values are
+ * the data to set on the data-transfer.
+ *
+ * @extends {Component}
+ */
+class SortableList {
+    setup() {
+        this.container = this.$el;
+        this.handleSelector = this.$opts.handleSelector;
+
+        const sortable = new Sortable(this.container, {
+            handle: this.handleSelector,
+            animation: 150,
+            onSort: () => {
+                this.$emit('sort', {ids: sortable.toArray()});
+            },
+            setData(dataTransferItem, dragEl) {
+                const jsonContent = dragEl.getAttribute('data-drag-content');
+                if (jsonContent) {
+                    const contentByType = JSON.parse(jsonContent);
+                    for (const [type, content] of Object.entries(contentByType)) {
+                        dataTransferItem.setData(type, content);
+                    }
+                }
+            },
+            revertOnSpill: true,
+            dropBubble: true,
+            dragoverBubble: false,
+        });
+    }
+}
+
+export default SortableList;
\ No newline at end of file
diff --git a/resources/js/components/submit-on-change.js b/resources/js/components/submit-on-change.js
new file mode 100644 (file)
index 0000000..9799672
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * Submit on change
+ * Simply submits a parent form when this input is changed.
+ * @extends {Component}
+ */
+class SubmitOnChange {
+
+    setup() {
+        this.$el.addEventListener('change', () => {
+            const form = this.$el.closest('form');
+            if (form) {
+                form.submit();
+            }
+        });
+    }
+
+}
+
+export default SubmitOnChange;
\ No newline at end of file
diff --git a/resources/js/components/tabs.js b/resources/js/components/tabs.js
new file mode 100644 (file)
index 0000000..7121d70
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * Tabs
+ * Works by matching 'tabToggle<Key>' with 'tabContent<Key>' sections.
+ * @extends {Component}
+ */
+import {onSelect} from "../services/dom";
+
+class Tabs {
+
+    setup() {
+        this.tabContentsByName = {};
+        this.tabButtonsByName = {};
+        this.allContents = [];
+        this.allButtons = [];
+
+        for (const [key, elems] of Object.entries(this.$manyRefs || {})) {
+            if (key.startsWith('toggle')) {
+                const cleanKey = key.replace('toggle', '').toLowerCase();
+                onSelect(elems, e => this.show(cleanKey));
+                this.allButtons.push(...elems);
+                this.tabButtonsByName[cleanKey] = elems;
+            }
+            if (key.startsWith('content')) {
+                const cleanKey = key.replace('content', '').toLowerCase();
+                this.tabContentsByName[cleanKey] = elems;
+                this.allContents.push(...elems);
+            }
+        }
+    }
+
+    show(key) {
+        this.allContents.forEach(c => {
+            c.classList.add('hidden');
+            c.classList.remove('selected');
+        });
+        this.allButtons.forEach(b => b.classList.remove('selected'));
+
+        const contents = this.tabContentsByName[key] || [];
+        const buttons = this.tabButtonsByName[key] || [];
+        if (contents.length > 0) {
+            contents.forEach(c => {
+                c.classList.remove('hidden')
+                c.classList.add('selected')
+            });
+            buttons.forEach(b => b.classList.add('selected'));
+        }
+    }
+
+}
+
+export default Tabs;
\ No newline at end of file
diff --git a/resources/js/components/tag-manager.js b/resources/js/components/tag-manager.js
new file mode 100644 (file)
index 0000000..99302b6
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * TagManager
+ * @extends {Component}
+ */
+class TagManager {
+    setup() {
+        this.addRemoveComponentEl = this.$refs.addRemove;
+        this.container = this.$el;
+        this.rowSelector = this.$opts.rowSelector;
+
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.container.addEventListener('change', event => {
+            const addRemoveComponent = this.addRemoveComponentEl.components['add-remove-rows'];
+            if (!this.hasEmptyRows()) {
+                addRemoveComponent.add();
+            }
+        });
+    }
+
+    hasEmptyRows() {
+        const rows = this.container.querySelectorAll(this.rowSelector);
+        const firstEmpty = [...rows].find(row => {
+            return [...row.querySelectorAll('input')].filter(input => input.value).length === 0;
+        });
+        return firstEmpty !== undefined;
+    }
+}
+
+export default TagManager;
\ No newline at end of file
index d004a43076e2e788aa3983f08283eb304ee212a2..f8b19a40c86c5baf51d4239e887c55beba97c48b 100644 (file)
@@ -56,6 +56,10 @@ class TemplateManager {
 
     setupSearchBox() {
         const searchBox = this.elem.querySelector('.search-box');
+
+        // Search box may not exist if there are no existing templates in the system.
+        if (!searchBox) return;
+
         const input = searchBox.querySelector('input');
         const submitButton = searchBox.querySelector('button');
         const cancelButton = searchBox.querySelector('button.search-box-cancel');
@@ -70,7 +74,7 @@ class TemplateManager {
         }
         performSearch = performSearch.bind(this);
 
-        // Searchbox enter press
+        // Search box enter press
         searchBox.addEventListener('keypress', event => {
             if (event.key === 'Enter') {
                 event.preventDefault();
diff --git a/resources/js/components/user-select.js b/resources/js/components/user-select.js
new file mode 100644 (file)
index 0000000..477c11d
--- /dev/null
@@ -0,0 +1,24 @@
+import {onChildEvent} from "../services/dom";
+
+class UserSelect {
+
+    setup() {
+
+        this.input = this.$refs.input;
+        this.userInfoContainer = this.$refs.userInfo;
+
+        this.hide = this.$el.components.dropdown.hide;
+
+        onChildEvent(this.$el, 'a.dropdown-search-item', 'click', this.selectUser.bind(this));
+    }
+
+    selectUser(event, userEl) {
+        const id = userEl.getAttribute('data-id');
+        this.input.value = id;
+        this.userInfoContainer.innerHTML = userEl.innerHTML;
+        this.hide();
+    }
+
+}
+
+export default UserSelect;
\ No newline at end of file
index 60a6743ea7679bdebb7100bd2c047e1b26101166..41b2273e2a0ad48918e5e50c334f5c1e29ef319f 100644 (file)
@@ -1,5 +1,6 @@
 import Code from "../services/code";
 import DrawIO from "../services/drawio";
+import Clipboard from "../services/clipboard";
 
 /**
  * Handle pasting images from clipboard.
@@ -8,33 +9,36 @@ import DrawIO from "../services/drawio";
  * @param editor
  */
 function editorPaste(event, editor, wysiwygComponent) {
-    const clipboardItems = event.clipboardData.items;
-    if (!event.clipboardData || !clipboardItems) return;
+    const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
 
-    // Don't handle if clipboard includes text content
-    for (let clipboardItem of clipboardItems) {
-        if (clipboardItem.type.includes('text/')) {
-            return;
-        }
+    // Don't handle the event ourselves if no items exist of contains table-looking data
+    if (!clipboard.hasItems() || clipboard.containsTabularData()) {
+        return;
     }
 
-    for (let clipboardItem of clipboardItems) {
-        if (!clipboardItem.type.includes("image")) {
-            continue;
-        }
+    const images = clipboard.getImages();
+    for (const imageFile of images) {
 
         const id = "image-" + Math.random().toString(16).slice(2);
         const loadingImage = window.baseUrl('/loading.gif');
-        const file = clipboardItem.getAsFile();
+        event.preventDefault();
 
         setTimeout(() => {
             editor.insertContent(`<p><img src="${loadingImage}" id="${id}"></p>`);
 
-            uploadImageFile(file, wysiwygComponent).then(resp => {
-                editor.dom.setAttrib(id, 'src', resp.thumbs.display);
+            uploadImageFile(imageFile, wysiwygComponent).then(resp => {
+                const safeName = resp.name.replace(/"/g, '');
+                const newImageHtml = `<img src="${resp.thumbs.display}" alt="${safeName}" />`;
+
+                const newEl = editor.dom.create('a', {
+                    target: '_blank',
+                    href: resp.url,
+                }, newImageHtml);
+
+                editor.dom.replace(newEl, id);
             }).catch(err => {
                 editor.dom.remove(id);
-                window.$events.emit('error', trans('errors.image_upload_error'));
+                window.$events.emit('error', wysiwygComponent.imageUploadErrorText);
                 console.log(err);
             });
         }, 10);
@@ -94,21 +98,15 @@ function registerEditorShortcuts(editor) {
 
     // Loop through callout styles
     editor.shortcuts.add('meta+9', '', function() {
-        let selectedNode = editor.selection.getNode();
-        let formats = ['info', 'success', 'warning', 'danger'];
+        const selectedNode = editor.selection.getNode();
+        const callout = selectedNode ? selectedNode.closest('.callout') : null;
 
-        if (!selectedNode || selectedNode.className.indexOf('callout') === -1) {
-            editor.formatter.apply('calloutinfo');
-            return;
-        }
+        const formats = ['info', 'success', 'warning', 'danger'];
+        const currentFormatIndex = formats.findIndex(format => callout && callout.classList.contains(format));
+        const newFormatIndex = (currentFormatIndex + 1) % formats.length;
+        const newFormat = formats[newFormatIndex];
 
-        for (let i = 0; i < formats.length; i++) {
-            if (selectedNode.className.indexOf(formats[i]) === -1) continue;
-            let newFormat = (i === formats.length -1) ? formats[0] : formats[i+1];
-            editor.formatter.apply('callout' + newFormat);
-            return;
-        }
-        editor.formatter.apply('p');
+        editor.formatter.apply('callout' + newFormat);
     });
 
 }
@@ -135,19 +133,21 @@ function codePlugin() {
     }
 
     function showPopup(editor) {
-        let selectedNode = editor.selection.getNode();
+        const selectedNode = editor.selection.getNode();
 
         if (!elemIsCodeBlock(selectedNode)) {
-            let providedCode = editor.selection.getNode().textContent;
-            window.vues['code-editor'].open(providedCode, '', (code, lang) => {
-                let wrap = document.createElement('div');
+            const providedCode = editor.selection.getNode().textContent;
+            window.components.first('code-editor').open(providedCode, '', (code, lang) => {
+                const wrap = document.createElement('div');
                 wrap.innerHTML = `<pre><code class="language-${lang}"></code></pre>`;
                 wrap.querySelector('code').innerText = code;
 
                 editor.formatter.toggle('pre');
-                let node = editor.selection.getNode();
+                const node = editor.selection.getNode();
                 editor.dom.setHTML(node, wrap.querySelector('pre').innerHTML);
                 editor.fire('SetContent');
+
+                editor.focus()
             });
             return;
         }
@@ -155,16 +155,18 @@ function codePlugin() {
         let lang = selectedNode.hasAttribute('data-lang') ? selectedNode.getAttribute('data-lang') : '';
         let currentCode = selectedNode.querySelector('textarea').textContent;
 
-        window.vues['code-editor'].open(currentCode, lang, (code, lang) => {
-            let editorElem = selectedNode.querySelector('.CodeMirror');
-            let cmInstance = editorElem.CodeMirror;
+        window.components.first('code-editor').open(currentCode, lang, (code, lang) => {
+            const editorElem = selectedNode.querySelector('.CodeMirror');
+            const cmInstance = editorElem.CodeMirror;
             if (cmInstance) {
                 Code.setContent(cmInstance, code);
-                Code.setMode(cmInstance, lang);
+                Code.setMode(cmInstance, lang, code);
             }
-            let textArea = selectedNode.querySelector('textarea');
+            const textArea = selectedNode.querySelector('textarea');
             if (textArea) textArea.textContent = code;
             selectedNode.setAttribute('data-lang', lang);
+
+            editor.focus()
         });
     }
 
@@ -234,7 +236,7 @@ function codePlugin() {
     });
 }
 
-function drawIoPlugin() {
+function drawIoPlugin(drawioUrl, isDarkMode, pageId, wysiwygComponent) {
 
     let pageEditor = null;
     let currentNode = null;
@@ -262,13 +264,12 @@ function drawIoPlugin() {
     function showDrawingEditor(mceEditor, selectedNode = null) {
         pageEditor = mceEditor;
         currentNode = selectedNode;
-        DrawIO.show(drawingInit, updateContent);
+        DrawIO.show(drawioUrl, drawingInit, updateContent);
     }
 
     async function updateContent(pngData) {
         const id = "image-" + Math.random().toString(16).slice(2);
         const loadingImage = window.baseUrl('/loading.gif');
-        const pageId = Number(document.getElementById('page-editor').getAttribute('page-id'));
 
         // Handle updating an existing image
         if (currentNode) {
@@ -279,7 +280,7 @@ function drawIoPlugin() {
                 pageEditor.dom.setAttrib(imgElem, 'src', img.url);
                 pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', img.id);
             } catch (err) {
-                window.$events.emit('error', trans('errors.image_upload_error'));
+                window.$events.emit('error', wysiwygComponent.imageUploadErrorText);
                 console.log(err);
             }
             return;
@@ -294,7 +295,7 @@ function drawIoPlugin() {
                 pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', img.id);
             } catch (err) {
                 pageEditor.dom.remove(id);
-                window.$events.emit('error', trans('errors.image_upload_error'));
+                window.$events.emit('error', wysiwygComponent.imageUploadErrorText);
                 console.log(err);
             }
         }, 5);
@@ -313,14 +314,17 @@ function drawIoPlugin() {
     window.tinymce.PluginManager.add('drawio', function(editor, url) {
 
         editor.addCommand('drawio', () => {
-            let selectedNode = editor.selection.getNode();
+            const selectedNode = editor.selection.getNode();
             showDrawingEditor(editor, isDrawing(selectedNode) ? selectedNode : null);
         });
 
         editor.addButton('drawio', {
             type: 'splitbutton',
             tooltip: 'Drawing',
-            image: `data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiMwMDAwMDAiICB4bWxucz0iaHR0cDovL3d3 dy53My5vcmcvMjAwMC9zdmciPgogICAgPHBhdGggZD0iTTIzIDdWMWgtNnYySDdWMUgxdjZoMnYx MEgxdjZoNnYtMmgxMHYyaDZ2LTZoLTJWN2gyek0zIDNoMnYySDNWM3ptMiAxOEgzdi0yaDJ2Mnpt MTItMkg3di0ySDVWN2gyVjVoMTB2MmgydjEwaC0ydjJ6bTQgMmgtMnYtMmgydjJ6TTE5IDVWM2gy djJoLTJ6bS01LjI3IDloLTMuNDlsLS43MyAySDcuODlsMy40LTloMS40bDMuNDEgOWgtMS42M2wt Ljc0LTJ6bS0zLjA0LTEuMjZoMi42MUwxMiA4LjkxbC0xLjMxIDMuODN6Ii8+CiAgICA8cGF0aCBk PSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+Cjwvc3ZnPg==`,
+            image: `data:image/svg+xml;base64,${btoa(`<svg viewBox="0 0 24 24" fill="${isDarkMode ? '#BBB' : '#000000'}"  xmlns="http://www.w3.org/2000/svg">
+    <path d="M23 7V1h-6v2H7V1H1v6h2v10H1v6h6v-2h10v2h6v-6h-2V7h2zM3 3h2v2H3V3zm2 18H3v-2h2v2zm12-2H7v-2H5V7h2V5h10v2h2v10h-2v2zm4 2h-2v-2h2v2zM19 5V3h2v2h-2zm-5.27 9h-3.49l-.73 2H7.89l3.4-9h1.4l3.41 9h-1.63l-.74-2zm-3.04-1.26h2.61L12 8.91l-1.31 3.83z"/>
+    <path d="M0 0h24v24H0z" fill="none"/>
+</svg>`)}`,
             cmd: 'drawio',
             menu: [
                 {
@@ -397,32 +401,46 @@ function listenForBookStackEditorEvents(editor) {
         editor.setContent(content);
     });
 
+    // Insert editor content at the current location
+    window.$events.listen('editor::insert', ({html}) => {
+        editor.insertContent(html);
+    });
+
+    // Focus on the editor
+    window.$events.listen('editor::focus', () => {
+        editor.focus();
+    });
 }
 
 class WysiwygEditor {
 
-    constructor(elem) {
-        this.elem = elem;
+    setup() {
+        this.elem = this.$el;
 
-        const pageEditor = document.getElementById('page-editor');
-        this.pageId = pageEditor.getAttribute('page-id');
-        this.textDirection = pageEditor.getAttribute('text-direction');
+        this.pageId = this.$opts.pageId;
+        this.textDirection = this.$opts.textDirection;
+        this.imageUploadErrorText = this.$opts.imageUploadErrorText;
+        this.isDarkMode = document.documentElement.classList.contains('dark-mode');
 
-        this.plugins = "image table textcolor paste link autolink fullscreen imagetools code customhr autosave lists codeeditor media";
+        this.plugins = "image imagetools table textcolor paste link autolink fullscreen code customhr autosave lists codeeditor media";
         this.loadPlugins();
 
         this.tinyMceConfig = this.getTinyMceConfig();
-        window.$events.emitPublic(elem, 'editor-tinymce::pre-init', {config: this.tinyMceConfig});
+        window.$events.emitPublic(this.elem, 'editor-tinymce::pre-init', {config: this.tinyMceConfig});
         window.tinymce.init(this.tinyMceConfig);
     }
 
     loadPlugins() {
         codePlugin();
         customHrPlugin();
-        if (document.querySelector('[drawio-enabled]').getAttribute('drawio-enabled') === 'true') {
-            drawIoPlugin();
+
+        const drawioUrlElem = document.querySelector('[drawio-url]');
+        if (drawioUrlElem) {
+            const url = drawioUrlElem.getAttribute('drawio-url');
+            drawIoPlugin(url, this.isDarkMode, this.pageId, this);
             this.plugins += ' drawio';
         }
+
         if (this.textDirection === 'rtl') {
             this.plugins += ' directionality'
         }
@@ -443,6 +461,7 @@ class WysiwygEditor {
                 window.baseUrl('/dist/styles.css'),
             ],
             branding: false,
+            skin: this.isDarkMode ? 'dark' : 'lightgray',
             body_class: 'page-content',
             browser_spellcheck: true,
             relative_urls: false,
@@ -459,7 +478,7 @@ class WysiwygEditor {
             plugins: this.plugins,
             imagetools_toolbar: 'imageoptions',
             toolbar: this.getToolBar(),
-            content_style: "html, body {background: #FFF;} body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}",
+            content_style: `html, body, html.dark-mode {background: ${this.isDarkMode ? '#222' : '#fff'};} body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}`,
             style_formats: [
                 {title: "Header Large", format: "h2"},
                 {title: "Header Medium", format: "h3"},
@@ -496,7 +515,15 @@ class WysiwygEditor {
                         const originalField = win.document.getElementById(field_name);
                         originalField.value = entity.link;
                         const mceForm = originalField.closest('.mce-form');
-                        mceForm.querySelectorAll('input')[2].value = entity.name;
+                        const inputs = mceForm.querySelectorAll('input');
+
+                        // Set text to display if not empty
+                        if (!inputs[1].value) {
+                            inputs[1].value = entity.name;
+                        }
+
+                        // Set title field
+                        inputs[2].value = entity.name;
                     });
                 }
 
@@ -558,7 +585,10 @@ class WysiwygEditor {
                 });
 
                 function editorChange() {
-                    let content = editor.getContent();
+                    const content = editor.getContent();
+                    if (context.isDarkMode) {
+                        editor.contentDocument.documentElement.classList.add('dark-mode');
+                    }
                     window.$events.emit('editor-html-change', content);
                 }
 
@@ -589,6 +619,7 @@ class WysiwygEditor {
                 registerEditorShortcuts(editor);
 
                 let wrap;
+                let draggedContentEditable;
 
                 function hasTextContent(node) {
                     return node && !!( node.textContent || node.innerText );
@@ -597,20 +628,28 @@ class WysiwygEditor {
                 editor.on('dragstart', function () {
                     let node = editor.selection.getNode();
 
-                    if (node.nodeName !== 'IMG') return;
-                    wrap = editor.dom.getParent(node, '.mceTemp');
+                    if (node.nodeName === 'IMG') {
+                        wrap = editor.dom.getParent(node, '.mceTemp');
+
+                        if (!wrap && node.parentNode.nodeName === 'A' && !hasTextContent(node.parentNode)) {
+                            wrap = node.parentNode;
+                        }
+                    }
 
-                    if (!wrap && node.parentNode.nodeName === 'A' && !hasTextContent(node.parentNode)) {
-                        wrap = node.parentNode;
+                    // Track dragged contenteditable blocks
+                    if (node.hasAttribute('contenteditable') && node.getAttribute('contenteditable') === 'false') {
+                        draggedContentEditable = node;
                     }
+
                 });
 
+                // Custom drop event handling
                 editor.on('drop', function (event) {
                     let dom = editor.dom,
                         rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc());
 
                     // Template insertion
-                    const templateId = event.dataTransfer.getData('bookstack/template');
+                    const templateId = event.dataTransfer && event.dataTransfer.getData('bookstack/template');
                     if (templateId) {
                         event.preventDefault();
                         window.$http.get(`/templates/${templateId}`).then(resp => {
@@ -634,6 +673,26 @@ class WysiwygEditor {
                         });
                     }
 
+                    // Handle contenteditable section drop
+                    if (!event.isDefaultPrevented() && draggedContentEditable) {
+                        event.preventDefault();
+                        editor.undoManager.transact(function () {
+                            const selectedNode = editor.selection.getNode();
+                            const range = editor.selection.getRng();
+                            const selectedNodeRoot = selectedNode.closest('body > *');
+                            if (range.startOffset > (range.startContainer.length / 2)) {
+                                editor.$(selectedNodeRoot).after(draggedContentEditable);
+                            } else {
+                                editor.$(selectedNodeRoot).before(draggedContentEditable);
+                            }
+                        });
+                    }
+
+                    // Handle image insert
+                    if (!event.isDefaultPrevented()) {
+                        editorPaste(event, editor, context);
+                    }
+
                     wrap = null;
                 });
 
index e0c7b34e5a85d5c22e57236c04346c95b7041a38..ffdb54e191e1557b7c3cbb6e9f6b4402913c40b9 100644 (file)
@@ -7,11 +7,10 @@ window.baseUrl = function(path) {
 };
 
 // Set events and http services on window
-import Events from "./services/events"
+import events from "./services/events"
 import httpInstance from "./services/http"
-const eventManager = new Events();
 window.$http = httpInstance;
-window.$events = eventManager;
+window.$events = events;
 
 // Translation setup
 // Creates a global function with name 'trans' to be used in the same way as Laravel's translation system
@@ -19,14 +18,8 @@ import Translations from "./services/translations"
 const translator = new Translations();
 window.trans = translator.get.bind(translator);
 window.trans_choice = translator.getPlural.bind(translator);
+window.trans_plural = translator.parsePlural.bind(translator);
 
-// Make services available to Vue instances
-import Vue from "vue"
-Vue.prototype.$http = httpInstance;
-Vue.prototype.$events = eventManager;
-
-// Load Vues and components
-import vues from "./vues/vues"
+// Load Components
 import components from "./components"
-vues();
 components();
\ No newline at end of file
index b6158ea5f8fbee97872d6350f0a8fa72e9954c13..278a765d5802e2af4feefd1fb1cb26aeaee9b805 100644 (file)
@@ -5,6 +5,22 @@
  */
 const animateStylesCleanupMap = new WeakMap();
 
+/**
+ * Fade in the given element.
+ * @param {Element} element
+ * @param {Number} animTime
+ * @param {Function|null} onComplete
+ */
+export function fadeIn(element, animTime = 400, onComplete = null) {
+    cleanupExistingElementAnimation(element);
+    element.style.display = 'block';
+    animateStyles(element, {
+        opacity: ['0', '1']
+    }, animTime, () => {
+        if (onComplete) onComplete();
+    });
+}
+
 /**
  * Fade out the given element.
  * @param {Element} element
diff --git a/resources/js/services/clipboard.js b/resources/js/services/clipboard.js
new file mode 100644 (file)
index 0000000..da921e5
--- /dev/null
@@ -0,0 +1,54 @@
+
+class Clipboard {
+
+    /**
+     * Constructor
+     * @param {DataTransfer} clipboardData
+     */
+    constructor(clipboardData) {
+        this.data = clipboardData;
+    }
+
+    /**
+     * Check if the clipboard has any items.
+     */
+    hasItems() {
+        return Boolean(this.data) && Boolean(this.data.types) && this.data.types.length > 0;
+    }
+
+    /**
+     * Check if the given event has tabular-looking data in the clipboard.
+     * @return {boolean}
+     */
+    containsTabularData() {
+        const rtfData = this.data.getData( 'text/rtf');
+        return rtfData && rtfData.includes('\\trowd');
+    }
+
+    /**
+     * Get the images that are in the clipboard data.
+     * @return {Array<File>}
+     */
+    getImages() {
+        const types = this.data.types;
+        const files = this.data.files;
+        const images = [];
+
+        for (const type of types) {
+            if (type.includes('image')) {
+                const item = this.data.getData(type);
+                images.push(item.getAsFile());
+            }
+        }
+
+        for (const file of files) {
+            if (file.type.includes('image')) {
+                images.push(file);
+            }
+        }
+
+        return images;
+    }
+}
+
+export default Clipboard;
\ No newline at end of file
index f69f28b8e96f7db6498260681925153acc49f6a0..e2aca1aad9e681d94a23763c95e550712e1bedfa 100644 (file)
@@ -5,15 +5,18 @@ import Clipboard from "clipboard/dist/clipboard.min";
 import 'codemirror/mode/css/css';
 import 'codemirror/mode/clike/clike';
 import 'codemirror/mode/diff/diff';
+import 'codemirror/mode/fortran/fortran';
 import 'codemirror/mode/go/go';
+import 'codemirror/mode/haskell/haskell';
 import 'codemirror/mode/htmlmixed/htmlmixed';
 import 'codemirror/mode/javascript/javascript';
 import 'codemirror/mode/julia/julia';
 import 'codemirror/mode/lua/lua';
-import 'codemirror/mode/haskell/haskell';
 import 'codemirror/mode/markdown/markdown';
 import 'codemirror/mode/mllike/mllike';
 import 'codemirror/mode/nginx/nginx';
+import 'codemirror/mode/perl/perl';
+import 'codemirror/mode/pascal/pascal';
 import 'codemirror/mode/php/php';
 import 'codemirror/mode/powershell/powershell';
 import 'codemirror/mode/properties/properties';
@@ -23,14 +26,16 @@ import 'codemirror/mode/rust/rust';
 import 'codemirror/mode/shell/shell';
 import 'codemirror/mode/sql/sql';
 import 'codemirror/mode/toml/toml';
+import 'codemirror/mode/vbscript/vbscript';
 import 'codemirror/mode/xml/xml';
 import 'codemirror/mode/yaml/yaml';
 
 // Addons
 import 'codemirror/addon/scroll/scrollpastend';
 
-// Mapping of potential languages or formats from user input
-// to their proper codemirror modes.
+// Mapping of possible languages or formats from user input to their codemirror modes.
+// Value can be a mode string or a function that will receive the code content & return the mode string.
+// The function option is used in the event the exact mode could be dynamic depending on the code.
 const modeMap = {
     css: 'css',
     c: 'text/x-csrc',
@@ -41,6 +46,8 @@ const modeMap = {
     'c#': 'text/x-csharp',
     csharp: 'text/x-csharp',
     diff: 'diff',
+    for: 'fortran',
+    fortran: 'fortran',
     go: 'go',
     haskell: 'haskell',
     hs: 'haskell',
@@ -57,10 +64,16 @@ const modeMap = {
     markdown: 'markdown',
     ml: 'mllike',
     nginx: 'nginx',
+    perl: 'perl',
+    pl: 'perl',
     powershell: 'powershell',
     properties: 'properties',
     ocaml: 'mllike',
-    php: 'php',
+    pascal: 'text/x-pascal',
+    pas: 'text/x-pascal',
+    php: (content) => {
+        return content.includes('<?php') ? 'php' : 'text/x-php';
+    },
     py: 'python',
     python: 'python',
     ruby: 'ruby',
@@ -72,6 +85,8 @@ const modeMap = {
     bash: 'shell',
     toml: 'toml',
     sql: 'text/x-sql',
+    vbs: 'vbscript',
+    vbscript: 'vbscript',
     xml: 'xml',
     yaml: 'yaml',
     yml: 'yaml',
@@ -81,9 +96,20 @@ const modeMap = {
  * Highlight pre elements on a page
  */
 function highlight() {
-    let codeBlocks = document.querySelectorAll('.page-content pre, .comment-box .content pre');
-    for (let i = 0; i < codeBlocks.length; i++) {
-        highlightElem(codeBlocks[i]);
+    const codeBlocks = document.querySelectorAll('.page-content pre, .comment-box .content pre');
+    for (const codeBlock of codeBlocks) {
+        highlightElem(codeBlock);
+    }
+}
+
+/**
+ * Highlight all code blocks within the given parent element
+ * @param {HTMLElement} parent
+ */
+function highlightWithin(parent) {
+    const codeBlocks = parent.querySelectorAll('pre');
+    for (const codeBlock of codeBlocks) {
+        highlightElem(codeBlock);
     }
 }
 
@@ -92,16 +118,17 @@ function highlight() {
  * @param {HTMLElement} elem
  */
 function highlightElem(elem) {
-    let innerCodeElem = elem.querySelector('code[class^=language-]');
+    const innerCodeElem = elem.querySelector('code[class^=language-]');
+    elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
+    const content = elem.textContent.trimEnd();
+
     let mode = '';
     if (innerCodeElem !== null) {
-        let langName = innerCodeElem.className.replace('language-', '');
-        mode = getMode(langName);
+        const langName = innerCodeElem.className.replace('language-', '');
+        mode = getMode(langName, content);
     }
-    elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
-    let content = elem.textContent.trim();
 
-    let cm = CodeMirror(function(elt) {
+    const cm = CodeMirror(function(elt) {
         elem.parentNode.replaceChild(elt, elem);
     }, {
         value: content,
@@ -142,12 +169,24 @@ function addCopyIcon(cmInstance) {
 
 /**
  * Search for a codemirror code based off a user suggestion
- * @param suggestion
+ * @param {String} suggestion
+ * @param {String} content
  * @returns {string}
  */
-function getMode(suggestion) {
+function getMode(suggestion, content) {
     suggestion = suggestion.trim().replace(/^\./g, '').toLowerCase();
-    return (typeof modeMap[suggestion] !== 'undefined') ? modeMap[suggestion] : '';
+
+    const modeMapType = typeof modeMap[suggestion];
+
+    if (modeMapType === 'undefined') {
+        return '';
+    }
+
+    if (modeMapType === 'function') {
+        return modeMap[suggestion](content);
+    }
+
+    return modeMap[suggestion];
 }
 
 /**
@@ -155,7 +194,8 @@ function getMode(suggestion) {
  * @returns {*|string}
  */
 function getTheme() {
-    return window.codeTheme || 'base16-light';
+    const darkMode = document.documentElement.classList.contains('dark-mode');
+    return window.codeTheme || (darkMode ? 'darcula' : 'default');
 }
 
 /**
@@ -165,8 +205,8 @@ function getTheme() {
  * @returns {{wrap: Element, editor: *}}
  */
 function wysiwygView(elem) {
-    let doc = elem.ownerDocument;
-    let codeElem = elem.querySelector('code');
+    const doc = elem.ownerDocument;
+    const codeElem = elem.querySelector('code');
 
     let lang = (elem.className || '').replace('language-', '');
     if (lang === '' && codeElem) {
@@ -174,9 +214,9 @@ function wysiwygView(elem) {
     }
 
     elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
-    let content = elem.textContent;
-    let newWrap = doc.createElement('div');
-    let newTextArea = doc.createElement('textarea');
+    const content = elem.textContent;
+    const newWrap = doc.createElement('div');
+    const newTextArea = doc.createElement('textarea');
 
     newWrap.className = 'CodeMirrorContainer';
     newWrap.setAttribute('data-lang', lang);
@@ -192,7 +232,7 @@ function wysiwygView(elem) {
         newWrap.appendChild(elt);
     }, {
         value: content,
-        mode:  getMode(lang),
+        mode:  getMode(lang, content),
         lineNumbers: true,
         lineWrapping: false,
         theme: getTheme(),
@@ -211,14 +251,14 @@ function wysiwygView(elem) {
  * @returns {*}
  */
 function popupEditor(elem, modeSuggestion) {
-    let content = elem.textContent;
+    const content = elem.textContent;
 
     return CodeMirror(function(elt) {
         elem.parentNode.insertBefore(elt, elem);
         elem.style.display = 'none';
     }, {
         value: content,
-        mode:  getMode(modeSuggestion),
+        mode:  getMode(modeSuggestion, content),
         lineNumbers: true,
         lineWrapping: false,
         theme: getTheme()
@@ -230,8 +270,8 @@ function popupEditor(elem, modeSuggestion) {
  * @param cmInstance
  * @param modeSuggestion
  */
-function setMode(cmInstance, modeSuggestion) {
-      cmInstance.setOption('mode', getMode(modeSuggestion));
+function setMode(cmInstance, modeSuggestion, content) {
+      cmInstance.setOption('mode', getMode(modeSuggestion, content));
 }
 
 /**
@@ -242,10 +282,18 @@ function setMode(cmInstance, modeSuggestion) {
 function setContent(cmInstance, codeContent) {
     cmInstance.setValue(codeContent);
     setTimeout(() => {
-        cmInstance.refresh();
+        updateLayout(cmInstance);
     }, 10);
 }
 
+/**
+ * Update the layout (codemirror refresh) of a cm instance.
+ * @param cmInstance
+ */
+function updateLayout(cmInstance) {
+    cmInstance.refresh();
+}
+
 /**
  * Get a CodeMirror instance to use for the markdown editor.
  * @param {HTMLElement} elem
@@ -281,10 +329,12 @@ function getMetaKey() {
 
 export default {
     highlight: highlight,
+    highlightWithin: highlightWithin,
     wysiwygView: wysiwygView,
     popupEditor: popupEditor,
     setMode: setMode,
     setContent: setContent,
+    updateLayout: updateLayout,
     markdownEditor: markdownEditor,
     getMetaKey: getMetaKey,
 };
index 966a4540e84eeb91117cec8ea89c41609d8b28e6..7a7b2c9bcfce6ce3ae0daf532a9d4be05f8229b7 100644 (file)
@@ -25,17 +25,42 @@ export function onEvents(listenerElement, events, callback) {
 /**
  * Helper to run an action when an element is selected.
  * A "select" is made to be accessible, So can be a click, space-press or enter-press.
- * @param listenerElement
- * @param callback
+ * @param {HTMLElement|Array} elements
+ * @param {function} callback
  */
-export function onSelect(listenerElement, callback) {
-    listenerElement.addEventListener('click', callback);
-    listenerElement.addEventListener('keydown', (event) => {
-        if (event.key === 'Enter' || event.key === ' ') {
-            event.preventDefault();
+export function onSelect(elements, callback) {
+    if (!Array.isArray(elements)) {
+        elements = [elements];
+    }
+
+    for (const listenerElement of elements) {
+        listenerElement.addEventListener('click', callback);
+        listenerElement.addEventListener('keydown', (event) => {
+            if (event.key === 'Enter' || event.key === ' ') {
+                event.preventDefault();
+                callback(event);
+            }
+        });
+    }
+}
+
+/**
+ * Listen to enter press on the given element(s).
+ * @param {HTMLElement|Array} elements
+ * @param {function} callback
+ */
+export function onEnterPress(elements, callback) {
+    if (!Array.isArray(elements)) {
+        elements = [elements];
+    }
+
+    const listener = event => {
+        if (event.key === 'Enter') {
             callback(event);
         }
-    });
+    }
+
+    elements.forEach(e => e.addEventListener('keypress', listener));
 }
 
 /**
@@ -72,4 +97,24 @@ export function findText(selector, text) {
         }
     }
     return null;
+}
+
+/**
+ * Show a loading indicator in the given element.
+ * This will effectively clear the element.
+ * @param {Element} element
+ */
+export function showLoading(element) {
+    element.innerHTML = `<div class="loading-container"><div></div><div></div><div></div></div>`;
+}
+
+/**
+ * Remove any loading indicators within the given element.
+ * @param {Element} element
+ */
+export function removeLoading(element) {
+    const loadingEls = element.querySelectorAll('.loading-container');
+    for (const el of loadingEls) {
+        el.remove();
+    }
 }
\ No newline at end of file
index a570737d11f5bcc0d77964d46fc03a93ab41bd96..17e57cd6b9165d567aa09f48197ea95e8b62551f 100644 (file)
@@ -1,22 +1,21 @@
-
-const drawIoUrl = 'https://www.draw.io/?embed=1&ui=atlas&spin=1&proto=json';
 let iFrame = null;
 
 let onInit, onSave;
 
 /**
  * Show the draw.io editor.
- * @param onInitCallback - Must return a promise with the xml to load for the editor.
- * @param onSaveCallback - Is called with the drawing data on save.
+ * @param {String} drawioUrl
+ * @param {Function} onInitCallback - Must return a promise with the xml to load for the editor.
+ * @param {Function} onSaveCallback - Is called with the drawing data on save.
  */
-function show(onInitCallback, onSaveCallback) {
+function show(drawioUrl, onInitCallback, onSaveCallback) {
     onInit = onInitCallback;
     onSave = onSaveCallback;
 
     iFrame = document.createElement('iframe');
     iFrame.setAttribute('frameborder', '0');
     window.addEventListener('message', drawReceive);
-    iFrame.setAttribute('src', drawIoUrl);
+    iFrame.setAttribute('src', drawioUrl);
     iFrame.setAttribute('class', 'fullscreen');
     iFrame.style.backgroundColor = '#FFFFFF';
     document.body.appendChild(iFrame);
index fa3ed7fdfcb55ebd341ee44543eafd58b697d14d..6668014e7b6913ca4fbee93fe11f69307ada3349 100644 (file)
@@ -1,55 +1,66 @@
+const listeners = {};
+const stack = [];
+
 /**
- * Simple global events manager
+ * Emit a custom event for any handlers to pick-up.
+ * @param {String} eventName
+ * @param {*} eventData
  */
-class Events {
-    constructor() {
-        this.listeners = {};
-        this.stack = [];
+function emit(eventName, eventData) {
+    stack.push({name: eventName, data: eventData});
+    if (typeof listeners[eventName] === 'undefined') return this;
+    let eventsToStart = listeners[eventName];
+    for (let i = 0; i < eventsToStart.length; i++) {
+        let event = eventsToStart[i];
+        event(eventData);
     }
+}
 
-    /**
-     * Emit a custom event for any handlers to pick-up.
-     * @param {String} eventName
-     * @param {*} eventData
-     * @returns {Events}
-     */
-    emit(eventName, eventData) {
-        this.stack.push({name: eventName, data: eventData});
-        if (typeof this.listeners[eventName] === 'undefined') return this;
-        let eventsToStart = this.listeners[eventName];
-        for (let i = 0; i < eventsToStart.length; i++) {
-            let event = eventsToStart[i];
-            event(eventData);
-        }
-        return this;
-    }
+/**
+ * Listen to a custom event and run the given callback when that event occurs.
+ * @param {String} eventName
+ * @param {Function} callback
+ * @returns {Events}
+ */
+function listen(eventName, callback) {
+    if (typeof listeners[eventName] === 'undefined') listeners[eventName] = [];
+    listeners[eventName].push(callback);
+}
 
-    /**
-     * Listen to a custom event and run the given callback when that event occurs.
-     * @param {String} eventName
-     * @param {Function} callback
-     * @returns {Events}
-     */
-    listen(eventName, callback) {
-        if (typeof this.listeners[eventName] === 'undefined') this.listeners[eventName] = [];
-        this.listeners[eventName].push(callback);
-        return this;
-    }
+/**
+ * Emit an event for public use.
+ * Sends the event via the native DOM event handling system.
+ * @param {Element} targetElement
+ * @param {String} eventName
+ * @param {Object} eventData
+ */
+function emitPublic(targetElement, eventName, eventData) {
+    const event = new CustomEvent(eventName, {
+        detail: eventData,
+        bubbles: true
+    });
+    targetElement.dispatchEvent(event);
+}
 
-    /**
-     * Emit an event for public use.
-     * Sends the event via the native DOM event handling system.
-     * @param {Element} targetElement
-     * @param {String} eventName
-     * @param {Object} eventData
-     */
-    emitPublic(targetElement, eventName, eventData) {
-        const event = new CustomEvent(eventName, {
-            detail: eventData,
-            bubbles: true
-        });
-        targetElement.dispatchEvent(event);
+/**
+ * Notify of a http error.
+ * Check for standard scenarios such as validation errors and
+ * formats an error notification accordingly.
+ * @param {Error} error
+ */
+function showValidationErrors(error) {
+    if (!error.status) return;
+    if (error.status === 422 && error.data) {
+        const message = Object.values(error.data).flat().join('\n');
+        emit('error', message);
     }
 }
 
-export default Events;
\ No newline at end of file
+export default {
+    emit,
+    emitPublic,
+    listen,
+    success: (msg) => emit('success', msg),
+    error: (msg) => emit('error', msg),
+    showValidationErrors,
+}
\ No newline at end of file
index 06dac9864a4d4523987e61f4009075ea34ed0594..b05dd23bfd7222834eade6395f655d5916b1b2c1 100644 (file)
@@ -67,11 +67,23 @@ async function dataRequest(method, url, data = null) {
         body: data,
     };
 
+    // Send data as JSON if a plain object
     if (typeof data === 'object' && !(data instanceof FormData)) {
-        options.headers = {'Content-Type': 'application/json'};
+        options.headers = {
+            'Content-Type': 'application/json',
+            'X-Requested-With': 'XMLHttpRequest',
+        };
         options.body = JSON.stringify(data);
     }
 
+    // Ensure FormData instances are sent over POST
+    // Since Laravel does not read multipart/form-data from other types
+    // of request. Hence the addition of the magic _method value.
+    if (data instanceof FormData && method !== 'post') {
+        data.append('_method', method);
+        options.method = 'post';
+    }
+
     return request(url, options)
 }
 
@@ -109,7 +121,7 @@ async function request(url, options = {}) {
 
     const response = await fetch(url, options);
     const content = await getResponseContent(response);
-    return {
+    const returnData = {
         data: content,
         headers: response.headers,
         redirected: response.redirected,
@@ -117,16 +129,26 @@ async function request(url, options = {}) {
         statusText: response.statusText,
         url: response.url,
         original: response,
+    };
+
+    if (!response.ok) {
+        throw returnData;
     }
+
+    return returnData;
 }
 
 /**
  * Get the content from a fetch response.
  * Checks the content-type header to determine the format.
- * @param response
+ * @param {Response} response
  * @returns {Promise<Object|String>}
  */
 async function getResponseContent(response) {
+    if (response.status === 204) {
+        return null;
+    }
+
     const responseContentType = response.headers.get('Content-Type');
     const subType = responseContentType.split('/').pop();
 
index b595a05e6f95b8e9a2d7862adbc5b5d003f6e163..62bb51f56aacb5f0216e0e4621ffdfcae0d34481 100644 (file)
@@ -47,7 +47,19 @@ class Translator {
      */
     getPlural(key, count, replacements) {
         const text = this.getTransText(key);
-        const splitText = text.split('|');
+        return this.parsePlural(text, count, replacements);
+    }
+
+    /**
+     * Parse the given translation and find the correct plural option
+     * to use. Similar format at laravel's 'trans_choice' helper.
+     * @param {String} translation
+     * @param {Number} count
+     * @param {Object} replacements
+     * @returns {String}
+     */
+    parsePlural(translation, count, replacements) {
+        const splitText = translation.split('|');
         const exactCountRegex = /^{([0-9]+)}/;
         const rangeRegex = /^\[([0-9]+),([0-9*]+)]/;
         let result = null;
index b2f2918725004920543a439d6031e3cce57aecc1..de2ca20c13eb934b00a475b817489e9fa29d9670 100644 (file)
@@ -45,4 +45,29 @@ export function scrollAndHighlightElement(element) {
         element.classList.remove('selectFade');
         element.style.backgroundColor = '';
     }, 3000);
+}
+
+/**
+ * Escape any HTML in the given 'unsafe' string.
+ * Take from https://stackoverflow.com/a/6234804.
+ * @param {String} unsafe
+ * @returns {string}
+ */
+export function escapeHtml(unsafe) {
+    return unsafe
+        .replace(/&/g, "&amp;")
+        .replace(/</g, "&lt;")
+        .replace(/>/g, "&gt;")
+        .replace(/"/g, "&quot;")
+        .replace(/'/g, "&#039;");
+}
+
+/**
+ * Generate a random unique ID.
+ *
+ * @returns {string}
+ */
+export function uniqueId() {
+    const S4 = () => (((1+Math.random())*0x10000)|0).toString(16).substring(1);
+    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
 }
\ No newline at end of file
diff --git a/resources/js/vues/attachment-manager.js b/resources/js/vues/attachment-manager.js
deleted file mode 100644 (file)
index 2467c64..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-import draggable from "vuedraggable";
-import dropzone from "./components/dropzone";
-
-function mounted() {
-    this.pageId = this.$el.getAttribute('page-id');
-    this.file = this.newFile();
-
-    this.$http.get(window.baseUrl(`/attachments/get/page/${this.pageId}`)).then(resp => {
-        this.files = resp.data;
-    }).catch(err => {
-        this.checkValidationErrors('get', err);
-    });
-}
-
-let data = {
-    pageId: null,
-    files: [],
-    fileToEdit: null,
-    file: {},
-    tab: 'list',
-    editTab: 'file',
-    errors: {link: {}, edit: {}, delete: {}}
-};
-
-const components = {dropzone, draggable};
-
-let methods = {
-
-    newFile() {
-        return {page_id: this.pageId};
-    },
-
-    getFileUrl(file) {
-        if (file.external && file.path.indexOf('http') !== 0) {
-            return file.path;
-        }
-        return window.baseUrl(`/attachments/${file.id}`);
-    },
-
-    fileSortUpdate() {
-        this.$http.put(window.baseUrl(`/attachments/sort/page/${this.pageId}`), {files: this.files}).then(resp => {
-            this.$events.emit('success', resp.data.message);
-        }).catch(err => {
-            this.checkValidationErrors('sort', err);
-        });
-    },
-
-    startEdit(file) {
-        this.fileToEdit = Object.assign({}, file);
-        this.fileToEdit.link = file.external ? file.path : '';
-        this.editTab = file.external ? 'link' : 'file';
-    },
-
-    deleteFile(file) {
-        if (!file.deleting) {
-            return this.$set(file, 'deleting', true);
-        }
-
-        this.$http.delete(window.baseUrl(`/attachments/${file.id}`)).then(resp => {
-            this.$events.emit('success', resp.data.message);
-            this.files.splice(this.files.indexOf(file), 1);
-        }).catch(err => {
-            this.checkValidationErrors('delete', err)
-        });
-    },
-
-    uploadSuccess(upload) {
-        this.files.push(upload.data);
-        this.$events.emit('success', trans('entities.attachments_file_uploaded'));
-    },
-
-    uploadSuccessUpdate(upload) {
-        let fileIndex = this.filesIndex(upload.data);
-        if (fileIndex === -1) {
-            this.files.push(upload.data)
-        } else {
-            this.files.splice(fileIndex, 1, upload.data);
-        }
-
-        if (this.fileToEdit && this.fileToEdit.id === upload.data.id) {
-            this.fileToEdit = Object.assign({}, upload.data);
-        }
-        this.$events.emit('success', trans('entities.attachments_file_updated'));
-    },
-
-    checkValidationErrors(groupName, err) {
-        if (typeof err.response.data === "undefined" && typeof err.response.data === "undefined") return;
-        this.errors[groupName] = err.response.data;
-    },
-
-    getUploadUrl(file) {
-        let url = window.baseUrl(`/attachments/upload`);
-        if (typeof file !== 'undefined') url += `/${file.id}`;
-        return url;
-    },
-
-    cancelEdit() {
-        this.fileToEdit = null;
-    },
-
-    attachNewLink(file) {
-        file.uploaded_to = this.pageId;
-        this.errors.link = {};
-        this.$http.post(window.baseUrl('/attachments/link'), file).then(resp => {
-            this.files.push(resp.data);
-            this.file = this.newFile();
-            this.$events.emit('success', trans('entities.attachments_link_attached'));
-        }).catch(err => {
-            this.checkValidationErrors('link', err);
-        });
-    },
-
-    updateFile(file) {
-        $http.put(window.baseUrl(`/attachments/${file.id}`), file).then(resp => {
-            let search = this.filesIndex(resp.data);
-            if (search === -1) {
-                this.files.push(resp.data);
-            } else {
-                this.files.splice(search, 1, resp.data);
-            }
-
-            if (this.fileToEdit && !file.external) this.fileToEdit.link = '';
-            this.fileToEdit = false;
-
-            this.$events.emit('success', trans('entities.attachments_updated_success'));
-        }).catch(err => {
-            this.checkValidationErrors('edit', err);
-        });
-    },
-
-    filesIndex(file) {
-        for (let i = 0, len = this.files.length; i < len; i++) {
-            if (this.files[i].id === file.id) return i;
-        }
-        return -1;
-    }
-
-};
-
-export default {
-    data, methods, mounted, components,
-};
\ No newline at end of file
diff --git a/resources/js/vues/code-editor.js b/resources/js/vues/code-editor.js
deleted file mode 100644 (file)
index c6df6b1..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-import codeLib from "../services/code";
-
-const methods = {
-    show() {
-        if (!this.editor) this.editor = codeLib.popupEditor(this.$refs.editor, this.language);
-        this.$refs.overlay.components.overlay.show();
-    },
-    hide() {
-        this.$refs.overlay.components.overlay.hide();
-    },
-    updateEditorMode(language) {
-        codeLib.setMode(this.editor, language);
-    },
-    updateLanguage(lang) {
-        this.language = lang;
-        this.updateEditorMode(lang);
-    },
-    open(code, language, callback) {
-        this.show();
-        this.updateEditorMode(language);
-        this.language = language;
-        codeLib.setContent(this.editor, code);
-        this.code = code;
-        this.callback = callback;
-    },
-    save() {
-        if (!this.callback) return;
-        this.callback(this.editor.getValue(), this.language);
-        this.hide();
-    }
-};
-
-const data = {
-    editor: null,
-    language: '',
-    code: '',
-    callback: null
-};
-
-export default {
-    methods,
-    data
-};
\ No newline at end of file
diff --git a/resources/js/vues/components/autosuggest.js b/resources/js/vues/components/autosuggest.js
deleted file mode 100644 (file)
index b809313..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-
-const template = `
-    <div>
-        <input :value="value" :autosuggest-type="type" ref="input"
-            :placeholder="placeholder" :name="name"
-            @input="inputUpdate($event.target.value)" @focus="inputUpdate($event.target.value)"
-            @blur="inputBlur"
-            @keydown="inputKeydown"
-            :aria-label="placeholder"
-        />
-        <ul class="suggestion-box" v-if="showSuggestions">
-            <li v-for="(suggestion, i) in suggestions"
-                @click="selectSuggestion(suggestion)"
-                :class="{active: (i === active)}">{{suggestion}}</li>
-        </ul>
-    </div>
-    
-`;
-
-function data() {
-    return {
-        suggestions: [],
-        showSuggestions: false,
-        active: 0,
-    };
-}
-
-const ajaxCache = {};
-
-const props = ['url', 'type', 'value', 'placeholder', 'name'];
-
-function getNameInputVal(valInput) {
-    let parentRow = valInput.parentNode.parentNode;
-    let nameInput = parentRow.querySelector('[autosuggest-type="name"]');
-    return (nameInput === null) ? '' : nameInput.value;
-}
-
-const methods = {
-
-    inputUpdate(inputValue) {
-        this.$emit('input', inputValue);
-        let params = {};
-
-        if (this.type === 'value') {
-            let nameVal = getNameInputVal(this.$el);
-            if (nameVal !== "") params.name = nameVal;
-        }
-
-        this.getSuggestions(inputValue.slice(0, 3), params).then(suggestions => {
-            if (inputValue.length === 0) {
-                this.displaySuggestions(suggestions.slice(0, 6));
-                return;
-            }
-            // Filter to suggestions containing searched term
-            suggestions = suggestions.filter(item => {
-                return item.toLowerCase().indexOf(inputValue.toLowerCase()) !== -1;
-            }).slice(0, 4);
-            this.displaySuggestions(suggestions);
-        });
-    },
-
-    inputBlur() {
-        setTimeout(() => {
-            this.$emit('blur');
-            this.showSuggestions = false;
-        }, 100);
-    },
-
-    inputKeydown(event) {
-        if (event.key === 'Enter') event.preventDefault();
-        if (!this.showSuggestions) return;
-
-        // Down arrow
-        if (event.key === 'ArrowDown') {
-            this.active = (this.active === this.suggestions.length - 1) ? 0 : this.active+1;
-        }
-        // Up Arrow
-        else if (event.key === 'ArrowUp') {
-            this.active = (this.active === 0) ? this.suggestions.length - 1 : this.active-1;
-        }
-        // Enter key
-        else if ((event.key === 'Enter') && !event.shiftKey) {
-            this.selectSuggestion(this.suggestions[this.active]);
-        }
-        // Escape key
-        else if (event.key === 'Escape') {
-            this.showSuggestions = false;
-        }
-    },
-
-    displaySuggestions(suggestions) {
-        if (suggestions.length === 0) {
-            this.suggestions = [];
-            this.showSuggestions = false;
-            return;
-        }
-
-        this.suggestions = suggestions;
-        this.showSuggestions = true;
-        this.active = 0;
-    },
-
-    selectSuggestion(suggestion) {
-        this.$refs.input.value = suggestion;
-        this.$refs.input.focus();
-        this.$emit('input', suggestion);
-        this.showSuggestions = false;
-    },
-
-    /**
-     * Get suggestions from BookStack. Store and use local cache if already searched.
-     * @param {String} input
-     * @param {Object} params
-     */
-    getSuggestions(input, params) {
-        params.search = input;
-        const cacheKey = `${this.url}:${JSON.stringify(params)}`;
-
-        if (typeof ajaxCache[cacheKey] !== "undefined") {
-            return Promise.resolve(ajaxCache[cacheKey]);
-        }
-
-        return this.$http.get(this.url, params).then(resp => {
-            ajaxCache[cacheKey] = resp.data;
-            return resp.data;
-        });
-    }
-
-};
-
-export default {template, data, props, methods};
\ No newline at end of file
diff --git a/resources/js/vues/components/dropzone.js b/resources/js/vues/components/dropzone.js
deleted file mode 100644 (file)
index 1c04572..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-import DropZone from "dropzone";
-import { fadeOut } from "../../services/animations";
-
-const template = `
-    <div class="dropzone-container text-center">
-        <button type="button" class="dz-message">{{placeholder}}</button>
-    </div>
-`;
-
-const props = ['placeholder', 'uploadUrl', 'uploadedTo'];
-
-function mounted() {
-   const container = this.$el;
-   const _this = this;
-   this._dz = new DropZone(container, {
-        addRemoveLinks: true,
-        dictRemoveFile: trans('components.image_upload_remove'),
-        timeout: Number(window.uploadTimeout) || 60000,
-        maxFilesize: Number(window.uploadLimit) || 256,
-        url: function() {
-            return _this.uploadUrl;
-        },
-        init: function () {
-            const dz = this;
-
-            dz.on('sending', function (file, xhr, data) {
-                const token = window.document.querySelector('meta[name=token]').getAttribute('content');
-                data.append('_token', token);
-                const uploadedTo = typeof _this.uploadedTo === 'undefined' ? 0 : _this.uploadedTo;
-                data.append('uploaded_to', uploadedTo);
-
-                xhr.ontimeout = function (e) {
-                    dz.emit('complete', file);
-                    dz.emit('error', file, trans('errors.file_upload_timeout'));
-                }
-            });
-
-            dz.on('success', function (file, data) {
-                _this.$emit('success', {file, data});
-                fadeOut(file.previewElement, 800, () => {
-                    dz.removeFile(file);
-                });
-            });
-
-            dz.on('error', function (file, errorMessage, xhr) {
-                _this.$emit('error', {file, errorMessage, xhr});
-
-                function setMessage(message) {
-                    const messsageEl = file.previewElement.querySelector('[data-dz-errormessage]');
-                    messsageEl.textContent = message;
-                }
-
-                if (xhr && xhr.status === 413) {
-                    setMessage(trans('errors.server_upload_limit'))
-                } else if (errorMessage.file) {
-                    setMessage(errorMessage.file);
-                }
-
-            });
-        }
-   });
-}
-
-function data() {
-    return {};
-}
-
-const methods = {
-    onClose: function () {
-        this._dz.removeAllFiles(true);
-    }
-};
-
-export default {
-    template,
-    props,
-    mounted,
-    data,
-    methods
-};
diff --git a/resources/js/vues/entity-dashboard.js b/resources/js/vues/entity-dashboard.js
deleted file mode 100644 (file)
index d10da70..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-let data = {
-    id: null,
-    type: '',
-    searching: false,
-    searchTerm: '',
-    searchResults: '',
-};
-
-let computed = {
-
-};
-
-let methods = {
-
-    searchBook() {
-        if (this.searchTerm.trim().length === 0) return;
-        this.searching = true;
-        this.searchResults = '';
-        let url = window.baseUrl(`/search/${this.type}/${this.id}`);
-        url += `?term=${encodeURIComponent(this.searchTerm)}`;
-        this.$http.get(url).then(resp => {
-            this.searchResults = resp.data;
-        });
-    },
-
-    checkSearchForm() {
-        this.searching = this.searchTerm > 0;
-    },
-
-    clearSearch() {
-        this.searching = false;
-        this.searchTerm = '';
-    }
-
-};
-
-function mounted() {
-    this.id = Number(this.$el.getAttribute('entity-id'));
-    this.type = this.$el.getAttribute('entity-type');
-}
-
-export default {
-    data, computed, methods, mounted
-};
\ No newline at end of file
diff --git a/resources/js/vues/image-manager.js b/resources/js/vues/image-manager.js
deleted file mode 100644 (file)
index 6df12d1..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-import * as Dates from "../services/dates";
-import dropzone from "./components/dropzone";
-
-let page = 1;
-let previousClickTime = 0;
-let previousClickImage = 0;
-let dataLoaded = false;
-let callback = false;
-let baseUrl = '';
-
-let preSearchImages = [];
-let preSearchHasMore = false;
-
-const data = {
-    images: [],
-
-    imageType: false,
-    uploadedTo: false,
-
-    selectedImage: false,
-    dependantPages: false,
-    showing: false,
-    filter: null,
-    hasMore: false,
-    searching: false,
-    searchTerm: '',
-
-    imageUpdateSuccess: false,
-    imageDeleteSuccess: false,
-    deleteConfirm: false,
-};
-
-const methods = {
-
-    show(providedCallback, imageType = null) {
-        callback = providedCallback;
-        this.showing = true;
-        this.$el.children[0].components.overlay.show();
-
-        // Get initial images if they have not yet been loaded in.
-        if (dataLoaded && imageType === this.imageType) return;
-        if (imageType) {
-            this.imageType = imageType;
-            this.resetState();
-        }
-        this.fetchData();
-        dataLoaded = true;
-    },
-
-    hide() {
-        if (this.$refs.dropzone) {
-            this.$refs.dropzone.onClose();
-        }
-        this.showing = false;
-        this.selectedImage = false;
-        this.$el.children[0].components.overlay.hide();
-    },
-
-    async fetchData() {
-        const params = {
-            page,
-            search: this.searching ? this.searchTerm : null,
-            uploaded_to: this.uploadedTo || null,
-            filter_type: this.filter,
-        };
-
-        const {data} = await this.$http.get(baseUrl, params);
-        this.images = this.images.concat(data.images);
-        this.hasMore = data.has_more;
-        page++;
-    },
-
-    setFilterType(filterType) {
-        this.filter = filterType;
-        this.resetState();
-        this.fetchData();
-    },
-
-    resetState() {
-        this.cancelSearch();
-        this.resetListView();
-        this.deleteConfirm = false;
-        baseUrl = window.baseUrl(`/images/${this.imageType}`);
-    },
-
-    resetListView() {
-        this.images = [];
-        this.hasMore = false;
-        page = 1;
-    },
-
-    searchImages() {
-        if (this.searchTerm === '') return this.cancelSearch();
-
-        // Cache current settings for later
-        if (!this.searching) {
-            preSearchImages = this.images;
-            preSearchHasMore = this.hasMore;
-        }
-
-        this.searching = true;
-        this.resetListView();
-        this.fetchData();
-    },
-
-    cancelSearch() {
-        if (!this.searching) return;
-        this.searching = false;
-        this.searchTerm = '';
-        this.images = preSearchImages;
-        this.hasMore = preSearchHasMore;
-    },
-
-    imageSelect(image) {
-        const dblClickTime = 300;
-        const currentTime = Date.now();
-        const timeDiff = currentTime - previousClickTime;
-        const isDblClick = timeDiff < dblClickTime && image.id === previousClickImage;
-
-        if (isDblClick) {
-            this.callbackAndHide(image);
-        } else {
-            this.selectedImage = image;
-            this.deleteConfirm = false;
-            this.dependantPages = false;
-        }
-
-        previousClickTime = currentTime;
-        previousClickImage = image.id;
-    },
-
-    callbackAndHide(imageResult) {
-        if (callback) callback(imageResult);
-        this.hide();
-    },
-
-    async saveImageDetails() {
-        let url = window.baseUrl(`/images/${this.selectedImage.id}`);
-        try {
-            await this.$http.put(url, this.selectedImage)
-        } catch (error) {
-            if (error.response.status === 422) {
-                let errors = error.response.data;
-                let message = '';
-                Object.keys(errors).forEach((key) => {
-                    message += errors[key].join('\n');
-                });
-                this.$events.emit('error', message);
-            }
-        }
-    },
-
-    async deleteImage() {
-
-        if (!this.deleteConfirm) {
-            const url = window.baseUrl(`/images/usage/${this.selectedImage.id}`);
-            try {
-                const {data} = await this.$http.get(url);
-                this.dependantPages = data;
-            } catch (error) {
-                console.error(error);
-            }
-            this.deleteConfirm = true;
-            return;
-        }
-
-        const url = window.baseUrl(`/images/${this.selectedImage.id}`);
-        await this.$http.delete(url);
-        this.images.splice(this.images.indexOf(this.selectedImage), 1);
-        this.selectedImage = false;
-        this.$events.emit('success', trans('components.image_delete_success'));
-        this.deleteConfirm = false;
-    },
-
-    getDate(stringDate) {
-        return Dates.formatDateTime(new Date(stringDate));
-    },
-
-    uploadSuccess(event) {
-        this.images.unshift(event.data);
-        this.$events.emit('success', trans('components.image_upload_success'));
-    },
-};
-
-const computed = {
-    uploadUrl() {
-        return window.baseUrl(`/images/${this.imageType}`);
-    }
-};
-
-function mounted() {
-    window.ImageManager = this;
-    this.imageType = this.$el.getAttribute('image-type');
-    this.uploadedTo = this.$el.getAttribute('uploaded-to');
-    baseUrl = window.baseUrl('/images/' + this.imageType)
-}
-
-export default {
-    mounted,
-    methods,
-    data,
-    computed,
-    components: {dropzone},
-};
diff --git a/resources/js/vues/page-editor.js b/resources/js/vues/page-editor.js
deleted file mode 100644 (file)
index fbf2857..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-import * as Dates from "../services/dates";
-
-let autoSaveFrequency = 30;
-
-let autoSave = false;
-let draftErroring = false;
-
-let currentContent = {
-    title: false,
-    html: false
-};
-
-let lastSave = 0;
-
-function mounted() {
-    let elem = this.$el;
-    this.draftsEnabled = elem.getAttribute('drafts-enabled') === 'true';
-    this.editorType = elem.getAttribute('editor-type');
-    this.pageId= Number(elem.getAttribute('page-id'));
-    this.isNewDraft = Number(elem.getAttribute('page-new-draft')) === 1;
-    this.isUpdateDraft = Number(elem.getAttribute('page-update-draft')) === 1;
-
-    if (this.pageId !== 0 && this.draftsEnabled) {
-        window.setTimeout(() => {
-            this.startAutoSave();
-        }, 1000);
-    }
-
-    if (this.isUpdateDraft || this.isNewDraft) {
-        this.draftText = trans('entities.pages_editing_draft');
-    } else {
-        this.draftText = trans('entities.pages_editing_page');
-    }
-
-    // Listen to save events from editor
-    window.$events.listen('editor-save-draft', this.saveDraft);
-    window.$events.listen('editor-save-page', this.savePage);
-
-    // Listen to content changes from the editor
-    window.$events.listen('editor-html-change', html => {
-        this.editorHTML = html;
-    });
-    window.$events.listen('editor-markdown-change', markdown => {
-        this.editorMarkdown = markdown;
-    });
-}
-
-let data = {
-    draftsEnabled: false,
-    editorType: 'wysiwyg',
-    pagedId: 0,
-    isNewDraft: false,
-    isUpdateDraft: false,
-
-    draftText: '',
-    draftUpdated : false,
-    changeSummary: '',
-
-    editorHTML: '',
-    editorMarkdown: '',
-};
-
-let methods = {
-
-    startAutoSave() {
-        currentContent.title = document.getElementById('name').value.trim();
-        currentContent.html = this.editorHTML;
-
-        autoSave = window.setInterval(() => {
-            // Return if manually saved recently to prevent bombarding the server
-            if (Date.now() - lastSave < (1000 * autoSaveFrequency)/2) return;
-            const newTitle = document.getElementById('name').value.trim();
-            const newHtml = this.editorHTML;
-
-            if (newTitle !== currentContent.title || newHtml !== currentContent.html) {
-                currentContent.html = newHtml;
-                currentContent.title = newTitle;
-                this.saveDraft();
-            }
-
-        }, 1000 * autoSaveFrequency);
-    },
-
-    saveDraft() {
-        if (!this.draftsEnabled) return;
-
-        const data = {
-            name: document.getElementById('name').value.trim(),
-            html: this.editorHTML
-        };
-
-        if (this.editorType === 'markdown') data.markdown = this.editorMarkdown;
-
-        const url = window.baseUrl(`/ajax/page/${this.pageId}/save-draft`);
-        window.$http.put(url, data).then(response => {
-            draftErroring = false;
-            if (!this.isNewDraft) this.isUpdateDraft = true;
-            this.draftNotifyChange(`${response.data.message} ${Dates.utcTimeStampToLocalTime(response.data.timestamp)}`);
-            lastSave = Date.now();
-        }, errorRes => {
-            if (draftErroring) return;
-            window.$events.emit('error', trans('errors.page_draft_autosave_fail'));
-            draftErroring = true;
-        });
-    },
-
-    savePage() {
-        this.$el.closest('form').submit();
-    },
-
-    draftNotifyChange(text) {
-        this.draftText = text;
-        this.draftUpdated = true;
-        window.setTimeout(() => {
-            this.draftUpdated = false;
-        }, 2000);
-    },
-
-    discardDraft() {
-        let url = window.baseUrl(`/ajax/page/${this.pageId}`);
-        window.$http.get(url).then(response => {
-            if (autoSave) window.clearInterval(autoSave);
-
-            this.draftText = trans('entities.pages_editing_page');
-            this.isUpdateDraft = false;
-            window.$events.emit('editor-html-update', response.data.html);
-            window.$events.emit('editor-markdown-update', response.data.markdown || response.data.html);
-
-            document.getElementById('name').value = response.data.name;
-            window.setTimeout(() => {
-                this.startAutoSave();
-            }, 1000);
-            window.$events.emit('success', trans('entities.pages_draft_discarded'));
-        });
-    },
-
-};
-
-let computed = {
-    changeSummaryShort() {
-        let len = this.changeSummary.length;
-        if (len === 0) return trans('entities.pages_edit_set_changelog');
-        if (len <= 16) return this.changeSummary;
-        return this.changeSummary.slice(0, 16) + '...';
-    }
-};
-
-export default {
-    mounted, data, methods, computed,
-};
\ No newline at end of file
diff --git a/resources/js/vues/search.js b/resources/js/vues/search.js
deleted file mode 100644 (file)
index c0b828b..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-import * as Dates from "../services/dates";
-
-let data = {
-    terms: '',
-    termString : '',
-    search: {
-        type: {
-            page: true,
-            chapter: true,
-            book: true,
-            bookshelf: true,
-        },
-        exactTerms: [],
-        tagTerms: [],
-        option: {},
-        dates: {
-            updated_after: false,
-            updated_before: false,
-            created_after: false,
-            created_before: false,
-        }
-    }
-};
-
-let computed = {
-
-};
-
-let methods = {
-
-    appendTerm(term) {
-        this.termString += ' ' + term;
-        this.termString = this.termString.replace(/\s{2,}/g, ' ');
-        this.termString = this.termString.replace(/^\s+/, '');
-        this.termString = this.termString.replace(/\s+$/, '');
-    },
-
-    exactParse(searchString) {
-        this.search.exactTerms = [];
-        let exactFilter = /"(.+?)"/g;
-        let matches;
-        while ((matches = exactFilter.exec(searchString)) !== null) {
-            this.search.exactTerms.push(matches[1]);
-        }
-    },
-
-    exactChange() {
-        let exactFilter = /"(.+?)"/g;
-        this.termString = this.termString.replace(exactFilter, '');
-        let matchesTerm = this.search.exactTerms.filter(term =>  term.trim() !== '').map(term => `"${term}"`).join(' ');
-        this.appendTerm(matchesTerm);
-    },
-
-    addExact() {
-        this.search.exactTerms.push('');
-        setTimeout(() => {
-            let exactInputs = document.querySelectorAll('.exact-input');
-            exactInputs[exactInputs.length - 1].focus();
-        }, 100);
-    },
-
-    removeExact(index) {
-        this.search.exactTerms.splice(index, 1);
-        this.exactChange();
-    },
-
-    tagParse(searchString) {
-        this.search.tagTerms = [];
-        let tagFilter = /\[(.+?)\]/g;
-        let matches;
-        while ((matches = tagFilter.exec(searchString)) !== null) {
-            this.search.tagTerms.push(matches[1]);
-        }
-    },
-
-    tagChange() {
-        let tagFilter = /\[(.+?)\]/g;
-        this.termString = this.termString.replace(tagFilter, '');
-        let matchesTerm = this.search.tagTerms.filter(term => {
-            return term.trim() !== '';
-        }).map(term => {
-            return `[${term}]`
-        }).join(' ');
-        this.appendTerm(matchesTerm);
-    },
-
-    addTag() {
-        this.search.tagTerms.push('');
-        setTimeout(() => {
-            let tagInputs = document.querySelectorAll('.tag-input');
-            tagInputs[tagInputs.length - 1].focus();
-        }, 100);
-    },
-
-    removeTag(index) {
-        this.search.tagTerms.splice(index, 1);
-        this.tagChange();
-    },
-
-    typeParse(searchString) {
-        let typeFilter = /{\s?type:\s?(.*?)\s?}/;
-        let match = searchString.match(typeFilter);
-        let type = this.search.type;
-        if (!match) {
-            type.page = type.book = type.chapter = type.bookshelf = true;
-            return;
-        }
-        let splitTypes = match[1].replace(/ /g, '').split('|');
-        type.page = (splitTypes.indexOf('page') !== -1);
-        type.chapter = (splitTypes.indexOf('chapter') !== -1);
-        type.book = (splitTypes.indexOf('book') !== -1);
-        type.bookshelf = (splitTypes.indexOf('bookshelf') !== -1);
-    },
-
-    typeChange() {
-        let typeFilter = /{\s?type:\s?(.*?)\s?}/;
-        let type = this.search.type;
-        if (type.page === type.chapter === type.book === type.bookshelf) {
-            this.termString = this.termString.replace(typeFilter, '');
-            return;
-        }
-        let selectedTypes = Object.keys(type).filter(type => this.search.type[type]).join('|');
-        let typeTerm = '{type:'+selectedTypes+'}';
-        if (this.termString.match(typeFilter)) {
-            this.termString = this.termString.replace(typeFilter, typeTerm);
-            return;
-        }
-        this.appendTerm(typeTerm);
-    },
-
-    optionParse(searchString) {
-        let optionFilter = /{([a-z_\-:]+?)}/gi;
-        let matches;
-        while ((matches = optionFilter.exec(searchString)) !== null) {
-            this.search.option[matches[1].toLowerCase()] = true;
-        }
-    },
-
-    optionChange(optionName) {
-        let isChecked = this.search.option[optionName];
-        if (isChecked) {
-            this.appendTerm(`{${optionName}}`);
-        } else {
-            this.termString = this.termString.replace(`{${optionName}}`, '');
-        }
-    },
-
-    updateSearch(e) {
-        e.preventDefault();
-        window.location = window.baseUrl('/search?term=' + encodeURIComponent(this.termString));
-    },
-
-    enableDate(optionName) {
-        this.search.dates[optionName.toLowerCase()] = Dates.getCurrentDay();
-        this.dateChange(optionName);
-    },
-
-    dateParse(searchString) {
-        let dateFilter = /{([a-z_\-]+?):([a-z_\-0-9]+?)}/gi;
-        let dateTags = Object.keys(this.search.dates);
-        let matches;
-        while ((matches = dateFilter.exec(searchString)) !== null) {
-            if (dateTags.indexOf(matches[1]) === -1) continue;
-            this.search.dates[matches[1].toLowerCase()] = matches[2];
-        }
-    },
-
-    dateChange(optionName) {
-        let dateFilter = new RegExp('{\\s?'+optionName+'\\s?:([a-z_\\-0-9]+?)}', 'gi');
-        this.termString = this.termString.replace(dateFilter, '');
-        if (!this.search.dates[optionName]) return;
-        this.appendTerm(`{${optionName}:${this.search.dates[optionName]}}`);
-    },
-
-    dateRemove(optionName) {
-        this.search.dates[optionName] = false;
-        this.dateChange(optionName);
-    }
-
-};
-
-function created() {
-    this.termString = document.querySelector('[name=searchTerm]').value;
-    this.typeParse(this.termString);
-    this.exactParse(this.termString);
-    this.tagParse(this.termString);
-    this.optionParse(this.termString);
-    this.dateParse(this.termString);
-}
-
-export default {
-    data, computed, methods, created
-};
diff --git a/resources/js/vues/tag-manager.js b/resources/js/vues/tag-manager.js
deleted file mode 100644 (file)
index 65233cb..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-import draggable from 'vuedraggable';
-import autosuggest from './components/autosuggest';
-
-const data = {
-    entityId: false,
-    entityType: null,
-    tags: [],
-};
-
-const components = {draggable, autosuggest};
-const directives = {};
-
-const methods = {
-
-    addEmptyTag() {
-        this.tags.push({name: '', value: '', key: Math.random().toString(36).substring(7)});
-    },
-
-    /**
-     * When an tag changes check if another empty editable field needs to be added onto the end.
-     * @param tag
-     */
-    tagChange(tag) {
-        let tagPos = this.tags.indexOf(tag);
-        if (tagPos === this.tags.length-1 && (tag.name !== '' || tag.value !== '')) this.addEmptyTag();
-    },
-
-    /**
-     * When an tag field loses focus check the tag to see if its
-     * empty and therefore could be removed from the list.
-     * @param tag
-     */
-    tagBlur(tag) {
-        let isLast = (this.tags.indexOf(tag) === this.tags.length-1);
-        if (tag.name !== '' || tag.value !== '' || isLast) return;
-        let cPos = this.tags.indexOf(tag);
-        this.tags.splice(cPos, 1);
-    },
-
-    removeTag(tag) {
-        let tagPos = this.tags.indexOf(tag);
-        if (tagPos === -1) return;
-        this.tags.splice(tagPos, 1);
-    },
-
-    getTagFieldName(index, key) {
-        return `tags[${index}][${key}]`;
-    },
-};
-
-function mounted() {
-    this.entityId = Number(this.$el.getAttribute('entity-id'));
-    this.entityType = this.$el.getAttribute('entity-type');
-
-    let url = window.baseUrl(`/ajax/tags/get/${this.entityType}/${this.entityId}`);
-    this.$http.get(url).then(response => {
-        let tags = response.data;
-        for (let i = 0, len = tags.length; i < len; i++) {
-            tags[i].key = Math.random().toString(36).substring(7);
-        }
-        this.tags = tags;
-        this.addEmptyTag();
-    });
-}
-
-export default {
-    data, methods, mounted, components, directives
-};
\ No newline at end of file
diff --git a/resources/js/vues/vues.js b/resources/js/vues/vues.js
deleted file mode 100644 (file)
index ec19237..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-import Vue from "vue";
-
-function exists(id) {
-    return document.getElementById(id) !== null;
-}
-
-import searchSystem from "./search";
-import entityDashboard from "./entity-dashboard";
-import codeEditor from "./code-editor";
-import imageManager from "./image-manager";
-import tagManager from "./tag-manager";
-import attachmentManager from "./attachment-manager";
-import pageEditor from "./page-editor";
-
-let vueMapping = {
-    'search-system': searchSystem,
-    'entity-dashboard': entityDashboard,
-    'code-editor': codeEditor,
-    'image-manager': imageManager,
-    'tag-manager': tagManager,
-    'attachment-manager': attachmentManager,
-    'page-editor': pageEditor,
-};
-
-window.vues = {};
-
-function load() {
-    let ids = Object.keys(vueMapping);
-    for (let i = 0, len = ids.length; i < len; i++) {
-        if (!exists(ids[i])) continue;
-        let config = vueMapping[ids[i]];
-        config.el = '#' + ids[i];
-        window.vues[ids[i]] = new Vue(config);
-    }
-}
-
-export default load;
-
-
-
index 348eba3985de052b0d1dd47d16436ecd8a18f3df..6dc8a83978dcc9e5b8f2c446fd368c68cf9f0722 100644 (file)
@@ -36,13 +36,14 @@ return [
     'book_sort_notification'      => 'تمت إعادة سرد الكتاب بنجاح',
 
     // Bookshelves
-    'bookshelf_create'            => 'created Bookshelf',
-    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
-    'bookshelf_update'                 => 'updated bookshelf',
-    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
-    'bookshelf_delete'                 => 'deleted bookshelf',
-    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+    'bookshelf_create'            => 'تم إنشاء رف الكتب',
+    'bookshelf_create_notification'    => 'تم إنشاء الرف بنجاح',
+    'bookshelf_update'                 => 'تم تحديث الرف',
+    'bookshelf_update_notification'    => 'تم تحديث الرف بنجاح',
+    'bookshelf_delete'                 => 'تم تحديث الرف',
+    'bookshelf_delete_notification'    => 'تم حذف الرف بنجاح',
 
     // Other
     'commented_on'                => 'تم التعليق',
+    'permissions_update'          => 'تحديث الأذونات',
 ];
index d9ed5cf27a91152e6b568eacb331762754c9ce37..60a7af4114a4c6c43f2302f97f15c199092f98c5 100644 (file)
@@ -26,8 +26,8 @@ return [
     'remember_me' => 'تذكرني',
     'ldap_email_hint' => 'الرجاء إدخال عنوان بريد إلكتروني لاستخدامه مع الحساب.',
     'create_account' => 'إنشاء حساب',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
+    'already_have_account' => 'لديك حساب بالفعل؟',
+    'dont_have_account' => 'ليس لديك حساب؟',
     'social_login' => 'تسجيل الدخول باستخدام حسابات التواصل الاجتماعي',
     'social_registration' => 'إنشاء حساب باستخدام حسابات التواصل الاجتماعي',
     'social_registration_text' => 'إنشاء حساب والدخول باستخدام خدمة أخرى.',
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'استعادة كلمة المرور',
     'reset_password_send_instructions' => 'أدخل بريدك الإلكتروني بالأسفل وسيتم إرسال رسالة برابط لاستعادة كلمة المرور.',
     'reset_password_send_button' => 'أرسل رابط الاستعادة',
-    'reset_password_sent_success' => 'تم إرسال رابط استعادة كلمة المرور إلى :email.',
+    'reset_password_sent' => 'سيتم إرسال رابط إعادة تعيين كلمة المرور إلى عنوان البريد الإلكتروني هذا إذا كان موجودًا في النظام.',
     'reset_password_success' => 'تمت استعادة كلمة المرور بنجاح.',
     'email_reset_subject' => 'استعد كلمة المرور الخاصة بتطبيق :appName',
     'email_reset_text' => 'تم إرسال هذه الرسالة بسبب تلقينا لطلب استعادة كلمة المرور الخاصة بحسابكم.',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => 'إعادة إرسال رسالة التأكيد',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'تم دعوتك للإنضمام إلى صفحة الحالة الخاصة بـ :app_name!',
+    'user_invite_email_greeting' => 'تم إنشاء حساب مستخدم لك على %site%.',
+    'user_invite_email_text' => 'انقر على الزر أدناه لتعيين كلمة مرور الحساب والحصول على الوصول:',
+    'user_invite_email_action' => 'كلمة سر المستخدم',
+    'user_invite_page_welcome' => 'مرحبا بكم في :appName!',
+    'user_invite_page_text' => 'لإكمال حسابك والحصول على حق الوصول تحتاج إلى تعيين كلمة مرور سيتم استخدامها لتسجيل الدخول إلى :appName في الزيارات المستقبلية.',
+    'user_invite_page_confirm_button' => 'تأكيد كلمة المرور',
+    'user_invite_success' => 'مجموعة كلمات المرور، لديك الآن حق الوصول إلى :appName!'
 ];
\ No newline at end of file
index 9505b2a9577d7198f1a34f686d9c12a93002f1e7..57d0b2dbd0629550458e7546c11d5c451a1c14aa 100644 (file)
@@ -11,7 +11,7 @@ return [
     'save' => 'حفظ',
     'continue' => 'استمرار',
     'select' => 'تحديد',
-    'toggle_all' => 'Toggle All',
+    'toggle_all' => 'تبديل الكل',
     'more' => 'المزيد',
 
     // Form Labels
@@ -24,7 +24,7 @@ return [
     // Actions
     'actions' => 'إجراءات',
     'view' => 'عرض',
-    'view_all' => 'View All',
+    'view_all' => 'عرض الكل',
     'create' => 'إنشاء',
     'update' => 'تحديث',
     'edit' => 'تعديل',
@@ -33,20 +33,22 @@ return [
     'copy' => 'نسخ',
     'reply' => 'رد',
     'delete' => 'حذف',
+    'delete_confirm' => 'تأكيد الحذف',
     'search' => 'بحث',
     'search_clear' => 'مسح البحث',
     'reset' => 'إعادة تعيين',
     'remove' => 'إزالة',
     'add' => 'إضافة',
+    'fullscreen' => 'شاشة كاملة',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
-    'sort_name' => 'Name',
-    'sort_created_at' => 'Created Date',
-    'sort_updated_at' => 'Updated Date',
+    'sort_options' => 'خيارات الترتيب',
+    'sort_direction_toggle' => 'الترتيب وفق الإتجاه',
+    'sort_ascending' => 'فرز تصاعدي',
+    'sort_descending' => 'فرز تنازلي',
+    'sort_name' => 'الاسم',
+    'sort_created_at' => 'تاريخ الإنشاء',
+    'sort_updated_at' => 'تاريخ التحديث',
 
     // Misc
     'deleted_user' => 'حذف مستخدم',
@@ -58,17 +60,19 @@ return [
     'details' => 'التفاصيل',
     'grid_view' => 'عرض شبكي',
     'list_view' => 'عرض منسدل',
-    'default' => 'Default',
-    'breadcrumb' => 'Breadcrumb',
+    'default' => 'افتراضي',
+    'breadcrumb' => 'شريط التنقل',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'قائمة ملف التعريف',
     'view_profile' => 'عرض الملف الشخصي',
     'edit_profile' => 'تعديل الملف الشخصي',
+    'dark_mode' => 'الوضع المظلم',
+    'light_mode' => 'الوضع المضيء',
 
     // Layout tabs
-    'tab_info' => 'Info',
-    'tab_content' => 'Content',
+    'tab_info' => 'معلومات',
+    'tab_content' => 'المحتوى',
 
     // Email Content
     'email_action_help' => 'إذا واجهتكم مشكلة بضغط زر ":actionText" فبإمكانكم نسخ الرابط أدناه ولصقه بالمتصفح:',
index aa3935bd900f5e7f4dba7054fd863a3fae08dbfb..422de114b2aba7490926a61b0e77c4de0245da7d 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'المزيد',
     'image_image_name' => 'اسم الصورة',
     'image_delete_used' => 'هذه الصورة مستخدمة بالصفحات أدناه.',
-    'image_delete_confirm' => 'اضغط زر الحذف مرة أخرى لتأكيد حذف هذه الصورة.',
+    'image_delete_confirm_text' => 'هل أنت متأكد من أنك تريد حذف هذه الصورة ؟',
     'image_select_image' => 'تحديد الصورة',
     'image_dropzone' => 'قم بإسقاط الصورة أو اضغط هنا للرفع',
     'images_deleted' => 'تم حذف الصور',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'تعديل الشفرة',
     'code_language' => 'لغة الشفرة',
     'code_content' => 'محتويات الشفرة',
+    'code_session_history' => 'سجل الدورة',
     'code_save' => 'حفظ الشفرة',
 ];
index 9278c8cf37caee241dfa456457f1b1948046ce92..98404373986b92ef4c61e2f7be862c5f4ae2b739 100644 (file)
@@ -11,7 +11,7 @@ return [
     'recently_updated_pages' => 'صفحات حُدثت مؤخراً',
     'recently_created_chapters' => 'فصول أنشئت مؤخراً',
     'recently_created_books' => 'كتب أنشئت مؤخراً',
-    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_created_shelves' => 'الأرفف المنشأة مؤخراً',
     'recently_update' => 'حُدثت مؤخراً',
     'recently_viewed' => 'عُرضت مؤخراً',
     'recent_activity' => 'نشاطات حديثة',
@@ -22,7 +22,8 @@ return [
     'meta_created_name' => 'أنشئ :timeLength بواسطة :user',
     'meta_updated' => 'مُحدث :timeLength',
     'meta_updated_name' => 'مُحدث :timeLength بواسطة :user',
-    'entity_select' => 'Entity Select',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'اختيار الكيان',
     'images' => 'صور',
     'my_recent_drafts' => 'مسوداتي الحديثة',
     'my_recently_viewed' => 'ما عرضته مؤخراً',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'في حال التفعيل, ستتم تبدية هذه الأذونات على أذونات الأدوار.',
     'permissions_enable' => 'تفعيل الأذونات المخصصة',
     'permissions_save' => 'حفظ الأذونات',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'نتائج البحث',
@@ -47,17 +49,18 @@ return [
     'search_no_pages' => 'لم يطابق بحثكم أي صفحة',
     'search_for_term' => 'ابحث عن :term',
     'search_more' => 'المزيد من النتائج',
-    'search_filters' => 'تصفية البحث',
+    'search_advanced' => 'بحث مفصل',
+    'search_terms' => 'البحث باستخدام المصطلحات',
     'search_content_type' => 'نوع المحتوى',
     'search_exact_matches' => 'نتائج مطابقة تماماً',
     'search_tags' => 'بحث الوسوم',
-    'search_options' => 'Options',
+    'search_options' => 'الخيارات',
     'search_viewed_by_me' => 'تم استعراضها من قبلي',
     'search_not_viewed_by_me' => 'لم يتم استعراضها من قبلي',
     'search_permissions_set' => 'حزمة الأذونات',
     'search_created_by_me' => 'أنشئت بواسطتي',
     'search_updated_by_me' => 'حُدثت بواسطتي',
-    'search_date_options' => 'Date Options',
+    'search_date_options' => 'خيارات التاريخ',
     'search_updated_before' => 'حدثت قبل',
     'search_updated_after' => 'حدثت بعد',
     'search_created_before' => 'أنشئت قبل',
@@ -66,46 +69,46 @@ return [
     'search_update' => 'تحديث البحث',
 
     // Shelves
-    'shelf' => 'Shelf',
-    'shelves' => 'Shelves',
-    'x_shelves' => ':count Shelf|:count Shelves',
-    'shelves_long' => 'Bookshelves',
-    'shelves_empty' => 'No shelves have been created',
-    'shelves_create' => 'Create New Shelf',
-    'shelves_popular' => 'Popular Shelves',
-    'shelves_new' => 'New Shelves',
-    'shelves_new_action' => 'New Shelf',
-    'shelves_popular_empty' => 'The most popular shelves will appear here.',
-    'shelves_new_empty' => 'The most recently created shelves will appear here.',
-    'shelves_save' => 'Save Shelf',
-    'shelves_books' => 'Books on this shelf',
-    'shelves_add_books' => 'Add books to this shelf',
-    'shelves_drag_books' => 'Drag books here to add them to this shelf',
-    'shelves_empty_contents' => 'This shelf has no books assigned to it',
-    'shelves_edit_and_assign' => 'Edit shelf to assign books',
-    'shelves_edit_named' => 'Edit Bookshelf :name',
-    'shelves_edit' => 'Edit Bookshelf',
-    'shelves_delete' => 'Delete Bookshelf',
-    'shelves_delete_named' => 'Delete Bookshelf :name',
-    'shelves_delete_explain' => "This will delete the bookshelf with the name ':name'. Contained books will not be deleted.",
-    'shelves_delete_confirmation' => 'Are you sure you want to delete this bookshelf?',
-    'shelves_permissions' => 'Bookshelf Permissions',
-    'shelves_permissions_updated' => 'Bookshelf Permissions Updated',
-    'shelves_permissions_active' => 'Bookshelf Permissions Active',
-    'shelves_copy_permissions_to_books' => 'Copy Permissions to Books',
-    'shelves_copy_permissions' => 'Copy Permissions',
-    'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this bookshelf to all books contained within. Before activating, ensure any changes to the permissions of this bookshelf have been saved.',
-    'shelves_copy_permission_success' => 'Bookshelf permissions copied to :count books',
+    'shelf' => 'رف',
+    'shelves' => 'الأرفف',
+    'x_shelves' => ':count رف|:count أرفف',
+    'shelves_long' => 'أرفف الكتب',
+    'shelves_empty' => 'لم يتم إنشاء أي أرفف',
+    'shelves_create' => 'إنشاء رف جديد',
+    'shelves_popular' => 'أرفف شعبية',
+    'shelves_new' => 'أرفف جديدة',
+    'shelves_new_action' => 'رف جديد',
+    'shelves_popular_empty' => 'ستظهر هنا الأرفف الأكثر رواجًا.',
+    'shelves_new_empty' => 'ستظهر هنا الأرفف التي تم إنشاؤها مؤخرًا.',
+    'shelves_save' => 'حفظ الرف',
+    'shelves_books' => 'كتب على هذا الرف',
+    'shelves_add_books' => 'إضافة كتب لهذا الرف',
+    'shelves_drag_books' => 'اسحب الكتب هنا لإضافتها لهذا الرف',
+    'shelves_empty_contents' => 'لا توجد كتب مخصصة لهذا الرف',
+    'shelves_edit_and_assign' => 'تحرير الرف لإدراج كتب',
+    'shelves_edit_named' => 'تحرير رف الكتب: الاسم',
+    'shelves_edit' => 'تحرير رف الكتب',
+    'shelves_delete' => 'حذف رف الكتب',
+    'shelves_delete_named' => 'حذف رف الكتب: الاسم',
+    'shelves_delete_explain' => "سيؤدي هذا إلى حذف رف الكتب مع الاسم ':المُسمى به'. لن يتم حذف الكتب المتضمنة.",
+    'shelves_delete_confirmation' => 'هل أنت متأكد من أنك تريد حذف هذا الرف؟',
+    'shelves_permissions' => 'أذونات رف الكتب',
+    'shelves_permissions_updated' => 'تم تحديث أذونات رف الكتب',
+    'shelves_permissions_active' => 'أذونات رف الكتب نشطة',
+    'shelves_copy_permissions_to_books' => 'نسخ أذونات الوصول إلى الكتب',
+    'shelves_copy_permissions' => 'نسخ الأذونات',
+    'shelves_copy_permissions_explain' => 'سيؤدي هذا إلى تطبيق إعدادات الأذونات الحالية لهذا الرف على جميع الكتب المتضمنة فيه. قبل التفعيل، تأكد من حفظ أي تغييرات في أذونات هذا الرف.',
+    'shelves_copy_permission_success' => 'تم نسخ أذونات رف الكتب إلى: عد الكتب',
 
     // Books
     'book' => 'كتاب',
-    'books' => 'كتب',
+    'books' => 'الكتب',
     'x_books' => ':count كتاب|:count كتب',
     'books_empty' => 'لم يتم إنشاء أي كتب',
     'books_popular' => 'كتب رائجة',
     'books_recent' => 'كتب حديثة',
     'books_new' => 'كتب جديدة',
-    'books_new_action' => 'New Book',
+    'books_new_action' => 'كتاب جديد',
     'books_popular_empty' => 'الكتب الأكثر رواجاً ستظهر هنا.',
     'books_new_empty' => 'الكتب المنشأة مؤخراً ستظهر هنا.',
     'books_create' => 'إنشاء كتاب جديد',
@@ -128,11 +131,11 @@ return [
     'books_navigation' => 'تصفح الكتاب',
     'books_sort' => 'فرز محتويات الكتاب',
     'books_sort_named' => 'فرز كتاب :bookName',
-    'books_sort_name' => 'Sort by Name',
-    'books_sort_created' => 'Sort by Created Date',
-    'books_sort_updated' => 'Sort by Updated Date',
-    'books_sort_chapters_first' => 'Chapters First',
-    'books_sort_chapters_last' => 'Chapters Last',
+    'books_sort_name' => 'ترتيب حسب الإسم',
+    'books_sort_created' => 'ترتيب حسب تاريخ الإنشاء',
+    'books_sort_updated' => 'فرز حسب تاريخ التحديث',
+    'books_sort_chapters_first' => 'الفصول الأولى',
+    'books_sort_chapters_last' => 'الفصول الأخيرة',
     'books_sort_show_other' => 'عرض كتب أخرى',
     'books_sort_save' => 'حفظ الترتيب الجديد',
 
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'إنشاء فصل جديد',
     'chapters_delete' => 'حذف الفصل',
     'chapters_delete_named' => 'حذف فصل :chapterName',
-    'chapters_delete_explain' => 'سيتم حذف فصل \':chapterName\'. جميع الصفحات ستزال وستتم إضافتها مباشرة للكتاب الرئيسي.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'تأكيد حذف الفصل؟',
     'chapters_edit' => 'تعديل الفصل',
     'chapters_edit_named' => 'تعديل فصل :chapterName',
@@ -176,7 +179,7 @@ return [
     'pages_delete_confirm' => 'تأكيد حذف الصفحة؟',
     'pages_delete_draft_confirm' => 'تأكيد حذف المسودة؟',
     'pages_editing_named' => ':pageName قيد التعديل',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'خيارات المسودة',
     'pages_edit_save_draft' => 'حفظ المسودة',
     'pages_edit_draft' => 'تعديل مسودة الصفحة',
     'pages_editing_draft' => 'المسودة قيد التعديل',
@@ -193,7 +196,7 @@ return [
     'pages_md_editor' => 'المحرر',
     'pages_md_preview' => 'معاينة',
     'pages_md_insert_image' => 'إدخال صورة',
-    'pages_md_insert_link' => 'Insert Entity Link',
+    'pages_md_insert_link' => 'إدراج ارتباط الكيان',
     'pages_md_insert_drawing' => 'إدخال رسمة',
     'pages_not_in_chapter' => 'صفحة ليست في فصل',
     'pages_move' => 'نقل الصفحة',
@@ -207,11 +210,12 @@ return [
     'pages_revisions' => 'مراجعات الصفحة',
     'pages_revisions_named' => 'مراجعات صفحة :pageName',
     'pages_revision_named' => 'مراجعة صفحة :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'أنشئ بواسطة',
     'pages_revisions_date' => 'تاريخ المراجعة',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_numbered' => 'مراجعة #: رقم تعريفي',
+    'pages_revisions_numbered_changes' => 'مراجعة #: رقم تعريفي التغييرات',
     'pages_revisions_changelog' => 'سجل التعديل',
     'pages_revisions_changes' => 'التعديلات',
     'pages_revisions_current' => 'النسخة الحالية',
@@ -230,24 +234,24 @@ return [
         'start_b' => ':userName بدأ بتعديل هذه الصفحة',
         'time_a' => 'منذ أن تم تحديث هذه الصفحة',
         'time_b' => 'في آخر :minCount دقيقة/دقائق',
-        'message' => ':start :time. Take care not to overwrite each other\'s updates!',
+        'message' => 'وقت البدء: احرص على عدم الكتابة فوق تحديثات بعضنا البعض!',
     ],
     'pages_draft_discarded' => 'تم التخلص من المسودة. تم تحديث المحرر بمحتوى الصفحة الحالي',
-    'pages_specific' => 'Specific Page',
-    'pages_is_template' => 'Page Template',
+    'pages_specific' => 'صفحة محددة',
+    'pages_is_template' => 'قالب الصفحة',
 
     // Editor Sidebar
     'page_tags' => 'وسوم الصفحة',
     'chapter_tags' => 'وسوم الفصل',
     'book_tags' => 'وسوم الكتاب',
-    'shelf_tags' => 'Shelf Tags',
+    'shelf_tags' => 'علامات الرف',
     'tag' => 'وسم',
     'tags' =>  'وسوم',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'اسم العلامة',
     'tag_value' => 'قيمة الوسم (اختياري)',
     'tags_explain' => "إضافة الوسوم تساعد بترتيب وتقسيم المحتوى. \n من الممكن وضع قيمة لكل وسم لترتيب أفضل وأدق.",
     'tags_add' => 'إضافة وسم آخر',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'إزالة هذه العلامة',
     'attachments' => 'المرفقات',
     'attachments_explain' => 'ارفع بعض الملفات أو أرفق بعض الروابط لعرضها بصفحتك. ستكون الملفات والروابط معروضة في الشريط الجانبي للصفحة.',
     'attachments_explain_instant_save' => 'سيتم حفظ التغييرات هنا بلحظتها',
@@ -255,15 +259,16 @@ return [
     'attachments_upload' => 'رفع ملف',
     'attachments_link' => 'إرفاق رابط',
     'attachments_set_link' => 'تحديد الرابط',
-    'attachments_delete_confirm' => 'اضغط على زر الحذف مرة أخرى لتأكيد حذف المرفق.',
+    'attachments_delete' => 'هل أنت متأكد من أنك تريد حذف هذا المرفق؟',
     'attachments_dropzone' => 'أسقط الملفات أو اضغط هنا لإرفاق ملف',
     'attachments_no_files' => 'لم يتم رفع أي ملفات',
     'attachments_explain_link' => 'بالإمكان إرفاق رابط في حال عدم تفضيل رفع ملف. قد يكون الرابط لصفحة أخرى أو لملف في أحد خدمات التخزين السحابي.',
     'attachments_link_name' => 'اسم الرابط',
     'attachment_link' => 'رابط المرفق',
-    'attachments_link_url' => 'Link to file',
+    'attachments_link_url' => 'رابط الملف',
     'attachments_link_url_hint' => 'رابط الموقع أو الملف',
-    'attach' => 'Attach',
+    'attach' => 'إرفاق',
+    'attachments_insert_link' => 'إضافة رابط مرفق إلى الصفحة',
     'attachments_edit_file' => 'تعديل الملف',
     'attachments_edit_file_name' => 'اسم الملف',
     'attachments_edit_drop_upload' => 'أسقط الملفات أو اضغط هنا للرفع والاستبدال',
@@ -273,20 +278,20 @@ return [
     'attachments_file_uploaded' => 'تم رفع الملف بنجاح',
     'attachments_file_updated' => 'تم تحديث الملف بنجاح',
     'attachments_link_attached' => 'تم إرفاق الرابط بالصفحة بنجاح',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'القوالب',
+    'templates_set_as_template' => 'هذه الصفحة عبارة عن قالب',
+    'templates_explain_set_as_template' => 'يمكنك تعيين هذه الصفحة كقالب بحيث تستخدم محتوياتها عند إنشاء صفحات أخرى. سيتمكن المستخدمون الآخرون من استخدام هذا القالب إذا كان لديهم أذونات عرض لهذه الصفحة.',
+    'templates_replace_content' => 'استبدال محتوى الصفحة',
+    'templates_append_content' => 'تذييل محتوى الصفحة',
+    'templates_prepend_content' => 'بادئة محتوى الصفحة',
 
     // Profile View
-    'profile_user_for_x' => 'User for :time',
+    'profile_user_for_x' => 'المستخدم لـ : الوقت',
     'profile_created_content' => 'المحتوى المنشأ',
     'profile_not_created_pages' => 'لم يتم إنشاء أي صفحات بواسطة :userName',
     'profile_not_created_chapters' => 'لم يتم إنشاء أي فصول بواسطة :userName',
     'profile_not_created_books' => 'لم يتم إنشاء أي كتب بواسطة :userName',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_not_created_shelves' => 'لم يقم "اسم المستخدم"بإنشاء أي أرفف',
 
     // Comments
     'comment' => 'تعليق',
@@ -307,8 +312,8 @@ return [
     'comment_in_reply_to' => 'رداً على :commentId',
 
     // Revision
-    'revision_delete_confirm' => 'Are you sure you want to delete this revision?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
-    'revision_delete_success' => 'Revision deleted',
-    'revision_cannot_delete_latest' => 'Cannot delete the latest revision.'
+    'revision_delete_confirm' => 'هل أنت متأكد من أنك تريد حذف هذا الإصدار؟',
+    'revision_restore_confirm' => 'هل أنت متأكد من أنك تريد استعادة هذا الإصدار؟ سيتم استبدال محتوى الصفحة الحالية.',
+    'revision_delete_success' => 'تم حذف الإصدار',
+    'revision_cannot_delete_latest' => 'لايمكن حذف آخر إصدار.'
 ];
\ No newline at end of file
index dd42338b5c0d843178c606c564ece024527d7174..93e8201ab7599e3a11a40fb44d076461d07137b8 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'تم تأكيد البريد الإلكتروني من قبل, الرجاء محاولة تسجيل الدخول.',
     'email_confirmation_invalid' => 'رابط التأكيد غير صحيح أو قد تم استخدامه من قبل, الرجاء محاولة التسجيل من جديد.',
     'email_confirmation_expired' => 'صلاحية رابط التأكيد انتهت, تم إرسال رسالة تأكيد جديدة لعنوان البريد الإلكتروني.',
+    'email_confirmation_awaiting' => 'عنوان البريد الإلكتروني للحساب قيد الاستخدام يحتاج إلى تأكيد',
     'ldap_fail_anonymous' => 'فشل الوصول إلى LDAP باستخدام الربط المجهول',
     'ldap_fail_authed' => 'فشل الوصول إلى LDAP باستخدام dn و password المعطاة',
     'ldap_extension_not_installed' => 'لم يتم تثبيت إضافة LDAP PHP',
     'ldap_cannot_connect' => 'لا يمكن الاتصال بخادم ldap, فشل الاتصال المبدئي',
+    'saml_already_logged_in' => 'تم تسجيل الدخول بالفعل',
+    'saml_user_not_registered' => 'المستخدم :name غير مسجل ويتم تعطيل التسجيل التلقائي',
+    'saml_no_email_address' => 'تعذر العثور على عنوان بريد إلكتروني، لهذا المستخدم، في البيانات المقدمة من نظام المصادقة الخارجي',
+    'saml_invalid_response_id' => 'لم يتم التعرف على الطلب من نظام التوثيق الخارجي من خلال عملية تبدأ بهذا التطبيق. العودة بعد تسجيل الدخول يمكن أن يسبب هذه المشكلة.',
+    'saml_fail_authed' => 'تسجيل الدخول باستخدام :system فشل، النظام لم يوفر التفويض الناجح',
     'social_no_action_defined' => 'لم يتم تعريف أي إجراء',
     'social_login_bad_response' => "حصل خطأ خلال تسجيل الدخول باستخدام :socialAccount \n:error",
     'social_account_in_use' => 'حساب :socialAccount قيد الاستخدام حالياً, الرجاء محاولة الدخول باستخدام خيار :socialAccount.',
@@ -25,9 +31,9 @@ return [
     'social_account_already_used_existing' => 'حساب :socialAccount مستخدَم من قبل مستخدم آخر.',
     'social_account_not_used' => 'حساب :socialAccount غير مرتبط بأي مستخدم. الرجاء ربطه من خلال إعدادات ملفكم. ',
     'social_account_register_instructions' => 'إذا لم يكن لديكم حساب فيمكنكم التجسيل باستخدام خيار :socialAccount.',
-    'social_driver_not_found' => 'Social driver not found',
-    'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'social_driver_not_found' => 'لم يتم العثور على السوشيال درايفر "Social driver"',
+    'social_driver_not_configured' => 'لم يتم تهيئة إعدادات حسابك الاجتماعي بشكل صحيح.',
+    'invite_token_expired' => 'انتهت صلاحية رابط هذه الدعوة. يمكنك بدلاً من ذلك محاولة إعادة تعيين كلمة مرور حسابك.',
 
     // System
     'path_not_writable' => 'لا يمكن الرفع إلى مسار :filePath. الرجاء التأكد من قابلية الكتابة إلى الخادم.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'انتهت عملية تحميل الملف.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'لم يتم العثور على المرفق',
 
     // Pages
@@ -48,8 +53,8 @@ return [
     'page_custom_home_deletion' => 'لا يمكن حذف الصفحة إذا كانت محددة كصفحة رئيسية',
 
     // Entities
-    'entity_not_found' => 'Entity not found',
-    'bookshelf_not_found' => 'Bookshelf not found',
+    'entity_not_found' => 'الكيان غير موجود',
+    'bookshelf_not_found' => 'رف الكتب غير موجود',
     'book_not_found' => 'لم يتم العثور على الكتاب',
     'page_not_found' => 'لم يتم العثور على الصفحة',
     'chapter_not_found' => 'لم يتم العثور على الفصل',
@@ -65,7 +70,7 @@ return [
     'role_cannot_be_edited' => 'لا يمكن تعديل هذا الدور',
     'role_system_cannot_be_deleted' => 'هذا الدور خاص بالنظام ولا يمكن حذفه',
     'role_registration_default_cannot_delete' => 'لا يمكن حذف الدور إذا كان مسجل كالدور الأساسي بعد تسجيل الحساب',
-    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+    'role_cannot_remove_only_admin' => 'هذا المستخدم هو المستخدم الوحيد المعين لدور المسؤول. قم بتعيين دور المسؤول لمستخدم آخر قبل محاولة إزالته هنا.',
 
     // Comments
     'comment_list' => 'حصل خطأ خلال جلب التعليقات.',
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'لم يتم العثور على الصفحة',
     'sorry_page_not_found' => 'عفواً, لا يمكن العثور على الصفحة التي تبحث عنها.',
+    'sorry_page_not_found_permission_warning' => 'إذا كنت تتوقع أن تكون هذه الصفحة موجودة، قد لا يكون لديك تصريح بمشاهدتها.',
     'return_home' => 'العودة للصفحة الرئيسية',
     'error_occurred' => 'حدث خطأ',
     'app_down' => ':appName لا يعمل حالياً',
     'back_soon' => 'سيعود للعمل قريباً.',
 
+    // API errors
+    'api_no_authorization_found' => 'لم يتم العثور على رمز ترخيص مميز في الطلب',
+    'api_bad_authorization_format' => 'تم العثور على رمز ترخيص مميز في الطلب ولكن يبدو أن التنسيق غير صحيح',
+    'api_user_token_not_found' => 'لم يتم العثور على رمز API مطابق لرمز الترخيص المُقدم',
+    'api_incorrect_token_secret' => 'الشفرة المُقدمة لرمز API المستخدم المحدد غير صحيحة',
+    'api_user_no_api_permission' => 'مالك رمز API المستخدم ليس لديه الصلاحية لإجراء مكالمات API',
+    'api_user_token_expired' => 'انتهت صلاحية رمز الترخيص المستخدم',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'حدث خطأ عند إرسال بريد إلكتروني تجريبي:',
+
 ];
index 23a8a7e7429a465b38a13c7cb1c05ee5dbbb7861..4ab447d5d56e5c3226063fbd6f085111d9cdb34e 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'يجب أن تتكون كلمة المرور من ستة أحرف على الأقل وأن تطابق التأكيد.',
     'user' => "لم يتم العثور على مستخدم بعنوان البريد الإلكتروني المعطى.",
-    'token' => 'رابط تجديد كلمة المرور غير صحيح.',
+    'token' => 'رمز إعادة تعيين كلمة المرور غير صالح لعنوان هذا البريد الإلكتروني.',
     'sent' => 'تم إرسال رابط تجديد كلمة المرور إلى بريدكم الإلكتروني!',
     'reset' => 'تم تجديد كلمة المرور الخاصة بكم!',
 
index d867e30b51301e6f3f247a3986991830248ffd46..f55451ff63e300db86b02dc9a8be4c44f77ad3c8 100755 (executable)
@@ -12,57 +12,109 @@ return [
     'settings_save_success' => 'تم حفظ الإعدادات',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
+    'app_customization' => 'تخصيص',
+    'app_features_security' => 'الميزات و الأمان',
     'app_name' => 'اسم التطبيق',
     'app_name_desc' => 'سيتم عرض هذا الاسم في الترويسة وفي أي رسالة بريد إلكتروني.',
     'app_name_header' => 'عرض اسم التطبيق في الترويسة؟',
-    'app_public_access' => 'Public Access',
-    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
-    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
-    'app_public_access_toggle' => 'Allow public access',
+    'app_public_access' => 'الوصول العام',
+    'app_public_access_desc' => 'تمكين هذا الخيار سيسمح للزوار، الذين لم يتم تسجيل دخولهم، بالوصول إلى المحتوى في مثيل مكدس الكتب الخاص بك.',
+    'app_public_access_desc_guest' => 'يمكن التحكم في وصول الزوار العموميين من خلال المستخدم "الضيف".',
+    'app_public_access_toggle' => 'السماح بالوصول العام',
     'app_public_viewing' => 'السماح بالعرض على العامة؟',
     'app_secure_images' => 'تفعيل حماية أكبر لرفع الصور؟',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_toggle' => 'لمزيد من الحماية',
     'app_secure_images_desc' => 'لتحسين أداء النظام, ستكون جميع الصور متاحة للعامة. هذا الخيار يضيف سلسلة من الحروف والأرقام العشوائية صعبة التخمين إلى رابط الصورة. الرجاء التأكد من تعطيل فهرسة المسارات لمنع الوصول السهل.',
     'app_editor' => 'محرر الصفحة',
     'app_editor_desc' => 'الرجاء اختيار محرر النص الذي سيستخدم من قبل جميع المستخدمين لتحرير الصفحات.',
     'app_custom_html' => 'Custom HTML head content',
-    'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the <head> section of every page. This is handy for overriding styles or adding analytics code.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_desc' => 'سيتم إدراج أي محتوى مضاف هنا في الجزء السفلي من قسم <head> من كل صفحة. هذا أمر مفيد لتجاوز الأنماط أو إضافة رمز التحليل.',
+    'app_custom_html_disabled_notice' => 'تم تعطيل محتوى HTML الرئيسي المخصص في صفحة الإعدادات هذه لضمان عكس أي تغييرات متتالية.',
     'app_logo' => 'شعار التطبيق',
     'app_logo_desc' => 'يجب أن تكون الصورة بارتفاع 43 بكسل. <br>سيتم تصغير الصور الأكبر من ذلك.',
     'app_primary_color' => 'اللون الأساسي للتطبيق',
     'app_primary_color_desc' => 'يجب أن تكون القيمة من نوع hex. <br>اترك الخانة فارغة للرجوع للون الافتراضي.',
     'app_homepage' => 'الصفحة الرئيسية للتطبيق',
     'app_homepage_desc' => 'الرجاء اختيار صفحة لتصبح الصفحة الرئيسية بدل من الافتراضية. سيتم تجاهل جميع الأذونات الخاصة بالصفحة المختارة.',
-    'app_homepage_select' => 'Select a page',
+    'app_homepage_select' => 'اختر صفحة',
     'app_disable_comments' => 'تعطيل التعليقات',
-    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_toggle' => 'تعطيل التعليقات',
     'app_disable_comments_desc' => 'تعطيل التعليقات على جميع الصفحات داخل التطبيق. التعليقات الموجودة من الأصل لن تكون ظاهرة.',
 
+    // Color settings
+    'content_colors' => 'ألوان المحتوى',
+    'content_colors_desc' => 'تعيين الألوان لجميع العناصر في التسلسل الهرمي لتنظيم الصفحات. يوصى باختيار الألوان ذات السطوع المماثل للألوان الافتراضية للقراءة.',
+    'bookshelf_color' => 'لون الرف',
+    'book_color' => 'لون الكتاب',
+    'chapter_color' => 'لون الفصل',
+    'page_color' => 'لون الصفحة',
+    'page_draft_color' => 'لون مسودة الصفحة',
+
     // Registration Settings
     'reg_settings' => 'إعدادات التسجيل',
-    'reg_enable' => 'Enable Registration',
-    'reg_enable_toggle' => 'Enable registration',
-    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_enable' => 'تمكين التسجيل',
+    'reg_enable_toggle' => 'تمكين التسجيل',
+    'reg_enable_desc' => 'عند تمكين التسجيل سيكون المستخدم قادرا على تسجيل نفسه كمستخدم تطبيق. عند التسجيل يعطى لهم دور مستخدم افتراضي وحيد.',
     'reg_default_role' => 'دور المستخدم الأساسي بعد التسجيل',
-    'reg_email_confirmation' => 'Email Confirmation',
-    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_enable_external_warning' => 'يتم تجاهل الخيار أعلاه بينما يتم تفعيل مصادقة LDAP الخارجية أو SAML. حسابات المستخدم للأعضاء غير الحاليين سيتم إنشاؤها تلقائياً إذا كانت المصادقة، مقابل النظام الخارجي المستخدم، ناجحة.',
+    'reg_email_confirmation' => 'تأكيد البريد الإلكتروني',
+    'reg_email_confirmation_toggle' => 'يتطلب تأكيد البريد الإلكتروني',
     'reg_confirm_email_desc' => 'إذا تم استخدام قيود للمجال سيصبح التأكيد عن طريق البريد الإلكتروني إلزامي وسيتم تجاهل القيمة أسفله.',
     'reg_confirm_restrict_domain' => 'تقييد التسجيل على مجال محدد',
-    'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
+    'reg_confirm_restrict_domain_desc' => 'أدخل قائمة مفصولة بفواصل لنطاقات البريد الإلكتروني التي ترغب في تقييد التسجيل إليها. سيتم إرسال بريد إلكتروني للمستخدمين لتأكيد عنوانهم قبل السماح لهم بالتفاعل مع التطبيق. <br> لاحظ أن المستخدمين سيكونون قادرين على تغيير عناوين البريد الإلكتروني الخاصة بهم بعد التسجيل بنجاح.',
     'reg_confirm_restrict_domain_placeholder' => 'لم يتم اختيار أي قيود',
 
     // Maintenance settings
     'maint' => 'الصيانة',
     'maint_image_cleanup' => 'تنظيف الصور',
-    'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
-    'maint_image_cleanup_ignore_revisions' => 'تجاهل الصور في المراجعات',
+    'maint_image_cleanup_desc' => "مسح الصفحة ومراجعة المحتوى للتحقق من أي الصور والرسوم المستخدمة حاليًا وأي الصور زائدة عن الحاجة. تأكد من إنشاء قاعدة بيانات كاملة و نسخة احتياطية للصور قبل تشغيل هذا.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'بدء التنظيف',
     'maint_image_cleanup_warning' => 'يوجد عدد :count من الصور المحتمل عدم استخدامها. تأكيد حذف الصور؟',
     'maint_image_cleanup_success' => 'تم إيجاد وحذف عدد :count من الصور المحتمل عدم استخدامها!',
     'maint_image_cleanup_nothing_found' => 'لم يتم حذف أي شيء لعدم وجود أي صور غير مسمتخدمة',
+    'maint_send_test_email' => 'إرسال بريد إلكتروني تجريبي',
+    'maint_send_test_email_desc' => 'يرسل هذا بريدًا إلكترونيًا تجريبيًا إلى عنوان بريدك الإلكتروني المحدد في ملفك الشخصي.',
+    'maint_send_test_email_run' => 'إرسال بريد إليكتروني تجريبي',
+    'maint_send_test_email_success' => 'تم إرسال البريد الإلكتروني إلى:العنوان',
+    'maint_send_test_email_mail_subject' => 'اختبار البريد الإلكتروني',
+    'maint_send_test_email_mail_greeting' => 'يبدو أن تسليم البريد الإلكتروني يعمل!',
+    'maint_send_test_email_mail_text' => 'تهانينا! كما تلقيت إشعار هذا البريد الإلكتروني، يبدو أن إعدادات البريد الإلكتروني الخاص بك قد تم تكوينها بشكل صحيح.',
+    'maint_recycle_bin_desc' => 'تُرسل الأرفف والكتب والفصول والصفحات المحذوفة إلى سلة المحذوفات حتى يمكن استعادتها أو حذفها نهائيًا. قد يتم إزالة العناصر الأقدم في سلة المحذوفات تلقائيًا بعد فترة اعتمادًا على تكوين النظام.',
+    'maint_recycle_bin_open' => 'افتح سلة المحذوفات',
+
+    // Recycle Bin
+    'recycle_bin' => 'سلة المحذوفات',
+    'recycle_bin_desc' => 'هنا يمكنك استعادة العناصر التي تم حذفها أو اختيار إزالتها نهائيا من النظام. هذه القائمة غير مصفاة خلافاً لقوائم الأنشطة المماثلة في النظام حيث يتم تطبيق عوامل تصفية الأذونات.',
+    'recycle_bin_deleted_item' => 'عنصر محذوف',
+    'recycle_bin_deleted_by' => 'حُذف بواسطة',
+    'recycle_bin_deleted_at' => 'وقت الحذف',
+    'recycle_bin_permanently_delete' => 'حُذف نهائيًا',
+    'recycle_bin_restore' => 'استرجاع',
+    'recycle_bin_contents_empty' => 'سلة المحذوفات فارغة حاليًا',
+    'recycle_bin_empty' => 'إفراغ سلة المحذوفات',
+    'recycle_bin_empty_confirm' => 'سيؤدي هذا إلى إتلاف جميع العناصر الموجودة في سلة المحذوفات بشكل دائم بما في ذلك المحتوى الموجود داخل كل عنصر. هل أنت متأكد من أنك تريد إفراغ سلة المحذوفات؟',
+    'recycle_bin_destroy_confirm' => 'سيؤدي هذا الإجراء إلى حذف هذا العنصر نهائيًا ، إلى جانب أي عناصر فرعية مدرجة أدناه ، من النظام ولن تتمكن من استعادة هذا المحتوى. هل أنت متأكد من أنك تريد حذف هذا العنصر نهائيًا؟',
+    'recycle_bin_destroy_list' => 'العناصر المراد تدميرها',
+    'recycle_bin_restore_list' => 'العناصر المراد استرجاعها',
+    'recycle_bin_restore_confirm' => 'سيعيد هذا الإجراء العنصر المحذوف ، بما في ذلك أي عناصر فرعية ، إلى موقعه الأصلي. إذا تم حذف الموقع الأصلي منذ ذلك الحين ، وهو الآن في سلة المحذوفات ، فسيلزم أيضًا استعادة العنصر الأصلي.',
+    'recycle_bin_restore_deleted_parent' => 'تم حذف أصل هذا العنصر أيضًا. سيبقى حذفه حتى يتم استعادة ذلك الأصل أيضًا.',
+    'recycle_bin_destroy_notification' => 'المحذوف: قُم بعد إجمالي العناصر من سلة المحذوفات.',
+    'recycle_bin_restore_notification' => 'المرتجع: قُم بعد إجمالي العناصر من سلة المحذوفات.',
+
+    // Audit Log
+    'audit' => 'سجل المراجعة',
+    'audit_desc' => 'يعرض هذا السجل قائمة بالأنشطة المتعقبة في النظام. هذه القائمة غير مصفاة خلافاً لقوائم الأنشطة المماثلة في النظام حيث يتم تطبيق عوامل تصفية الأذونات.',
+    'audit_event_filter' => 'تصفية الحدث',
+    'audit_event_filter_no_filter' => 'لا يوجد فلتر',
+    'audit_deleted_item' => 'عنصر محذوف',
+    'audit_deleted_item_name' => 'الاسم: كتابة الاسم',
+    'audit_table_user' => 'المستخدم',
+    'audit_table_event' => 'الحدث',
+    'audit_table_related' => 'العنصر أو التفاصيل ذات الصلة',
+    'audit_table_date' => 'تاريخ النشاط',
+    'audit_date_from' => 'نطاق التاريخ من',
+    'audit_date_to' => 'نطاق التاريخ إلى',
 
     // Role Settings
     'roles' => 'الأدوار',
@@ -71,7 +123,7 @@ return [
     'role_create_success' => 'تم إنشاء الدور بنجاح',
     'role_delete' => 'حذف الدور',
     'role_delete_confirm' => 'سيتم حذف الدور المسمى \':roleName\'.',
-    'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
+    'role_delete_users_assigned' => 'هذا الدور له: عدد المستخدمين المعينين له. إذا كنت ترغب في ترحيل المستخدمين من هذا الدور ، فحدد دورًا جديدًا أدناه.',
     'role_delete_no_migration' => "لا تقم بترجيل المستخدمين",
     'role_delete_sure' => 'تأكيد حذف الدور؟',
     'role_delete_success' => 'تم حذف الدور بنجاح',
@@ -79,20 +131,22 @@ return [
     'role_details' => 'تفاصيل الدور',
     'role_name' => 'اسم الدور',
     'role_desc' => 'وصف مختصر للدور',
-    'role_external_auth_id' => 'External Authentication IDs',
+    'role_external_auth_id' => 'ربط الحساب بمواقع التواصل',
     'role_system' => 'أذونات النظام',
     'role_manage_users' => 'إدارة المستخدمين',
     'role_manage_roles' => 'إدارة الأدوار وأذوناتها',
     'role_manage_entity_permissions' => 'إدارة جميع أذونات الكتب والفصول والصفحات',
     'role_manage_own_entity_permissions' => 'إدارة الأذونات الخاصة بكتابك أو فصلك أو صفحاتك',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => 'إدارة قوالب الصفحة',
+    'role_access_api' => 'الوصول إلى واجهة برمجة تطبيقات النظام API',
     'role_manage_settings' => 'إدارة إعدادات التطبيق',
-    'role_asset' => 'Asset Permissions',
-    'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
-    'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
+    'role_asset' => 'أذونات الأصول',
+    'roles_system_warning' => 'اعلم أن الوصول إلى أي من الأذونات الثلاثة المذكورة أعلاه يمكن أن يسمح للمستخدم بتغيير امتيازاته الخاصة أو امتيازات الآخرين في النظام. قم بتعيين الأدوار مع هذه الأذونات فقط للمستخدمين الموثوق بهم.',
+    'role_asset_desc' => 'تتحكم هذه الأذونات في الوصول الافتراضي إلى الأصول داخل النظام. ستتجاوز الأذونات الخاصة بالكتب والفصول والصفحات هذه الأذونات.',
+    'role_asset_admins' => 'يُمنح المسؤولين حق الوصول تلقائيًا إلى جميع المحتويات ولكن هذه الخيارات قد تعرض خيارات واجهة المستخدم أو تخفيها.',
     'role_all' => 'الكل',
-    'role_own' => 'Own',
-    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_own' => 'ما يخص',
+    'role_controlled_by_asset' => 'يتحكم فيها الأصول التي يتم رفعها إلى',
     'role_save' => 'حفظ الدور',
     'role_update_success' => 'تم تحديث الدور بنجاح',
     'role_users' => 'مستخدمون داخل هذا الدور',
@@ -103,37 +157,67 @@ return [
     'user_profile' => 'ملف المستخدم',
     'users_add_new' => 'إضافة مستخدم جديد',
     'users_search' => 'بحث عن مستخدم',
-    'users_details' => 'User Details',
-    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
-    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_latest_activity' => 'أحدث نشاط',
+    'users_details' => 'بيانات المستخدم',
+    'users_details_desc' => 'قم بتعيين اسم عرض وعنوان بريد إلكتروني لهذا المستخدم. سيتم استخدام عنوان البريد الإلكتروني لتسجيل الدخول إلى التطبيق.',
+    'users_details_desc_no_email' => 'قم بتعيين اسم عرض لهذا المستخدم حتى يتمكن الآخرون من التعرف عليه.',
     'users_role' => 'أدوار المستخدمين',
-    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
-    'users_password' => 'User Password',
-    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
-    'users_external_auth_id' => 'External Authentication ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+    'users_role_desc' => 'حدد الأدوار التي سيتم تعيين هذا المستخدم لها. إذا تم تعيين مستخدم لأدوار متعددة ، فسيتم تكديس الأذونات من هذه الأدوار وسيتلقى كل قدرات الأدوار المعينة.',
+    'users_password' => 'كلمة مرور المستخدم',
+    'users_password_desc' => 'قم بتعيين كلمة مرور مستخدمة لتسجيل الدخول إلى التطبيق. يجب ألا يقل طول هذه الكلمة عن 6 أحرف.',
+    'users_send_invite_text' => 'يمكنك اختيار إرسال دعوة بالبريد الإلكتروني إلى هذا المستخدم مما يسمح له بتعيين كلمة المرور الخاصة به أو يمكنك تعيين كلمة المرور الخاصة به بنفسك.',
+    'users_send_invite_option' => 'أرسل بريدًا إلكترونيًا لدعوة المستخدم',
+    'users_external_auth_id' => 'ربط الحساب بمواقع التواصل',
+    'users_external_auth_id_desc' => 'تستخدم هذه الهوية لإثبات شخصية المستخدم عند الدخول إلى مواقع التواصل الخاصة بك.',
     'users_password_warning' => 'الرجاء ملئ الحقل أدناه فقط في حال أردتم تغيير كلمة المرور:',
     'users_system_public' => 'هذا المستخدم يمثل أي ضيف يقوم بزيارة شيء يخصك. لا يمكن استخدامه لتسجيل الدخول ولكن يتم تعيينه تلقائياً.',
     'users_delete' => 'حذف المستخدم',
     'users_delete_named' => 'حذف المستخدم :userName',
     'users_delete_warning' => 'سيتم حذف المستخدم \':userName\' بشكل تام من النظام.',
     'users_delete_confirm' => 'تأكيد حذف المستخدم؟',
-    'users_delete_success' => 'تم حذف المستخدم بنجاح',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'تعديل المستخدم',
     'users_edit_profile' => 'تعديل الملف',
     'users_edit_success' => 'تم تحديث المستخدم بنجاح',
     'users_avatar' => 'صورة المستخدم',
     'users_avatar_desc' => 'يجب أن تكون الصورة مربعة ومقاربة لحجم 256 بكسل',
     'users_preferred_language' => 'اللغة المفضلة',
-    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_preferred_language_desc' => 'سيؤدي هذا الخيار إلى تغيير اللغة المستخدمة لواجهة المستخدم الخاصة بالتطبيق. لن يؤثر هذا على أي محتوى قد أنشائه المستخدم.',
     'users_social_accounts' => 'الحسابات الاجتماعية',
     'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.',
     'users_social_connect' => 'ربط الحساب',
     'users_social_disconnect' => 'فصل الحساب',
     'users_social_connected' => 'تم ربط حساب :socialAccount بملفك بنجاح.',
     'users_social_disconnected' => 'تم فصل حساب :socialAccount من ملفك بنجاح.',
+    'users_api_tokens' => 'رموز الـ API',
+    'users_api_tokens_none' => 'لم يتم إنشاء رموز API لهذا المستخدم',
+    'users_api_tokens_create' => 'قم بإنشاء رمز مميز',
+    'users_api_tokens_expires' => 'انتهاء مدة الصلاحية',
+    'users_api_tokens_docs' => 'وثائق API',
+
+    // API Tokens
+    'user_api_token_create' => 'قم بإنشاء رمز API',
+    'user_api_token_name' => 'الاسم',
+    'user_api_token_name_desc' => 'اعطي الرمز الخاص بك اسمًا يمكن قراءته للتذكير مستقبلًا بالغرض المقصود منه.',
+    'user_api_token_expiry' => 'تاريخ انتهاء الصلاحية',
+    'user_api_token_expiry_desc' => 'حدد التاريخ الذي تنتهي فيه صلاحية هذا الرمز. بعد هذا التاريخ ، لن تعمل الطلبات المقدمة باستخدام هذا الرمز. سيؤدي ترك هذا الحقل فارغًا إلى تعيين انتهاء صلاحية لمدة 100 عام في المستقبل.',
+    'user_api_token_create_secret_message' => 'عقب إنشاء هذا الرمز مباشرة، سيتم إنشاء "مُعرّف الرمز" و "رمز سري" وعرضهما. وسيتم عرض الرمز السري لمرة واحدة فقط ، لذا تأكد من نسخ قيمة هذا الرمز إلى مكان آمن ومضمون قبل المتابعة.',
+    'user_api_token_create_success' => 'تم إنشاء رمز الـ API بنجاح',
+    'user_api_token_update_success' => 'تم تحديث رمز الـ API بنجاح',
+    'user_api_token' => 'رمز الـ API',
+    'user_api_token_id' => 'مُعرّف الرمز',
+    'user_api_token_id_desc' => 'هذا مُعرّف تم إنشاؤه بواسطة النظام غير قابل للتحرير لهذا الرمز والذي يجب توفيره في طلبات API.',
+    'user_api_token_secret' => 'الرمز السري',
+    'user_api_token_secret_desc' => 'هذا الرمز السري تم إنشاؤه بواسطة النظام والذي يجب توفيره ضمن طلبات API. سيتم عرضه لمرة واحدة فقط ، لذا انسخ قيمة هذا الرمز إلى مكان آمن ومضمون.',
+    'user_api_token_created' => 'تم إنشاء رمز :الوقت الزمني',
+    'user_api_token_updated' => 'تم تحديث الرمز :الوقت الزمني',
+    'user_api_token_delete' => 'حذف الرمز',
+    'user_api_token_delete_warning' => 'سيؤدي هذا إلى حذف رمز API المُشار إليه بالكامل باسم \'اسم الرمز\' من النظام.',
+    'user_api_token_delete_confirm' => 'هل أنت متأكد من أنك تريد حذف رمز API؟',
+    'user_api_token_delete_success' => 'تم حذف رمز الـ API بنجاح',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 7d6d13e817713fed9832856a4bc57458a054edf0..d0b17460bccbb992c93f8c71a3895d4fdbe2fad3 100644 (file)
@@ -14,7 +14,7 @@ return [
     'alpha'                => 'يجب أن يقتصر :attribute على الحروف فقط.',
     'alpha_dash'           => 'يجب أن يقتصر :attribute على حروف أو أرقام أو شرطات فقط.',
     'alpha_num'            => 'يجب أن يقتصر :attribute على الحروف والأرقام فقط.',
-    'array'                => 'The :attribute must be an array.',
+    'array'                => 'يجب أن تكون السمة مصفوفة.',
     'before'               => 'يجب أن يكون التاريخ :attribute قبل :date.',
     'between'              => [
         'numeric' => 'يجب أن يكون :attribute بين :min و :max.',
@@ -22,7 +22,7 @@ return [
         'string'  => 'يجب أن يكون :attribute بين :min و :max حرف / حروف.',
         'array'   => 'يجب أن يكون :attribute بين :min و :max عنصر / عناصر.',
     ],
-    'boolean'              => 'The :attribute field must be true or false.',
+    'boolean'              => 'يجب أن يحتمل حقل السمة الصحة أو الخطأ.',
     'confirmed'            => ':attribute غير مطابق.',
     'date'                 => ':attribute ليس تاريخ صالح.',
     'date_format'          => ':attribute لا يطابق الصيغة :format.',
@@ -30,40 +30,40 @@ return [
     'digits'               => 'يجب أن يكون :attribute بعدد :digits خانات.',
     'digits_between'       => 'يجب أن يكون :attribute بعدد خانات بين :min و :max.',
     'email'                => 'يجب أن يكون :attribute عنوان بريد إلكتروني صالح.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => 'يجب أن تنتهي السمة بأحد القيم التالية',
     'filled'               => 'حقل :attribute مطلوب.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => 'يجب أن تكون السمة أكبر من: القيمة.',
+        'file'    => 'يجب أن تكون السمة أكبر من: القيمة كيلوبايت.',
+        'string'  => 'يجب أن تكون السمة أكبر من: أحرف القيمة.',
+        'array'   => 'يجب أن تحتوي السمة على أكثر من: عناصر القيمة.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => 'يجب أن تكون السمة أكبر من أو تساوي: القيمة.',
+        'file'    => 'يجب أن تكون السمة أكبر من أو تساوي: القيمة كيلوبايت.',
+        'string'  => 'يجب أن تكون السمة أكبر من أو تساوي: أحرف القيمة.',
+        'array'   => 'يجب أن تحتوي السمة على: عناصر القيمة أو أكثر.',
     ],
     'exists'               => ':attribute المحدد غير صالح.',
     'image'                => 'يجب أن يكون :attribute صورة.',
-    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'image_extension'      => 'يجب أن تحتوي السمة على امتداد صورة صالح ومدعوم.',
     'in'                   => ':attribute المحدد غير صالح.',
     'integer'              => 'يجب أن يكون :attribute عدد صحيح.',
     'ip'                   => 'يجب أن يكون :attribute عنوان IP صالح.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ipv4'                 => 'يجب أن تكون السمة: عنوان IPv4 صالحًا.',
+    'ipv6'                 => 'يجب أن تكون السمة: عنوان IPv6 صالحًا.',
+    'json'                 => 'يجب أن تكون السمة: سلسلة من نوع جسون JSON صالح.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => 'يجب أن تكون السمة أقل من: القيمة.',
+        'file'    => 'يجب أن تكون السمة أقل من: القيمة كيلوبايت.',
+        'string'  => 'يجب أن تكون السمة أقل من: أحرف القيمة.',
+        'array'   => 'يجب أن تحتوي السمة على أقل من: عناصر القيمة.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => 'يجب أن تكون السمة أقل من أو تساوي: القيمة.',
+        'file'    => 'يجب أن تكون السمة أقل من أو تساوي: القيمة كيلوبايت.',
+        'string'  => 'يجب أن تكون السمة أقل من أو تساوي: أحرف القيمة.',
+        'array'   => 'يجب ألا تحتوي السمة على أكثر من: عناصر القيمة.',
     ],
     'max'                  => [
         'numeric' => 'يجب ألا يكون :attribute أكبر من :max.',
@@ -78,9 +78,9 @@ return [
         'string'  => 'يجب أن يكون :attribute على الأقل :min حرف / حروف.',
         'array'   => 'يجب أن يحتوي :attribute على :min عنصر / عناصر كحد أدنى.',
     ],
-    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'no_double_extension'  => 'يجب أن يكون للسمة: امتداد ملف واحد فقط.',
     'not_in'               => ':attribute المحدد غير صالح.',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => 'صيغة السمة: غير صالحة.',
     'numeric'              => 'يجب أن يكون :attribute رقم.',
     'regex'                => 'صيغة :attribute غير صالحة.',
     'required'             => 'حقل :attribute مطلوب.',
@@ -90,17 +90,18 @@ return [
     'required_without'     => 'حقل :attribute مطلوب عندما تكون :values غير موجودة.',
     'required_without_all' => 'حقل :attribute مطلوب عندما لا يكون أي من :values موجودة.',
     'same'                 => 'يجب تطابق :attribute مع :other.',
+    'safe_url'             => 'قد لايكون الرابط المتوفر آمنا.',
     'size'                 => [
         'numeric' => 'يجب أن يكون :attribute بحجم :size.',
         'file'    => 'يجب أن يكون :attribute بحجم :size كيلو بايت.',
         'string'  => 'يجب أن يكون :attribute بعدد :size حرف / حروف.',
         'array'   => 'يجب أن يحتوي :attribute على :size عنصر / عناصر.',
     ],
-    'string'               => 'The :attribute must be a string.',
+    'string'               => 'يجب أن تكون السمة: سلسلة.',
     'timezone'             => 'يجب أن تكون :attribute منطقة صالحة.',
     'unique'               => 'تم حجز :attribute من قبل.',
     'url'                  => 'صيغة :attribute غير صالحة.',
-    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+    'uploaded'             => 'تعذر تحميل الملف. قد لا يقبل الخادم ملفات بهذا الحجم.',
 
     // Custom validation lines
     'custom' => [
diff --git a/resources/lang/bg/activities.php b/resources/lang/bg/activities.php
new file mode 100644 (file)
index 0000000..a495733
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'създадена страница',
+    'page_create_notification'    => 'Страницата беше успешно създадена',
+    'page_update'                 => 'обновена страница',
+    'page_update_notification'    => 'Страницата успешно обновена',
+    'page_delete'                 => 'изтрита страница',
+    'page_delete_notification'    => 'Страницата беше успешно изтрита',
+    'page_restore'                => 'възстановена страница',
+    'page_restore_notification'   => 'Страницата беше успешно възстановена',
+    'page_move'                   => 'преместена страница',
+
+    // Chapters
+    'chapter_create'              => 'създадена страница',
+    'chapter_create_notification' => 'Главата беше успешно създадена',
+    'chapter_update'              => 'обновена глава',
+    'chapter_update_notification' => 'Главата беше успешно обновена',
+    'chapter_delete'              => 'изтрита глава',
+    'chapter_delete_notification' => 'Главата беше успешно изтрита',
+    'chapter_move'                => 'преместена глава',
+
+    // Books
+    'book_create'                 => 'създадена книга',
+    'book_create_notification'    => 'Книгата беше успешно създадена',
+    'book_update'                 => 'обновена книга',
+    'book_update_notification'    => 'Книгата беше успешно обновена',
+    'book_delete'                 => 'изтрита книга',
+    'book_delete_notification'    => 'Книгата беше успешно изтрита',
+    'book_sort'                   => 'сортирана книга',
+    'book_sort_notification'      => 'Книгата беше успешно преподредена',
+
+    // Bookshelves
+    'bookshelf_create'            => 'създаден рафт',
+    'bookshelf_create_notification'    => 'Рафтът беше успешно създаден',
+    'bookshelf_update'                 => 'обновен рафт',
+    'bookshelf_update_notification'    => 'Рафтът беше успешно обновен',
+    'bookshelf_delete'                 => 'изтрит рафт',
+    'bookshelf_delete_notification'    => 'Рафтът беше успешно изтрит',
+
+    // Other
+    'commented_on'                => 'коментирано на',
+    'permissions_update'          => 'updated permissions',
+];
diff --git a/resources/lang/bg/auth.php b/resources/lang/bg/auth.php
new file mode 100644 (file)
index 0000000..018e871
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'Въведените удостоверителни данни не съвпадат с нашите записи.',
+    'throttle' => 'Твърде много опити за влизане. Опитайте пак след :seconds секунди.',
+
+    // Login & Register
+    'sign_up' => 'Регистриране',
+    'log_in' => 'Влизане',
+    'log_in_with' => 'Влизане с :socialDriver',
+    'sign_up_with' => 'Регистриране с :socialDriver',
+    'logout' => 'Изход',
+
+    'name' => 'Име',
+    'username' => 'Потребител',
+    'email' => 'Имейл',
+    'password' => 'Парола',
+    'password_confirm' => 'Потвърди паролата',
+    'password_hint' => 'Трябва да бъде поне 7 символа',
+    'forgot_password' => 'Забравена парола?',
+    'remember_me' => 'Запомни ме',
+    'ldap_email_hint' => 'Моля въведете емейл, който да използвате за дадения акаунт.',
+    'create_account' => 'Създай Акаунт',
+    'already_have_account' => 'Вече имате акаунт?',
+    'dont_have_account' => 'Нямате акаунт?',
+    'social_login' => 'Влизане по друг начин',
+    'social_registration' => 'Регистрация по друг начин',
+    'social_registration_text' => 'Регистрация и влизане използвайки друг начин.',
+
+    'register_thanks' => 'Благодарим Ви за регистрацията!',
+    'register_confirm' => 'Моля проверете своя емейл и натиснете върху бутона за потвърждение, за да влезете в :appName.',
+    'registrations_disabled' => 'Регистрациите към момента са забранени',
+    'registration_email_domain_invalid' => 'Този емейл домейн към момента няма достъп до приложението',
+    'register_success' => 'Благодарим Ви за регистрацията! В момента сте регистриран и сте вписани в приложението.',
+
+
+    // Password Reset
+    'reset_password' => 'Нулиране на паролата',
+    'reset_password_send_instructions' => 'Въведете емейла си и ще ви бъде изпратен емейл с линк за нулиране на паролата.',
+    'reset_password_send_button' => 'Изпращане на линк за нулиране',
+    'reset_password_sent' => 'Линк за нулиране на паролата ще Ви бъде изпратен на :email, ако емейлът Ви бъде открит в системата.',
+    'reset_password_success' => 'Паролата Ви е променена успешно.',
+    'email_reset_subject' => 'Възстановете паролата си за :appName',
+    'email_reset_text' => 'Вие получихте този емейл, защото поискахте вашата парола да бъде занулена.',
+    'email_reset_not_requested' => 'Ако Вие не сте поискали зануляването на паролата, няма нужда от други действия.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Потвърди емейла си за :appName',
+    'email_confirm_greeting' => 'Благодарим Ви, че се присъединихте към :appName!',
+    'email_confirm_text' => 'Моля, потвърдете вашия имейл адрес, като следвате връзката по-долу:',
+    'email_confirm_action' => 'Потвърдете имейл',
+    'email_confirm_send_error' => 'Нужно ви е потвърждение чрез емейл, но системата не успя да го изпрати. Моля свържете се с администратора, за да проверите дали вашият емейл адрес е конфигуриран правилно.',
+    'email_confirm_success' => 'Адресът на електронната ви поща е потвърден!',
+    'email_confirm_resent' => 'Беше изпратен имейл с потвърждение, Моля, проверете кутията си.',
+
+    'email_not_confirmed' => 'Имейл адресът не е потвърден',
+    'email_not_confirmed_text' => 'Вашият емейл адрес все още не е потвърден.',
+    'email_not_confirmed_click_link' => 'Моля да последвате линка, който ви беше изпратен непосредствено след регистрацията.',
+    'email_not_confirmed_resend' => 'Ако не откривате писмото, може да го изпратите отново като попълните формуляра по-долу.',
+    'email_not_confirmed_resend_button' => 'Изпрати отново емейла за потвърждение',
+
+    // User Invite
+    'user_invite_email_subject' => 'Вие бяхте поканен да се присъедините към :appName!',
+    'user_invite_email_greeting' => 'Беше създаден акаунт за Вас във :appName.',
+    'user_invite_email_text' => 'Натисните бутона по-долу за да определите парола и да получите достъп:',
+    'user_invite_email_action' => 'Парола на акаунта',
+    'user_invite_page_welcome' => 'Добре дошли в :appName!',
+    'user_invite_page_text' => 'За да финализирате вашият акаунт и да получите достъп трябва да определите парола, която да бъде използвана за следващия влизания в :appName.',
+    'user_invite_page_confirm_button' => 'Потвърди паролата',
+    'user_invite_success' => 'Паролата е потвърдена и вече имате достъп до :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/bg/common.php b/resources/lang/bg/common.php
new file mode 100644 (file)
index 0000000..99930dd
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Отказ',
+    'confirm' => 'Потвърди',
+    'back' => 'Назад',
+    'save' => 'Запази',
+    'continue' => 'Продължи',
+    'select' => 'Избери',
+    'toggle_all' => 'Избери всички',
+    'more' => 'Повече',
+
+    // Form Labels
+    'name' => 'Име',
+    'description' => 'Описание',
+    'role' => 'Роля',
+    'cover_image' => 'Основно изображение',
+    'cover_image_description' => 'Картината трябва да е приблизително 440х250 пиксела.',
+    
+    // Actions
+    'actions' => 'Действия',
+    'view' => 'Преглед',
+    'view_all' => 'Преглед на всички',
+    'create' => 'Създай',
+    'update' => 'Обновяване',
+    'edit' => 'Редактиране',
+    'sort' => 'Сортиране',
+    'move' => 'Преместване',
+    'copy' => 'Копирай',
+    'reply' => 'Отговори',
+    'delete' => 'Изтрий',
+    'delete_confirm' => 'Confirm Deletion',
+    'search' => 'Търси',
+    'search_clear' => 'Изчисти търсенето',
+    'reset' => 'Нулирай',
+    'remove' => 'Премахване',
+    'add' => 'Добави',
+    'fullscreen' => 'Пълен екран',
+
+    // Sort Options
+    'sort_options' => 'Опции за сортиране',
+    'sort_direction_toggle' => 'Активирай сортиране',
+    'sort_ascending' => 'Сортирай възходящо',
+    'sort_descending' => 'Низходящо сортиране',
+    'sort_name' => 'Име',
+    'sort_created_at' => 'Дата на създаване',
+    'sort_updated_at' => 'Дата на обновяване',
+
+    // Misc
+    'deleted_user' => 'Изтриване на потребител',
+    'no_activity' => 'Няма активност за показване',
+    'no_items' => 'Няма налични артикули',
+    'back_to_top' => 'Върнете се в началото',
+    'toggle_details' => 'Активирай детайли',
+    'toggle_thumbnails' => 'Активирай миниатюри',
+    'details' => 'Подробности',
+    'grid_view' => 'Табличен изглед',
+    'list_view' => 'Изглед списък',
+    'default' => 'Основен',
+    'breadcrumb' => 'Трасиране',
+
+    // Header
+    'profile_menu' => 'Профил меню',
+    'view_profile' => 'Разглеждане на профил',
+    'edit_profile' => 'Редактиране на профила',
+    'dark_mode' => 'Тъмен режим',
+    'light_mode' => 'Светъл режим',
+
+    // Layout tabs
+    'tab_info' => 'Информация',
+    'tab_content' => 'Съдържание',
+
+    // Email Content
+    'email_action_help' => 'Ако имате проблеми с бутона ":actionText" по-горе, копирайте и поставете URL адреса по-долу в уеб браузъра си:',
+    'email_rights' => 'Всички права запазени',
+];
diff --git a/resources/lang/bg/components.php b/resources/lang/bg/components.php
new file mode 100644 (file)
index 0000000..b15aa6f
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Избор на изображение',
+    'image_all' => 'Всички',
+    'image_all_title' => 'Преглед на всички изображения',
+    'image_book_title' => 'Виж изображенията прикачени към тази книга',
+    'image_page_title' => 'Виж изображенията прикачени към страницата',
+    'image_search_hint' => 'Търси по име на картина',
+    'image_uploaded' => 'Качено :uploadedDate',
+    'image_load_more' => 'Зареди повече',
+    'image_image_name' => 'Име на изображението',
+    'image_delete_used' => 'Това изображение е използвано в страницата по-долу.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_select_image' => 'Изберете изображение',
+    'image_dropzone' => 'Поставете тук изображение или кликнете тук за да качите',
+    'images_deleted' => 'Изображението е изтрито',
+    'image_preview' => 'Преглед на изображенията',
+    'image_upload_success' => 'Изображението бе качено успешно',
+    'image_update_success' => 'Данните за изобтажението са обновенни успешно',
+    'image_delete_success' => 'Изображението е успешно изтрито',
+    'image_upload_remove' => 'Премахване',
+
+    // Code Editor
+    'code_editor' => 'Редактиране на кода',
+    'code_language' => 'Език на кода',
+    'code_content' => 'Съдържание на кода',
+    'code_session_history' => 'Session History',
+    'code_save' => 'Запази кода',
+];
diff --git a/resources/lang/bg/entities.php b/resources/lang/bg/entities.php
new file mode 100644 (file)
index 0000000..7f9cae6
--- /dev/null
@@ -0,0 +1,319 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Наскоро създадени',
+    'recently_created_pages' => 'Наскоро създадени страници',
+    'recently_updated_pages' => 'Наскоро актуализирани страници',
+    'recently_created_chapters' => 'Наскоро създадени глави',
+    'recently_created_books' => 'Наскоро създадени книги',
+    'recently_created_shelves' => 'Наскоро създадени рафтове',
+    'recently_update' => 'Наскоро актуализирани',
+    'recently_viewed' => 'Скорошно разгледани',
+    'recent_activity' => 'Последна активност',
+    'create_now' => 'Създай една сега',
+    'revisions' => 'Ревизии',
+    'meta_revision' => 'Ревизия #:revisionCount',
+    'meta_created' => 'Създадено преди :timeLength',
+    'meta_created_name' => 'Създадено преди :timeLength от :user',
+    'meta_updated' => 'Актуализирано :timeLength',
+    'meta_updated_name' => 'Актуализирано преди :timeLength от :user',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'Избор на обект',
+    'images' => 'Изображения',
+    'my_recent_drafts' => 'Моите скорошни драфтове',
+    'my_recently_viewed' => 'Моите скорошни преглеждания',
+    'no_pages_viewed' => 'Не сте прегледали никакви страници',
+    'no_pages_recently_created' => 'Не са били създавани страници скоро',
+    'no_pages_recently_updated' => 'Не са били актуализирани страници скоро',
+    'export' => 'Експортиране',
+    'export_html' => 'Прикачени уеб файлове',
+    'export_pdf' => 'PDF файл',
+    'export_text' => 'Обикновен текстов файл',
+
+    // Permissions and restrictions
+    'permissions' => 'Права',
+    'permissions_intro' => 'Веднъж добавени, тези права ще вземат приоритет над всички други установени права.',
+    'permissions_enable' => 'Разреши уникални права',
+    'permissions_save' => 'Запази права',
+    'permissions_owner' => 'Owner',
+
+    // Search
+    'search_results' => 'Резултати от търсенето',
+    'search_total_results_found' => ':count резултати намерени|:count общо намерени резултати',
+    'search_clear' => 'Изчисти търсенето',
+    'search_no_pages' => 'Няма страници отговарящи на търсенето',
+    'search_for_term' => 'Търси :term',
+    'search_more' => 'Още резултати',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
+    'search_content_type' => 'Тип на съдържание',
+    'search_exact_matches' => 'Точни съвпадения',
+    'search_tags' => 'Търсене на тагове',
+    'search_options' => 'Настройки',
+    'search_viewed_by_me' => 'Прегледано от мен',
+    'search_not_viewed_by_me' => 'Непрегледано от мен',
+    'search_permissions_set' => 'Задаване на права',
+    'search_created_by_me' => 'Създадено от мен',
+    'search_updated_by_me' => 'Обновено от мен',
+    'search_date_options' => 'Настройки на дати',
+    'search_updated_before' => 'Обновено преди',
+    'search_updated_after' => 'Обновено след',
+    'search_created_before' => 'Създадено преди',
+    'search_created_after' => 'Създадено след',
+    'search_set_date' => 'Задаване на дата',
+    'search_update' => 'Обнови търсенето',
+
+    // Shelves
+    'shelf' => 'Рафт',
+    'shelves' => 'Рафтове',
+    'x_shelves' => ':count Рафт|:count Рафтове',
+    'shelves_long' => 'Рафтове с книги',
+    'shelves_empty' => 'Няма създадени рафтове',
+    'shelves_create' => 'Създай нов рафт',
+    'shelves_popular' => 'Популярни рафтове',
+    'shelves_new' => 'Нови рафтове',
+    'shelves_new_action' => 'Нов рафт',
+    'shelves_popular_empty' => 'Най-популярните рафтове ще излязат тук.',
+    'shelves_new_empty' => 'Най-новите рафтове ще излязат тук.',
+    'shelves_save' => 'Запази рафт',
+    'shelves_books' => 'Книги на този рафт',
+    'shelves_add_books' => 'Добави книги към този рафт',
+    'shelves_drag_books' => 'Издърпай книги тук, за да ги добавиш към рафта',
+    'shelves_empty_contents' => 'Този рафт няма добавени книги',
+    'shelves_edit_and_assign' => 'Редактирай рафта за да добавиш книги',
+    'shelves_edit_named' => 'Редактирай рафт с книги :name',
+    'shelves_edit' => 'Редактирай рафт с книги',
+    'shelves_delete' => 'Изтрий рафт с книги',
+    'shelves_delete_named' => 'Изтрий рафт с книги :name',
+    'shelves_delete_explain' => "Ще бъде изтрит рафта с книги със следното име ':name'. Съдържащите се книги няма да бъдат изтрити.",
+    'shelves_delete_confirmation' => 'Сигурни ли сте, че искате да изтриете този рафт с книги?',
+    'shelves_permissions' => 'Настройки за достъп до рафта с книги',
+    'shelves_permissions_updated' => 'Настройките за достъп до рафта с книги е обновен',
+    'shelves_permissions_active' => 'Настройките за достъп до рафта с книги е активен',
+    'shelves_copy_permissions_to_books' => 'Копирай настойките за достъп към книгите',
+    'shelves_copy_permissions' => 'Копирай настройките за достъп',
+    'shelves_copy_permissions_explain' => 'Това ще приложи настоящите настройки за достъп на този рафт с книги за всички книги, съдържащи се в него. Преди да активирате, уверете се, че всички промени в настройките за достъп на този рафт са запазени.',
+    'shelves_copy_permission_success' => 'Настройките за достъп на рафта с книги бяха копирани върху :count books',
+
+    // Books
+    'book' => 'Книга',
+    'books' => 'Книги',
+    'x_books' => ':count Книга|:count Книги',
+    'books_empty' => 'Няма създадени книги',
+    'books_popular' => 'Популярни книги',
+    'books_recent' => 'Скоро разглеждани книги',
+    'books_new' => 'Нови книги',
+    'books_new_action' => 'Нова книга',
+    'books_popular_empty' => 'Най-популярните книги ще излязат тук.',
+    'books_new_empty' => 'Най-новите книги ще излязат тук.',
+    'books_create' => 'Създай нова книга',
+    'books_delete' => 'Изтрита книга',
+    'books_delete_named' => 'Изтрий книга :bookName',
+    'books_delete_explain' => 'Това действие ще изтрие книга с името \':bookName\'. Всички страници и глави ще бъдат изтрити.',
+    'books_delete_confirmation' => 'Сигурен ли сте, че искате да изтриете книгата?',
+    'books_edit' => 'Редактиране на книга',
+    'books_edit_named' => 'Редактирай книга :bookName',
+    'books_form_book_name' => 'Име на книга',
+    'books_save' => 'Запази книга',
+    'books_permissions' => 'Настройки за достъп до книгата',
+    'books_permissions_updated' => 'Настройките за достъп до книгата бяха обновени',
+    'books_empty_contents' => 'Няма създадени страници или глави към тази книга.',
+    'books_empty_create_page' => 'Създаване на нова страница',
+    'books_empty_sort_current_book' => 'Сортирай настоящата книга',
+    'books_empty_add_chapter' => 'Добавяне на раздел',
+    'books_permissions_active' => 'Настройките за достъп до книгата са активни',
+    'books_search_this' => 'Търси в книгата',
+    'books_navigation' => 'Навигация на книгата',
+    'books_sort' => 'Сортирай съдържанието на книгата',
+    'books_sort_named' => 'Сортирай книга :bookName',
+    'books_sort_name' => 'Сортиране по име',
+    'books_sort_created' => 'Сортирай по дата на създаване',
+    'books_sort_updated' => 'Сортирай по дата на обновяване',
+    'books_sort_chapters_first' => 'Първа глава',
+    'books_sort_chapters_last' => 'Последна глава',
+    'books_sort_show_other' => 'Покажи други книги',
+    'books_sort_save' => 'Запази новата подредба',
+
+    // Chapters
+    'chapter' => 'Глава',
+    'chapters' => 'Глави',
+    'x_chapters' => ':count Глава|:count Глави',
+    'chapters_popular' => 'Популярни глави',
+    'chapters_new' => 'Нова глава',
+    'chapters_create' => 'Създай нова глава',
+    'chapters_delete' => 'Изтрий глава',
+    'chapters_delete_named' => 'Изтрий глава :chapterName',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_confirm' => 'Сигурни ли сте, че искате да изтриете тази глава?',
+    'chapters_edit' => 'Редактирай глава',
+    'chapters_edit_named' => 'Актуализирай глава :chapterName',
+    'chapters_save' => 'Запази глава',
+    'chapters_move' => 'Премести глава',
+    'chapters_move_named' => 'Премести глава :chapterName',
+    'chapter_move_success' => 'Главата беше преместена в :bookName',
+    'chapters_permissions' => 'Настойки за достъп на главата',
+    'chapters_empty' => 'Няма създадени страници в тази глава.',
+    'chapters_permissions_active' => 'Настройките за достъп до глава са активни',
+    'chapters_permissions_success' => 'Настройките за достъп до главата бяха обновени',
+    'chapters_search_this' => 'Търси в тази глава',
+
+    // Pages
+    'page' => 'Страница',
+    'pages' => 'Страници',
+    'x_pages' => ':count Страница|:count Страници',
+    'pages_popular' => 'Популярни страници',
+    'pages_new' => 'Нова страница',
+    'pages_attachments' => 'Прикачени файлове',
+    'pages_navigation' => 'Навигация на страница',
+    'pages_delete' => 'Изтрий страница',
+    'pages_delete_named' => 'Изтрий страница :pageName',
+    'pages_delete_draft_named' => 'Изтрий чернова :pageName',
+    'pages_delete_draft' => 'Изтрий чернова',
+    'pages_delete_success' => 'Страницата е изтрита',
+    'pages_delete_draft_success' => 'Черновата на страницата бе изтрита',
+    'pages_delete_confirm' => 'Сигурни ли сте, че искате да изтриете тази страница?',
+    'pages_delete_draft_confirm' => 'Сигурни ли сте, че искате да изтриете тази чернова?',
+    'pages_editing_named' => 'Редактиране на страница :pageName',
+    'pages_edit_draft_options' => 'Настройки на черновата',
+    'pages_edit_save_draft' => 'Запазване на чернова',
+    'pages_edit_draft' => 'Редактирай на черновата',
+    'pages_editing_draft' => 'Редактиране на чернова',
+    'pages_editing_page' => 'Редактиране на страница',
+    'pages_edit_draft_save_at' => 'Черновата е запазена в ',
+    'pages_edit_delete_draft' => 'Изтрий чернова',
+    'pages_edit_discard_draft' => 'Отхвърляне на черновата',
+    'pages_edit_set_changelog' => 'Задайте регистър на промените',
+    'pages_edit_enter_changelog_desc' => 'Въведете кратко резюме на промените, които сте създали',
+    'pages_edit_enter_changelog' => 'Въведи регистър на промените',
+    'pages_save' => 'Запазване на страницата',
+    'pages_title' => 'Заглавие на страницата',
+    'pages_name' => 'Име на страницата',
+    'pages_md_editor' => 'Редактор',
+    'pages_md_preview' => 'Предварителен преглед',
+    'pages_md_insert_image' => 'Добавяна на изображение',
+    'pages_md_insert_link' => 'Добави линк към обекта',
+    'pages_md_insert_drawing' => 'Вмъкни рисунка',
+    'pages_not_in_chapter' => 'Страницата не принадлежи в никоя глава',
+    'pages_move' => 'Премести страницата',
+    'pages_move_success' => 'Страницата беше преместена в ":parentName"',
+    'pages_copy' => 'Копиране на страницата',
+    'pages_copy_desination' => 'Копиране на дестинацията',
+    'pages_copy_success' => 'Страницата беше успешно копирана',
+    'pages_permissions' => 'Настройки за достъп на страницата',
+    'pages_permissions_success' => 'Настройките за достъп до страницата бяха обновени',
+    'pages_revision' => 'Ревизия',
+    'pages_revisions' => 'Ревизии на страницата',
+    'pages_revisions_named' => 'Ревизии на страницата :pageName',
+    'pages_revision_named' => 'Ревизия на страницата :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions_created_by' => 'Създадено от',
+    'pages_revisions_date' => 'Дата на ревизията',
+    'pages_revisions_number' => '№',
+    'pages_revisions_numbered' => 'Ревизия №:id',
+    'pages_revisions_numbered_changes' => 'Ревизия №:id Промени',
+    'pages_revisions_changelog' => 'История на промените',
+    'pages_revisions_changes' => 'Промени',
+    'pages_revisions_current' => 'Текуща версия',
+    'pages_revisions_preview' => 'Предварителен преглед',
+    'pages_revisions_restore' => 'Възстановяване',
+    'pages_revisions_none' => 'Тази страница няма ревизии',
+    'pages_copy_link' => 'Копирай връзката',
+    'pages_edit_content_link' => 'Редактиране на съдържанието',
+    'pages_permissions_active' => 'Настройките за достъп до страницата са активни',
+    'pages_initial_revision' => 'Първо публикуване',
+    'pages_initial_name' => 'Нова страница',
+    'pages_editing_draft_notification' => 'В момента редактирате чернова, която беше последно обновена :timeDiff.',
+    'pages_draft_edited_notification' => 'Тази страница беше актуализирана от тогава. Препоръчително е да изтриете настоящата чернова.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count потребителя започнаха да редактират настоящата страница',
+        'start_b' => ':userName в момента редактира тази страница',
+        'time_a' => 'от както страницата беше актуализирана',
+        'time_b' => 'в последните :minCount минути',
+        'message' => ':start :time. Внимавайте да не попречите на актуализацията на другия!',
+    ],
+    'pages_draft_discarded' => 'Черновата беше отхърлена, Редактора беше обновен с актуалното съдържание на страницата',
+    'pages_specific' => 'Определена страница',
+    'pages_is_template' => 'Шаблон на страницата',
+
+    // Editor Sidebar
+    'page_tags' => 'Тагове на страницата',
+    'chapter_tags' => 'Тагове на главата',
+    'book_tags' => 'Тагове на книгата',
+    'shelf_tags' => 'Тагове на рафта',
+    'tag' => 'Таг',
+    'tags' =>  'Тагове',
+    'tag_name' =>  'Име на таг',
+    'tag_value' => 'Съдържание на тага (Опционално)',
+    'tags_explain' => "Добавете няколко тага за да категоризирате по добре вашето съдържание. \n Може да добавите съдържание на таговете за по-подробна организация.",
+    'tags_add' => 'Добави друг таг',
+    'tags_remove' => 'Премахни този таг',
+    'attachments' => 'Прикачени файлове',
+    'attachments_explain' => 'Прикачете файлове или линкове, които да са видими на вашата страница. Същите ще бъдат видими във вашето странично поле.',
+    'attachments_explain_instant_save' => 'Промените тук се запазват веднага.',
+    'attachments_items' => 'Прикачен файл',
+    'attachments_upload' => 'Прикачен файл',
+    'attachments_link' => 'Прикачване на линк',
+    'attachments_set_link' => 'Поставяне на линк',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
+    'attachments_dropzone' => 'Поставете файлове или цъкнете тук за да прикачите файл',
+    'attachments_no_files' => 'Няма прикачени фалове',
+    'attachments_explain_link' => 'Може да прикачите линк, ако не искате да качвате файл. Този линк може да бъде към друга страница или към файл в облакова пространство.',
+    'attachments_link_name' => 'Има на линка',
+    'attachment_link' => 'Линк към прикачения файл',
+    'attachments_link_url' => 'Линк към файла',
+    'attachments_link_url_hint' => 'Url на сайт или файл',
+    'attach' => 'Прикачване',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
+    'attachments_edit_file' => 'Редактирай файл',
+    'attachments_edit_file_name' => 'Име на файл',
+    'attachments_edit_drop_upload' => 'Поставете файл или цъкнете тук за да прикачите и обновите',
+    'attachments_order_updated' => 'Прикачения файл беше обновен',
+    'attachments_updated_success' => 'Данните на прикачения файл бяха обновени',
+    'attachments_deleted' => 'Прикачения файл беше изтрит',
+    'attachments_file_uploaded' => 'Файлът беше качен успешно',
+    'attachments_file_updated' => 'Файлът беше обновен успешно',
+    'attachments_link_attached' => 'Линкът беше успешно прикачен към страницата',
+    'templates' => 'Шаблони',
+    'templates_set_as_template' => 'Страницата е шаблон',
+    'templates_explain_set_as_template' => 'Можете да зададете тази страница като шаблон, така че нейното съдържание да бъде използвано при създаването на други страници. Други потребители ще могат да използват този шаблон, ако имат разрешения за преглед на тази страница.',
+    'templates_replace_content' => 'Замени съдържанието на страницата',
+    'templates_append_content' => 'Добави в края на съдържанието на страницата',
+    'templates_prepend_content' => 'Добави в началото на съдържанието на страницата',
+
+    // Profile View
+    'profile_user_for_x' => 'Потребител от :time',
+    'profile_created_content' => 'Създадено съдържание',
+    'profile_not_created_pages' => ':userName не е създал страници',
+    'profile_not_created_chapters' => ':userName не е създавал глави',
+    'profile_not_created_books' => ':userName не е създавал книги',
+    'profile_not_created_shelves' => ':userName не е създавал рафтове',
+
+    // Comments
+    'comment' => 'Коментирай',
+    'comments' => 'Коментари',
+    'comment_add' => 'Добавяне на коментар',
+    'comment_placeholder' => 'Напишете коментар',
+    'comment_count' => '{0} Няма коментари|{1} 1 коментар|[2,*] :count коментара',
+    'comment_save' => 'Запази коментар',
+    'comment_saving' => 'Запазване на коментар...',
+    'comment_deleting' => 'Изтриване на коментар...',
+    'comment_new' => 'Нов коментар',
+    'comment_created' => 'коментирано :createDiff',
+    'comment_updated' => 'Актуализирано :updateDiff от :username',
+    'comment_deleted_success' => 'Коментарът е изтрит',
+    'comment_created_success' => 'Коментарът е добавен',
+    'comment_updated_success' => 'Коментарът е обновен',
+    'comment_delete_confirm' => 'Наистина ли искате да изтриете този коментар?',
+    'comment_in_reply_to' => 'В отговор на :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Наистина ли искате да изтриете тази версия?',
+    'revision_restore_confirm' => 'Сигурни ли сте, че искате да изтриете тази версия? Настоящата страница ще бъде заместена.',
+    'revision_delete_success' => 'Версията беше изтрита',
+    'revision_cannot_delete_latest' => 'Не може да изтриете последната версия.'
+];
\ No newline at end of file
diff --git a/resources/lang/bg/errors.php b/resources/lang/bg/errors.php
new file mode 100644 (file)
index 0000000..4b062af
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'Нямате права за достъп до избраната страница.',
+    'permissionJson' => 'Нямате права да извършите тази операция.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'Потребител с емайл :email вече съществува но с други данни.',
+    'email_already_confirmed' => 'Емейлът вече беше потвърден. Моля опитрайте да влезете.',
+    'email_confirmation_invalid' => 'Този код за достъп не е валиден или вече е бил използван, Моля опитрайте се да се регистрирате отново.',
+    'email_confirmation_expired' => 'Кодът за потвърждение изтече, нов емейл за потвърждение беше изпратен.',
+    'email_confirmation_awaiting' => 'Емайл адреса, който използвате трябва да се потвърди',
+    'ldap_fail_anonymous' => 'LDAP протокола прекъсна, използвайки анонимни настройки',
+    'ldap_fail_authed' => 'Опита за достъп чрез LDAP с използваната парола не беше успешен',
+    'ldap_extension_not_installed' => 'LDAP PHP не беше инсталирана',
+    'ldap_cannot_connect' => 'Не може да се свържете с Ldap сървъра, първоначалната връзка се разпадна',
+    'saml_already_logged_in' => 'Вече сте влезли',
+    'saml_user_not_registered' => 'Потребителят :name не е регистриран и автоматичната регистрация не е достъпна',
+    'saml_no_email_address' => 'Не успяхме да намерим емейл адрес, за този потребител, от информацията предоставена от външната система',
+    'saml_invalid_response_id' => 'Заявката от външната система не е разпознат от процеса започнат от това приложение. Връщането назад след влизане може да породи този проблем.',
+    'saml_fail_authed' => 'Влизането чрез :system не беше успешно, системата не успя да оторизира потребителя',
+    'social_no_action_defined' => 'Действието не беше дефинирано',
+    'social_login_bad_response' => "Възникна грешка по време на :socialAccount login: \n:error",
+    'social_account_in_use' => 'Този :socialAccount вече е използван. Опитайте се да влезете чрез опцията за :socialAccount.',
+    'social_account_email_in_use' => 'Този емейл адрес вече е бил използван. Ако вече имате профил, може да го свържете чрез :socialAccount от вашия профил.',
+    'social_account_existing' => 'Този :socialAccount вече в свързан с вашия профил.',
+    'social_account_already_used_existing' => 'Този :socialAccount вече се използва от друг потребител.',
+    'social_account_not_used' => 'Този :socialAccount не е свързан с профил. Моля свържете го с вашия профил. ',
+    'social_account_register_instructions' => 'Ако все още нямате профил, може да се регистрирате чрез :socialAccount опцията.',
+    'social_driver_not_found' => 'Social driver not found',
+    'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
+    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+
+    // System
+    'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
+    'cannot_get_image_from_url' => 'Cannot get image from :url',
+    'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
+    'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'uploaded'  => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'image_upload_error' => 'An error occurred uploading the image',
+    'image_upload_type_error' => 'The image type being uploaded is invalid',
+    'file_upload_timeout' => 'The file upload has timed out.',
+
+    // Attachments
+    'attachment_not_found' => 'Attachment not found',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
+    'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage',
+
+    // Entities
+    'entity_not_found' => 'Entity not found',
+    'bookshelf_not_found' => 'Bookshelf not found',
+    'book_not_found' => 'Book not found',
+    'page_not_found' => 'Page not found',
+    'chapter_not_found' => 'Chapter not found',
+    'selected_book_not_found' => 'The selected book was not found',
+    'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
+    'guests_cannot_save_drafts' => 'Guests cannot save drafts',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
+    'users_cannot_delete_guest' => 'You cannot delete the guest user',
+
+    // Roles
+    'role_cannot_be_edited' => 'This role cannot be edited',
+    'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
+    'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
+    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+
+    // Comments
+    'comment_list' => 'An error occurred while fetching the comments.',
+    'cannot_add_comment_to_draft' => 'Не може да добавяте коментари към чернова.',
+    'comment_add' => 'Възникна грешка при актуализиране/добавяне на коментар.',
+    'comment_delete' => 'Възникна грешка при изтриването на коментара.',
+    'empty_comment' => 'Не може да добавите празен коментар.',
+
+    // Error pages
+    '404_page_not_found' => 'Страницата не е намерена',
+    'sorry_page_not_found' => 'Страницата, която търсите не може да бъде намерена.',
+    'sorry_page_not_found_permission_warning' => 'Ако смятате, че тази страница съществува, най-вероятно нямате право да я преглеждате.',
+    'return_home' => 'Назад към Начало',
+    'error_occurred' => 'Възникна грешка',
+    'app_down' => ':appName не е достъпно в момента',
+    'back_soon' => 'Ще се върне обратно онлайн скоро.',
+
+    // API errors
+    'api_no_authorization_found' => 'Но беше намерен код за достъп в заявката',
+    'api_bad_authorization_format' => 'В заявката имаше код за достъп, но формата изглежда е неправилен',
+    'api_user_token_not_found' => 'Няма открит API код, който да отговоря на предоставения такъв',
+    'api_incorrect_token_secret' => 'Секретния код, който беше предоставен за достъп до API-а е неправилен',
+    'api_user_no_api_permission' => 'Собственика на АPI кода няма право да прави API заявки',
+    'api_user_token_expired' => 'Кода за достъп, който беше използван, вече не е валиден',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Беше върната грешка, когато се изпрати тестовият емейл:',
+
+];
diff --git a/resources/lang/bg/pagination.php b/resources/lang/bg/pagination.php
new file mode 100644 (file)
index 0000000..7844171
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; Предишна',
+    'next'     => 'Следваща &raquo;',
+
+];
diff --git a/resources/lang/bg/passwords.php b/resources/lang/bg/passwords.php
new file mode 100644 (file)
index 0000000..871fdee
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+    'password' => 'Паролите трябва да имат поне 8 символа и да съвпадат с потвърждението.',
+    'user' => "Не можем да намерим потребител с този имейл адрес.",
+    'token' => 'Кодът за зануляване на паролата е невалиден за този емейл адрес.',
+    'sent' => 'Пратихме връзка за нулиране на паролата до имейла ви!',
+    'reset' => 'Вашата парола е нулирана!',
+
+];
diff --git a/resources/lang/bg/settings.php b/resources/lang/bg/settings.php
new file mode 100644 (file)
index 0000000..dcfe44b
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Настройки',
+    'settings_save' => 'Запази настройките',
+    'settings_save_success' => 'Настройките са записани',
+
+    // App Settings
+    'app_customization' => 'Персонализиране',
+    'app_features_security' => 'Екстри и Сигурност',
+    'app_name' => 'Име на приложението',
+    'app_name_desc' => 'Това име е включено във всяка шапка и във всеки имейл изпратен от системата.',
+    'app_name_header' => 'Покажи името в шапката',
+    'app_public_access' => 'Публичен достъп',
+    'app_public_access_desc' => 'Активирането на тази настройка, ще позволи на гости, които не са влезли в системта, да имат достъп до съдържанието на вашето приложение.',
+    'app_public_access_desc_guest' => 'Достъпа на гостите може да бъде контролиран от "Guest" потребителя.',
+    'app_public_access_toggle' => 'Позволяване на публичен достъп',
+    'app_public_viewing' => 'Позволване на публичен достъп?',
+    'app_secure_images' => 'По-висока сигурност при качване на изображения',
+    'app_secure_images_toggle' => 'Активиране на по-висока сигурност при качване на изображения',
+    'app_secure_images_desc' => 'С цел производителност, всички изображения са публични. Тази настройка добавя случаен, труден за отгатване низ от символи пред линка на изображението. Подсигурете, че индексите на директорията не са включени за да предотвратите лесен достъп.',
+    'app_editor' => 'Редактор на страница',
+    'app_editor_desc' => 'Изберете кой редактор да се използва от всички потребители за да редактират страници.',
+    'app_custom_html' => 'Персонализирано съдържание на HTML шапката',
+    'app_custom_html_desc' => 'Всяко съдържание, добавено тук, ще бъде поставено в долната част на секцията <head> на всяка страница. Това е удобно за преобладаващи стилове или добавяне на код за анализ.',
+    'app_custom_html_disabled_notice' => 'Съдържанието на персонализираната HTML шапка е деактивирано на страницата с настройки, за да се гарантира, че евентуални лоши промени могат да бъдат върнати.',
+    'app_logo' => 'Лого на приложението',
+    'app_logo_desc' => 'Това изображение трябва да е с 43px височина. <br> Големите изображения ще бъдат намалени.',
+    'app_primary_color' => 'Основен цвят на приложението',
+    'app_primary_color_desc' => 'Изберете основния цвят на приложението, включително на банера, бутоните и линковете.',
+    'app_homepage' => 'Application Homepage',
+    'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
+    'app_homepage_select' => 'Select a page',
+    'app_disable_comments' => 'Disable Comments',
+    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
+
+    // Color settings
+    'content_colors' => 'Content Colors',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
+    // Registration Settings
+    'reg_settings' => 'Registration',
+    'reg_enable' => 'Enable Registration',
+    'reg_enable_toggle' => 'Enable registration',
+    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_default_role' => 'Default user role after registration',
+    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    'reg_email_confirmation' => 'Email Confirmation',
+    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.',
+    'reg_confirm_restrict_domain' => 'Domain Restriction',
+    'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
+    'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
+
+    // Maintenance settings
+    'maint' => 'Maintenance',
+    'maint_image_cleanup' => 'Cleanup Images',
+    'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_run' => 'Run Cleanup',
+    'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
+    'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
+    'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
+    'maint_send_test_email' => 'Send a Test Email',
+    'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
+    'maint_send_test_email_run' => 'Send test email',
+    'maint_send_test_email_success' => 'Email sent to :address',
+    'maint_send_test_email_mail_subject' => 'Test Email',
+    'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
+    'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
+    // Role Settings
+    'roles' => 'Roles',
+    'role_user_roles' => 'User Roles',
+    'role_create' => 'Create New Role',
+    'role_create_success' => 'Role successfully created',
+    'role_delete' => 'Delete Role',
+    'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
+    'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
+    'role_delete_no_migration' => "Don't migrate users",
+    'role_delete_sure' => 'Are you sure you want to delete this role?',
+    'role_delete_success' => 'Role successfully deleted',
+    'role_edit' => 'Редактиране на роля',
+    'role_details' => 'Детайли на роля',
+    'role_name' => 'Име на ролята',
+    'role_desc' => 'Кратко описание на ролята',
+    'role_external_auth_id' => 'Външни ауторизиращи ID-a',
+    'role_system' => 'Настойки за достъп на системата',
+    'role_manage_users' => 'Управление на потребители',
+    'role_manage_roles' => 'Управление роли и права',
+    'role_manage_entity_permissions' => 'Управление на правата за достъп всички книги, глави и страници',
+    'role_manage_own_entity_permissions' => 'Управление на правата за достъп на собствени книги, глави и страници',
+    'role_manage_page_templates' => 'Управление на шаблони на страници',
+    'role_access_api' => 'Достъп до API на системата',
+    'role_manage_settings' => 'Управление на настройките на приложението',
+    'role_asset' => 'Настройки за достъп до активи',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => 'Тези настойки за достъп контролират достъпа по подразбиране до активите в системата. Настойките за достъп до книги, глави и страници ще отменят тези настройки.',
+    'role_asset_admins' => 'Администраторите автоматично получават достъп до цялото съдържание, но тези опции могат да показват или скриват опциите за потребителския интерфейс.',
+    'role_all' => 'Всички',
+    'role_own' => 'Собствени',
+    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_save' => 'Save Role',
+    'role_update_success' => 'Role successfully updated',
+    'role_users' => 'Users in this role',
+    'role_users_none' => 'No users are currently assigned to this role',
+
+    // Users
+    'users' => 'Users',
+    'user_profile' => 'User Profile',
+    'users_add_new' => 'Add New User',
+    'users_search' => 'Search Users',
+    'users_latest_activity' => 'Latest Activity',
+    'users_details' => 'User Details',
+    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
+    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_role' => 'User Roles',
+    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
+    'users_password' => 'User Password',
+    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
+    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
+    'users_send_invite_option' => 'Send user invite email',
+    'users_external_auth_id' => 'External Authentication ID',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+    'users_password_warning' => 'Only fill the below if you would like to change your password.',
+    'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
+    'users_delete' => 'Delete User',
+    'users_delete_named' => 'Delete user :userName',
+    'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
+    'users_delete_confirm' => 'Are you sure you want to delete this user?',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
+    'users_edit' => 'Edit User',
+    'users_edit_profile' => 'Edit Profile',
+    'users_edit_success' => 'User successfully updated',
+    'users_avatar' => 'User Avatar',
+    'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.',
+    'users_preferred_language' => 'Preferred Language',
+    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_social_accounts' => 'Social Accounts',
+    'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.',
+    'users_social_connect' => 'Connect Account',
+    'users_social_disconnect' => 'Disconnect Account',
+    'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
+    'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
+    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'Slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/bg/validation.php b/resources/lang/bg/validation.php
new file mode 100644 (file)
index 0000000..578ea99
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => 'The :attribute must be accepted.',
+    'active_url'           => 'The :attribute is not a valid URL.',
+    'after'                => 'The :attribute must be a date after :date.',
+    'alpha'                => 'The :attribute may only contain letters.',
+    'alpha_dash'           => 'The :attribute may only contain letters, numbers, dashes and underscores.',
+    'alpha_num'            => 'The :attribute may only contain letters and numbers.',
+    'array'                => 'The :attribute must be an array.',
+    'before'               => 'The :attribute must be a date before :date.',
+    'between'              => [
+        'numeric' => 'The :attribute must be between :min and :max.',
+        'file'    => 'The :attribute must be between :min and :max kilobytes.',
+        'string'  => 'The :attribute must be between :min and :max characters.',
+        'array'   => 'The :attribute must have between :min and :max items.',
+    ],
+    'boolean'              => 'The :attribute field must be true or false.',
+    'confirmed'            => 'The :attribute confirmation does not match.',
+    'date'                 => 'The :attribute is not a valid date.',
+    'date_format'          => 'The :attribute does not match the format :format.',
+    'different'            => 'The :attribute and :other must be different.',
+    'digits'               => 'The :attribute must be :digits digits.',
+    'digits_between'       => 'The :attribute must be between :min and :max digits.',
+    'email'                => 'The :attribute must be a valid email address.',
+    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'filled'               => 'The :attribute field is required.',
+    'gt'                   => [
+        'numeric' => 'The :attribute must be greater than :value.',
+        'file'    => 'The :attribute must be greater than :value kilobytes.',
+        'string'  => 'The :attribute must be greater than :value characters.',
+        'array'   => 'The :attribute must have more than :value items.',
+    ],
+    'gte'                  => [
+        'numeric' => 'The :attribute must be greater than or equal :value.',
+        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be greater than or equal :value characters.',
+        'array'   => 'The :attribute must have :value items or more.',
+    ],
+    'exists'               => 'The selected :attribute is invalid.',
+    'image'                => 'The :attribute must be an image.',
+    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'in'                   => 'The selected :attribute is invalid.',
+    'integer'              => 'The :attribute must be an integer.',
+    'ip'                   => 'The :attribute must be a valid IP address.',
+    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
+    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
+    'json'                 => 'The :attribute must be a valid JSON string.',
+    'lt'                   => [
+        'numeric' => 'The :attribute must be less than :value.',
+        'file'    => 'The :attribute must be less than :value kilobytes.',
+        'string'  => 'The :attribute must be less than :value characters.',
+        'array'   => 'The :attribute must have less than :value items.',
+    ],
+    'lte'                  => [
+        'numeric' => 'The :attribute must be less than or equal :value.',
+        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be less than or equal :value characters.',
+        'array'   => 'The :attribute must not have more than :value items.',
+    ],
+    'max'                  => [
+        'numeric' => 'The :attribute may not be greater than :max.',
+        'file'    => 'The :attribute may not be greater than :max kilobytes.',
+        'string'  => 'The :attribute may not be greater than :max characters.',
+        'array'   => 'The :attribute may not have more than :max items.',
+    ],
+    'mimes'                => 'The :attribute must be a file of type: :values.',
+    'min'                  => [
+        'numeric' => 'The :attribute must be at least :min.',
+        'file'    => 'The :attribute must be at least :min kilobytes.',
+        'string'  => 'The :attribute must be at least :min characters.',
+        'array'   => 'The :attribute must have at least :min items.',
+    ],
+    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'not_in'               => 'The selected :attribute is invalid.',
+    'not_regex'            => 'The :attribute format is invalid.',
+    'numeric'              => 'The :attribute must be a number.',
+    'regex'                => 'The :attribute format is invalid.',
+    'required'             => 'The :attribute field is required.',
+    'required_if'          => 'The :attribute field is required when :other is :value.',
+    'required_with'        => 'The :attribute field is required when :values is present.',
+    'required_with_all'    => 'The :attribute field is required when :values is present.',
+    'required_without'     => 'The :attribute field is required when :values is not present.',
+    'required_without_all' => 'The :attribute field is required when none of :values are present.',
+    'same'                 => 'The :attribute and :other must match.',
+    'safe_url'             => 'The provided link may not be safe.',
+    'size'                 => [
+        'numeric' => 'The :attribute must be :size.',
+        'file'    => 'The :attribute must be :size kilobytes.',
+        'string'  => 'The :attribute must be :size characters.',
+        'array'   => 'The :attribute must contain :size items.',
+    ],
+    'string'               => 'The :attribute must be a string.',
+    'timezone'             => 'The :attribute must be a valid zone.',
+    'unique'               => 'The :attribute has already been taken.',
+    'url'                  => 'The :attribute format is invalid.',
+    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Password confirmation required',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 23fce810812f8d2c2b7e5002e82f95952f7ed6f1..818369331b6ebea37e9274da3370b717da3ca6d4 100644 (file)
@@ -6,43 +6,44 @@
 return [
 
     // Pages
-    'page_create'                 => 'vytvořená stránka',
+    'page_create'                 => 'vytvořil/a stránku',
     'page_create_notification'    => 'Stránka byla úspěšně vytvořena',
-    'page_update'                 => 'aktualizovaná stránka',
+    'page_update'                 => 'aktualizoval/a stránku',
     'page_update_notification'    => 'Stránka byla úspěšně aktualizována',
-    'page_delete'                 => 'smazaná stránka',
+    'page_delete'                 => 'odstranil/a stránku',
     'page_delete_notification'    => 'Stránka byla úspěšně smazána',
-    'page_restore'                => 'renovovaná stránka',
-    'page_restore_notification'   => 'Stránka byla úspěšně renovována',
-    'page_move'                   => 'přesunutá stránka',
+    'page_restore'                => 'obnovil/a stránku',
+    'page_restore_notification'   => 'Stránka byla úspěšně obnovena',
+    'page_move'                   => 'přesunul/a stránku',
 
     // Chapters
-    'chapter_create'              => 'vytvořená kapitola',
+    'chapter_create'              => 'vytvořil/a kapitolu',
     'chapter_create_notification' => 'Kapitola byla úspěšně vytvořena',
-    'chapter_update'              => 'aktualizovaná kapitola',
+    'chapter_update'              => 'aktualizoval/a kapitolu',
     'chapter_update_notification' => 'Kapitola byla úspěšně aktualizována',
-    'chapter_delete'              => 'smazaná kapitola',
+    'chapter_delete'              => 'smazal/a kapitolu',
     'chapter_delete_notification' => 'Kapitola byla úspěšně smazána',
-    'chapter_move'                => 'přesunutá kapitola',
+    'chapter_move'                => 'přesunul/a kapitolu',
 
     // Books
-    'book_create'                 => 'vytvořená kniha',
+    'book_create'                 => 'vytvořil/a knihu',
     'book_create_notification'    => 'Kniha byla úspěšně vytvořena',
-    'book_update'                 => 'aktualizovaná kniha',
+    'book_update'                 => 'aktualizoval/a knihu',
     'book_update_notification'    => 'Kniha byla úspěšně aktualizována',
-    'book_delete'                 => 'smazaná kniha',
+    'book_delete'                 => 'smazal/a knihu',
     'book_delete_notification'    => 'Kniha byla úspěšně smazána',
-    'book_sort'                   => 'seřazená kniha',
+    'book_sort'                   => 'seřadil/a knihu',
     'book_sort_notification'      => 'Kniha byla úspěšně seřazena',
 
     // Bookshelves
-    'bookshelf_create'            => 'vytvořená knihovna',
-    'bookshelf_create_notification'    => 'Knihovna úspěšně vytvořena',
-    'bookshelf_update'                 => 'aktualizovaná knihovna',
+    'bookshelf_create'            => 'vytvořil/a knihovnu',
+    'bookshelf_create_notification'    => 'Knihovna byla úspěšně vytvořena',
+    'bookshelf_update'                 => 'aktualizoval/a knihovnu',
     'bookshelf_update_notification'    => 'Knihovna byla úspěšně aktualizována',
-    'bookshelf_delete'                 => 'smazaná knihovna',
-    'bookshelf_delete_notification'    => 'Knihovna byla úspěšně smazána',
+    'bookshelf_delete'                 => 'odstranil/a knihovnu',
+    'bookshelf_delete_notification'    => 'Knihovna byla úspěšně odstraněna',
 
     // Other
-    'commented_on'                => 'okomentováno v',
+    'commented_on'                => 'okomentoval/a',
+    'permissions_update'          => 'updated permissions',
 ];
index fbda0150d72688d446387174d31430b934b9be2f..a9f36c390b9cff31c109da8256f95574e8569932 100644 (file)
@@ -6,72 +6,72 @@
  */
 return [
 
-    'failed' => 'Neplatné přihlašovací údaje.',
-    'throttle' => 'Příliš pokusů o přihlášení. Zkuste to prosím znovu za :seconds sekund.',
+    'failed' => 'Tyto přihlašovací údaje neodpovídají našim záznamům.',
+    'throttle' => 'Příliš mnoho pokusů o přihlášení. Zkuste to prosím znovu za :seconds sekund.',
 
     // Login & Register
     'sign_up' => 'Registrace',
     'log_in' => 'Přihlášení',
-    'log_in_with' => 'Přihlásit přes :socialDriver',
-    'sign_up_with' => 'Registrovat se přes :socialDriver',
+    'log_in_with' => 'Přihlásit se pomocí :socialDriver',
+    'sign_up_with' => 'Registrovat se pomocí :socialDriver',
     'logout' => 'Odhlásit',
 
     'name' => 'Jméno',
-    'username' => 'Jméno účtu',
-    'email' => 'Email',
+    'username' => 'Uživatelské jméno',
+    'email' => 'E-mail',
     'password' => 'Heslo',
     'password_confirm' => 'Potvrdit heslo',
-    'password_hint' => 'Musí mít víc než 7 znaků',
+    'password_hint' => 'Musí mít více než 7 znaků',
     'forgot_password' => 'Zapomněli jste heslo?',
-    'remember_me' => 'Neodhlašovat',
+    'remember_me' => 'Zapamatovat si mě',
     'ldap_email_hint' => 'Zadejte email, který chcete přiřadit k tomuto účtu.',
     'create_account' => 'Vytvořit účet',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
-    'social_login' => 'Přihlášení přes sociální sítě',
-    'social_registration' => 'Registrace přes sociální sítě',
-    'social_registration_text' => 'Registrovat a přihlásit se přes jinou službu',
+    'already_have_account' => 'Již máte účet?',
+    'dont_have_account' => 'Nemáte účet?',
+    'social_login' => 'Přihlášení pomocí sociálních sítí',
+    'social_registration' => 'Přihlášení pomocí sociálních sítí',
+    'social_registration_text' => 'Registrovat a přihlásit se pomocí jiné služby.',
 
-    'register_thanks' => 'Díky za registraci!',
-    'register_confirm' => 'Zkontrolujte prosím váš email a klikněte na potvrzovací tlačítko pro dokončení registrace do :appName.',
-    'registrations_disabled' => 'Registrace jsou momentálně pozastaveny',
-    'registration_email_domain_invalid' => 'Registrace z této emailové domény nejsou povoleny.',
-    'register_success' => 'Díky za registraci! Jste registrovaní a přihlášení.',
+    'register_thanks' => 'Děkujeme za registraci!',
+    'register_confirm' => 'Zkontrolujte prosím svůj e-mail a klikněte na potvrzovací tlačítko pro přístup do :appName.',
+    'registrations_disabled' => 'Registrace jsou aktuálně zakázány',
+    'registration_email_domain_invalid' => 'Tato e-mailová doména nemá přístup k této aplikaci',
+    'register_success' => 'Děkujeme za registraci! Nyní jste zaregistrováni a přihlášeni.',
 
 
     // Password Reset
-    'reset_password' => 'Resetovat heslo',
-    'reset_password_send_instructions' => 'Zadejte vaší emailovou adresu a bude vám zaslán odkaz na resetování hesla.',
-    'reset_password_send_button' => 'Poslat odkaz pro reset hesla',
-    'reset_password_sent_success' => 'Odkaz na resetování hesla vám byl zaslán na :email.',
-    'reset_password_success' => 'Vaše heslo bylo úspěšně resetováno.',
-    'email_reset_subject' => 'Reset hesla do :appName',
-    'email_reset_text' => 'Tento email jste obdrželi, protože jsme dostali žádost o resetování vašeho hesla k účtu v :appName.',
-    'email_reset_not_requested' => 'Pokud jste o reset vašeho hesla nežádali, prostě tento dopis smažte a je to.',
+    'reset_password' => 'Obnovit heslo',
+    'reset_password_send_instructions' => 'Níže zadejte svou e-mailovou adresu a bude vám zaslán e-mail s odkazem pro obnovení hesla.',
+    'reset_password_send_button' => 'Zaslat odkaz pro obnovení',
+    'reset_password_sent' => 'Odkaz pro obnovení hesla bude odeslán na :email, pokud bude tato e-mailová adresa nalezena v systému.',
+    'reset_password_success' => 'Vaše heslo bylo úspěšně obnoveno.',
+    'email_reset_subject' => 'Obnovit heslo do :appName',
+    'email_reset_text' => 'Tento e-mail jste obdrželi, protože jsme obdrželi žádost o obnovení hesla k vašemu účtu.',
+    'email_reset_not_requested' => 'Pokud jste o obnovení hesla nežádali, není vyžadována žádná další akce.',
 
 
     // Email Confirmation
-    'email_confirm_subject' => 'Potvrďte vaši emailovou adresu pro :appName',
+    'email_confirm_subject' => 'Potvrďte svůj e-mail pro :appName',
     'email_confirm_greeting' => 'Díky že jste se přidali do :appName!',
-    'email_confirm_text' => 'Prosíme potvrďte funkčnost vaší emailové adresy kliknutím na tlačítko níže:',
-    'email_confirm_action' => 'Potvrdit emailovou adresu',
-    'email_confirm_send_error' => 'Potvrzení emailové adresy je vyžadováno, ale systém vám nedokázal odeslat email. Kontaktujte správce aby to dal do kupy a potvrzovací email vám dorazil.',
-    'email_confirm_success' => 'Vaše emailová adresa byla potvrzena!',
-    'email_confirm_resent' => 'Email s žádostí o potvrzení vaší emailové adresy byl odeslán. Podívejte se do příchozí pošty.',
+    'email_confirm_text' => 'Prosíme potvrďte svou e-mailovou adresu kliknutím na níže uvedené tlačítko:',
+    'email_confirm_action' => 'Potvrdit e-mail',
+    'email_confirm_send_error' => 'Potvrzení e-mailu je vyžadováno, ale systém nemohl odeslat e-mail. Obraťte se na správce, abyste se ujistili, že je e-mail správně nastaven.',
+    'email_confirm_success' => 'Váš e-mail byla potvrzen!',
+    'email_confirm_resent' => 'E-mail s potvrzením byl znovu odeslán. Zkontrolujte svou příchozí poštu.',
 
-    'email_not_confirmed' => 'Emailová adresa nebyla potvrzena',
-    'email_not_confirmed_text' => 'Vaše emailová adresa nebyla dosud potvrzena.',
-    'email_not_confirmed_click_link' => 'Klikněte na odkaz v emailu který jsme vám zaslali ihned po registraci.',
-    'email_not_confirmed_resend' => 'Pokud nemůžete nalézt email v příchozí poště, můžete si jej nechat poslat znovu pomocí formuláře níže.',
-    'email_not_confirmed_resend_button' => 'Znovu poslat email pro potvrzení emailové adresy',
+    'email_not_confirmed' => 'E-mailová adresa nebyla potvrzena',
+    'email_not_confirmed_text' => 'Vaše e-mailová adresa nebyla dosud potvrzena.',
+    'email_not_confirmed_click_link' => 'Klikněte prosím na odkaz v e-mailu, který byl odeslán krátce po registraci.',
+    'email_not_confirmed_resend' => 'Pokud nemůžete e-mail nalézt, můžete znovu odeslat potvrzovací e-mail odesláním níže uvedeného formuláře.',
+    'email_not_confirmed_resend_button' => 'Znovu odeslat potvrzovací e-mail',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Byli jste pozváni přidat se do :appName!',
+    'user_invite_email_greeting' => 'Byl pro vás vytvořen účet na :appName.',
+    'user_invite_email_text' => 'Klikněte na níže uvedené tlačítko pro nastavení hesla k účtu a získání přístupu:',
+    'user_invite_email_action' => 'Nastavit heslo k účtu',
+    'user_invite_page_welcome' => 'Vítejte v :appName!',
+    'user_invite_page_text' => 'Pro dokončení vašeho účtu a získání přístupu musíte nastavit heslo, které bude použito k přihlášení do :appName při budoucích návštěvách.',
+    'user_invite_page_confirm_button' => 'Potvrdit heslo',
+    'user_invite_success' => 'Heslo nastaveno, nyní máte přístup k :appName!'
 ];
\ No newline at end of file
index cad26410c1df09beea9d34123670e29517143866..342610f5d9942ea73b3bd123cbed7b44c1d7073d 100644 (file)
@@ -5,72 +5,76 @@
 return [
 
     // Buttons
-    'cancel' => 'Storno',
+    'cancel' => 'Zrušit',
     'confirm' => 'Potvrdit',
     'back' => 'Zpět',
     'save' => 'Uložit',
     'continue' => 'Pokračovat',
-    'select' => 'Zvolit',
-    'toggle_all' => 'Toggle All',
+    'select' => 'Vybrat',
+    'toggle_all' => 'Přepnout vše',
     'more' => 'Více',
 
     // Form Labels
-    'name' => 'Jméno',
+    'name' => 'Název',
     'description' => 'Popis',
     'role' => 'Role',
-    'cover_image' => 'Obrázek na přebal',
-    'cover_image_description' => 'Obrázek by měl být asi 440 × 250px.',
+    'cover_image' => 'Obrázek obálky',
+    'cover_image_description' => 'Obrázek by měl být přibližně 440×250px.',
     
     // Actions
     'actions' => 'Akce',
-    'view' => 'Pohled',
-    'view_all' => 'View All',
+    'view' => 'Zobrazit',
+    'view_all' => 'Zobrazit vše',
     'create' => 'Vytvořit',
     'update' => 'Aktualizovat',
     'edit' => 'Upravit',
-    'sort' => 'Řadit',
+    'sort' => 'Seřadit',
     'move' => 'Přesunout',
     'copy' => 'Kopírovat',
     'reply' => 'Odpovědět',
-    'delete' => 'Smazat',
+    'delete' => 'Odstranit',
+    'delete_confirm' => 'Potvrdit odstranění',
     'search' => 'Hledat',
-    'search_clear' => 'Vyčistit hledání',
-    'reset' => 'Reset',
-    'remove' => 'Odstranit',
+    'search_clear' => 'Vymazat hledání',
+    'reset' => 'Obnovit',
+    'remove' => 'Odebrat',
     'add' => 'Přidat',
+    'fullscreen' => 'Celá obrazovka',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
-    'sort_name' => 'Name',
-    'sort_created_at' => 'Created Date',
-    'sort_updated_at' => 'Updated Date',
+    'sort_options' => 'Možnosti řazení',
+    'sort_direction_toggle' => 'Přepínač směru řazení',
+    'sort_ascending' => 'Řadit vzestupně',
+    'sort_descending' => 'Řadit sestupně',
+    'sort_name' => 'Název',
+    'sort_created_at' => 'Datum vytvoření',
+    'sort_updated_at' => 'Datum aktualizace',
 
     // Misc
-    'deleted_user' => 'Smazaný uživatel',
+    'deleted_user' => 'Odstraněný uživatel',
     'no_activity' => 'Žádná aktivita k zobrazení',
-    'no_items' => 'Žádné položky nejsou k mání',
+    'no_items' => 'Žádné položky k dispozici',
     'back_to_top' => 'Zpět na začátek',
-    'toggle_details' => 'Ukázat detaily',
-    'toggle_thumbnails' => 'Ukázat náhledy',
-    'details' => 'Detaily',
-    'grid_view' => 'Zobrazit dlaždice',
-    'list_view' => 'Zobrazit seznam',
+    'toggle_details' => 'Přepnout podrobnosti',
+    'toggle_thumbnails' => 'Přepnout náhledy',
+    'details' => 'Podrobnosti',
+    'grid_view' => 'Zobrazení mřížky',
+    'list_view' => 'Zobrazení seznamu',
     'default' => 'Výchozí',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Drobečková navigace',
 
     // Header
-    'profile_menu' => 'Profile Menu',
-    'view_profile' => 'Ukázat profil',
+    'profile_menu' => 'Nabídka profilu',
+    'view_profile' => 'Zobrazit profil',
     'edit_profile' => 'Upravit profil',
+    'dark_mode' => 'Tmavý režim',
+    'light_mode' => 'Světelný režim',
 
     // Layout tabs
-    'tab_info' => 'Info',
-    'tab_content' => 'Content',
+    'tab_info' => 'Informace',
+    'tab_content' => 'Obsah',
 
     // Email Content
-    'email_action_help' => 'Pokud se vám nedaří kliknout na tlačítko ":actionText", zkopírujte odkaz níže přímo do webového prohlížeče:',
+    'email_action_help' => 'Pokud se vám nedaří kliknout na tlačítko „:actionText“, zkopírujte a vložte níže uvedenou URL do vašeho webového prohlížeče:',
     'email_rights' => 'Všechna práva vyhrazena',
 ];
index a62914f1bc680c21ba9077eae4444dd6f7c9c540..e06462b0021c9f9a493cc6ef52cbae494e97caf7 100644 (file)
@@ -5,29 +5,30 @@
 return [
 
     // Image Manager
-    'image_select' => 'Volba obrázku',
+    'image_select' => 'Výběr obrázku',
     'image_all' => 'Vše',
     'image_all_title' => 'Zobrazit všechny obrázky',
-    'image_book_title' => 'Zobrazit obrázky nahrané k této knize',
-    'image_page_title' => 'Zobrazit obrázky nahrané k této stránce',
+    'image_book_title' => 'Zobrazit obrázky nahrané do této knihy',
+    'image_page_title' => 'Zobrazit obrázky nahrané na tuto stránku',
     'image_search_hint' => 'Hledat podle názvu obrázku',
     'image_uploaded' => 'Nahráno :uploadedDate',
     'image_load_more' => 'Načíst další',
     'image_image_name' => 'Název obrázku',
-    'image_delete_used' => 'Tento obrázek je použit v následujících stránkách.',
-    'image_delete_confirm' => 'Stisknětě smazat ještě jednou pro potvrzení smazání tohoto obrázku.',
-    'image_select_image' => 'Zvolte obrázek',
-    'image_dropzone' => 'Přetáhněte sem obrázky myší nebo sem klikněte pro vybrání souboru.',
-    'images_deleted' => 'Obrázky smazány',
+    'image_delete_used' => 'Tento obrázek je použit na níže uvedených stránkách.',
+    'image_delete_confirm_text' => 'Opravdu chcete odstranit tento obrázek?',
+    'image_select_image' => 'Vyberte obrázek',
+    'image_dropzone' => 'Přetáhněte obrázky nebo klikněte sem pro nahrání',
+    'images_deleted' => 'Obrázky odstraněny',
     'image_preview' => 'Náhled obrázku',
     'image_upload_success' => 'Obrázek byl úspěšně nahrán',
     'image_update_success' => 'Podrobnosti o obrázku byly úspěšně aktualizovány',
-    'image_delete_success' => 'Obrázek byl úspěšně smazán',
-    'image_upload_remove' => 'Odstranit',
+    'image_delete_success' => 'Obrázek byl úspěšně odstraněn',
+    'image_upload_remove' => 'Odebrat',
 
     // Code Editor
     'code_editor' => 'Upravit kód',
     'code_language' => 'Jazyk kódu',
     'code_content' => 'Obsah kódu',
+    'code_session_history' => 'Historie relace',
     'code_save' => 'Uložit kód',
 ];
index 579d49127b86f2a312f796aa806775b38c184920..983239c82648fd2fa1ea8e437ff61d7a661ded14 100644 (file)
@@ -11,72 +11,75 @@ return [
     'recently_updated_pages' => 'Nedávno aktualizované stránky',
     'recently_created_chapters' => 'Nedávno vytvořené kapitoly',
     'recently_created_books' => 'Nedávno vytvořené knihy',
-    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_created_shelves' => 'Nedávno vytvořené knihovny',
     'recently_update' => 'Nedávno aktualizované',
-    'recently_viewed' => 'Nedávno prohlížené',
-    'recent_activity' => 'Nedávné činnosti',
-    'create_now' => 'Vytvořte jí',
+    'recently_viewed' => 'Nedávno zobrazené',
+    'recent_activity' => 'Nedávné aktivity',
+    'create_now' => 'Vytvořte ji nyní',
     'revisions' => 'Revize',
-    'meta_revision' => 'Revize #:revisionCount',
+    'meta_revision' => 'Revize č. :revisionCount',
     'meta_created' => 'Vytvořeno :timeLength',
     'meta_created_name' => 'Vytvořeno :timeLength uživatelem :user',
     'meta_updated' => 'Aktualizováno :timeLength',
     'meta_updated_name' => 'Aktualizováno :timeLength uživatelem :user',
-    'entity_select' => 'Volba prvku',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'Výběr entity',
     'images' => 'Obrázky',
     'my_recent_drafts' => 'Mé nedávné koncepty',
-    'my_recently_viewed' => 'Naposledy navštívené',
-    'no_pages_viewed' => 'Zatím jste nic neshlédli',
-    'no_pages_recently_created' => 'Zatím nebyly vytvořeny žádné stránky',
-    'no_pages_recently_updated' => 'Zatím nebyly aktualizovány žádné stránky',
-    'export' => 'Export',
-    'export_html' => 'Všeobjímající HTML',
-    'export_pdf' => 'PDF dokument',
-    'export_text' => 'Čistý text (txt)',
+    'my_recently_viewed' => 'Mé nedávno zobrazené',
+    'no_pages_viewed' => 'Nezobrazili jste žádné stránky',
+    'no_pages_recently_created' => 'Nedávno nebyly vytvořeny žádné stránky',
+    'no_pages_recently_updated' => 'Nedávno nebyly aktualizovány žádné stránky',
+    'export' => 'Exportovat',
+    'export_html' => 'Konsolidovaný webový soubor',
+    'export_pdf' => 'Soubor PDF',
+    'export_text' => 'Textový soubor',
 
     // Permissions and restrictions
-    'permissions' => 'Práva',
-    'permissions_intro' => 'Zaškrtnutím překryjete práva v uživatelských rolích nastavením níže.',
-    'permissions_enable' => 'Zapnout vlastní práva',
-    'permissions_save' => 'Uložit práva',
+    'permissions' => 'Oprávnění',
+    'permissions_intro' => 'Pokud je povoleno, tato oprávnění budou mít přednost před všemi nastavenými oprávněními role.',
+    'permissions_enable' => 'Povolit vlastní oprávnění',
+    'permissions_save' => 'Uložit oprávnění',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Výsledky hledání',
     'search_total_results_found' => 'Nalezen :count výsledek|Nalezeny :count výsledky|Nalezeny :count výsledky|Nalezeny :count výsledky|Nalezeno :count výsledků',
-    'search_clear' => 'Vyčistit hledání',
-    'search_no_pages' => 'Žádná stránka neodpovídá hledanému výrazu',
+    'search_clear' => 'Vymazat hledání',
+    'search_no_pages' => 'Tomuto hledání neodpovídají žádné stránky',
     'search_for_term' => 'Hledat :term',
     'search_more' => 'Další výsledky',
-    'search_filters' => 'Filtry hledání',
+    'search_advanced' => 'Rozšířené hledání',
+    'search_terms' => 'Hledané výrazy',
     'search_content_type' => 'Typ obsahu',
-    'search_exact_matches' => 'Musí obsahovat',
-    'search_tags' => 'Hledat štítky (tagy)',
-    'search_options' => 'Volby',
-    'search_viewed_by_me' => 'Shlédnuto mnou',
-    'search_not_viewed_by_me' => 'Neshlédnuto mnou',
-    'search_permissions_set' => 'Sada práv',
+    'search_exact_matches' => 'Přesné shody',
+    'search_tags' => 'Hledat štítky',
+    'search_options' => 'Možnosti',
+    'search_viewed_by_me' => 'Zobrazeno mnou',
+    'search_not_viewed_by_me' => 'Nezobrazeno mnou',
+    'search_permissions_set' => 'Sada oprávnění',
     'search_created_by_me' => 'Vytvořeno mnou',
-    'search_updated_by_me' => 'Aktualizováno',
-    'search_date_options' => 'Volby datumu',
+    'search_updated_by_me' => 'Aktualizováno mnou',
+    'search_date_options' => 'Možnosti data',
     'search_updated_before' => 'Aktualizováno před',
     'search_updated_after' => 'Aktualizováno po',
     'search_created_before' => 'Vytvořeno před',
     'search_created_after' => 'Vytvořeno po',
-    'search_set_date' => 'Datum',
-    'search_update' => 'Hledat znovu',
+    'search_set_date' => 'Nastavit datum',
+    'search_update' => 'Aktualizovat hledání',
 
     // Shelves
     'shelf' => 'Knihovna',
     'shelves' => 'Knihovny',
-    'x_shelves' => ':count Shelf|:count Shelves',
+    'x_shelves' => '{0}:count knihoven|{1}:count knihovna|[2,4]:count knihovny|[5,*]:count knihoven',
     'shelves_long' => 'Knihovny',
-    'shelves_empty' => 'Žádné knihovny nebyly vytvořeny',
+    'shelves_empty' => 'Nebyly vytvořeny žádné knihovny',
     'shelves_create' => 'Vytvořit novou knihovnu',
     'shelves_popular' => 'Populární knihovny',
     'shelves_new' => 'Nové knihovny',
-    'shelves_new_action' => 'New Shelf',
+    'shelves_new_action' => 'Nová Knihovna',
     'shelves_popular_empty' => 'Nejpopulárnější knihovny se objeví zde.',
-    'shelves_new_empty' => 'Nejnovější knihovny se objeví zde.',
+    'shelves_new_empty' => 'Zde se objeví nejnověji vytvořené knihovny.',
     'shelves_save' => 'Uložit knihovnu',
     'shelves_books' => 'Knihy v této knihovně',
     'shelves_add_books' => 'Přidat knihy do knihovny',
@@ -85,67 +88,67 @@ return [
     'shelves_edit_and_assign' => 'Pro přidáni knih do knihovny stiskněte úprvy.',
     'shelves_edit_named' => 'Upravit knihovnu :name',
     'shelves_edit' => 'Upravit knihovnu',
-    'shelves_delete' => 'Smazat knihovnu',
-    'shelves_delete_named' => 'Smazat knihovnu :name',
-    'shelves_delete_explain' => "Chystáte se smazat knihovnu ':name'. Knihy v ní obsažené zůstanou zachovány.",
-    'shelves_delete_confirmation' => 'Opravdu chcete smazat tuto knihovnu?',
-    'shelves_permissions' => 'Práva knihovny',
-    'shelves_permissions_updated' => 'Práva knihovny byla aktualizována',
-    'shelves_permissions_active' => 'Účinná práva knihovny',
-    'shelves_copy_permissions_to_books' => 'Přenést práva na knihy',
-    'shelves_copy_permissions' => 'Zkopírovat práva',
-    'shelves_copy_permissions_explain' => 'Práva knihovny budou aplikována na všechny knihy v ní obsažené. Před použitím se ujistěte, že jste uložili změny práv knihovny.',
-    'shelves_copy_permission_success' => 'Práva knihovny přenesena na knihy (celkem :count)',
+    'shelves_delete' => 'Odstranit knihovnu',
+    'shelves_delete_named' => 'Odstranit knihovnu :name',
+    'shelves_delete_explain' => "Toto odstraní knihovnu s názvem ‚:name‘. Obsažené knihy nebudou odstraněny.",
+    'shelves_delete_confirmation' => 'Opravdu chcete odstranit tuto knihovnu?',
+    'shelves_permissions' => 'Oprávnění knihovny',
+    'shelves_permissions_updated' => 'Oprávnění knihovny byla aktualizována',
+    'shelves_permissions_active' => 'Oprávnění knihovny jsou aktivní',
+    'shelves_copy_permissions_to_books' => 'Kopírovat oprávnění na knihy',
+    'shelves_copy_permissions' => 'Kopírovat oprávnění',
+    'shelves_copy_permissions_explain' => 'Toto použije aktuální nastavení oprávnění této knihovny na všechny knihy v ní obsažené. Před aktivací se ujistěte, že byly uloženy všechny změny oprávnění této knihovny.',
+    'shelves_copy_permission_success' => 'Oprávnění knihovny byla zkopírována na :count knih',
 
     // Books
     'book' => 'Kniha',
     'books' => 'Knihy',
-    'x_books' => ':count Kniha|:count Knihy|:count Knihy|:count Knihy|:count Knih',
-    'books_empty' => 'Žádné knihy nebyly vytvořeny',
-    'books_popular' => 'Populární knihy',
+    'x_books' => '{0}:count knih|{1}:count kniha|[2,4]:count knihy|[5,*]:count knih',
+    'books_empty' => 'Nebyly vytvořeny žádné knihy',
+    'books_popular' => 'Oblíbené knihy',
     'books_recent' => 'Nedávné knihy',
     'books_new' => 'Nové knihy',
-    'books_new_action' => 'New Book',
-    'books_popular_empty' => 'Zde budou zobrazeny nejpopulárnější knihy.',
-    'books_new_empty' => 'Zde budou zobrazeny nově vytvořené knihy.',
+    'books_new_action' => 'Nová kniha',
+    'books_popular_empty' => 'Zde se objeví nejoblíbenější knihy.',
+    'books_new_empty' => 'Zde se objeví nejnověji vytvořené knihy.',
     'books_create' => 'Vytvořit novou knihu',
-    'books_delete' => 'Smazat knihu',
-    'books_delete_named' => 'Smazat knihu :bookName',
-    'books_delete_explain' => 'Kniha \':bookName\' bude smazána. Všechny její stránky a kapitoly budou taktéž smazány.',
-    'books_delete_confirmation' => 'Opravdu chcete tuto knihu smazat.',
+    'books_delete' => 'Odstranit knihu',
+    'books_delete_named' => 'Odstranit knihu :bookName',
+    'books_delete_explain' => 'Toto odstraní knihu s názvem ‚:bookName‘. Všechny stránky a kapitoly budou odebrány.',
+    'books_delete_confirmation' => 'Opravdu chcete odstranit tuto knihu?',
     'books_edit' => 'Upravit knihu',
     'books_edit_named' => 'Upravit knihu :bookName',
     'books_form_book_name' => 'Název knihy',
     'books_save' => 'Uložit knihu',
-    'books_permissions' => 'Práva knihy',
-    'books_permissions_updated' => 'Práva knihy upravena',
-    'books_empty_contents' => 'V této knize nebyly vytvořeny žádné stránky ani kapitoly.',
+    'books_permissions' => 'Oprávnění knihy',
+    'books_permissions_updated' => 'Oprávnění knihy aktualizována',
+    'books_empty_contents' => 'Pro tuto knihu nebyly vytvořeny žádné stránky nebo kapitoly.',
     'books_empty_create_page' => 'Vytvořit novou stránku',
-    'books_empty_sort_current_book' => 'Seřadit tuto knihu',
+    'books_empty_sort_current_book' => 'Seřadit aktuální knihu',
     'books_empty_add_chapter' => 'Přidat kapitolu',
-    'books_permissions_active' => 'Účinná práva knihy',
+    'books_permissions_active' => 'Oprávnění knihy jsou aktivní',
     'books_search_this' => 'Prohledat tuto knihu',
-    'books_navigation' => 'Obsah knihy',
+    'books_navigation' => 'Navigace knihy',
     'books_sort' => 'Seřadit obsah knihy',
     'books_sort_named' => 'Seřadit knihu :bookName',
-    'books_sort_name' => 'Sort by Name',
-    'books_sort_created' => 'Sort by Created Date',
-    'books_sort_updated' => 'Sort by Updated Date',
-    'books_sort_chapters_first' => 'Chapters First',
-    'books_sort_chapters_last' => 'Chapters Last',
-    'books_sort_show_other' => 'Ukázat ostatní knihy',
+    'books_sort_name' => 'Seřadit podle názvu',
+    'books_sort_created' => 'Seřadit podle data vytvoření',
+    'books_sort_updated' => 'Seřadit podle data aktualizace',
+    'books_sort_chapters_first' => 'Kapitoly první',
+    'books_sort_chapters_last' => 'Kapitoly poslední',
+    'books_sort_show_other' => 'Zobrazit ostatní knihy',
     'books_sort_save' => 'Uložit nové pořadí',
 
     // Chapters
     'chapter' => 'Kapitola',
     'chapters' => 'Kapitoly',
-    'x_chapters' => ':count kapitola|:count kapitoly|:count kapitoly|:count kapitoly|:count kapitol',
+    'x_chapters' => '{0}:count Kapitol|{1}:count Kapitola|[2,4]:count Kapitoly|[5,*]:count Kapitol',
     'chapters_popular' => 'Populární kapitoly',
     'chapters_new' => 'Nová kapitola',
     'chapters_create' => 'Vytvořit novou kapitolu',
     'chapters_delete' => 'Smazat kapitolu',
     'chapters_delete_named' => 'Smazat kapitolu :chapterName',
-    'chapters_delete_explain' => 'Kapitola \':chapterName\' bude smazána. Všechny stránky v ní obsažené budou přesunuty přímo pod samotnou knihu.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'Opravdu chcete tuto kapitolu smazat?',
     'chapters_edit' => 'Upravit kapitolu',
     'chapters_edit_named' => 'Upravit kapitolu :chapterName',
@@ -162,7 +165,7 @@ return [
     // Pages
     'page' => 'Stránka',
     'pages' => 'Stránky',
-    'x_pages' => ':count strana|:count strany|:count strany|:count strany|:count stran',
+    'x_pages' => '{0}:count Stran|{1}:count Strana|[2,4]:count Strany|[5,*]:count Stran',
     'pages_popular' => 'Populární stránky',
     'pages_new' => 'Nová stránka',
     'pages_attachments' => 'Přílohy',
@@ -170,55 +173,56 @@ return [
     'pages_delete' => 'Smazat stránku',
     'pages_delete_named' => 'Smazat stránku :pageName',
     'pages_delete_draft_named' => 'Smazat koncept stránky :pageName',
-    'pages_delete_draft' => 'Smazat koncept stránky',
-    'pages_delete_success' => 'Stránka smazána',
-    'pages_delete_draft_success' => 'Koncept stránky smazán',
-    'pages_delete_confirm' => 'Opravdu chcete tuto stránku smazat?',
-    'pages_delete_draft_confirm' => 'Opravdu chcete tento koncept stránky smazat?',
+    'pages_delete_draft' => 'Odstranit koncept stránky',
+    'pages_delete_success' => 'Stránka odstraněna',
+    'pages_delete_draft_success' => 'Koncept stránky odstraněn',
+    'pages_delete_confirm' => 'Opravdu chcete odstranit tuto stránku?',
+    'pages_delete_draft_confirm' => 'Opravdu chcete odstranit tento koncept stránky?',
     'pages_editing_named' => 'Úpravy stránky :pageName',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Možnosti konceptu',
     'pages_edit_save_draft' => 'Uložit koncept',
     'pages_edit_draft' => 'Upravit koncept stránky',
-    'pages_editing_draft' => 'Úpravy konceptu',
+    'pages_editing_draft' => 'Úprava konceptu',
     'pages_editing_page' => 'Úpravy stránky',
     'pages_edit_draft_save_at' => 'Koncept uložen v ',
-    'pages_edit_delete_draft' => 'Smazat koncept',
+    'pages_edit_delete_draft' => 'Odstranit koncept',
     'pages_edit_discard_draft' => 'Zahodit koncept',
-    'pages_edit_set_changelog' => 'Zadat komentář ke změnám',
-    'pages_edit_enter_changelog_desc' => 'Zadejte stručný popis změn, které jste provedli.',
-    'pages_edit_enter_changelog' => 'Vložit komentáře ke změnám',
+    'pages_edit_set_changelog' => 'Nastavit protokol změn',
+    'pages_edit_enter_changelog_desc' => 'Zadejte stručný popis změn, které jste provedli',
+    'pages_edit_enter_changelog' => 'Zadejte protokol změn',
     'pages_save' => 'Uložit stránku',
     'pages_title' => 'Nadpis stránky',
     'pages_name' => 'Název stránky',
     'pages_md_editor' => 'Editor',
     'pages_md_preview' => 'Náhled',
     'pages_md_insert_image' => 'Vložit obrázek',
-    'pages_md_insert_link' => 'Vložit odkaz na prvek',
+    'pages_md_insert_link' => 'Vložit odkaz na entitu',
     'pages_md_insert_drawing' => 'Vložit kresbu',
-    'pages_not_in_chapter' => 'Stránka není součástí žádné kapitoly',
+    'pages_not_in_chapter' => 'Stránka není v kapitole',
     'pages_move' => 'Přesunout stránku',
     'pages_move_success' => 'Stránka přesunuta do ":parentName"',
     'pages_copy' => 'Kopírovat stránku',
     'pages_copy_desination' => 'Cíl kopírování',
     'pages_copy_success' => 'Stránka byla úspěšně zkopírována',
-    'pages_permissions' => 'Práva stránky',
-    'pages_permissions_success' => 'Práva stránky aktualizována',
+    'pages_permissions' => 'Oprávnění stránky',
+    'pages_permissions_success' => 'Oprávnění stránky aktualizována',
     'pages_revision' => 'Revize',
     'pages_revisions' => 'Revize stránky',
-    'pages_revisions_named' => 'Revize stránky :pageName',
-    'pages_revision_named' => 'Revize stránky :pageName',
+    'pages_revisions_named' => 'Revize stránky pro :pageName',
+    'pages_revision_named' => 'Revize stránky pro :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Vytvořeno uživatelem',
     'pages_revisions_date' => 'Datum revize',
-    'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
-    'pages_revisions_changelog' => 'Komentáře změn',
+    'pages_revisions_number' => 'Č.',
+    'pages_revisions_numbered' => 'Revize č. :id',
+    'pages_revisions_numbered_changes' => 'Změny revize č. :id',
+    'pages_revisions_changelog' => 'Protokol změn',
     'pages_revisions_changes' => 'Změny',
     'pages_revisions_current' => 'Aktuální verze',
     'pages_revisions_preview' => 'Náhled',
-    'pages_revisions_restore' => 'Renovovat',
+    'pages_revisions_restore' => 'Obnovit',
     'pages_revisions_none' => 'Tato stránka nemá žádné revize',
-    'pages_copy_link' => 'Zkopírovat odkaz',
+    'pages_copy_link' => 'Kopírovat odkaz',
     'pages_edit_content_link' => 'Upravit obsah',
     'pages_permissions_active' => 'Účinná práva stránky',
     'pages_initial_revision' => 'První vydání',
@@ -227,14 +231,14 @@ return [
     'pages_draft_edited_notification' => 'Tato stránka se od té doby změnila. Je doporučeno aktuální koncept zahodit.',
     'pages_draft_edit_active' => [
         'start_a' => 'Uživatelé začali upravovat tuto stránku (celkem :count)',
-        'start_b' => 'Uživatel :userName začal upravovat tuto stránku',
+        'start_b' => ':userName začal/a upravovat tuto stránku',
         'time_a' => 'od doby, kdy byla tato stránky naposledy aktualizována',
         'time_b' => 'v posledních minutách (:minCount min.)',
         'message' => ':start :time. Dávejte pozor abyste nepřepsali změny ostatním!',
     ],
     'pages_draft_discarded' => 'Koncept zahozen. Editor nyní obsahuje aktuální verzi stránky.',
     'pages_specific' => 'Konkrétní stránka',
-    'pages_is_template' => 'Page Template',
+    'pages_is_template' => 'Šablona stránky',
 
     // Editor Sidebar
     'page_tags' => 'Štítky stránky',
@@ -243,11 +247,11 @@ return [
     'shelf_tags' => 'Štítky knihovny',
     'tag' => 'Štítek',
     'tags' =>  'Štítky',
-    'tag_name' =>  'Tag Name',
-    'tag_value' => 'Hodnota Å títku (volitelné)',
+    'tag_name' =>  'Název štítku',
+    'tag_value' => 'Hodnota Å¡títku (volitelné)',
     'tags_explain' => "Přidejte si štítky pro lepší kategorizaci knih. \n Štítky mohou nést i hodnotu pro detailnější klasifikaci.",
     'tags_add' => 'Přidat další štítek',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Odstranit tento štítek',
     'attachments' => 'Přílohy',
     'attachments_explain' => 'Nahrajte soubory nebo připojte odkazy, které se zobrazí na stránce. Budou k nalezení v postranní liště.',
     'attachments_explain_instant_save' => 'Změny zde provedené se okamžitě ukládají.',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Nahrát soubor',
     'attachments_link' => 'Připojit odkaz',
     'attachments_set_link' => 'Nastavit odkaz',
-    'attachments_delete_confirm' => 'Stiskněte smazat znovu pro potvrzení smazání.',
+    'attachments_delete' => 'Jste si jisti, že chcete odstranit tuto přílohu?',
     'attachments_dropzone' => 'Přetáhněte sem soubory myší nebo sem kliknětě pro vybrání souboru.',
     'attachments_no_files' => 'Žádné soubory nebyli nahrány',
     'attachments_explain_link' => 'Můžete pouze připojit odkaz pokud nechcete nahrávat soubor přímo. Může to být odkaz na jinou stránku nebo na soubor v cloudu.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Odkaz na soubor',
     'attachments_link_url_hint' => 'URL stránky nebo souboru',
     'attach' => 'Připojit',
+    'attachments_insert_link' => 'Přidat odkaz na přílohu do stránky',
     'attachments_edit_file' => 'Upravit soubor',
     'attachments_edit_file_name' => 'Název souboru',
     'attachments_edit_drop_upload' => 'Přetáhněte sem soubor myší nebo klikněte pro nahrání nového a následné přepsání starého.',
@@ -273,20 +278,20 @@ return [
     'attachments_file_uploaded' => 'Soubor byl úspěšně nahrán',
     'attachments_file_updated' => 'Soubor byl úspěšně aktualizován',
     'attachments_link_attached' => 'Odkaz úspěšně přiložen ke stránce',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Šablony',
+    'templates_set_as_template' => 'Tato stránka je šablona',
+    'templates_explain_set_as_template' => 'Tuto stránku můžete nastavit jako šablonu, aby byl její obsah využit při vytváření dalších stránek. Ostatní uživatelé budou moci použít tuto šablonu, pokud mají oprávnění k zobrazení této stránky.',
+    'templates_replace_content' => 'Nahradit obsah stránky',
+    'templates_append_content' => 'Připojit za obsah stránky',
+    'templates_prepend_content' => 'Připojit před obsah stránky',
 
     // Profile View
     'profile_user_for_x' => 'Uživatelem již :time',
     'profile_created_content' => 'Vytvořený obsah',
-    'profile_not_created_pages' => ':userName nevytvoÅ\99il/a Å¾Ã¡dný obsah',
+    'profile_not_created_pages' => ':userName nevytvoÅ\99il/a Å¾Ã¡dné stránky',
     'profile_not_created_chapters' => ':userName nevytvořil/a žádné kapitoly',
     'profile_not_created_books' => ':userName nevytvořil/a žádné knihy',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_not_created_shelves' => ':userName nevytvořil/a žádné knihovny',
 
     // Comments
     'comment' => 'Komentář',
@@ -308,7 +313,7 @@ return [
 
     // Revision
     'revision_delete_confirm' => 'Opravdu chcete smazat tuto revizi?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_restore_confirm' => 'Jste si jisti, že chcete obnovit tuto revizi? Aktuální obsah stránky bude nahrazen.',
     'revision_delete_success' => 'Revize smazána',
     'revision_cannot_delete_latest' => 'Nelze smazat poslední revizi.'
 ];
\ No newline at end of file
index fa00491b02ddec6b07ba50d798ff13b51dc0cd81..43ba536f0496236363baa7f18e1ff4cf17b398e3 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'Emailová adresa již byla potvrzena. Zkuste se přihlásit.',
     'email_confirmation_invalid' => 'Tento potvrzovací odkaz již neplatí nebo už byl použit. Zkuste prosím registraci znovu.',
     'email_confirmation_expired' => 'Potvrzovací odkaz už neplatí, email s novým odkazem už byl poslán.',
+    'email_confirmation_awaiting' => 'E-mailová adresa pro používaný účet musí být potvrzena',
     'ldap_fail_anonymous' => 'Přístup k adresáři LDAP jako anonymní uživatel (anonymous bind) selhal',
     'ldap_fail_authed' => 'Přístup k adresáři LDAP pomocí zadaného jména (dn) a hesla selhal',
     'ldap_extension_not_installed' => 'Není nainstalováno rozšíření LDAP pro PHP',
     'ldap_cannot_connect' => 'Nelze se připojit k adresáři LDAP. Prvotní připojení selhalo.',
+    'saml_already_logged_in' => 'Již jste přihlášeni',
+    'saml_user_not_registered' => 'Uživatel :name není registrován a automatická registrace je zakázána',
+    'saml_no_email_address' => 'Nelze najít e-mailovou adresu pro tohoto uživatele v datech poskytnutých externím přihlašovacím systémem',
+    'saml_invalid_response_id' => 'Požadavek z externího ověřovacího systému nebyl rozpoznám procesem, který tato aplikace spustila. Tento problém může způsobit stisknutí tlačítka Zpět po přihlášení.',
+    'saml_fail_authed' => 'Přihlášení pomocí :system selhalo, systém neposkytl úspěšnou autorizaci',
     'social_no_action_defined' => 'Nebyla zvolena žádá akce',
     'social_login_bad_response' => "Nastala chyba během přihlašování přes :socialAccount \n:error",
     'social_account_in_use' => 'Tento účet na :socialAccount se již používá. Pokuste se s ním přihlásit volbou Přihlásit přes :socialAccount.',
@@ -27,7 +33,7 @@ return [
     'social_account_register_instructions' => 'Pokud ještě nemáte náš účet, můžete se zaregistrovat pomocí vašeho účtu na :socialAccount.',
     'social_driver_not_found' => 'Doplněk pro tohoto správce identity nebyl nalezen.',
     'social_driver_not_configured' => 'Nastavení vašeho účtu na :socialAccount není správné. :socialAccount musí mít vaše svolení pro naší aplikaci vás přihlásit.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Odkaz v pozvánce již bohužel vypršel. Namísto toho ale můžete zkusit resetovat heslo do Vašeho účtu.',
 
     // System
     'path_not_writable' => 'Nelze zapisovat na cestu k souboru :filePath. Zajistěte aby se dalo nahrávat na server.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'Nahrávání souboru trvalo příliš dlouho a tak bylo ukončeno.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Došlo ke zmatení stránky během nahrávání přílohy.',
     'attachment_not_found' => 'Příloha nenalezena',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Stránka nenalezena',
     'sorry_page_not_found' => 'Omlouváme se, ale stránka, kterou hledáte nebyla nalezena.',
+    'sorry_page_not_found_permission_warning' => 'Pokud očekáváte, že by stránka měla existovat, možná jen nemáte oprávnění pro její zobrazení.',
     'return_home' => 'Návrat domů',
     'error_occurred' => 'Nastala chyba',
     'app_down' => ':appName je momentálně vypnutá',
     'back_soon' => 'Brzy naběhne.',
 
+    // API errors
+    'api_no_authorization_found' => 'V požadavku nebyla nalezen žádný autorizační token',
+    'api_bad_authorization_format' => 'V požadavku byl nalezen autorizační token, ale jeho formát se zdá být chybný',
+    'api_user_token_not_found' => 'Pro zadaný autorizační token nebyl nalezen žádný odpovídající API token',
+    'api_incorrect_token_secret' => 'Poskytnutý Token Secret neodpovídá použitému API tokenu',
+    'api_user_no_api_permission' => 'Vlastník použitého API tokenu nemá oprávnění provádět API volání',
+    'api_user_token_expired' => 'Platnost autorizačního tokenu vypršela',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Při posílání testovacího e-mailu nastala chyba:',
+
 ];
index 6281ff05845c0668cbbd09b92900b2220c14f59e..eb0beabb9898819cc73954014fc5e27d3912f570 100644 (file)
@@ -6,7 +6,7 @@
  */
 return [
 
-    'previous' => '\'&laquo; P',
-    'next'     => 'Dal',
+    'previous' => '&laquo; Předchozí',
+    'next'     => 'Další &raquo;',
 
 ];
index 02001633012f6dbd71bb76da5e32e24e59d325cf..cc5669d4825d6e4892c20e9b149d2879c6bdcbdd 100644 (file)
@@ -6,10 +6,10 @@
  */
 return [
 
-    'password' => 'Heslo musí být alespoň 6 znaků dlouhé a shodovat se v obou polích.',
-    'user' => "Nemůžeme najít uživatele se zadanou emailovou adresou.",
-    'token' => 'Tento odkaz pro reset hesla je neplatný.',
-    'sent' => 'Poslali jsme vám odkaz pro reset hesla!',
-    'reset' => 'Vaše heslo bylo resetováno!',
+    'password' => 'Heslo musí mít alespoň osm znaků a musí odpovídat potvrzení.',
+    'user' => "Nemůžeme nalézt uživatele s touto e-mailovou adresou.",
+    'token' => 'Token pro obnovení hesla je neplatný pro tuto e-mailovou adresu.',
+    'sent' => 'Poslali jsme vám e-mail s odkazem pro obnovení hesla!',
+    'reset' => 'Vaše heslo bylo obnoveno!',
 
 ];
index bb6e6be09ac8e9a90f8d0cf3cf129949dbde1bce..80aa6dc385a445c9c0abf5b99bde240d2ee60e41 100644 (file)
@@ -9,60 +9,112 @@ return [
     // Common Messages
     'settings' => 'Nastavení',
     'settings_save' => 'Uložit nastavení',
-    'settings_save_success' => 'Nastavení bylo uloženo',
+    'settings_save_success' => 'Nastavení uloženo',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
+    'app_customization' => 'Přizpůsobení',
+    'app_features_security' => 'Funkce a zabezpečení',
     'app_name' => 'Název aplikace',
-    'app_name_desc' => 'Název se bude zobrazovat v záhlaví této aplikace a v odesílaných emailech.',
-    'app_name_header' => 'Zobrazovát název aplikace v záhlaví?',
-    'app_public_access' => 'Public Access',
-    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
-    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
-    'app_public_access_toggle' => 'Allow public access',
+    'app_name_desc' => 'Název se bude zobrazovat v záhlaví této aplikace a v e-mailech odesílaných systémem.',
+    'app_name_header' => 'Zobrazovat název aplikace v záhlaví',
+    'app_public_access' => 'Veřejný přístup',
+    'app_public_access_desc' => 'Zapnutím této volby umožníte nepřihlášeným návštěvníkům přístup k Vašemu obsahu v BookStack aplikaci.',
+    'app_public_access_desc_guest' => 'Přístup pro nepřihlášené návštěvníky je možné nastavit přes uživatele "Guest".',
+    'app_public_access_toggle' => 'Povolit veřejný přístup',
     'app_public_viewing' => 'Povolit prohlížení veřejností?',
-    'app_secure_images' => 'Nahrávat obrázky neveřejně a zabezpečeně?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
-    'app_secure_images_desc' => 'Z výkonnostních důvodů jsou všechny obrázky veřejné. Tato volba přidá do adresy obrázku náhodné číslo, aby nikdo neodhadnul adresu obrázku. Zajistěte ať adresáře nikomu nezobrazují seznam souborů.',
+    'app_secure_images' => 'Nahrávat obrázky neveřejně a zabezpečeně',
+    'app_secure_images_toggle' => 'Zapnout bezpečnější nahrávání obrázků',
+    'app_secure_images_desc' => 'Z výkonnostních důvodů jsou všechny obrázky veřejně dostupné. Tato volba přidá do adresy obrázku náhodný řetězec, aby nikdo neodhadnul adresu obrázku. Ujistěte se, že server nezobrazuje v adresáři seznam souborů, což by přístup k přístup opět otevřelo.',
     'app_editor' => 'Editor stránek',
     'app_editor_desc' => 'Zvolte který editor budou užívat všichni uživatelé k úpravě stránek.',
-    'app_custom_html' => 'Vlastní HTML kód pro sekci hlavičky (<head>).',
+    'app_custom_html' => 'Vlastní obsah hlavičky HTML',
     'app_custom_html_desc' => 'Cokoliv sem napíšete bude přidáno na konec sekce <head> v každém místě této aplikace. To se hodí pro přidávání nebo změnu CSS stylů nebo přidání kódu pro analýzu používání (např.: google analytics.).',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'Na této stránce nastavení je zakázán vlastní obsah HTML hlavičky, aby bylo zajištěno, že bude možné vrátit případnou problematickou úpravu.',
     'app_logo' => 'Logo aplikace',
-    'app_logo_desc' => 'Obrázek by měl mít 43 pixelů na výšku. <br>Větší obrázky zmenšíme na tuto velikost.',
+    'app_logo_desc' => 'Tento obrázek by měl mít výšku 43px. <br>Větší obrázky zmenšíme na tuto velikost.',
     'app_primary_color' => 'Hlavní barva aplikace',
-    'app_primary_color_desc' => 'Zápis by měl být hexa (#aabbcc). <br>Pro základní barvu nechte pole prázdné.',
+    'app_primary_color_desc' => 'Nastaví hlavní barvu aplikace včetně panelů, tlačítek a odkazů.',
     'app_homepage' => 'Úvodní stránka aplikace',
-    'app_homepage_desc' => 'Zvolte pohled který se objeví jako úvodní stránka po přihlášení. Pokud zvolíte stránku, její specifická oprávnění budou ignorována (výjimka z výjimky 😜).',
+    'app_homepage_desc' => 'Vyberte si zobrazení, které se použije jako úvodní stránka. U zvolených stránek bude ignorováno jejich oprávnění.',
     'app_homepage_select' => 'Zvolte stránku',
-    'app_disable_comments' => 'Zakázání komentářů',
-    'app_disable_comments_toggle' => 'Disable comments',
-    'app_disable_comments_desc' => 'Zakáže komentáře napříč všemi stránkami. Existující komentáře se přestanou zobrazovat.',
+    'app_disable_comments' => 'Vypnutí komentářů',
+    'app_disable_comments_toggle' => 'Vypnout komentáře',
+    'app_disable_comments_desc' => 'Vypne komentáře napříč všemi stránkami. <br> Existující komentáře se přestanou zobrazovat.',
+
+    // Color settings
+    'content_colors' => 'Barvy obsahu',
+    'content_colors_desc' => 'Nastaví barvy pro všechny prvky v organizační struktuře stránky. Pro lepší čitelnost doporučujeme zvolit barvy s podobným jasem, jakou mají výchozí barvy.',
+    'bookshelf_color' => 'Barva knihovny',
+    'book_color' => 'Barva knihy',
+    'chapter_color' => 'Barva kapitoly',
+    'page_color' => 'Barva stránky',
+    'page_draft_color' => 'Barva návrhu stránky',
 
     // Registration Settings
     'reg_settings' => 'Nastavení registrace',
-    'reg_enable' => 'Enable Registration',
-    'reg_enable_toggle' => 'Enable registration',
-    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_enable' => 'Povolení registrace',
+    'reg_enable_toggle' => 'Povolit registrace',
+    'reg_enable_desc' => 'Pokud jsou povoleny registrace, bude se uživatel moci sám registrovat jako uživatel aplikace. Po registraci dostane jednu výchozí uživatelskou roli.',
     'reg_default_role' => 'Role přiřazená po registraci',
-    'reg_email_confirmation' => 'Email Confirmation',
-    'reg_email_confirmation_toggle' => 'Require email confirmation',
-    'reg_confirm_email_desc' => 'Pokud zapnete omezení emailové domény, tak bude ověřování emailové adresy vyžadováno vždy.',
+    'reg_enable_external_warning' => 'Pokud je povolené externí ověřování přes LDAP nebo SAML, je výše uvedená možnost ignorována. Uživatelský účet budou automaticky vytvořen i neexistujícímu uživateli, jakmile se úspěšně přihlásí přes použitý externí přihlašovací systém.',
+    'reg_email_confirmation' => 'Ověření e-mailu',
+    'reg_email_confirmation_toggle' => 'Vyžadovat ověření e-mailu',
+    'reg_confirm_email_desc' => 'Pokud je zapnuté Omezení registrace podle domény, bude e-mail ověřován vždy a tato volba bude ignorována.',
     'reg_confirm_restrict_domain' => 'Omezit registraci podle domény',
-    'reg_confirm_restrict_domain_desc' => 'Zadejte emailové domény, kterým bude povolena registrace uživatelů. Oddělujete čárkou. Uživatelům bude odeslán email s odkazem pro potvrzení vlastnictví emailové adresy. Bez potvrzení nebudou moci aplikaci používat. <br> Pozn.: Uživatelé si mohou emailovou adresu změnit po úspěšné registraci.',
-    'reg_confirm_restrict_domain_placeholder' => 'Žádná omezení nebyla nastvena',
+    'reg_confirm_restrict_domain_desc' => 'Zadejte seznam e-mailových domén oddělených čárkami, na které chcete registraci omezit. Registrujícímu se uživateli bude zaslán e-mail, aby ověřil svoji e-mailovou adresu před tím, než mu bude přístup do aplikace povolen. <br> Upozorňujeme, že po úspěšné registraci může uživatel svoji e-mailovou adresu změnit.',
+    'reg_confirm_restrict_domain_placeholder' => 'Žádná omezení nebyla nastavena',
 
     // Maintenance settings
     'maint' => 'Údržba',
     'maint_image_cleanup' => 'Pročistění obrázků',
     'maint_image_cleanup_desc' => "Prohledá stránky a jejich revize, aby zjistil, které obrázky a kresby jsou momentálně používány a které jsou zbytečné. Zajistěte plnou zálohu databáze a obrázků než se do toho pustíte.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignorovat obrázky v revizích',
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Spustit pročištění',
     'maint_image_cleanup_warning' => 'Nalezeno :count potenciálně nepoužitých obrázků. Jste si jistí, že je chcete smazat?',
     'maint_image_cleanup_success' => 'Potenciálně nepoužité obrázky byly smazány. Celkem :count.',
     'maint_image_cleanup_nothing_found' => 'Žádné potenciálně nepoužité obrázky nebyly nalezeny. Nic nebylo smazáno.',
+    'maint_send_test_email' => 'Odeslat zkušební e-mail',
+    'maint_send_test_email_desc' => 'Toto pošle zkušební e-mail na vaši e-mailovou adresu uvedenou ve vašem profilu.',
+    'maint_send_test_email_run' => 'Odeslat zkušební e-mail',
+    'maint_send_test_email_success' => 'E-mail odeslán na :address',
+    'maint_send_test_email_mail_subject' => 'Testovací e-mail',
+    'maint_send_test_email_mail_greeting' => 'Zdá se, že posílání e-mailů funguje!',
+    'maint_send_test_email_mail_text' => 'Gratulujeme! Protože jste dostali tento e-mail, zdá se, že nastavení e-mailů je v pořádku.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Protokol auditu',
+    'audit_desc' => 'Tento protokol auditu zobrazuje seznam činností zaznamenaných v systému. Tento seznam není filtrován na rozdíl od podobných seznamů aktivit v systému, kde jsou použity filtry podle oprávnění.',
+    'audit_event_filter' => 'Filtr událostí',
+    'audit_event_filter_no_filter' => 'Bez filtru',
+    'audit_deleted_item' => 'Odstraněná položka',
+    'audit_deleted_item_name' => 'Jméno: :name',
+    'audit_table_user' => 'Uživatel',
+    'audit_table_event' => 'Událost',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Datum aktivity',
+    'audit_date_from' => 'Časový rozsah od',
+    'audit_date_to' => 'Časový rozsah do',
 
     // Role Settings
     'roles' => 'Role',
@@ -81,59 +133,91 @@ return [
     'role_desc' => 'Stručný popis role',
     'role_external_auth_id' => 'Přihlašovací identifikátory třetích stran',
     'role_system' => 'Systémová oprávnění',
-    'role_manage_users' => 'Správa úživatelů',
+    'role_manage_users' => 'Správa uživatelů',
     'role_manage_roles' => 'Správa rolí a jejich práv',
     'role_manage_entity_permissions' => 'Správa práv všech knih, kapitol a stránek',
     'role_manage_own_entity_permissions' => 'Správa práv vlastních knih, kapitol a stránek',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => 'Správa šablon stránek',
+    'role_access_api' => 'Přístup k systémovému API',
     'role_manage_settings' => 'Správa nastavení aplikace',
-    'role_asset' => 'Práva děl',
-    'role_asset_desc' => 'Tato práva řídí přístup k dílům v rámci systému. Specifická práva na knihách, kapitolách a stránkách překryjí tato nastavení.',
+    'role_asset' => 'Obsahová oprávnění',
+    'roles_system_warning' => 'Berte na vědomí, že přístup k některému ze tří výše uvedených oprávnění může uživateli umožnit změnit svá vlastní oprávnění nebo oprávnění ostatních uživatelů v systému. Přiřazujte role s těmito oprávněními pouze důvěryhodným uživatelům.',
+    'role_asset_desc' => 'Tato práva řídí přístup k obsahu napříč systémem. Specifická práva na knihách, kapitolách a stránkách převáží tato nastavení.',
     'role_asset_admins' => 'Administrátoři automaticky dostávají přístup k veškerému obsahu, ale tyto volby mohou ukázat nebo skrýt volby v uživatelském rozhraní.',
     'role_all' => 'Vše',
     'role_own' => 'Vlastní',
-    'role_controlled_by_asset' => 'Řídí se dílem do kterého jsou nahrávány',
-    'role_save' => 'Uloži roli',
+    'role_controlled_by_asset' => 'Řídí se obsahem, do kterého jsou nahrávány',
+    'role_save' => 'Uložit roli',
     'role_update_success' => 'Role úspěšně aktualizována',
     'role_users' => 'Uživatelé mající tuto roli',
-    'role_users_none' => 'Žádný uživatel nemá tuto roli.',
+    'role_users_none' => 'Žádný uživatel nemá tuto roli',
 
     // Users
     'users' => 'Uživatelé',
     'user_profile' => 'Profil uživatele',
     'users_add_new' => 'Přidat nového uživatele',
     'users_search' => 'Vyhledávání uživatelů',
-    'users_details' => 'User Details',
-    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
-    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_latest_activity' => 'Latest Activity',
+    'users_details' => 'Údaje o uživateli',
+    'users_details_desc' => 'Nastavte zobrazované jméno a e-mailovou adresu pro tohoto uživatele. E-mailová adresa bude použita pro přihlášení do aplikace.',
+    'users_details_desc_no_email' => 'Nastavte zobrazované jméno pro tohoto uživatele, aby jej ostatní uživatele poznali.',
     'users_role' => 'Uživatelské role',
-    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
-    'users_password' => 'User Password',
-    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
-    'users_external_auth_id' => 'Přihlašovací identifikátory třetích stran',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
-    'users_password_warning' => 'Vyplňujte pouze v případě, že chcete heslo změnit:',
-    'users_system_public' => 'Symbolizuje libovolného veřejného návštěvníka, který navštívil vaší aplikaci. Nelze ho použít k přihlášení ale je přiřazen automaticky veřejnosti.',
+    'users_role_desc' => 'Zvolte role, do kterých chcete uživatele zařadit. Pokud bude uživatel zařazen do více rolí, oprávnění z těchto rolí se sloučí a uživateli bude dovoleno vše, k čemu mají jednotlivé role oprávnění.',
+    'users_password' => 'Heslo uživatele',
+    'users_password_desc' => 'Zadejte heslo pro přihlášení do aplikace. Heslo musí být nejméně 6 znaků dlouhé.',
+    'users_send_invite_text' => 'Uživateli můžete poslat pozvánku e-mailem, která umožní uživateli, aby si zvolil sám svoje heslo do aplikace a nebo můžete zadat heslo sami.',
+    'users_send_invite_option' => 'Poslat uživateli pozvánku e-mailem',
+    'users_external_auth_id' => 'Přihlašovací identifikátor třetích stran',
+    'users_external_auth_id_desc' => 'ID použité pro rozpoznání tohoto uživatele když komunikuje s externím přihlašovacím systémem.',
+    'users_password_warning' => 'Vyplňujte pouze v případě, že chcete heslo změnit.',
+    'users_system_public' => 'Symbolizuje každého nepřihlášeného návštěvníka, který navštívil aplikaci. Nelze ho použít k přihlášení ale je přiřazen automaticky nepřihlášeným.',
     'users_delete' => 'Smazat uživatele',
-    'users_delete_named' => 'Smazat uživatele :userName',
-    'users_delete_warning' => 'Uživatel \':userName\' bude úplně smazán ze systému.',
+    'users_delete_named' => 'Odstranit uživatele :userName',
+    'users_delete_warning' => 'Uživatel \':userName\' bude zcela smazán ze systému.',
     'users_delete_confirm' => 'Opravdu chcete tohoto uživatele smazat?',
-    'users_delete_success' => 'Uživatel byl úspěšně smazán',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Upravit uživatele',
     'users_edit_profile' => 'Upravit profil',
     'users_edit_success' => 'Uživatel byl úspěšně aktualizován',
-    'users_avatar' => 'Uživatelský obrázek',
-    'users_avatar_desc' => 'Obrázek by měl být čtverec 256 pixelů široký. Bude oříznut do kruhu.',
-    'users_preferred_language' => 'Upřednostňovaný jazyk',
-    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
-    'users_social_accounts' => 'Přidružené účty ze sociálních sítí',
-    'users_social_accounts_info' => 'Zde můžete přidat vaše účty ze sociálních sítí pro pohodlnější přihlašování. Zrušení přidružení zde neznamená, že tato aplikace pozbude práva číst detaily z vašeho účtu. Zakázat této aplikaci přístup k detailům vašeho účtu musíte přímo ve vašem profilu na dané sociální síti.',
-    'users_social_connect' => 'Přidružit účet',
-    'users_social_disconnect' => 'Zrušit přidružení',
-    'users_social_connected' => 'Účet :socialAccount byl úspěšně přidružen k vašemu profilu.',
-    'users_social_disconnected' => 'Přidružení účtu :socialAccount k vašemu profilu bylo úspěšně zrušeno.',
+    'users_avatar' => 'Obrázek uživatele',
+    'users_avatar_desc' => 'Vyberte obrázek, který bude reprezentovat tohoto uživatele. Měl by být přibližně 256px velký ve tvaru čtverce.',
+    'users_preferred_language' => 'Preferovaný jazyk',
+    'users_preferred_language_desc' => 'Tato volba ovlivní pouze jazyk používaný v uživatelském rozhraní aplikace. Volba nemá vliv na žádný uživateli vytvářený obsah.',
+    'users_social_accounts' => 'Sociální účty',
+    'users_social_accounts_info' => 'Zde můžete přidat vaše účty ze sociálních sítí pro pohodlnější přihlašování. Odpojení účtů neznamená, že tato aplikace ztratí práva číst detaily z vašeho účtu. Zakázat této aplikaci přístup k detailům vašeho účtu musíte přímo ve svém profilu na dané sociální síti.',
+    'users_social_connect' => 'Připojit účet',
+    'users_social_disconnect' => 'Odpojit účet',
+    'users_social_connected' => 'Účet :socialAccount byl úspěšně připojen k vašemu profilu.',
+    'users_social_disconnected' => 'Účet :socialAccount byl úspěšně odpojen od vašeho profilu.',
+    'users_api_tokens' => 'API Tokeny',
+    'users_api_tokens_none' => 'Tento uživatel nemá vytvořené žádné API Tokeny',
+    'users_api_tokens_create' => 'Vytvořit Token',
+    'users_api_tokens_expires' => 'Vyprší',
+    'users_api_tokens_docs' => 'API Dokumentace',
+
+    // API Tokens
+    'user_api_token_create' => 'Vytvořit API Klíč',
+    'user_api_token_name' => 'Název',
+    'user_api_token_name_desc' => 'Zadejte srozumitelný název tokenu, který vám později může pomoci připomenout účel, za jakým jste token vytvářeli.',
+    'user_api_token_expiry' => 'Platný do',
+    'user_api_token_expiry_desc' => 'Zadejte datum, kdy platnost tokenu vyprší. Po tomto datu nebudou požadavky, které používají tento token, fungovat. Pokud ponecháte pole prázdné, bude tokenu nastavena platnost na dalších 100 let.',
+    'user_api_token_create_secret_message' => 'Ihned po vytvoření tokenu Vám bude vygenerován a zobrazen "Token ID" a "Token Secret". Upozorňujeme, že "Token Secret" bude možné zobrazit pouze jednou, ujistěte se, že si jej poznamenáte a uložíte na bezpečné místo před tím, než budete pokračovat dále.',
+    'user_api_token_create_success' => 'API klíč úspěšně vytvořen',
+    'user_api_token_update_success' => 'API klíč úspěšně aktualizován',
+    'user_api_token' => 'API Klíč',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'Toto je neupravitelný systémový identifikátor generovaný pro tento klíč, který musí být uveden v API requestu.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'Toto je systémem generovaný "secret" pro tento klíč, který musí být v API requestech. Toto bude zobrazeno pouze jednou, takže si uložte tuto hodnotu na bezpečné místo.',
+    'user_api_token_created' => 'Token vytvořen :timeAgo',
+    'user_api_token_updated' => 'Token aktualizován :timeAgo',
+    'user_api_token_delete' => 'Odstranit Token',
+    'user_api_token_delete_warning' => 'Tímto plně smažete tento API klíč s názvem \':tokenName\' ze systému.',
+    'user_api_token_delete_confirm' => 'Opravdu chcete odstranit tento API klíč?',
+    'user_api_token_delete_success' => 'API Klíč úspěšně odstraněn',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 13a8f790fc058856cbad7b2e1cb1c585815675d5..a843973497637e5b2905c98021e0b115fa4e17c9 100644 (file)
@@ -30,7 +30,7 @@ return [
     'digits'               => ':attribute musí být :digits pozic dlouhé.',
     'digits_between'       => ':attribute musí být dlouhé nejméně :min a nejvíce :max pozic.',
     'email'                => ':attribute není platný formát.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ':attribute musí končit jednou z následujících hodnot: :values',
     'filled'               => ':attribute musí být vyplněno.',
     'gt'                   => [
         'numeric' => ':attribute musí být větší než :value.',
@@ -46,7 +46,7 @@ return [
     ],
     'exists'               => 'Zvolená hodnota pro :attribute není platná.',
     'image'                => ':attribute musí být obrázek.',
-    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'image_extension'      => ':attribute musí mít platné a podporované rozšíření obrázku.',
     'in'                   => 'Zvolená hodnota pro :attribute je neplatná.',
     'integer'              => ':attribute musí být celé číslo.',
     'ip'                   => ':attribute musí být platnou IP adresou.',
@@ -78,7 +78,7 @@ return [
         'string'  => ':attribute musí být delší než :min znaků.',
         'array'   => ':attribute musí obsahovat více než :min prvků.',
     ],
-    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'no_double_extension'  => ':attribute musí obsahovat pouze jednu příponu souboru.',
     'not_in'               => 'Zvolená hodnota pro :attribute je neplatná.',
     'not_regex'            => ':attribute musí být regulární výraz.',
     'numeric'              => ':attribute musí být číslo.',
@@ -90,6 +90,7 @@ return [
     'required_without'     => ':attribute musí být vyplněno pokud :values není vyplněno.',
     'required_without_all' => ':attribute musí být vyplněno pokud není žádné z :values zvoleno.',
     'same'                 => ':attribute a :other se musí shodovat.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
         'numeric' => ':attribute musí být přesně :size.',
         'file'    => ':attribute musí mít přesně :size Kilobytů.',
@@ -105,7 +106,7 @@ return [
     // Custom validation lines
     'custom' => [
         'password-confirm' => [
-            'required_with' => 'Password confirmation required',
+            'required_with' => 'Je nutné potvrdit heslo',
         ],
     ],
 
diff --git a/resources/lang/da/activities.php b/resources/lang/da/activities.php
new file mode 100644 (file)
index 0000000..264deec
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'oprettede side',
+    'page_create_notification'    => 'Siden blev oprettet',
+    'page_update'                 => 'opdaterede side',
+    'page_update_notification'    => 'Siden blev opdateret',
+    'page_delete'                 => 'slettede side',
+    'page_delete_notification'    => 'Siden blev slettet',
+    'page_restore'                => 'gendannede side',
+    'page_restore_notification'   => 'Siden blev gendannet',
+    'page_move'                   => 'flyttede side',
+
+    // Chapters
+    'chapter_create'              => 'oprettede kapitel',
+    'chapter_create_notification' => 'Kapitel blev oprettet',
+    'chapter_update'              => 'opdaterede kapitel',
+    'chapter_update_notification' => 'Kapitel blev opdateret',
+    'chapter_delete'              => 'slettede kapitel',
+    'chapter_delete_notification' => 'Kapitel blev slettet',
+    'chapter_move'                => 'flyttede kapitel',
+
+    // Books
+    'book_create'                 => 'oprettede bog',
+    'book_create_notification'    => 'Bogen blev oprettet',
+    'book_update'                 => 'opdaterede bog',
+    'book_update_notification'    => 'Bogen blev opdateret',
+    'book_delete'                 => 'slettede bog',
+    'book_delete_notification'    => 'Bogen blev slettet',
+    'book_sort'                   => 'sorterede bogen',
+    'book_sort_notification'      => 'Bogen blev re-sorteret',
+
+    // Bookshelves
+    'bookshelf_create'            => 'oprettede bogreol',
+    'bookshelf_create_notification'    => 'Bogreolen blev oprettet',
+    'bookshelf_update'                 => 'opdaterede bogreolen',
+    'bookshelf_update_notification'    => 'Bogreolen blev opdateret',
+    'bookshelf_delete'                 => 'slettede bogreol',
+    'bookshelf_delete_notification'    => 'Bogreolen blev opdateret',
+
+    // Other
+    'commented_on'                => 'kommenterede til',
+    'permissions_update'          => 'updated permissions',
+];
diff --git a/resources/lang/da/auth.php b/resources/lang/da/auth.php
new file mode 100644 (file)
index 0000000..f060b24
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'Det indtastede stemmer ikke overens med vores registrering.',
+    'throttle' => 'For mange mislykkede loginforsøg. Prøv igen om :seconds seconds.',
+
+    // Login & Register
+    'sign_up' => 'Registrér',
+    'log_in' => 'Log ind',
+    'log_in_with' => 'Log ind med :socialDriver',
+    'sign_up_with' => 'Registrér med :socialDriver',
+    'logout' => 'Log ud',
+
+    'name' => 'Navn',
+    'username' => 'Brugernavn',
+    'email' => 'E-mail',
+    'password' => 'Adgangskode',
+    'password_confirm' => 'Bekræft adgangskode',
+    'password_hint' => 'Skal være på mindst 8 karakterer',
+    'forgot_password' => 'Glemt Adgangskode?',
+    'remember_me' => 'Husk Mig',
+    'ldap_email_hint' => 'Angiv venligst din kontos e-mail.',
+    'create_account' => 'Opret Konto',
+    'already_have_account' => 'Har du allerede en konto?',
+    'dont_have_account' => 'Har du ikke en konto?',
+    'social_login' => 'Social Log ind',
+    'social_registration' => 'Social Registrering',
+    'social_registration_text' => 'Registrér og log ind med anden service.',
+
+    'register_thanks' => 'Tak for registreringen!',
+    'register_confirm' => 'Check venligst din e-mail og klik deri på bekræftelses knappen for at tilgå :appName.',
+    'registrations_disabled' => 'Registrering er i øjeblikket deaktiveret',
+    'registration_email_domain_invalid' => 'E-Mail domænet har ikke adgang til denne applikation',
+    'register_success' => 'Tak for din registrering. Du er nu registeret og logget ind.',
+
+
+    // Password Reset
+    'reset_password' => 'Nulstil adgangskode',
+    'reset_password_send_instructions' => 'Indtast din E-Mail herunder og du vil blive sendt en E-Mail med et link til at nulstille din adgangskode.',
+    'reset_password_send_button' => 'Send link til nulstilling',
+    'reset_password_sent' => 'Et link til nulstilling af adgangskode sendes til :email, hvis den e-mail-adresse findes i systemet.',
+    'reset_password_success' => 'Din adgangskode er blevet nulstillet.',
+    'email_reset_subject' => 'Nulstil din :appName adgangskode',
+    'email_reset_text' => 'Du modtager denne E-Mail fordi vi har modtaget en anmodning om at nulstille din adgangskode.',
+    'email_reset_not_requested' => 'Hvis du ikke har anmodet om at få din adgangskode nulstillet, behøver du ikke at foretage dig noget.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Bekræft din E-Mail på :appName',
+    'email_confirm_greeting' => 'Tak for at oprette dig på :appName!',
+    'email_confirm_text' => 'Bekræft venligst din E-Mail adresse ved at klikke på linket nedenfor:',
+    'email_confirm_action' => 'Bekræft E-Mail',
+    'email_confirm_send_error' => 'E-Mail-bekræftelse kræves, men systemet kunne ikke sende E-Mailen. Kontakt administratoren for at sikre, at E-Mail er konfigureret korrekt.',
+    'email_confirm_success' => 'Din E-Mail er blevet bekræftet!',
+    'email_confirm_resent' => 'Bekræftelsesmail sendt, tjek venligst din indboks.',
+
+    'email_not_confirmed' => 'E-Mail adresse ikke bekræftet',
+    'email_not_confirmed_text' => 'Din E-Mail adresse er endnu ikke blevet bekræftet.',
+    'email_not_confirmed_click_link' => 'Klik venligst på linket i E-Mailen der blev sendt kort efter du registrerede dig.',
+    'email_not_confirmed_resend' => 'Hvis du ikke kan finde E-Mailen, kan du du få gensendt bekræftelsesemailen ved at trykke herunder.',
+    'email_not_confirmed_resend_button' => 'Gensend bekræftelsesemail',
+
+    // User Invite
+    'user_invite_email_subject' => 'Du er blevet inviteret til :appName!',
+    'user_invite_email_greeting' => 'En konto er blevet oprettet til dig på :appName.',
+    'user_invite_email_text' => 'Klik på knappen nedenunderm for at sætte en adgangskode og opnå adgang:',
+    'user_invite_email_action' => 'Set adgangskode',
+    'user_invite_page_welcome' => 'Velkommen til :appName!',
+    'user_invite_page_text' => 'For at færdiggøre din konto og få adgang skal du indstille en adgangskode, der bruges til at logge ind på :appName ved fremtidige besøg.',
+    'user_invite_page_confirm_button' => 'Bekræft adgangskode',
+    'user_invite_success' => 'Adgangskode indstillet, du har nu adgang til :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/da/common.php b/resources/lang/da/common.php
new file mode 100644 (file)
index 0000000..dbd76d0
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Annuller',
+    'confirm' => 'Bekræft',
+    'back' => 'Tilbage',
+    'save' => 'Gem',
+    'continue' => 'Fortsæt',
+    'select' => 'Vælg',
+    'toggle_all' => 'Vælg/Fravælg alle',
+    'more' => 'Mere',
+
+    // Form Labels
+    'name' => 'Navn',
+    'description' => 'Beskrivelse',
+    'role' => 'Rolle',
+    'cover_image' => 'Coverbillede',
+    'cover_image_description' => 'Dette billede skal være omtrent 440x250px.',
+    
+    // Actions
+    'actions' => 'Handlinger',
+    'view' => 'Vis',
+    'view_all' => 'Vis alle',
+    'create' => 'Opret',
+    'update' => 'Opdater',
+    'edit' => 'Rediger',
+    'sort' => 'Sorter',
+    'move' => 'Flyt',
+    'copy' => 'Kopier',
+    'reply' => 'Besvar',
+    'delete' => 'Slet',
+    'delete_confirm' => 'Confirm Deletion',
+    'search' => 'Søg',
+    'search_clear' => 'Ryd søgning',
+    'reset' => 'Nulstil',
+    'remove' => 'Fjern',
+    'add' => 'Tilføj',
+    'fullscreen' => 'Fuld skærm',
+
+    // Sort Options
+    'sort_options' => 'Sorteringsindstillinger',
+    'sort_direction_toggle' => 'Sorteringsretning',
+    'sort_ascending' => 'Sorter stigende',
+    'sort_descending' => 'Sorter faldende',
+    'sort_name' => 'Navn',
+    'sort_created_at' => 'Oprettelsesdato',
+    'sort_updated_at' => 'Opdateringsdato',
+
+    // Misc
+    'deleted_user' => 'Slettet bruger',
+    'no_activity' => 'Ingen aktivitet at vise',
+    'no_items' => 'Intet indhold tilgængeligt',
+    'back_to_top' => 'Tilbage til toppen',
+    'toggle_details' => 'Vis/skjul detaljer',
+    'toggle_thumbnails' => 'Vis/skjul miniaturer',
+    'details' => 'Detaljer',
+    'grid_view' => 'Gittervisning',
+    'list_view' => 'Listevisning',
+    'default' => 'Standard',
+    'breadcrumb' => 'Brødkrumme',
+
+    // Header
+    'profile_menu' => 'Profilmenu',
+    'view_profile' => 'Vis profil',
+    'edit_profile' => 'Redigér Profil',
+    'dark_mode' => 'Mørk tilstand',
+    'light_mode' => 'Lys tilstand',
+
+    // Layout tabs
+    'tab_info' => 'Info',
+    'tab_content' => 'Indhold',
+
+    // Email Content
+    'email_action_help' => 'Hvis du har problemer med at trykke på ":actionText" knappen, prøv at kopiere og indsætte linket herunder ind i din webbrowser:',
+    'email_rights' => 'Alle rettigheder forbeholdes',
+];
diff --git a/resources/lang/da/components.php b/resources/lang/da/components.php
new file mode 100644 (file)
index 0000000..28e1404
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Billedselektion',
+    'image_all' => 'Alt',
+    'image_all_title' => 'Se alle billeder',
+    'image_book_title' => 'Vis billeder uploadet til denne bog',
+    'image_page_title' => 'Vis billeder uploadet til denne side',
+    'image_search_hint' => 'Søg efter billednavn',
+    'image_uploaded' => 'Uploadet :uploadedDate',
+    'image_load_more' => 'Indlæse mere',
+    'image_image_name' => 'Billednavn',
+    'image_delete_used' => 'Dette billede er brugt på siderne nedenfor.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_select_image' => 'Vælg billede',
+    'image_dropzone' => 'Træk-og-slip billede eller klik her for at uploade',
+    'images_deleted' => 'Billede slettet',
+    'image_preview' => 'Billedeksempel',
+    'image_upload_success' => 'Foto uploadet',
+    'image_update_success' => 'Billeddetaljer succesfuldt opdateret',
+    'image_delete_success' => 'Billede slettet',
+    'image_upload_remove' => 'Fjern',
+
+    // Code Editor
+    'code_editor' => 'Rediger kode',
+    'code_language' => 'Kodesprog',
+    'code_content' => 'Kodeindhold',
+    'code_session_history' => 'Session History',
+    'code_save' => 'Gem kode',
+];
diff --git a/resources/lang/da/entities.php b/resources/lang/da/entities.php
new file mode 100644 (file)
index 0000000..fee4d41
--- /dev/null
@@ -0,0 +1,319 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Nyligt oprettet',
+    'recently_created_pages' => 'Nyligt oprettede sider',
+    'recently_updated_pages' => 'Nyligt opdaterede sider',
+    'recently_created_chapters' => 'Nyligt oprettede kapitler',
+    'recently_created_books' => 'Nyligt oprettede bøger',
+    'recently_created_shelves' => 'Nyligt oprettede reoler',
+    'recently_update' => 'Opdateret for nyligt',
+    'recently_viewed' => 'Senest viste',
+    'recent_activity' => 'Seneste aktivitet',
+    'create_now' => 'Opret en nu',
+    'revisions' => 'Revisioner',
+    'meta_revision' => 'Revision #:revisionCount',
+    'meta_created' => 'Oprettet :timeLength',
+    'meta_created_name' => 'Oprettet :timeLength af :user',
+    'meta_updated' => 'Opdateret :timeLength',
+    'meta_updated_name' => 'Opdateret :timeLength af :user',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'Vælg emne',
+    'images' => 'Billeder',
+    'my_recent_drafts' => 'Mine seneste kladder',
+    'my_recently_viewed' => 'Mine senest viste',
+    'no_pages_viewed' => 'Du har ikke besøgt nogle sider',
+    'no_pages_recently_created' => 'Ingen sider er blevet oprettet for nyligt',
+    'no_pages_recently_updated' => 'Ingen sider er blevet opdateret for nyligt',
+    'export' => 'Exporter',
+    'export_html' => 'Indeholdt webfil',
+    'export_pdf' => 'PDF-fil',
+    'export_text' => 'Almindelig tekstfil',
+
+    // Permissions and restrictions
+    'permissions' => 'Rettigheder',
+    'permissions_intro' => 'Når de er aktiveret, vil disse tilladelser have prioritet over alle indstillede rolletilladelser.',
+    'permissions_enable' => 'Aktivér tilpassede tilladelser',
+    'permissions_save' => 'Gem tilladelser',
+    'permissions_owner' => 'Owner',
+
+    // Search
+    'search_results' => 'Søgeresultater',
+    'search_total_results_found' => ':count resultat fundet|:count resultater fundet',
+    'search_clear' => 'Ryd søgning',
+    'search_no_pages' => 'Ingen sider matchede søgning',
+    'search_for_term' => 'Søgning for :term',
+    'search_more' => 'Flere resultater',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
+    'search_content_type' => 'Indholdstype',
+    'search_exact_matches' => 'Nøjagtige matches',
+    'search_tags' => 'Tagsøgninger',
+    'search_options' => 'Indstillinger',
+    'search_viewed_by_me' => 'Set af mig',
+    'search_not_viewed_by_me' => 'Ikke set af mig',
+    'search_permissions_set' => 'Rettigheders sæt',
+    'search_created_by_me' => 'Oprettet af mig',
+    'search_updated_by_me' => 'Opdateret af mig',
+    'search_date_options' => 'Datoindstillinger',
+    'search_updated_before' => 'Opdateret før',
+    'search_updated_after' => 'Opdateret efter',
+    'search_created_before' => 'Oprettet før',
+    'search_created_after' => 'Oprettet efter',
+    'search_set_date' => 'Sæt dato',
+    'search_update' => 'Opdatér søgning',
+
+    // Shelves
+    'shelf' => 'Reol',
+    'shelves' => 'Reoler',
+    'x_shelves' => ':count reol|:count reoler',
+    'shelves_long' => 'Bogreoler',
+    'shelves_empty' => 'Ingen reoler er blevet oprettet',
+    'shelves_create' => 'Opret ny reol',
+    'shelves_popular' => 'Populære reoler',
+    'shelves_new' => 'Nye reoler',
+    'shelves_new_action' => 'Ny reol',
+    'shelves_popular_empty' => 'De mest populære reoler vil blive vist her.',
+    'shelves_new_empty' => 'De nyeste reoler vil blive vist her.',
+    'shelves_save' => 'Gem reol',
+    'shelves_books' => 'Bøger på denne reol',
+    'shelves_add_books' => 'Tilføj bøger til denne reol',
+    'shelves_drag_books' => 'Træk bog her for at tilføje dem til denne reol',
+    'shelves_empty_contents' => 'Denne reol har ingen bøger tilknyttet til den',
+    'shelves_edit_and_assign' => 'Rediger reol for at tilføje bøger',
+    'shelves_edit_named' => 'Rediger reol :name',
+    'shelves_edit' => 'Rediger reol',
+    'shelves_delete' => 'Slet reol',
+    'shelves_delete_named' => 'Slet bogreol :name',
+    'shelves_delete_explain' => "Dette vil slette bogreolen med navn ':name'. Bøger heri vil ikke blive slettet.",
+    'shelves_delete_confirmation' => 'Er du sikker på at du vil slette denne bogreol?',
+    'shelves_permissions' => 'Reoltilladelser',
+    'shelves_permissions_updated' => 'Reoltilladelser opdateret',
+    'shelves_permissions_active' => 'Aktive reoltilladelser',
+    'shelves_copy_permissions_to_books' => 'Kopier tilladelser til bøger',
+    'shelves_copy_permissions' => 'Kopier tilladelser',
+    'shelves_copy_permissions_explain' => 'Dette vil anvende de aktuelle tilladelsesindstillinger på denne boghylde på alle bøger indeholdt i. Før aktivering skal du sikre dig, at ændringer i tilladelserne til denne boghylde er blevet gemt.',
+    'shelves_copy_permission_success' => 'Reolstilladelser kopieret til :count bøger',
+
+    // Books
+    'book' => 'Bog',
+    'books' => 'Bøger',
+    'x_books' => ':count bog|:count bøger',
+    'books_empty' => 'Ingen bøger er blevet oprettet',
+    'books_popular' => 'Populære bøger',
+    'books_recent' => 'Nylige bøger',
+    'books_new' => 'Nye bøger',
+    'books_new_action' => 'Ny bog',
+    'books_popular_empty' => 'De mest populære bøger vil blive vist her.',
+    'books_new_empty' => 'De nyeste boger vil blive vist her.',
+    'books_create' => 'Lav en ny bog',
+    'books_delete' => 'Slet bog',
+    'books_delete_named' => 'Slet bog :bookName',
+    'books_delete_explain' => 'Dette vil slette bogen ved navn \':bookName\'. Alle sider og kapitler vil blive slettet.',
+    'books_delete_confirmation' => 'Er du sikker på at du vil slette denne bog?',
+    'books_edit' => 'Rediger bog',
+    'books_edit_named' => 'Rediger bog :bookName',
+    'books_form_book_name' => 'Bognavn',
+    'books_save' => 'Gem bog',
+    'books_permissions' => 'Bogtilladelser',
+    'books_permissions_updated' => 'Bogtilladelser opdateret',
+    'books_empty_contents' => 'Ingen sider eller kapitler er blevet oprettet i denne bog.',
+    'books_empty_create_page' => 'Opret en ny side',
+    'books_empty_sort_current_book' => 'Sortér denne bog',
+    'books_empty_add_chapter' => 'Tilføj et kapitel',
+    'books_permissions_active' => 'Aktive bogtilladelser',
+    'books_search_this' => 'Søg i denne bog',
+    'books_navigation' => 'Bognavigation',
+    'books_sort' => 'Sorter bogindhold',
+    'books_sort_named' => 'Sorter bog :bookName',
+    'books_sort_name' => 'Sortér efter navn',
+    'books_sort_created' => 'Sortér efter oprettelsesdato',
+    'books_sort_updated' => 'Sortér efter opdateringsdato',
+    'books_sort_chapters_first' => 'Kapitler først',
+    'books_sort_chapters_last' => 'Kapitler sidst',
+    'books_sort_show_other' => 'Vis andre bøger',
+    'books_sort_save' => 'Gem ny ordre',
+
+    // Chapters
+    'chapter' => 'Kapitel',
+    'chapters' => 'Kapitler',
+    'x_chapters' => ':count kapitel|:count kapitler',
+    'chapters_popular' => 'Populære kapitler',
+    'chapters_new' => 'Nyt kapitel',
+    'chapters_create' => 'Opret nyt kapitel',
+    'chapters_delete' => 'Slet kapitel',
+    'chapters_delete_named' => 'Slet kapitel :chapterName',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_confirm' => 'Er du sikker på du vil slette dette kapitel?',
+    'chapters_edit' => 'Rediger kapitel',
+    'chapters_edit_named' => 'Rediger kapitel :chapterName',
+    'chapters_save' => 'Gem kapitel',
+    'chapters_move' => 'Flyt kapitel',
+    'chapters_move_named' => 'Flyt kapitel :chapterName',
+    'chapter_move_success' => 'Kapitel flyttet til :bookName',
+    'chapters_permissions' => 'Kapiteltilladelser',
+    'chapters_empty' => 'Der er lige nu ingen sider i dette kapitel.',
+    'chapters_permissions_active' => 'Aktive kapiteltilladelser',
+    'chapters_permissions_success' => 'Kapiteltilladelser opdateret',
+    'chapters_search_this' => 'Søg i dette kapitel',
+
+    // Pages
+    'page' => 'Side',
+    'pages' => 'Sider',
+    'x_pages' => ':count Side|:count Sider',
+    'pages_popular' => 'Populære sider',
+    'pages_new' => 'Ny side',
+    'pages_attachments' => 'Vedhæftninger',
+    'pages_navigation' => 'Sidenavigation',
+    'pages_delete' => 'Slet side',
+    'pages_delete_named' => 'Slet side :pageName',
+    'pages_delete_draft_named' => 'Slet kladdesidde :pageName',
+    'pages_delete_draft' => 'Slet kladdeside',
+    'pages_delete_success' => 'Side slettet',
+    'pages_delete_draft_success' => 'Kladdeside slettet',
+    'pages_delete_confirm' => 'Er du sikker på, du vil slette denne side?',
+    'pages_delete_draft_confirm' => 'Er du sikker på at du vil slette denne kladdesidde?',
+    'pages_editing_named' => 'Redigerer :pageName',
+    'pages_edit_draft_options' => 'Kladdeindstillinger',
+    'pages_edit_save_draft' => 'Gem kladde',
+    'pages_edit_draft' => 'Rediger sidekladde',
+    'pages_editing_draft' => 'Redigerer kladde',
+    'pages_editing_page' => 'Redigerer side',
+    'pages_edit_draft_save_at' => 'Kladde gemt ved ',
+    'pages_edit_delete_draft' => 'Slet kladde',
+    'pages_edit_discard_draft' => 'Kassér kladde',
+    'pages_edit_set_changelog' => 'Sæt ændringsoversigt',
+    'pages_edit_enter_changelog_desc' => 'Indtast en kort beskrivelse af ændringer du har lavet',
+    'pages_edit_enter_changelog' => 'Indtast ændringsoversigt',
+    'pages_save' => 'Gem siden',
+    'pages_title' => 'Overskrift',
+    'pages_name' => 'Sidenavn',
+    'pages_md_editor' => 'Editor',
+    'pages_md_preview' => 'Forhåndsvisning',
+    'pages_md_insert_image' => 'Indsæt billede',
+    'pages_md_insert_link' => 'Indsæt emnelink',
+    'pages_md_insert_drawing' => 'Indsæt tegning',
+    'pages_not_in_chapter' => 'Side er ikke i et kapitel',
+    'pages_move' => 'Flyt side',
+    'pages_move_success' => 'Flyt side til ":parentName"',
+    'pages_copy' => 'Kopier side',
+    'pages_copy_desination' => 'Kopier destination',
+    'pages_copy_success' => 'Side kopieret succesfuldt',
+    'pages_permissions' => 'Sidetilladelser',
+    'pages_permissions_success' => 'Sidetilladelser opdateret',
+    'pages_revision' => 'Revision',
+    'pages_revisions' => 'Sidserevisioner',
+    'pages_revisions_named' => 'Siderevisioner for :pageName',
+    'pages_revision_named' => 'Siderevision for :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions_created_by' => 'Oprettet af',
+    'pages_revisions_date' => 'Revisionsdato',
+    'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'Revision #:id',
+    'pages_revisions_numbered_changes' => 'Revision #:id ændringer',
+    'pages_revisions_changelog' => 'Ændringsoversigt',
+    'pages_revisions_changes' => 'Ændringer',
+    'pages_revisions_current' => 'Nuværende version',
+    'pages_revisions_preview' => 'Forhåndsvisning',
+    'pages_revisions_restore' => 'Gendan',
+    'pages_revisions_none' => 'Denne side har ingen revisioner',
+    'pages_copy_link' => 'Kopier link',
+    'pages_edit_content_link' => 'Redigér indhold',
+    'pages_permissions_active' => 'Aktive sidetilladelser',
+    'pages_initial_revision' => 'Første udgivelse',
+    'pages_initial_name' => 'Ny side',
+    'pages_editing_draft_notification' => 'Du redigerer en kladde der sidst var gemt :timeDiff.',
+    'pages_draft_edited_notification' => 'Siden har været opdateret siden da. Det er anbefalet at du kasserer denne kladde.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count brugerer har begyndt at redigere denne side',
+        'start_b' => ':userName er begyndt at redigere denne side',
+        'time_a' => 'siden siden sidst blev opdateret',
+        'time_b' => 'i de sidste :minCount minutter',
+        'message' => ':start : time. Pas på ikke at overskrive hinandens opdateringer!',
+    ],
+    'pages_draft_discarded' => 'Kladde kasseret, editoren er blevet opdateret med aktuelt sideindhold',
+    'pages_specific' => 'Specifik side',
+    'pages_is_template' => 'Sideskabelon',
+
+    // Editor Sidebar
+    'page_tags' => 'Sidetags',
+    'chapter_tags' => 'Kapiteltags',
+    'book_tags' => 'Bogtags',
+    'shelf_tags' => 'Reoltags',
+    'tag' => 'Tag',
+    'tags' =>  'Tags',
+    'tag_name' =>  'Tagnavn',
+    'tag_value' => 'Tagværdi (valgfri)',
+    'tags_explain' => "Tilføj nogle tags for bedre at kategorisere dit indhold. \n Du kan tildele en værdi til et tag for mere dybdegående organisering.",
+    'tags_add' => 'Tilføj endnu et tag',
+    'tags_remove' => 'Fjern dette tag',
+    'attachments' => 'Vedhæftninger',
+    'attachments_explain' => 'Upload nogle filer, eller vedhæft nogle links, der skal vises på siden. Disse er synlige i sidepanelet.',
+    'attachments_explain_instant_save' => 'Ændringer her gemmes med det samme.',
+    'attachments_items' => 'Vedhæftede emner',
+    'attachments_upload' => 'Upload fil',
+    'attachments_link' => 'Vedhæft link',
+    'attachments_set_link' => 'Sæt link',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
+    'attachments_dropzone' => 'Slip filer eller klik her for at vedhæfte en fil',
+    'attachments_no_files' => 'Ingen filer er blevet overført',
+    'attachments_explain_link' => 'Du kan vedhæfte et link, hvis du foretrækker ikke at uploade en fil. Dette kan være et link til en anden side eller et link til en fil i skyen.',
+    'attachments_link_name' => 'Linknavn',
+    'attachment_link' => 'Vedhæftningslink',
+    'attachments_link_url' => 'Link til filen',
+    'attachments_link_url_hint' => 'Adresse (URL) på side eller fil',
+    'attach' => 'Vedhæft',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
+    'attachments_edit_file' => 'Rediger fil',
+    'attachments_edit_file_name' => 'Filnavn',
+    'attachments_edit_drop_upload' => 'Slip filer eller klik her for at uploade og overskrive',
+    'attachments_order_updated' => 'Vedhæftningsordre opdateret',
+    'attachments_updated_success' => 'Vedhæftningsdetaljer opdateret',
+    'attachments_deleted' => 'Vedhæftning slettet',
+    'attachments_file_uploaded' => 'Filen blev uploadet korrekt',
+    'attachments_file_updated' => 'Filen blev opdateret korrekt',
+    'attachments_link_attached' => 'Link succesfuldt vedhæftet til side',
+    'templates' => 'Skabeloner',
+    'templates_set_as_template' => 'Side er en skabelon',
+    'templates_explain_set_as_template' => 'Du kan indstille denne side som en skabelon, så dens indhold bruges, når du opretter andre sider. Andre brugere vil kunne bruge denne skabelon, hvis de har visningstilladelser til denne side.',
+    'templates_replace_content' => 'Udskift sideindhold',
+    'templates_append_content' => 'Tilføj efter sideindhold',
+    'templates_prepend_content' => 'Tilføj før sideindhold',
+
+    // Profile View
+    'profile_user_for_x' => 'Bruger i :time',
+    'profile_created_content' => 'Oprettet indhold',
+    'profile_not_created_pages' => ':userName har ikke oprettet nogle sider',
+    'profile_not_created_chapters' => ':userName har ikke oprettet nogle kapitler',
+    'profile_not_created_books' => ':userName har ikke oprettet nogle bøger',
+    'profile_not_created_shelves' => ':userName har ikke oprettet nogle reoler',
+
+    // Comments
+    'comment' => 'Kommentar',
+    'comments' => 'Kommentarer',
+    'comment_add' => 'Tilføj kommentar',
+    'comment_placeholder' => 'Skriv en kommentar her',
+    'comment_count' => '{0} Ingen kommentarer|{1} 1 Kommentar|[2,*] :count kommentarer',
+    'comment_save' => 'Gem kommentar',
+    'comment_saving' => 'Gemmer kommentar...',
+    'comment_deleting' => 'Sletter kommentar...',
+    'comment_new' => 'Ny kommentar',
+    'comment_created' => 'kommenteret :createDiff',
+    'comment_updated' => 'Opdateret :updateDiff af :username',
+    'comment_deleted_success' => 'Kommentar slettet',
+    'comment_created_success' => 'Kommentaren er tilføjet',
+    'comment_updated_success' => 'Kommentaren er opdateret',
+    'comment_delete_confirm' => 'Er du sikker på, at du vil slette denne kommentar?',
+    'comment_in_reply_to' => 'Som svar til :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Er du sikker på at du vil slette denne revision?',
+    'revision_restore_confirm' => 'Er du sikker på at du ønsker at gendanne denne revision? Nuværende sideindhold vil blive erstattet.',
+    'revision_delete_success' => 'Revision slettet',
+    'revision_cannot_delete_latest' => 'Kan ikke slette seneste revision.'
+];
\ No newline at end of file
diff --git a/resources/lang/da/errors.php b/resources/lang/da/errors.php
new file mode 100644 (file)
index 0000000..0f6287f
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'Du har ikke tilladelse til at tilgå den efterspurgte side.',
+    'permissionJson' => 'Du har ikke tilladelse til at udføre den valgte handling.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'En bruger med email :email eksistere allerede, men med andre legitimationsoplysninger.',
+    'email_already_confirmed' => 'Email er allerede bekræftet. Prøv at logge ind.',
+    'email_confirmation_invalid' => 'Denne bekræftelsestoken er ikke gyldig eller er allerede blevet brugt. Prøv at registrere dig igen.',
+    'email_confirmation_expired' => 'Bekræftelsestoken er udløbet. En ny bekræftelsesmail er blevet sendt.',
+    'email_confirmation_awaiting' => 'Mail-adressen for din konto i brug er nød til at blive bekræftet',
+    'ldap_fail_anonymous' => 'LDAP-adgang fejlede ved brugen af annonym bind',
+    'ldap_fail_authed' => 'LDAP adgang fejlede med de givne DN & kodeord oplysninger',
+    'ldap_extension_not_installed' => 'LDAP PHP udvidelse er ikke installeret',
+    'ldap_cannot_connect' => 'Kan ikke forbinde til ldap server. Indledende forbindelse mislykkedes',
+    'saml_already_logged_in' => 'Allerede logget ind',
+    'saml_user_not_registered' => 'Brugeren :name er ikke registreret, og automatisk registrering er slået fra',
+    'saml_no_email_address' => 'Kunne ikke finde en e-mail-adresse for denne bruger i de data, der leveres af det eksterne godkendelsessystem',
+    'saml_invalid_response_id' => 'Anmodningen fra det eksterne godkendelsessystem genkendes ikke af en proces, der er startet af denne applikation. Navigering tilbage efter et login kan forårsage dette problem.',
+    'saml_fail_authed' => 'Login ved hjælp af :system failed, systemet har ikke givet tilladelse',
+    'social_no_action_defined' => 'Ingen handling er defineret',
+    'social_login_bad_response' => "Der opstod en fejl under :socialAccount login:\n:error",
+    'social_account_in_use' => 'Denne :socialAccount konto er allerede i brug, prøv at logge ind med :socialAccount loginmetoden.',
+    'social_account_email_in_use' => 'Emailen :email er allerede i brug. Hvis du allerede har en konto, kan du forbinde din :socialAccount fra dine profilindstillinger.',
+    'social_account_existing' => ':socialAccount er allerede tilknyttet din profil.',
+    'social_account_already_used_existing' => 'Denne :socialAccount konto er allerede i brug af en anden bruger.',
+    'social_account_not_used' => 'Denne :socialAccount konto er ikke tilknyttet nogle brugere. Tilknyt den i dine profilindstillinger. ',
+    'social_account_register_instructions' => 'Hvis du ikke har en konto, kan du registrere en konto gennem :socialAccount loginmetoden.',
+    'social_driver_not_found' => 'Socialdriver ikke fundet',
+    'social_driver_not_configured' => 'Dine :socialAccount indstillinger er ikke konfigureret korret.',
+    'invite_token_expired' => 'Dette invitationslink er udløbet. I stedet kan du prøve at nulstille din kontos kodeord.',
+
+    // System
+    'path_not_writable' => 'Filsti :filePath kunne ikke uploades til. Sørg for at den kan skrives til af webserveren.',
+    'cannot_get_image_from_url' => 'Kan ikke finde billede på :url',
+    'cannot_create_thumbs' => 'Serveren kan ikke oprette miniaturer. Kontroller, at GD PHP-udvidelsen er installeret.',
+    'server_upload_limit' => 'Serveren tillader ikke uploads af denne størrelse. Prøv en mindre filstørrelse.',
+    'uploaded'  => 'Serveren tillader ikke uploads af denne størrelse. Prøv en mindre filstørrelse.',
+    'image_upload_error' => 'Der opstod en fejl ved upload af billedet',
+    'image_upload_type_error' => 'Billedtypen, der uploades, er ugyldig',
+    'file_upload_timeout' => 'Filuploaden udløb.',
+
+    // Attachments
+    'attachment_not_found' => 'Vedhæftning ikke fundet',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Kunne ikke gemme kladde. Tjek at du har internetforbindelse før du gemmer siden',
+    'page_custom_home_deletion' => 'Kan ikke slette en side der er sat som forside',
+
+    // Entities
+    'entity_not_found' => 'Emne ikke fundet',
+    'bookshelf_not_found' => 'Bogreol ikke fundet',
+    'book_not_found' => 'Bog ikke fundet',
+    'page_not_found' => 'Side ikke fundet',
+    'chapter_not_found' => 'Kapitel ikke fundet',
+    'selected_book_not_found' => 'Den valgte bog kunne ikke findes',
+    'selected_book_chapter_not_found' => 'Den valgte bog eller kapitel kunne ikke findes',
+    'guests_cannot_save_drafts' => 'Gæster kan ikke gemme kladder',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'Du kan ikke slette den eneste admin',
+    'users_cannot_delete_guest' => 'Du kan ikke slette gæstebrugeren',
+
+    // Roles
+    'role_cannot_be_edited' => 'Denne rolle kan ikke redigeres',
+    'role_system_cannot_be_deleted' => 'Denne rolle er en systemrolle og kan ikke slettes',
+    'role_registration_default_cannot_delete' => 'Kan ikke slette rollen mens den er sat som standardrolle for registrerede brugere',
+    'role_cannot_remove_only_admin' => 'Denne bruger er den eneste bruger der har administratorrollen. Tilføj en anden bruger til administratorrollen før du forsøger at slette den her.',
+
+    // Comments
+    'comment_list' => 'Der opstod en fejl under hentning af kommentarerne.',
+    'cannot_add_comment_to_draft' => 'Du kan ikke kommentere på en kladde.',
+    'comment_add' => 'Der opstod en fejl under tilføjelse/opdatering af kommentaren.',
+    'comment_delete' => 'Der opstod en fejl under sletning af kommentaren.',
+    'empty_comment' => 'Kan ikke tilføje en tom kommentar.',
+
+    // Error pages
+    '404_page_not_found' => 'Siden blev ikke fundet',
+    'sorry_page_not_found' => 'Beklager, siden du leder efter blev ikke fundet.',
+    'sorry_page_not_found_permission_warning' => 'Hvis du forventede, at denne side skulle eksistere, har du muligvis ikke tilladelse til at se den.',
+    'return_home' => 'Gå tilbage til hjem',
+    'error_occurred' => 'Der opstod en fejl',
+    'app_down' => ':appName er nede lige nu',
+    'back_soon' => 'Den er oppe igen snart.',
+
+    // API errors
+    'api_no_authorization_found' => 'Der blev ikke fundet nogen autorisationstoken på anmodningen',
+    'api_bad_authorization_format' => 'En autorisationstoken blev fundet på anmodningen, men formatet var forkert',
+    'api_user_token_not_found' => 'Der blev ikke fundet nogen matchende API-token for det angivne autorisationstoken',
+    'api_incorrect_token_secret' => 'Hemmeligheden leveret til det givne anvendte API-token er forkert',
+    'api_user_no_api_permission' => 'Ejeren af den brugte API token har ikke adgang til at foretage API-kald',
+    'api_user_token_expired' => 'Den brugte godkendelsestoken er udløbet',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Følgende fejl opstod under afsendelse af testemail:',
+
+];
diff --git a/resources/lang/da/pagination.php b/resources/lang/da/pagination.php
new file mode 100644 (file)
index 0000000..821d780
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; Forrige',
+    'next'     => 'Næste &raquo;',
+
+];
diff --git a/resources/lang/da/passwords.php b/resources/lang/da/passwords.php
new file mode 100644 (file)
index 0000000..343fa2b
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+    'password' => 'Adgangskoder skal være mindst otte tegn og svare til bekræftelsen.',
+    'user' => "Vi kan ikke finde en bruger med den e-mail adresse.",
+    'token' => 'Linket til nulstilling af adgangskode er ugyldigt for denne e-mail-adresse.',
+    'sent' => 'Vi har sendt dig en e-mail med et link til at nulstille adgangskoden!',
+    'reset' => 'Dit kodeord er blevet nulstillet!',
+
+];
diff --git a/resources/lang/da/settings.php b/resources/lang/da/settings.php
new file mode 100644 (file)
index 0000000..80ebcb1
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Indstillinger',
+    'settings_save' => 'Gem indstillinger',
+    'settings_save_success' => 'Indstillinger gemt',
+
+    // App Settings
+    'app_customization' => 'Tilpasning',
+    'app_features_security' => 'Funktioner & sikkerhed',
+    'app_name' => 'Programnavn',
+    'app_name_desc' => 'Dette er navnet vist i headeren og i systemafsendte E-Mails.',
+    'app_name_header' => 'Vis navn i header',
+    'app_public_access' => 'Offentlig adgang',
+    'app_public_access_desc' => 'Aktivering af denne funktion giver besøgende, der ikke er logget ind, adgang til indhold i din BookStack-instans.',
+    'app_public_access_desc_guest' => 'Adgang for ikke-registrerede besøgende kan kontrolleres via "Gæst" -brugeren.',
+    'app_public_access_toggle' => 'Tillad offentlig adgang',
+    'app_public_viewing' => 'Tillad offentlig visning?',
+    'app_secure_images' => 'Højere sikkerhed for billeduploads',
+    'app_secure_images_toggle' => 'Aktiver højere sikkerhed for billeduploads',
+    'app_secure_images_desc' => 'Af ydeevneårsager er alle billeder offentlige. Denne funktion tilføjer en tilfældig, vanskelig at gætte streng foran billed-Url\'er. Sørg for, at mappeindekser ikke er aktiveret for at forhindre nem adgang.',
+    'app_editor' => 'Sideeditor',
+    'app_editor_desc' => 'Vælg hvilken editor der skal bruges af alle brugere til at redigere sider.',
+    'app_custom_html' => 'Tilpasset HTML head-indhold',
+    'app_custom_html_desc' => 'Al indhold tilføjet her, vil blive indsat i bunden af <head> sektionen på alle sider. Dette er brugbart til overskrivning af styling og tilføjelse af analysekode.',
+    'app_custom_html_disabled_notice' => 'Brugerdefineret HTML-head indhold er deaktiveret på denne indstillingsside for at sikre, at ødelæggende ændringer kan rettes.',
+    'app_logo' => 'Programlogo',
+    'app_logo_desc' => 'Dette billede skal være 43px højt. <br> Større billeder vil blive skaleret ned.',
+    'app_primary_color' => 'Primær programfarve',
+    'app_primary_color_desc' => 'Sætter den primære farve for applikationen herunder banneret, knapper og links.',
+    'app_homepage' => 'Programforside',
+    'app_homepage_desc' => 'Vælg en visning, der skal vises på startsiden i stedet for standardvisningen. Sidetilladelser ignoreres for valgte sider.',
+    'app_homepage_select' => 'Vælg en side',
+    'app_disable_comments' => 'Deaktiver kommentarer',
+    'app_disable_comments_toggle' => 'Deaktiver kommentar',
+    'app_disable_comments_desc' => 'Deaktiverer kommentarer på tværs af alle sider i applikationen. <br> Eksisterende kommentarer vises ikke.',
+
+    // Color settings
+    'content_colors' => 'Indholdsfarver',
+    'content_colors_desc' => 'Sætter farver for alle elementer i sideorganisationshierarkiet. Valg af farver med en lignende lysstyrke som standardfarverne anbefales af hensyn til læsbarhed.',
+    'bookshelf_color' => 'Bogreolfarve',
+    'book_color' => 'Bogfarve',
+    'chapter_color' => 'Kapitelfarve',
+    'page_color' => 'Sidefarve',
+    'page_draft_color' => 'Sidekladdefarve',
+
+    // Registration Settings
+    'reg_settings' => 'Registrering',
+    'reg_enable' => 'Aktivér tilmelding',
+    'reg_enable_toggle' => 'Aktivér tilmelding',
+    'reg_enable_desc' => 'Når registrering er aktiveret, vil alle kunne registrere sig som en applikationsbruger. Ved registrering får de en standardbrugerrolle.',
+    'reg_default_role' => 'Standardrolle efter registrering',
+    'reg_enable_external_warning' => 'Indstillingen ovenfor ignoreres, mens ekstern LDAP- eller SAML-godkendelse er aktiv. Brugerkonti for ikke-eksisterende medlemmer oprettes automatisk, hvis godkendelse mod det eksterne system, der er i brug, er vellykket.',
+    'reg_email_confirmation' => 'Email bekræftelse',
+    'reg_email_confirmation_toggle' => 'Kræv E-Mail bekræftelse',
+    'reg_confirm_email_desc' => 'Hvis domænebegrænsning bruges, kræves e-mail-bekræftelse, og denne indstilling ignoreres.',
+    'reg_confirm_restrict_domain' => 'Domæneregistrering',
+    'reg_confirm_restrict_domain_desc' => 'Indtast en kommasepareret liste over e-mail-domæner, som du vil begrænse registreringen til. Brugere får en E-Mail for at bekræfte deres adresse, før de får tilladelse til at interagere med applikationen. <br> Bemærk, at brugere vil kunne ændre deres e-mail-adresser efter vellykket registrering.',
+    'reg_confirm_restrict_domain_placeholder' => 'Ingen restriktion opsat',
+
+    // Maintenance settings
+    'maint' => 'Vedligeholdelse',
+    'maint_image_cleanup' => 'Ryd op i billeder',
+    'maint_image_cleanup_desc' => "Scanner side & revisionsindhold for at kontrollere, hvilke billeder og tegninger, der i øjeblikket er i brug, og hvilke billeder, der er overflødige. Sørg for, at du opretter en komplet database og billedbackup, før du kører dette.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_run' => 'Kør Oprydning',
+    'maint_image_cleanup_warning' => 'der blev fundet :count potentielt ubrugte billeder. Er du sikker på, at du vil slette disse billeder?',
+    'maint_image_cleanup_success' => ':count: potentielt ubrugte billeder fundet og slettet!',
+    'maint_image_cleanup_nothing_found' => 'Ingen ubrugte billeder fundet, intet slettet!',
+    'maint_send_test_email' => 'Send en Testemail',
+    'maint_send_test_email_desc' => 'Dette sender en testmail til din mailadresse specificeret på din profil.',
+    'maint_send_test_email_run' => 'Afsend test E-Mail',
+    'maint_send_test_email_success' => 'E-Mail sendt til :address',
+    'maint_send_test_email_mail_subject' => 'Test E-Mail',
+    'maint_send_test_email_mail_greeting' => 'E-Mail levering ser ud til at virke!',
+    'maint_send_test_email_mail_text' => 'Tillykke! Da du har modtaget denne mailnotifikation, ser det ud som om, at dine mailindstillinger er opsat korrekt.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
+    // Role Settings
+    'roles' => 'Roller',
+    'role_user_roles' => 'Brugerroller',
+    'role_create' => 'Opret en ny rolle',
+    'role_create_success' => 'Rollen blev oprette korrekt',
+    'role_delete' => 'Slet rolle',
+    'role_delete_confirm' => 'Dette vil slette rollen med navnet \':roleName\'.',
+    'role_delete_users_assigned' => 'Denne rolle er tildelt :userCount brugere. Hvis du vil rykke disse brugere fra denne rolle, kan du vælge en ny nedenunder.',
+    'role_delete_no_migration' => "Ryk ikke brugere",
+    'role_delete_sure' => 'Er du sikker på, at du vil slette denne rolle?',
+    'role_delete_success' => 'Rollen blev slettet',
+    'role_edit' => 'Rediger rolle',
+    'role_details' => 'Rolledetaljer',
+    'role_name' => 'Rollenavn',
+    'role_desc' => 'Kort beskrivelse af rolle',
+    'role_external_auth_id' => 'Eksterne godkendelses-IDer',
+    'role_system' => 'Systemtilladelser',
+    'role_manage_users' => 'Administrere brugere',
+    'role_manage_roles' => 'Administrer roller & rollerettigheder',
+    'role_manage_entity_permissions' => 'Administrer alle bog-, kapitel- & side-rettigheder',
+    'role_manage_own_entity_permissions' => 'Administrer tilladelser på egne bøger, kapitler og sider',
+    'role_manage_page_templates' => 'Administrer side-skabeloner',
+    'role_access_api' => 'Tilgå system-API',
+    'role_manage_settings' => 'Administrer app-indstillinger',
+    'role_asset' => 'Tilladelser for medier og "assets"',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => 'Disse tilladelser kontrollerer standardadgang til medier og "assets" i systemet. Tilladelser til bøger, kapitler og sider tilsidesætter disse tilladelser.',
+    'role_asset_admins' => 'Administratorer får automatisk adgang til alt indhold, men disse indstillinger kan vise eller skjule UI-indstillinger.',
+    'role_all' => 'Alle',
+    'role_own' => 'Eget',
+    'role_controlled_by_asset' => 'Styres af det medie/"asset", de uploades til',
+    'role_save' => 'Gem rolle',
+    'role_update_success' => 'Rollen blev opdateret',
+    'role_users' => 'Brugere med denne rolle',
+    'role_users_none' => 'Ingen brugere er i øjeblikket tildelt denne rolle',
+
+    // Users
+    'users' => 'Brugere',
+    'user_profile' => 'Brugerprofil',
+    'users_add_new' => 'Tilføj ny bruger',
+    'users_search' => 'Søg efter brugere',
+    'users_latest_activity' => 'Latest Activity',
+    'users_details' => 'Brugeroplysninger',
+    'users_details_desc' => 'Angiv et visningsnavn og en E-Mail-adresse for denne bruger. E-Mail-adressen bruges til at logge ind på applikationen.',
+    'users_details_desc_no_email' => 'Sætter et visningsnavn for denne bruger, så andre kan genkende dem.',
+    'users_role' => 'Brugerroller',
+    'users_role_desc' => 'Vælg hvilke roller denne bruger skal tildeles. Hvis en bruger er tildelt flere roller, sammenføres tilladelserne fra disse roller, og de får alle evnerne fra de tildelte roller.',
+    'users_password' => 'Brugeradgangskode',
+    'users_password_desc' => 'Sæt et kodeord, der bruges til at logge på applikationen. Dette skal være mindst 6 tegn langt.',
+    'users_send_invite_text' => 'Du kan vælge at sende denne bruger en invitation på E-Mail, som giver dem mulighed for at indstille deres egen adgangskode, ellers kan du indstille deres adgangskode selv.',
+    'users_send_invite_option' => 'Send bruger en invitationsmail',
+    'users_external_auth_id' => 'Ekstern godkendelses ID',
+    'users_external_auth_id_desc' => 'Dette er det ID, der bruges til at matche denne bruger ved kommunikation med dit eksterne godkendelsessystem.',
+    'users_password_warning' => 'Udfyld kun nedenstående, hvis du vil ændre din adgangskode.',
+    'users_system_public' => 'Denne bruger repræsenterer alle gæstebrugere, der besøger din instans. Den kan ikke bruges til at logge på, men tildeles automatisk.',
+    'users_delete' => 'Slet bruger',
+    'users_delete_named' => 'Slet bruger :userName',
+    'users_delete_warning' => 'Dette vil helt slette denne bruger med navnet \':userName\' fra systemet.',
+    'users_delete_confirm' => 'Er du sikker på, at du vil slette denne bruger?',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
+    'users_edit' => 'Rediger bruger',
+    'users_edit_profile' => 'Rediger profil',
+    'users_edit_success' => 'Bruger suscesfuldt opdateret',
+    'users_avatar' => 'Brugeravatar',
+    'users_avatar_desc' => 'Vælg et billede for at repræsentere denne bruger. Dette skal være ca. 256px kvadratisk.',
+    'users_preferred_language' => 'Foretrukket sprog',
+    'users_preferred_language_desc' => 'Denne indstilling ændrer det sprog, der bruges til applikationens brugergrænseflade. Dette påvirker ikke noget brugeroprettet indhold.',
+    'users_social_accounts' => 'Sociale konti',
+    'users_social_accounts_info' => 'Her kan du forbinde dine andre konti for hurtigere og lettere login. Afbrydelse af en konto her tilbagekalder ikke tidligere autoriseret adgang. Tilbagekald adgang fra dine profilindstillinger på den tilsluttede sociale konto.',
+    'users_social_connect' => 'Forbind konto',
+    'users_social_disconnect' => 'Frakobl konto',
+    'users_social_connected' => ':socialAccount kontoen blev knyttet til din profil.',
+    'users_social_disconnected' => ':socialAccount kontoen blev afbrudt fra din profil.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'Ingen API tokens er blevet oprettet for denne bruger',
+    'users_api_tokens_create' => 'Opret Token',
+    'users_api_tokens_expires' => 'Udløber',
+    'users_api_tokens_docs' => 'API-dokumentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Opret API-token',
+    'user_api_token_name' => 'Navn',
+    'user_api_token_name_desc' => 'Giv din token et læsbart navn som en fremtidig påmindelse om dets tilsigtede formål.',
+    'user_api_token_expiry' => 'Udløbsdato',
+    'user_api_token_expiry_desc' => 'Indstil en dato, hvorpå denne token udløber. Efter denne dato fungerer anmodninger, der er lavet med denne token, ikke længere. Hvis du lader dette felt være tomt, udløber den 100 år ud i fremtiden.',
+    'user_api_token_create_secret_message' => 'Umiddelbart efter oprettelse af denne token genereres og vises et "Token-ID" og Token hemmelighed". Hemmeligheden vises kun en gang, så husk at kopiere værdien til et sikkert sted inden du fortsætter.',
+    'user_api_token_create_success' => 'API token succesfuldt oprettet',
+    'user_api_token_update_success' => 'API token succesfuldt opdateret',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token-ID',
+    'user_api_token_id_desc' => 'Dette er en ikke-redigerbar systemgenereret identifikator for denne token, som skal sendes i API-anmodninger.',
+    'user_api_token_secret' => 'Token hemmelighed',
+    'user_api_token_secret_desc' => 'Dette er et system genereret hemmelighed for denne token, som skal sendes i API-anmodninger. Dette vises kun denne ene gang, så kopier denne værdi til et sikkert sted.',
+    'user_api_token_created' => 'Token oprettet :timeAgo',
+    'user_api_token_updated' => 'Token opdateret :timeAgo',
+    'user_api_token_delete' => 'Slet Token',
+    'user_api_token_delete_warning' => 'Dette vil helt slette API-token\'en med navnet \':tokenName\' fra systemet.',
+    'user_api_token_delete_confirm' => 'Er du sikker på, at du vil slette denne API-token?',
+    'user_api_token_delete_success' => 'API-token slettet',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'Hebraisk',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'Slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/da/validation.php b/resources/lang/da/validation.php
new file mode 100644 (file)
index 0000000..7647132
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => ':attribute skal være accepteret.',
+    'active_url'           => ':attribute er ikke en gyldig URL.',
+    'after'                => ':attribute skal være en dato efter :date.',
+    'alpha'                => ':attribute må kun indeholde bogstaver.',
+    'alpha_dash'           => ':attribute må kun bestå af bogstaver, tal, binde- og under-streger.',
+    'alpha_num'            => ':attribute må kun indeholde bogstaver og tal.',
+    'array'                => ':attribute skal være et array.',
+    'before'               => ':attribute skal være en dato før :date.',
+    'between'              => [
+        'numeric' => ':attribute skal være mellem :min og :max.',
+        'file'    => ':attribute skal være mellem :min og :max kilobytes.',
+        'string'  => ':attribute skal være mellem :min og :max tegn.',
+        'array'   => ':attribute skal have mellem :min og :max elementer.',
+    ],
+    'boolean'              => ':attribute-feltet skal være enten sandt eller falsk.',
+    'confirmed'            => ':attribute-bekræftelsen matcher ikke.',
+    'date'                 => ':attribute er ikke en gyldig dato.',
+    'date_format'          => ':attribute matcher ikke formatet :format.',
+    'different'            => ':attribute og :other skal være forskellige.',
+    'digits'               => ':attribute skal være :digits cifre.',
+    'digits_between'       => ':attribute skal være mellem :min og :max cifre.',
+    'email'                => ':attribute skal være en gyldig mail-adresse.',
+    'ends_with' => ':attribute skal slutte på en af følgende værdier: :values',
+    'filled'               => ':attribute er obligatorisk.',
+    'gt'                   => [
+        'numeric' => ':attribute skal være større end :value.',
+        'file'    => ':attribute skal være større end :value kilobytes.',
+        'string'  => ':attribute skal have mere end :value tegn.',
+        'array'   => ':attribute skal indeholde mere end :value elementer.',
+    ],
+    'gte'                  => [
+        'numeric' => ':attribute skal mindst være :value.',
+        'file'    => ':attribute skal være mindst :value kilobytes.',
+        'string'  => ':attribute skal indeholde mindst :value tegn.',
+        'array'   => ':attribute skal have :value elementer eller flere.',
+    ],
+    'exists'               => 'Den valgte :attribute er ikke gyldig.',
+    'image'                => ':attribute skal være et billede.',
+    'image_extension'      => ':attribute skal være et gyldigt og understøttet billedformat.',
+    'in'                   => 'Den valgte :attribute er ikke gyldig.',
+    'integer'              => ':attribute skal være et heltal.',
+    'ip'                   => ':attribute skal være en gyldig IP-adresse.',
+    'ipv4'                 => ':attribute skal være en gyldig IPv4-adresse.',
+    'ipv6'                 => ':attribute skal være en gyldig IPv6-adresse.',
+    'json'                 => ':attribute skal være en gyldig JSON-streng.',
+    'lt'                   => [
+        'numeric' => ':attribute skal være mindre end :value.',
+        'file'    => ':attribute skal være mindre end :value kilobytes.',
+        'string'  => ':attribute skal have mindre end :value tegn.',
+        'array'   => ':attribute skal indeholde mindre end :value elementer.',
+    ],
+    'lte'                  => [
+        'numeric' => ':attribute skal være mindre end eller lig med :value.',
+        'file'    => 'The :attribute skal være mindre eller lig med :value kilobytes.',
+        'string'  => ':attribute skal maks være :value tegn.',
+        'array'   => ':attribute må ikke indeholde mere end :value elementer.',
+    ],
+    'max'                  => [
+        'numeric' => ':attribute må ikke overstige :max.',
+        'file'    => ':attribute må ikke overstige :max kilobytes.',
+        'string'  => ':attribute må ikke overstige :max. tegn.',
+        'array'   => ':attribute må ikke have mere end :max elementer.',
+    ],
+    'mimes'                => ':attribute skal være en fil af typen: :values.',
+    'min'                  => [
+        'numeric' => ':attribute skal mindst være :min.',
+        'file'    => ':attribute skal være mindst :min kilobytes.',
+        'string'  => ':attribute skal mindst være :min tegn.',
+        'array'   => ':attribute skal have mindst :min elementer.',
+    ],
+    'no_double_extension'  => ':attribute må kun indeholde én filtype.',
+    'not_in'               => 'Den valgte :attribute er ikke gyldig.',
+    'not_regex'            => ':attribute-formatet er ugyldigt.',
+    'numeric'              => ':attribute skal være et tal.',
+    'regex'                => ':attribute-formatet er ugyldigt.',
+    'required'             => ':attribute er obligatorisk.',
+    'required_if'          => ':attribute skal udfyldes når :other er :value.',
+    'required_with'        => ':attribute skal udfyldes når :values er udfyldt.',
+    'required_with_all'    => ':attribute skal udfyldes når :values er udfyldt.',
+    'required_without'     => ':attribute skal udfyldes når :values ikke er udfyldt.',
+    'required_without_all' => ':attribute skal udfyldes når ingen af :values er udfyldt.',
+    'same'                 => ':attribute og :other skal være ens.',
+    'safe_url'             => 'The provided link may not be safe.',
+    'size'                 => [
+        'numeric' => ':attribute skal være :size.',
+        'file'    => ':attribute skal være :size kilobytes.',
+        'string'  => ':attribute skal være :size tegn.',
+        'array'   => ':attribute skal indeholde :size elementer.',
+    ],
+    'string'               => ':attribute skal være tekst.',
+    'timezone'             => ':attribute skal være en gyldig zone.',
+    'unique'               => ':attribute er allerede i brug.',
+    'url'                  => ':attribute-formatet er ugyldigt.',
+    'uploaded'             => 'Filen kunne ikke oploades. Serveren accepterer muligvis ikke filer af denne størrelse.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Adgangskodebekræftelse påkrævet',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 170a1910825ac8637a702183c274b87f55dda810..562a9add365decee4f8d5fe06b37dcd91f35dd0c 100644 (file)
@@ -6,43 +6,44 @@
 return [
 
     // Pages
-    'page_create'                 => 'erstellt Seite',
-    'page_create_notification'    => 'Die Seite wurde erfolgreich erstellt.',
-    'page_update'                 => 'aktualisiert Seite',
-    'page_update_notification'    => 'Die Seite wurde erfolgreich aktualisiert.',
-    'page_delete'                 => 'löscht Seite',
-    'page_delete_notification'    => 'Die Seite wurde erfolgreich gelöscht.',
-    'page_restore'                => 'stellt Seite wieder her',
-    'page_restore_notification'   => 'Die Seite wurde erfolgreich wiederhergestellt.',
-    'page_move'                   => 'verschiebt Seite',
+    'page_create'                 => 'hat die Seite erstellt',
+    'page_create_notification'    => 'Die Seite wurde erfolgreich erstellt',
+    'page_update'                 => 'hat die Seite aktualisiert',
+    'page_update_notification'    => 'Die Seite wurde erfolgreich aktualisiert',
+    'page_delete'                 => 'hat die Seite gelöscht',
+    'page_delete_notification'    => 'Die Seite wurde erfolgreich gelöscht',
+    'page_restore'                => 'hat die Seite wiederhergestellt',
+    'page_restore_notification'   => 'Die Seite wurde erfolgreich wiederhergestellt',
+    'page_move'                   => 'hat die Seite verschoben',
 
     // Chapters
-    'chapter_create'              => 'erstellt Kapitel',
-    'chapter_create_notification' => 'Das Kapitel wurde erfolgreich erstellt.',
-    'chapter_update'              => 'aktualisiert Kapitel',
-    'chapter_update_notification' => 'Das Kapitel wurde erfolgreich aktualisiert.',
-    'chapter_delete'              => 'löscht Kapitel',
-    'chapter_delete_notification' => 'Das Kapitel wurde erfolgreich gelöscht.',
-    'chapter_move'                => 'verschiebt Kapitel',
+    'chapter_create'              => 'hat das Kapitel erstellt',
+    'chapter_create_notification' => 'Das Kapitel wurde erfolgreich erstellt',
+    'chapter_update'              => 'hat das Kapitel geändert',
+    'chapter_update_notification' => 'Das Kapitel wurde erfolgreich aktualisiert',
+    'chapter_delete'              => 'hat das Kapitel gelöscht',
+    'chapter_delete_notification' => 'Das Kapitel wurde erfolgreich gelöscht',
+    'chapter_move'                => 'hat das Kapitel verschoben',
 
     // Books
-    'book_create'                 => 'erstellt Buch',
-    'book_create_notification'    => 'Das Buch wurde erfolgreich erstellt.',
-    'book_update'                 => 'aktualisiert Buch',
-    'book_update_notification'    => 'Das Buch wurde erfolgreich aktualisiert.',
-    'book_delete'                 => 'löscht Buch',
-    'book_delete_notification'    => 'Das Buch wurde erfolgreich gelöscht.',
-    'book_sort'                   => 'sortiert Buch',
-    'book_sort_notification'      => 'Das Buch wurde erfolgreich umsortiert.',
+    'book_create'                 => 'hat das Buch erstellt',
+    'book_create_notification'    => 'Das Buch wurde erfolgreich erstellt',
+    'book_update'                 => 'hat das Buch aktualisiert',
+    'book_update_notification'    => 'Das Buch wurde erfolgreich aktualisiert',
+    'book_delete'                 => 'hat das Buch gelöscht',
+    'book_delete_notification'    => 'Das Buch wurde erfolgreich gelöscht',
+    'book_sort'                   => 'hat die Buch-Sortierung geändert',
+    'book_sort_notification'      => 'Das Buch wurde erfolgreich umsortiert',
 
     // Bookshelves
-    'bookshelf_create'            => 'erstellt Bücherregal',
+    'bookshelf_create'            => 'hat das Bücherregal erstellt',
     'bookshelf_create_notification'    => 'Das Bücherregal wurde erfolgreich erstellt',
-    'bookshelf_update'                 => 'aktualisiert Bücherregal',
-    'bookshelf_update_notification'    => 'Das Bücherregal wurde erfolgreich aktualisiert',
-    'bookshelf_delete'                 => 'löscht Bücherregal',
+    'bookshelf_update'                 => 'hat das Bücherregal geändert',
+    'bookshelf_update_notification'    => 'Das Bücherregal wurde erfolgreich geändert',
+    'bookshelf_delete'                 => 'hat das Bücherregal gelöscht',
     'bookshelf_delete_notification'    => 'Das Bücherregal wurde erfolgreich gelöscht',
 
     // Other
-    'commented_on'                => 'kommentiert',
+    'commented_on'                => 'hat einen Kommentar hinzugefügt',
+    'permissions_update'          => 'hat die Berechtigungen aktualisiert',
 ];
index 3d0db9dc88e886e84f5cdb1be71357cfe75af3b2..1f5a49cbddc240b36b70ff02fec035e6355abdf3 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Passwort vergessen',
     'reset_password_send_instructions' => 'Bitte geben Sie Ihre E-Mail-Adresse ein. Danach erhalten Sie eine E-Mail mit einem Link zum Zurücksetzen Ihres Passwortes.',
     'reset_password_send_button' => 'Passwort zurücksetzen',
-    'reset_password_sent_success' => 'Eine E-Mail mit dem Link zum Zurücksetzen Ihres Passwortes wurde an :email gesendet.',
+    'reset_password_sent' => 'Ein Link zum Zurücksetzen des Passworts wird an :email gesendet, wenn diese E-Mail-Adresse im System gefunden wird.',
     'reset_password_success' => 'Ihr Passwort wurde erfolgreich zurückgesetzt.',
     'email_reset_subject' => 'Passwort zurücksetzen für :appName',
     'email_reset_text' => 'Sie erhalten diese E-Mail, weil jemand versucht hat, Ihr Passwort zurückzusetzen.',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => 'Bestätigungs-E-Mail erneut senden',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Du wurdest eingeladen :appName beizutreten!',
+    'user_invite_email_greeting' => 'Ein Konto wurde für Sie auf :appName erstellt.',
+    'user_invite_email_text' => 'Klicken Sie auf die Schaltfläche unten, um ein Passwort festzulegen und Zugriff zu erhalten:',
+    'user_invite_email_action' => 'Account-Passwort festlegen',
+    'user_invite_page_welcome' => 'Willkommen bei :appName!',
+    'user_invite_page_text' => 'Um die Anmeldung abzuschließen und Zugriff auf :appName zu bekommen muss noch ein Passwort festgelegt werden. Dieses wird in Zukunft zum Einloggen benötigt.',
+    'user_invite_page_confirm_button' => 'Passwort wiederholen',
+    'user_invite_success' => 'Passwort gesetzt, Sie haben nun Zugriff auf :appName!'
 ];
\ No newline at end of file
index 94e59d154f4f4b59a3a110df0ca7904414b6ae0a..fc44a9250839eb2dc54c4ab8726d8876c2d72307 100644 (file)
@@ -33,17 +33,19 @@ return [
     'copy' => 'Kopieren',
     'reply' => 'Antworten',
     'delete' => 'Löschen',
+    'delete_confirm' => 'Löschen Bestätigen',
     'search' => 'Suchen',
     'search_clear' => 'Suche löschen',
     'reset' => 'Zurücksetzen',
     'remove' => 'Entfernen',
     'add' => 'Hinzufügen',
+    'fullscreen' => 'Vollbild',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
+    'sort_options' => 'Sortieroptionen',
+    'sort_direction_toggle' => 'Sortierreihenfolge umkehren',
+    'sort_ascending' => 'Aufsteigend sortieren',
+    'sort_descending' => 'Absteigend sortieren',
     'sort_name' => 'Name',
     'sort_created_at' => 'Erstellungsdatum',
     'sort_updated_at' => 'Aktualisierungsdatum',
@@ -59,12 +61,14 @@ return [
     'grid_view' => 'Gitteransicht',
     'list_view' => 'Listenansicht',
     'default' => 'Voreinstellung',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Brotkrumen',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Profilmenü',
     'view_profile' => 'Profil ansehen',
     'edit_profile' => 'Profil bearbeiten',
+    'dark_mode' => 'Dunkler Modus',
+    'light_mode' => 'Heller Modus',
 
     // Layout tabs
     'tab_info' => 'Info',
index 4e56722a8cda3dac9dac2d0cb74f27281907d96b..bda1ce3768e0e0d119e367af25689ab0aa752c2a 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Mehr',
     'image_image_name' => 'Bildname',
     'image_delete_used' => 'Dieses Bild wird auf den folgenden Seiten benutzt. ',
-    'image_delete_confirm' => 'Bitte klicken Sie erneut auf löschen, wenn Sie dieses Bild wirklich entfernen möchten.',
+    'image_delete_confirm_text' => 'Möchten Sie dieses Bild wirklich löschen?',
     'image_select_image' => 'Bild auswählen',
     'image_dropzone' => 'Ziehen Sie Bilder hierher oder klicken Sie, um ein Bild auszuwählen',
     'images_deleted' => 'Bilder gelöscht',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Code editieren',
     'code_language' => 'Code Sprache',
     'code_content' => 'Code Inhalt',
+    'code_session_history' => 'Sitzungsverlauf',
     'code_save' => 'Code speichern',
 ];
index 164d7a79450773428630e3a90e158dd9b7c5d378..2ea11a6cf979faed1f6e4c77aec02d6fe31ed722 100644 (file)
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => 'Erstellt: :timeLength von :user',
     'meta_updated' => 'Zuletzt aktualisiert: :timeLength',
     'meta_updated_name' => 'Zuletzt aktualisiert: :timeLength von :user',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Eintrag auswählen',
     'images' => 'Bilder',
     'my_recent_drafts' => 'Meine kürzlichen Entwürfe',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Wenn individuelle Berechtigungen aktiviert werden, überschreiben diese Einstellungen durch Rollen zugewiesene Berechtigungen.',
     'permissions_enable' => 'Individuelle Berechtigungen aktivieren',
     'permissions_save' => 'Berechtigungen speichern',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Suchergebnisse',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Keine Seiten gefunden',
     'search_for_term' => 'Nach :term suchen',
     'search_more' => 'Mehr Ergebnisse',
-    'search_filters' => 'Filter',
+    'search_advanced' => 'Erweiterte Suche',
+    'search_terms' => 'Suchbegriffe',
     'search_content_type' => 'Inhaltstyp',
     'search_exact_matches' => 'Exakte Treffer',
     'search_tags' => 'Nach Schlagwort suchen',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Neues Kapitel anlegen',
     'chapters_delete' => 'Kapitel entfernen',
     'chapters_delete_named' => 'Kapitel ":chapterName" entfernen',
-    'chapters_delete_explain' => 'Das Kapitel ":chapterName" wird gelöscht und alle zugehörigen Seiten dem übergeordneten Buch zugeordnet.',
+    'chapters_delete_explain' => 'Dies löscht das Kapitel mit dem Namen \':chapterName\'. Alle Seiten, die innerhalb dieses Kapitels existieren, werden ebenfalls gelöscht.',
     'chapters_delete_confirm' => 'Sind Sie sicher, dass Sie dieses Kapitel löschen möchten?',
     'chapters_edit' => 'Kapitel bearbeiten',
     'chapters_edit_named' => 'Kapitel ":chapterName" bearbeiten',
@@ -176,7 +179,7 @@ return [
     'pages_delete_confirm' => 'Sind Sie sicher, dass Sie diese Seite löschen möchen?',
     'pages_delete_draft_confirm' => 'Sind Sie sicher, dass Sie diesen Seitenentwurf löschen möchten?',
     'pages_editing_named' => 'Seite ":pageName" bearbeiten',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Entwurfsoptionen',
     'pages_edit_save_draft' => 'Entwurf speichern',
     'pages_edit_draft' => 'Seitenentwurf bearbeiten',
     'pages_editing_draft' => 'Seitenentwurf bearbeiten',
@@ -207,11 +210,12 @@ return [
     'pages_revisions' => 'Seitenversionen',
     'pages_revisions_named' => 'Seitenversionen von ":pageName"',
     'pages_revision_named' => 'Seitenversion von ":pageName"',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Erstellt von',
     'pages_revisions_date' => 'Versionsdatum',
     'pages_revisions_number' => '#',
     'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_numbered_changes' => 'Revision #:id Änderungen',
     'pages_revisions_changelog' => 'Änderungsprotokoll',
     'pages_revisions_changes' => 'Änderungen',
     'pages_revisions_current' => 'Aktuelle Version',
@@ -233,8 +237,8 @@ return [
         'message' => ':start :time. Achten Sie darauf, keine Änderungen von anderen Benutzern zu überschreiben!',
     ],
     'pages_draft_discarded' => 'Entwurf verworfen. Der aktuelle Seiteninhalt wurde geladen.',
-    'pages_specific' => 'Specific Page',
-    'pages_is_template' => 'Page Template',
+    'pages_specific' => 'Spezifische Seite',
+    'pages_is_template' => 'Seitenvorlage',
 
     // Editor Sidebar
     'page_tags' => 'Seiten-Schlagwörter',
@@ -243,11 +247,11 @@ return [
     'shelf_tags' => 'Regal-Schlagwörter',
     'tag' => 'Schlagwort',
     'tags' =>  'Schlagwörter',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'Schlagwort Name',
     'tag_value' => 'Inhalt (Optional)',
     'tags_explain' => "Fügen Sie Schlagwörter hinzu, um Ihren Inhalt zu kategorisieren.\nSie können einen erklärenden Inhalt hinzufügen, um eine genauere Unterteilung vorzunehmen.",
     'tags_add' => 'Weiteres Schlagwort hinzufügen',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Diesen Tag entfernen',
     'attachments' => 'Anhänge',
     'attachments_explain' => 'Sie können auf Ihrer Seite Dateien hochladen oder Links hinzufügen. Diese werden in der Seitenleiste angezeigt.',
     'attachments_explain_instant_save' => 'Änderungen werden direkt gespeichert.',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Datei hochladen',
     'attachments_link' => 'Link hinzufügen',
     'attachments_set_link' => 'Link setzen',
-    'attachments_delete_confirm' => 'Klicken Sie erneut auf löschen, um diesen Anhang zu entfernen.',
+    'attachments_delete' => 'Sind Sie sicher, dass Sie diesen Anhang löschen möchten?',
     'attachments_dropzone' => 'Ziehen Sie Dateien hierher oder klicken Sie, um eine Datei auszuwählen',
     'attachments_no_files' => 'Es wurden bisher keine Dateien hochgeladen.',
     'attachments_explain_link' => 'Wenn Sie keine Datei hochladen möchten, können Sie stattdessen einen Link hinzufügen. Dieser Link kann auf eine andere Seite oder eine Datei im Internet weisen.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Link zu einer Datei',
     'attachments_link_url_hint' => 'URL einer Seite oder Datei',
     'attach' => 'Hinzufügen',
+    'attachments_insert_link' => 'Link zum Anhang auf Seite einfügen',
     'attachments_edit_file' => 'Datei bearbeiten',
     'attachments_edit_file_name' => 'Dateiname',
     'attachments_edit_drop_upload' => 'Ziehen Sie Dateien hierher, um diese hochzuladen und zu überschreiben',
@@ -273,12 +278,12 @@ return [
     'attachments_file_uploaded' => 'Datei erfolgreich hochgeladen',
     'attachments_file_updated' => 'Datei erfolgreich aktualisiert',
     'attachments_link_attached' => 'Link erfolgreich der Seite hinzugefügt',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Vorlagen',
+    'templates_set_as_template' => 'Seite ist eine Vorlage',
+    'templates_explain_set_as_template' => 'Sie können diese Seite als Vorlage festlegen, damit deren Inhalt beim Erstellen anderer Seiten verwendet werden kann. Andere Benutzer können diese Vorlage verwenden, wenn sie die Zugriffsrechte für diese Seite haben.',
+    'templates_replace_content' => 'Seiteninhalt ersetzen',
+    'templates_append_content' => 'An Seiteninhalt anhängen',
+    'templates_prepend_content' => 'Seiteninhalt voranstellen',
 
     // Profile View
     'profile_user_for_x' => 'Benutzer seit :time',
index ccec60561138cfc526bbe7d2dac846c2305badf6..32be9b248f5933fe8942701363d12b3f15249a78 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'Die E-Mail-Adresse ist bereits bestätigt. Bitte melden Sie sich an.',
     'email_confirmation_invalid' => 'Der Bestätigungslink ist nicht gültig oder wurde bereits verwendet. Bitte registrieren Sie sich erneut.',
     'email_confirmation_expired' => 'Der Bestätigungslink ist abgelaufen. Es wurde eine neue Bestätigungs-E-Mail gesendet.',
+    'email_confirmation_awaiting' => 'Die E-Mail-Adresse für das verwendete Konto muss bestätigt werden',
     'ldap_fail_anonymous' => 'Anonymer LDAP-Zugriff ist fehlgeschlafgen',
     'ldap_fail_authed' => 'LDAP-Zugriff mit DN und Passwort ist fehlgeschlagen',
     'ldap_extension_not_installed' => 'LDAP-PHP-Erweiterung ist nicht installiert.',
     'ldap_cannot_connect' => 'Die Verbindung zum LDAP-Server ist fehlgeschlagen. Beim initialen Verbindungsaufbau trat ein Fehler auf.',
+    'saml_already_logged_in' => 'Sie sind bereits angemeldet',
+    'saml_user_not_registered' => 'Kein Benutzer mit ID :name registriert und die automatische Registrierung ist deaktiviert',
+    'saml_no_email_address' => 'Es konnte keine E-Mail-Adresse für diesen Benutzer in den vom externen Authentifizierungssystem zur Verfügung gestellten Daten gefunden werden',
+    'saml_invalid_response_id' => 'Die Anfrage vom externen Authentifizierungssystem wird von einem von dieser Anwendung gestarteten Prozess nicht erkannt. Das Zurückgehen nach einem Login könnte dieses Problem verursachen.',
+    'saml_fail_authed' => 'Anmeldung mit :system fehlgeschlagen, System konnte keine erfolgreiche Autorisierung bereitstellen',
     'social_no_action_defined' => 'Es ist keine Aktion definiert',
     'social_login_bad_response' => "Fehler bei der :socialAccount-Anmeldung: \n:error",
     'social_account_in_use' => 'Dieses :socialAccount-Konto wird bereits verwendet. Bitte melden Sie sich mit dem :socialAccount-Konto an.',
@@ -27,7 +33,7 @@ return [
     'social_account_register_instructions' => 'Wenn Sie bisher keinen Social-Media Konto besitzen, können Sie ein solches Konto mit der :socialAccount Option anlegen.',
     'social_driver_not_found' => 'Treiber für Social-Media-Konten nicht gefunden',
     'social_driver_not_configured' => 'Ihr :socialAccount-Konto ist nicht korrekt konfiguriert.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Dieser Einladungslink ist abgelaufen. Sie können stattdessen versuchen, Ihr Passwort zurückzusetzen.',
 
     // System
     'path_not_writable' => 'Die Datei kann nicht in den angegebenen Pfad :filePath hochgeladen werden. Stellen Sie sicher, dass dieser Ordner auf dem Server beschreibbar ist.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'Der Upload der Datei ist abgelaufen.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Die Seite stimmte nach dem Hochladen des Anhangs nicht überein.',
     'attachment_not_found' => 'Anhang konnte nicht gefunden werden.',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Seite nicht gefunden',
     'sorry_page_not_found' => 'Entschuldigung. Die Seite, die Sie angefordert haben, wurde nicht gefunden.',
+    'sorry_page_not_found_permission_warning' => 'Wenn Sie erwartet haben, dass diese Seite existiert, haben Sie möglicherweise nicht die Berechtigung, sie anzuzeigen.',
     'return_home' => 'Zurück zur Startseite',
     'error_occurred' => 'Es ist ein Fehler aufgetreten',
     'app_down' => ':appName befindet sich aktuell im Wartungsmodus.',
     'back_soon' => 'Wir werden so schnell wie möglich wieder online sein.',
 
+    // API errors
+    'api_no_authorization_found' => 'Kein Autorisierungs-Token für die Anfrage gefunden',
+    'api_bad_authorization_format' => 'Ein Autorisierungs-Token wurde auf die Anfrage gefunden, aber das Format schien falsch zu sein',
+    'api_user_token_not_found' => 'Es wurde kein passender API-Token für den angegebenen Autorisierungs-Token gefunden',
+    'api_incorrect_token_secret' => 'Das für den angegebenen API-Token angegebene Kennwort ist falsch',
+    'api_user_no_api_permission' => 'Der Besitzer des verwendeten API-Token hat keine Berechtigung für API-Aufrufe',
+    'api_user_token_expired' => 'Das verwendete Autorisierungs-Token ist abgelaufen',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Fehler beim Senden einer Test E-Mail:',
+
 ];
index 74149a7eda09f40a2f07d84ff535584a6882f8de..3da092cb8c7e234a0f5a852afb272e183a5999d9 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Passwörter müssen aus mindestens sechs Zeichen bestehen und mit der eingegebenen Wiederholung übereinstimmen.',
     'user' => "Es wurde kein Benutzer mit dieser E-Mail-Adresse gefunden.",
-    'token' => 'Dieser Link zum Zurücksetzen des Passwortes ist ungültig!',
+    'token' => 'Der Link zum Zurücksetzen Ihres Passworts ist entweder ungültig oder abgelaufen.',
     'sent' => 'Der Link zum Zurücksetzen Ihres Passwortes wurde Ihnen per E-Mail zugesendet.',
     'reset' => 'Ihr Passwort wurde zurückgesetzt!',
 
index fa7df43f8893c8eaeab6487d112e09612a190cda..60d4361d9e851225b235b46315ab0070f185ce34 100644 (file)
@@ -23,13 +23,13 @@ return [
     'app_public_access_toggle' => 'Öffentlichen Zugriff erlauben',
     'app_public_viewing' => 'Öffentliche Ansicht erlauben?',
     'app_secure_images' => 'Erhöhte Sicherheit für hochgeladene Bilder aktivieren?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_toggle' => 'Aktiviere Bild-Upload höherer Sicherheit',
     'app_secure_images_desc' => 'Aus Leistungsgründen sind alle Bilder öffentlich sichtbar. Diese Option fügt zufällige, schwer zu eratene, Zeichenketten zu Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnisindizes deaktiviert sind, um einen einfachen Zugriff zu verhindern.',
     'app_editor' => 'Seiteneditor',
     'app_editor_desc' => 'Wählen Sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.',
     'app_custom_html' => 'Benutzerdefinierter HTML <head> Inhalt',
     'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugefügt wird, wird am Ende der <head> Sektion jeder Seite eingefügt. Diese kann praktisch sein, um CSS Styles anzupassen oder Analytics-Code hinzuzufügen.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'Benutzerdefinierte HTML-Kopfzeileninhalte sind auf dieser Einstellungsseite deaktiviert, um sicherzustellen, dass alle Änderungen rückgängig gemacht werden können.',
     'app_logo' => 'Anwendungslogo',
     'app_logo_desc' => 'Dieses Bild sollte 43px hoch sein.
 Größere Bilder werden verkleinert.',
@@ -43,12 +43,22 @@ Wenn Sie nicht eingeben, wird die Anwendung auf die Standardfarbe zurückgesetzt
     'app_disable_comments_toggle' => 'Kommentare deaktivieren',
     'app_disable_comments_desc' => 'Deaktiviert Kommentare über alle Seiten in der Anwendung. Vorhandene Kommentare werden nicht angezeigt.',
 
+    // Color settings
+    'content_colors' => 'Inhaltsfarben',
+    'content_colors_desc' => 'Legt Farben für alle Elemente in der Seitenorganisationshierarchie fest. Die Auswahl von Farben mit einer ähnlichen Helligkeit wie die Standardfarben wird zur Lesbarkeit empfohlen.',
+    'bookshelf_color' => 'Regalfarbe',
+    'book_color' => 'Buchfarbe',
+    'chapter_color' => 'Kapitelfarbe',
+    'page_color' => 'Seitenfarbe',
+    'page_draft_color' => 'Seitenentwurfsfarbe',
+
     // Registration Settings
     'reg_settings' => 'Registrierungseinstellungen',
     'reg_enable' => 'Registrierung erlauben?',
     'reg_enable_toggle' => 'Registrierung erlauben',
     'reg_enable_desc' => 'Wenn die Registrierung erlaubt ist, kann sich der Benutzer als Anwendungsbenutzer anmelden. Bei der Registrierung erhält er eine einzige, voreingestellte Benutzerrolle.',
     'reg_default_role' => 'Standard-Benutzerrolle nach Registrierung',
+    'reg_enable_external_warning' => 'Die obige Option wird ignoriert, während eine externe LDAP oder SAML Authentifizierung aktiv ist. Benutzerkonten für nicht existierende Mitglieder werden automatisch erzeugt, wenn die Authentifizierung gegen das verwendete externe System erfolgreich ist.',
     'reg_email_confirmation' => 'Bestätigung per E-Mail',
     'reg_email_confirmation_toggle' => 'Bestätigung per E-Mail erforderlich',
     'reg_confirm_email_desc' => 'Falls die Einschränkung für Domains genutzt wird, ist die Bestätigung per E-Mail zwingend erforderlich und der untenstehende Wert wird ignoriert.',
@@ -61,11 +71,53 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'maint' => 'Wartung',
     'maint_image_cleanup' => 'Bilder bereinigen',
     'maint_image_cleanup_desc' => "Überprüft Seiten- und Versionsinhalte auf ungenutzte und mehrfach vorhandene Bilder. Erstellen Sie vor dem Start ein Backup Ihrer Datenbank und Bilder.",
-    'maint_image_cleanup_ignore_revisions' => 'Bilder in Versionen ignorieren',
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Reinigung starten',
     'maint_image_cleanup_warning' => ':count eventuell unbenutze Bilder wurden gefunden. Möchten Sie diese Bilder löschen?',
     'maint_image_cleanup_success' => ':count eventuell unbenutze Bilder wurden gefunden und gelöscht.',
     'maint_image_cleanup_nothing_found' => 'Keine unbenutzen Bilder gefunden. Nichts zu löschen!',
+    'maint_send_test_email' => 'Test Email versenden',
+    'maint_send_test_email_desc' => 'Dies sendet eine Test E-Mail an Ihre in Ihrem Profil angegebene E-Mail-Adresse.',
+    'maint_send_test_email_run' => 'Sende eine Test E-Mail',
+    'maint_send_test_email_success' => 'E-Mail wurde an :address gesendet',
+    'maint_send_test_email_mail_subject' => 'Test E-Mail',
+    'maint_send_test_email_mail_greeting' => 'E-Mail-Versand scheint zu funktionieren!',
+    'maint_send_test_email_mail_text' => 'Glückwunsch! Da Sie diese E-Mail Benachrichtigung erhalten haben, scheinen Ihre E-Mail-Einstellungen korrekt konfiguriert zu sein.',
+    'maint_recycle_bin_desc' => 'Gelöschte Regale, Bücher, Kapitel & Seiten werden in den Papierkorb verschoben, so dass sie wiederhergestellt oder dauerhaft gelöscht werden können. Ältere Gegenstände im Papierkorb können, in Abhängigkeit von der Systemkonfiguration, nach einer Weile automatisch entfernt werden.',
+    'maint_recycle_bin_open' => 'Papierkorb öffnen',
+
+    // Recycle Bin
+    'recycle_bin' => 'Papierkorb',
+    'recycle_bin_desc' => 'Hier können Sie gelöschte Elemente wiederherstellen oder sie dauerhaft aus dem System entfernen. Diese Liste ist nicht gefiltert, im Gegensatz zu ähnlichen Aktivitätslisten im System, wo Berechtigungsfilter angewendet werden.',
+    'recycle_bin_deleted_item' => 'Gelöschtes Element',
+    'recycle_bin_deleted_by' => 'Gelöscht von',
+    'recycle_bin_deleted_at' => 'Löschzeitpunkt',
+    'recycle_bin_permanently_delete' => 'Dauerhaft löschen',
+    'recycle_bin_restore' => 'Wiederherstellen',
+    'recycle_bin_contents_empty' => 'Der Papierkorb ist derzeit leer',
+    'recycle_bin_empty' => 'Papierkorb leeren',
+    'recycle_bin_empty_confirm' => 'Dies wird alle Gegenstände im Papierkorb dauerhaft entfernen, einschließlich der Inhalte, die darin enthalten sind. Sind Sie sicher, dass Sie den Papierkorb leeren möchten?',
+    'recycle_bin_destroy_confirm' => 'Diese Aktion wird dieses Element zusammen mit allen unten aufgeführten Unterelementen dauerhaft aus dem System löschen und Sie werden nicht in der Lage sein, diesen Inhalt wiederherzustellen. Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten?',
+    'recycle_bin_destroy_list' => 'Zu löschende Elemente',
+    'recycle_bin_restore_list' => 'Zu wiederherzustellende Elemente',
+    'recycle_bin_restore_confirm' => 'Mit dieser Aktion wird das gelöschte Element einschließlich aller untergeordneten Elemente an seinen ursprünglichen Ort wiederherstellen. Wenn der ursprüngliche Ort gelöscht wurde und sich nun im Papierkorb befindet, muss auch das übergeordnete Element wiederhergestellt werden.',
+    'recycle_bin_restore_deleted_parent' => 'Das übergeordnete Elements wurde ebenfalls gelöscht. Dieses Element wird weiterhin als gelöscht zählen, bis auch das übergeordnete Element wiederhergestellt wurde.',
+    'recycle_bin_destroy_notification' => ':count Elemente wurden aus dem Papierkorb gelöscht.',
+    'recycle_bin_restore_notification' => ':count Elemente wurden aus dem Papierkorb wiederhergestellt.',
+
+    // Audit Log
+    'audit' => 'Audit-Protokoll',
+    'audit_desc' => 'Dieses Audit-Protokoll zeigt eine Liste der Aktivitäten an, welche vom System protokolliert werden. Im Gegensatz zu den anderen Aktivitätslisten im System, bei denen Berechtigungen angewendet werden, ist diese Liste ungefiltert.',
+    'audit_event_filter' => 'Ereignisfilter',
+    'audit_event_filter_no_filter' => 'Kein Filter',
+    'audit_deleted_item' => 'Gelöschtes Objekt',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'Benutzer',
+    'audit_table_event' => 'Ereignis',
+    'audit_table_related' => 'Verknüpftes Element oder Detail',
+    'audit_table_date' => 'Aktivitätsdatum',
+    'audit_date_from' => 'Zeitraum von',
+    'audit_date_to' => 'Zeitraum bis',
 
     // Role Settings
     'roles' => 'Rollen',
@@ -88,9 +140,11 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'role_manage_roles' => 'Rollen und Rollen-Berechtigungen verwalten',
     'role_manage_entity_permissions' => 'Alle Buch-, Kapitel- und Seiten-Berechtigungen verwalten',
     'role_manage_own_entity_permissions' => 'Nur Berechtigungen eigener Bücher, Kapitel und Seiten verwalten',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => 'Seitenvorlagen verwalten',
+    'role_access_api' => 'Systemzugriffs-API',
     'role_manage_settings' => 'Globaleinstellungen verwalten',
     'role_asset' => 'Berechtigungen',
+    'roles_system_warning' => 'Beachten Sie, dass der Zugriff auf eine der oben genannten drei Berechtigungen einem Benutzer erlauben kann, seine eigenen Berechtigungen oder die Rechte anderer im System zu ändern. Weisen Sie nur Rollen, mit diesen Berechtigungen, vertrauenswürdigen Benutzern zu.',
     'role_asset_desc' => 'Diese Berechtigungen gelten für den Standard-Zugriff innerhalb des Systems. Berechtigungen für Bücher, Kapitel und Seiten überschreiben diese Berechtigungenen.',
     'role_asset_admins' => 'Administratoren erhalten automatisch Zugriff auf alle Inhalte, aber diese Optionen können Oberflächenoptionen ein- oder ausblenden.',
     'role_all' => 'Alle',
@@ -106,6 +160,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'user_profile' => 'Benutzerprofil',
     'users_add_new' => 'Benutzer hinzufügen',
     'users_search' => 'Benutzer suchen',
+    'users_latest_activity' => 'Neueste Aktivitäten',
     'users_details' => 'Benutzerdetails',
     'users_details_desc' => 'Legen Sie für diesen Benutzer einen Anzeigenamen und eine E-Mail-Adresse fest. Die E-Mail-Adresse wird bei der Anmeldung verwendet.',
     'users_details_desc_no_email' => 'Legen Sie für diesen Benutzer einen Anzeigenamen fest, damit andere ihn erkennen können.',
@@ -113,17 +168,20 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'users_role_desc' => 'Wählen Sie aus, welchen Rollen dieser Benutzer zugeordnet werden soll. Wenn ein Benutzer mehreren Rollen zugeordnet ist, werden die Berechtigungen dieser Rollen gestapelt und er erhält alle Fähigkeiten der zugewiesenen Rollen.',
     'users_password' => 'Benutzerpasswort',
     'users_password_desc' => 'Legen Sie ein Passwort fest, mit dem Sie sich anmelden möchten. Diese muss mindestens 5 Zeichen lang sein.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
+    'users_send_invite_text' => 'Sie können diesem Benutzer eine Einladungs-E-Mail senden, die es ihm erlaubt, sein eigenes Passwort zu setzen, andernfalls können Sie sein Passwort selbst setzen.',
+    'users_send_invite_option' => 'Benutzer-Einladungs-E-Mail senden',
     'users_external_auth_id' => 'Externe Authentifizierungs-ID',
-    'users_external_auth_id_desc' => 'Dies ist die ID, die verwendet wird, um diesen Benutzer bei der Kommunikation mit Ihrem LDAP-System abzugleichen.',
+    'users_external_auth_id_desc' => 'Dies ist die ID, mit der dieser Benutzer bei der Kommunikation mit Ihrem externen Authentifizierungssystem übereinstimmt.',
     'users_password_warning' => 'Füllen Sie die folgenden Felder nur aus, wenn Sie Ihr Passwort ändern möchten:',
     'users_system_public' => 'Dieser Benutzer repräsentiert alle unangemeldeten Benutzer, die diese Seite betrachten. Er kann nicht zum Anmelden benutzt werden, sondern wird automatisch zugeordnet.',
     'users_delete' => 'Benutzer löschen',
     'users_delete_named' => 'Benutzer ":userName" löschen',
     'users_delete_warning' => 'Der Benutzer ":userName" wird aus dem System gelöscht.',
     'users_delete_confirm' => 'Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?',
-    'users_delete_success' => 'Benutzer erfolgreich gelöscht.',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Benutzer bearbeiten',
     'users_edit_profile' => 'Profil bearbeiten',
     'users_edit_success' => 'Benutzer erfolgreich aktualisisert',
@@ -137,6 +195,32 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'users_social_disconnect' => 'Social-Media-Konto lösen',
     'users_social_connected' => ':socialAccount-Konto wurde erfolgreich mit dem Profil verknüpft.',
     'users_social_disconnected' => ':socialAccount-Konto wurde erfolgreich vom Profil gelöst.',
+    'users_api_tokens' => 'API-Token',
+    'users_api_tokens_none' => 'Für diesen Benutzer wurden keine API-Token erstellt',
+    'users_api_tokens_create' => 'Token erstellen',
+    'users_api_tokens_expires' => 'Endet',
+    'users_api_tokens_docs' => 'API Dokumentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Neuen API-Token erstellen',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Geben Sie Ihrem Token einen aussagekräftigen Namen als spätere Erinnerung an seinen Verwendungszweck.',
+    'user_api_token_expiry' => 'Ablaufdatum',
+    'user_api_token_expiry_desc' => 'Legen Sie ein Datum fest, an dem dieser Token abläuft. Nach diesem Datum funktionieren Anfragen, die mit diesem Token gestellt werden, nicht mehr. Wenn Sie dieses Feld leer lassen, wird ein Ablaufdatum von 100 Jahren in der Zukunft festgelegt.',
+    'user_api_token_create_secret_message' => 'Unmittelbar nach der Erstellung dieses Tokens wird eine "Token ID" & ein "Token Kennwort" generiert und angezeigt. Das Kennwort wird nur ein einziges Mal angezeigt. Stellen Sie also sicher, dass Sie den Inhalt an einen sicheren Ort kopieren, bevor Sie fortfahren.',
+    'user_api_token_create_success' => 'API-Token erfolgreich erstellt',
+    'user_api_token_update_success' => 'API-Token erfolgreich aktualisiert',
+    'user_api_token' => 'API-Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'Dies ist ein nicht editierbarer, vom System generierter Identifikator für diesen Token, welcher bei API-Anfragen angegeben werden muss.',
+    'user_api_token_secret' => 'Token Kennwort',
+    'user_api_token_secret_desc' => 'Dies ist ein systemgeneriertes Kennwort für diesen Token, das bei API-Anfragen zur Verfügung gestellt werden muss. Es wird nur dieses eine Mal angezeigt, deshalb kopieren Sie diesen Wert an einen sicheren und geschützten Ort.',
+    'user_api_token_created' => 'Token erstellt :timeAgo',
+    'user_api_token_updated' => 'Token aktualisiert :timeAgo',
+    'user_api_token_delete' => 'Lösche Token',
+    'user_api_token_delete_warning' => 'Dies löscht den API-Token mit dem Namen \':tokenName\' vollständig aus dem System.',
+    'user_api_token_delete_confirm' => 'Sind Sie sicher, dass Sie diesen API-Token löschen möchten?',
+    'user_api_token_delete_success' => 'API-Token erfolgreich gelöscht',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -144,26 +228,32 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bulgarisch',
+        'cs' => 'Česky',
+        'da' => 'Dänisch',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'Hebräisch',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 1cf2176d5283129f916a755b3df083f52a1dff3e..8b1febc85d8da7078606d6c0e76f8f8a6364db2e 100644 (file)
@@ -30,19 +30,19 @@ return [
     'digits'               => ':attribute muss :digits Stellen haben.',
     'digits_between'       => ':attribute muss zwischen :min und :max Stellen haben.',
     'email'                => ':attribute muss eine valide E-Mail-Adresse sein.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ':attribute muss mit einem der folgenden Werte: :values enden',
     'filled'               => ':attribute ist erforderlich.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute muss größer als :value sein.',
+        'file'    => ':attribute muss mindestens :value Kilobytes groß sein.',
+        'string'  => ':attribute muss mehr als :value Zeichen haben.',
+        'array'   => ':attribute muss mindestens :value Elemente haben.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => ':attribute muss größer-gleich :value sein.',
+        'file'    => ':attribute muss mindestens :value Kilobytes groß sein.',
+        'string'  => ':attribute muss mindestens :value Zeichen enthalten.',
+        'array'   => ':attribute muss :value Elemente oder mehr haben.',
     ],
     'exists'               => ':attribute ist ungültig.',
     'image'                => ':attribute muss ein Bild sein.',
@@ -50,20 +50,20 @@ return [
     'in'                   => ':attribute ist ungültig.',
     'integer'              => ':attribute muss eine Zahl sein.',
     'ip'                   => ':attribute muss eine valide IP-Adresse sein.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ipv4'                 => ':attribute muss eine gültige IPv4 Adresse sein.',
+    'ipv6'                 => ':attribute muss eine gültige IPv6-Adresse sein.',
+    'json'                 => 'Das Attribut muss eine gültige JSON-Zeichenfolge sein.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute muss kleiner sein :value sein.',
+        'file'    => ':attribute muss kleiner als :value Kilobytes sein.',
+        'string'  => ':attribute muss weniger als :value Zeichen haben.',
+        'array'   => ':attribute muss weniger als :value Elemente haben.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute muss kleiner oder gleich :value sein.',
+        'file'    => ':attribute muss kleiner oder gleich :value Kilobytes sein.',
+        'string'  => ':attribute darf höchstens :value Zeichen besitzen.',
+        'array'   => ':attribute darf höchstens :value Elemente haben.',
     ],
     'max'                  => [
         'numeric' => ':attribute darf nicht größer als :max sein.',
@@ -80,7 +80,7 @@ return [
     ],
     'no_double_extension'  => ':attribute darf nur eine gültige Dateiendung',
     'not_in'               => ':attribute ist ungültig.',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => ':attribute ist kein valides Format.',
     'numeric'              => ':attribute muss eine Zahl sein.',
     'regex'                => ':attribute ist in einem ungültigen Format.',
     'required'             => ':attribute ist erforderlich.',
@@ -90,6 +90,7 @@ return [
     'required_without'     => ':attribute ist erforderlich, wenn :values nicht vorhanden ist.',
     'required_without_all' => ':attribute ist erforderlich, wenn :values nicht vorhanden sind.',
     'same'                 => ':attribute und :other müssen übereinstimmen.',
+    'safe_url'             => 'Der angegebene Link ist möglicherweise nicht sicher.',
     'size'                 => [
         'numeric' => ':attribute muss :size sein.',
         'file'    => ':attribute muss :size Kilobytes groß sein.',
index 170a1910825ac8637a702183c274b87f55dda810..01c54bf4f714b3ba616da646e72d32115245653e 100644 (file)
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'kommentiert',
+    'permissions_update'          => 'hat die Berechtigungen aktualisiert',
 ];
index ffe518b931566930720db97784ae5fbd854a6f4f..918598533969b4d69d2c38f9a03b42d367f79069 100644 (file)
@@ -20,7 +20,7 @@ return [
     'username' => 'Benutzername',
     'email' => 'E-Mail',
     'password' => 'Passwort',
-    'password_confirm' => 'Passwort best&auml;tigen',
+    'password_confirm' => 'Passwort bestätigen',
     'password_hint' => 'Mindestlänge: 7 Zeichen',
     'forgot_password' => 'Passwort vergessen?',
     'remember_me' => 'Angemeldet bleiben',
@@ -32,22 +32,22 @@ return [
     'social_registration' => 'Mit Sozialem Netzwerk registrieren',
     'social_registration_text' => 'Mit einer dieser Dienste registrieren oder anmelden',
 
-    'register_thanks' => 'Vielen Dank für Ihre Registrierung!',
+    'register_thanks' => 'Vielen Dank für deine Registrierung!',
     'register_confirm' => 'Bitte prüfe Deinen Posteingang und bestätig die Registrierung.',
     'registrations_disabled' => 'Eine Registrierung ist momentan nicht möglich',
     'registration_email_domain_invalid' => 'Du kannst dich mit dieser E-Mail nicht registrieren.',
-    'register_success' => 'Vielen Dank für Deine Registrierung! Die Daten sind gespeichert und Du bist angemeldet.',
+    'register_success' => 'Vielen Dank für deine Registrierung! Du bist jetzt registriert und eingeloggt.',
 
 
     // Password Reset
     'reset_password' => 'Passwort vergessen',
     'reset_password_send_instructions' => 'Bitte gib Deine E-Mail-Adresse ein. Danach erhältst Du eine E-Mail mit einem Link zum Zurücksetzen Deines Passwortes.',
     'reset_password_send_button' => 'Passwort zurücksetzen',
-    'reset_password_sent_success' => 'Eine E-Mail mit dem Link zum Zurücksetzen Deines Passwortes wurde an :email gesendet.',
+    'reset_password_sent' => 'Ein Link zum Zurücksetzen des Passworts wird an :email gesendet, wenn diese E-Mail-Adresse im System gefunden wird.',
     'reset_password_success' => 'Dein Passwort wurde erfolgreich zurückgesetzt.',
     'email_reset_subject' => 'Passwort zurücksetzen für :appName',
     'email_reset_text' => 'Du erhältsts diese E-Mail, weil jemand versucht hat, Dein Passwort zurückzusetzen.',
-    'email_reset_not_requested' => 'Wenn Du das nicht warst, brauchst Du nichts weiter zu tun.',
+    'email_reset_not_requested' => 'Wenn du das zurücksetzen des Passworts nicht angefordert hast, ist keine weitere Aktion erforderlich.',
 
 
     // Email Confirmation
@@ -55,8 +55,8 @@ return [
     'email_confirm_greeting' => 'Danke, dass Du dich für :appName registrierst hast!',
     'email_confirm_text' => 'Bitte bestätige Deine E-Mail-Adresse, indem Du auf die Schaltfläche klickst:',
     'email_confirm_action' => 'E-Mail-Adresse bestätigen',
-    'email_confirm_send_error' => 'Leider konnte die für die Registrierung notwendige E-Mail zur Bestätigung Deine E-Mail-Adresse nicht versandt werden. Bitte kontaktiere den Systemadministrator!',
-    'email_confirm_success' => 'Deine E-Mail-Adresse wurde best&auml;tigt!',
+    'email_confirm_send_error' => 'Leider konnte die für die Registrierung notwendige E-Mail zur Bestätigung Deiner E-Mail-Adresse nicht versandt werden. Bitte kontaktiere den Systemadministrator!',
+    'email_confirm_success' => 'Deine E-Mail-Adresse wurde bestätigt!',
     'email_confirm_resent' => 'Bestätigungs-E-Mail wurde erneut versendet, bitte überprüfe Deinen Posteingang.',
 
     'email_not_confirmed' => 'E-Mail-Adresse ist nicht bestätigt',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => 'Bestätigungs-E-Mail erneut senden',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Du wurdest eingeladen :appName beizutreten!',
+    'user_invite_email_greeting' => 'Ein Konto wurde für dich auf :appName erstellt.',
+    'user_invite_email_text' => 'Klicke auf die Schaltfläche unten, um ein Passwort festzulegen und Zugriff zu erhalten:',
+    'user_invite_email_action' => 'Konto-Passwort festlegen',
+    'user_invite_page_welcome' => 'Willkommen bei :appName!',
+    'user_invite_page_text' => 'Um die Anmeldung abzuschließen und Zugriff auf :appName zu bekommen muss noch ein Passwort festgelegt werden. Dieses wird in Zukunft zum Einloggen benötigt.',
+    'user_invite_page_confirm_button' => 'Passwort bestätigen',
+    'user_invite_success' => 'Das Passwort wurde gesetzt, du hast nun Zugriff auf :appName!'
 ];
\ No newline at end of file
index 8d9b3aeab1307a8ae70915916232bec2c5c9346c..c18f786f61c495c7728372ea4a55861ad97c3dd9 100644 (file)
@@ -33,17 +33,19 @@ return [
     'copy' => 'Kopieren',
     'reply' => 'Antworten',
     'delete' => 'Löschen',
+    'delete_confirm' => 'Löschen Bestätigen',
     'search' => 'Suchen',
     'search_clear' => 'Suche löschen',
     'reset' => 'Zurücksetzen',
     'remove' => 'Entfernen',
     'add' => 'Hinzufügen',
+    'fullscreen' => 'Vollbild',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
+    'sort_options' => 'Sortieroptionen',
+    'sort_direction_toggle' => 'Sortierreihenfolge umkehren',
+    'sort_ascending' => 'Aufsteigend sortieren',
+    'sort_descending' => 'Absteigend sortieren',
     'sort_name' => 'Name',
     'sort_created_at' => 'Erstellungsdatum',
     'sort_updated_at' => 'Aktualisierungsdatum',
@@ -59,12 +61,14 @@ return [
     'grid_view' => 'Gitteransicht',
     'list_view' => 'Listenansicht',
     'default' => 'Voreinstellung',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Brotkrumen',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Profilmenü',
     'view_profile' => 'Profil ansehen',
     'edit_profile' => 'Profil bearbeiten',
+    'dark_mode' => 'Dunkler Modus',
+    'light_mode' => 'Heller Modus',
 
     // Layout tabs
     'tab_info' => 'Info',
index 4d98235a4370b6a243b7ec90ee3cd7553232bc2c..56060ea236e1e30f6eeacfc44622cdc24f0e29ec 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Mehr',
     'image_image_name' => 'Bildname',
     'image_delete_used' => 'Dieses Bild wird auf den folgenden Seiten benutzt. ',
-    'image_delete_confirm' => 'Bitte klicke erneut auf löschen, wenn Du dieses Bild wirklich entfernen möchtest.',
+    'image_delete_confirm_text' => 'Bist Du sicher, dass Du diese Seite löschen möchtest?',
     'image_select_image' => 'Bild auswählen',
     'image_dropzone' => 'Ziehe Bilder hierher oder klicke hier, um ein Bild auszuwählen',
     'images_deleted' => 'Bilder gelöscht',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Code editieren',
     'code_language' => 'Code Sprache',
     'code_content' => 'Code Inhalt',
+    'code_session_history' => 'Sitzungsverlauf',
     'code_save' => 'Code speichern',
 ];
index 1a64f25a7932596d379e27f719586924c11a2363..b899324732faebf2f0497981f879e586a921ed3b 100644 (file)
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => 'Erstellt: :timeLength von :user',
     'meta_updated' => 'Zuletzt aktualisiert: :timeLength',
     'meta_updated_name' => 'Zuletzt aktualisiert: :timeLength von :user',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Eintrag auswählen',
     'images' => 'Bilder',
     'my_recent_drafts' => 'Meine kürzlichen Entwürfe',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Wenn individuelle Berechtigungen aktiviert werden, überschreiben diese Einstellungen durch Rollen zugewiesene Berechtigungen.',
     'permissions_enable' => 'Individuelle Berechtigungen aktivieren',
     'permissions_save' => 'Berechtigungen speichern',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Suchergebnisse',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Keine Seiten gefunden',
     'search_for_term' => 'Nach :term suchen',
     'search_more' => 'Mehr Ergebnisse',
-    'search_filters' => 'Filter',
+    'search_advanced' => 'Erweiterte Suche',
+    'search_terms' => 'Suchbegriffe',
     'search_content_type' => 'Inhaltstyp',
     'search_exact_matches' => 'Exakte Treffer',
     'search_tags' => 'Nach Schlagwort suchen',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Neues Kapitel anlegen',
     'chapters_delete' => 'Kapitel entfernen',
     'chapters_delete_named' => 'Kapitel ":chapterName" entfernen',
-    'chapters_delete_explain' => 'Das Kapitel ":chapterName" wird gelöscht und alle zugehörigen Seiten dem übergeordneten Buch zugeordnet.',
+    'chapters_delete_explain' => 'Dies löscht das Kapitel mit dem Namen \':chapterName\'. Alle Seiten, die innerhalb dieses Kapitels existieren, werden ebenfalls gelöscht.',
     'chapters_delete_confirm' => 'Bist Du sicher, dass Du dieses Kapitel löschen möchtest?',
     'chapters_edit' => 'Kapitel bearbeiten',
     'chapters_edit_named' => 'Kapitel ":chapterName" bearbeiten',
@@ -176,7 +179,7 @@ return [
     'pages_delete_confirm' => 'Bist Du sicher, dass Du diese Seite löschen möchtest?',
     'pages_delete_draft_confirm' => 'Bist Du sicher, dass Du diesen Seitenentwurf löschen möchtest?',
     'pages_editing_named' => 'Seite ":pageName" bearbeiten',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Entwurfsoptionen',
     'pages_edit_save_draft' => 'Entwurf speichern',
     'pages_edit_draft' => 'Seitenentwurf bearbeiten',
     'pages_editing_draft' => 'Seitenentwurf bearbeiten',
@@ -207,11 +210,12 @@ return [
     'pages_revisions' => 'Seitenversionen',
     'pages_revisions_named' => 'Seitenversionen von ":pageName"',
     'pages_revision_named' => 'Seitenversion von ":pageName"',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Erstellt von',
     'pages_revisions_date' => 'Versionsdatum',
     'pages_revisions_number' => '#',
     'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_numbered_changes' => 'Revision #:id Änderungen',
     'pages_revisions_changelog' => 'Änderungsprotokoll',
     'pages_revisions_changes' => 'Änderungen',
     'pages_revisions_current' => 'Aktuelle Version',
@@ -233,8 +237,8 @@ return [
         'message' => ':start :time. Achte darauf, keine Änderungen von anderen Benutzern zu überschreiben!',
     ],
     'pages_draft_discarded' => 'Entwurf verworfen. Der aktuelle Seiteninhalt wurde geladen.',
-    'pages_specific' => 'Specific Page',
-    'pages_is_template' => 'Page Template',
+    'pages_specific' => 'Spezifische Seite',
+    'pages_is_template' => 'Seitenvorlage',
 
     // Editor Sidebar
     'page_tags' => 'Seiten-Schlagwörter',
@@ -243,11 +247,11 @@ return [
     'shelf_tags' => 'Regal-Schlagwörter',
     'tag' => 'Schlagwort',
     'tags' =>  'Schlagwörter',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'Schlagwort Name',
     'tag_value' => 'Inhalt (Optional)',
     'tags_explain' => "Füge Schlagwörter hinzu, um ihren Inhalt zu kategorisieren.\nDu kannst einen erklärenden Inhalt hinzufügen, um eine genauere Unterteilung vorzunehmen.",
     'tags_add' => 'Weiteres Schlagwort hinzufügen',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Diesen Tag entfernen',
     'attachments' => 'Anhänge',
     'attachments_explain' => 'Du kannst auf Deiner Seite Dateien hochladen oder Links hinzufügen. Diese werden in der Seitenleiste angezeigt.',
     'attachments_explain_instant_save' => 'Änderungen werden direkt gespeichert.',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Datei hochladen',
     'attachments_link' => 'Link hinzufügen',
     'attachments_set_link' => 'Link setzen',
-    'attachments_delete_confirm' => 'Klicke erneut auf löschen, um diesen Anhang zu entfernen.',
+    'attachments_delete' => 'Bist Du sicher, dass Du diesen Anhang löschen möchtest?',
     'attachments_dropzone' => 'Ziehe Dateien hierher oder klicke hier, um eine Datei auszuwählen',
     'attachments_no_files' => 'Es wurden bisher keine Dateien hochgeladen.',
     'attachments_explain_link' => 'Wenn Du keine Datei hochladen möchtest, kannst Du stattdessen einen Link hinzufügen. Dieser Link kann auf eine andere Seite oder eine Datei im Internet verweisen.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Link zu einer Datei',
     'attachments_link_url_hint' => 'URL einer Seite oder Datei',
     'attach' => 'Hinzufügen',
+    'attachments_insert_link' => 'Link zum Anhang auf Seite einfügen',
     'attachments_edit_file' => 'Datei bearbeiten',
     'attachments_edit_file_name' => 'Dateiname',
     'attachments_edit_drop_upload' => 'Ziehe Dateien hierher, um diese hochzuladen und zu überschreiben',
@@ -273,12 +278,12 @@ return [
     'attachments_file_uploaded' => 'Datei erfolgreich hochgeladen',
     'attachments_file_updated' => 'Datei erfolgreich aktualisiert',
     'attachments_link_attached' => 'Link erfolgreich der Seite hinzugefügt',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Vorlagen',
+    'templates_set_as_template' => 'Seite ist eine Vorlage',
+    'templates_explain_set_as_template' => 'Du kannst diese Seite als Vorlage festlegen, damit deren Inhalt beim Erstellen anderer Seiten verwendet werden kann. Andere Benutzer können diese Vorlage verwenden, wenn diese die Zugriffsrechte für diese Seite haben.',
+    'templates_replace_content' => 'Seiteninhalt ersetzen',
+    'templates_append_content' => 'An Seiteninhalt anhängen',
+    'templates_prepend_content' => 'Seiteninhalt voranstellen',
 
     // Profile View
     'profile_user_for_x' => 'Benutzer seit :time',
index 9b5b5166b810d40cab20f4f94e40cc0a26b3e7fa..656c3c23647133f1d12b38e0714bc68dd48f9a7c 100644 (file)
@@ -13,12 +13,18 @@ return [
     'email_already_confirmed' => 'Die E-Mail-Adresse ist bereits bestätigt. Bitte melde dich an.',
     'email_confirmation_invalid' => 'Der Bestätigungslink ist nicht gültig oder wurde bereits verwendet. Bitte registriere dich erneut.',
     'email_confirmation_expired' => 'Der Bestätigungslink ist abgelaufen. Es wurde eine neue Bestätigungs-E-Mail gesendet.',
+    'email_confirmation_awaiting' => 'Die E-Mail-Adresse für das verwendete Konto muss bestätigt werden',
     'ldap_fail_anonymous' => 'Anonymer LDAP-Zugriff ist fehlgeschlafgen',
     'ldap_fail_authed' => 'LDAP-Zugriff mit DN und Passwort ist fehlgeschlagen',
-    'ldap_extension_not_installed' => 'LDAP-PHP-Erweiterung ist nicht installiert.',
-    'ldap_cannot_connect' => 'Die Verbindung zum LDAP-Server ist fehlgeschlagen. Beim initialen Verbindungsaufbau trat ein Fehler auf.',
+    'ldap_extension_not_installed' => 'LDAP-PHP-Erweiterung ist nicht installiert',
+    'ldap_cannot_connect' => 'Die Verbindung zum LDAP-Server ist fehlgeschlagen. Beim initialen Verbindungsaufbau trat ein Fehler auf',
+    'saml_already_logged_in' => 'Du bist bereits angemeldet',
+    'saml_user_not_registered' => 'Kein Benutzer mit ID :name registriert und die automatische Registrierung ist deaktiviert',
+    'saml_no_email_address' => 'Es konnte keine E-Mail-Adresse für diesen Benutzer in den vom externen Authentifizierungssystem zur Verfügung gestellten Daten gefunden werden',
+    'saml_invalid_response_id' => 'Die Anfrage vom externen Authentifizierungssystem wird von einem von dieser Anwendung gestarteten Prozess nicht erkannt. Das Zurückgehen nach einem Login könnte dieses Problem verursachen.',
+    'saml_fail_authed' => 'Anmeldung mit :system fehlgeschlagen, System konnte keine erfolgreiche Autorisierung bereitstellen',
     'social_no_action_defined' => 'Es ist keine Aktion definiert',
-    'social_login_bad_response' => "Fehler bei der :socialAccount-Anmeldung: \n:error",
+    'social_login_bad_response' => "Fehler bei :socialAccount Login: \n:error",
     'social_account_in_use' => 'Dieses :socialAccount-Konto wird bereits verwendet. Bitte melde dich mit dem :socialAccount-Konto an.',
     'social_account_email_in_use' => 'Die E-Mail-Adresse ":email" ist bereits registriert. Wenn Du bereits registriert bist, kannst Du Dein :socialAccount-Konto in Deinen Profil-Einstellungen verknüpfen.',
     'social_account_existing' => 'Dieses :socialAccount-Konto ist bereits mit Ihrem Profil verknüpft.',
@@ -27,7 +33,7 @@ return [
     'social_account_register_instructions' => 'Wenn Du bisher kein Social-Media Konto besitzt, kannst Du ein solches Konto mit der :socialAccount Option anlegen.',
     'social_driver_not_found' => 'Treiber für Social-Media-Konten nicht gefunden',
     'social_driver_not_configured' => 'Ihr :socialAccount-Konto ist nicht korrekt konfiguriert.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Dieser Einladungslink ist abgelaufen. Sie können stattdessen versuchen, Ihr Passwort zurückzusetzen.',
 
     // System
     'path_not_writable' => 'Die Datei kann nicht in den angegebenen Pfad :filePath hochgeladen werden. Stelle sicher, dass dieser Ordner auf dem Server beschreibbar ist.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'Der Upload der Datei ist abgelaufen.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Die Seite stimmte nach dem Hochladen des Anhangs nicht überein.',
     'attachment_not_found' => 'Anhang konnte nicht gefunden werden.',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Seite nicht gefunden',
     'sorry_page_not_found' => 'Entschuldigung. Die Seite, die Du angefordert hast, wurde nicht gefunden.',
+    'sorry_page_not_found_permission_warning' => 'Wenn du erwartet hast, dass diese Seite existiert, hast du möglicherweise nicht die Berechtigung, sie anzuzeigen.',
     'return_home' => 'Zurück zur Startseite',
     'error_occurred' => 'Es ist ein Fehler aufgetreten',
     'app_down' => ':appName befindet sich aktuell im Wartungsmodus.',
     'back_soon' => 'Wir werden so schnell wie möglich wieder online sein.',
 
+    // API errors
+    'api_no_authorization_found' => 'Kein Autorisierungs-Token für die Anfrage gefunden',
+    'api_bad_authorization_format' => 'Ein Autorisierungs-Token wurde auf die Anfrage gefunden, aber das Format schien falsch zu sein',
+    'api_user_token_not_found' => 'Es wurde kein passender API-Token für den angegebenen Autorisierungs-Token gefunden',
+    'api_incorrect_token_secret' => 'Das für den API-Token angegebene geheimen Token ist falsch',
+    'api_user_no_api_permission' => 'Der Besitzer des verwendeten API-Token hat keine Berechtigung für API-Aufrufe',
+    'api_user_token_expired' => 'Das verwendete Autorisierungs-Token ist abgelaufen',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Fehler beim Senden einer Test E-Mail:',
+
 ];
index 74149a7eda09f40a2f07d84ff535584a6882f8de..0dd28c019f13452473408060a81110c5806da8a5 100644 (file)
@@ -8,8 +8,8 @@ return [
 
     'password' => 'Passwörter müssen aus mindestens sechs Zeichen bestehen und mit der eingegebenen Wiederholung übereinstimmen.',
     'user' => "Es wurde kein Benutzer mit dieser E-Mail-Adresse gefunden.",
-    'token' => 'Dieser Link zum Zurücksetzen des Passwortes ist ungültig!',
-    'sent' => 'Der Link zum Zurücksetzen Ihres Passwortes wurde Ihnen per E-Mail zugesendet.',
-    'reset' => 'Ihr Passwort wurde zurückgesetzt!',
+    'token' => 'Der Token zum Zurücksetzen des Passworts für diese E-Mail-Adresse ist ungültig.',
+    'sent' => 'Wir haben dir einen Link zum Zurücksetzen des Passwortes per E-Mail geschickt!',
+    'reset' => 'Dein Passwort wurde zurückgesetzt!',
 
 ];
index ed8d8a11fc76aa4f873d12eebf617756bbc9804a..d0caef9ee9b81422ff56865d259e1a918c866f1e 100644 (file)
@@ -23,13 +23,13 @@ return [
     'app_public_access_toggle' => 'Öffentlichen Zugriff erlauben',
     'app_public_viewing' => 'Öffentliche Ansicht erlauben?',
     'app_secure_images' => 'Erhöhte Sicherheit für hochgeladene Bilder aktivieren?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_toggle' => 'Aktiviere Bild-Upload mit höherer Sicherheit',
     'app_secure_images_desc' => 'Aus Leistungsgründen sind alle Bilder öffentlich sichtbar. Diese Option fügt zufällige, schwer zu eratene, Zeichenketten zu Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnisindizes deaktiviert sind, um einen einfachen Zugriff zu verhindern.',
     'app_editor' => 'Seiteneditor',
     'app_editor_desc' => 'Wähle den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.',
     'app_custom_html' => 'Benutzerdefinierter HTML <head> Inhalt',
     'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugefügt wird, wird am Ende der <head> Sektion jeder Seite eingefügt. Diese kann praktisch sein, um CSS Styles anzupassen oder Analytics-Code hinzuzufügen.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'Benutzerdefinierte HTML-Kopfzeileninhalte sind auf dieser Einstellungsseite deaktiviert, um sicherzustellen, dass alle Änderungen rückgängig gemacht werden können.',
     'app_logo' => 'Anwendungslogo',
     'app_logo_desc' => 'Dieses Bild sollte 43px hoch sein.
 Größere Bilder werden verkleinert.',
@@ -43,12 +43,22 @@ Wenn Du nichts eingibst, wird die Anwendung auf die Standardfarbe zurückgesetzt
     'app_disable_comments_toggle' => 'Kommentare deaktivieren',
     'app_disable_comments_desc' => 'Deaktiviert Kommentare über alle Seiten in der Anwendung. Vorhandene Kommentare werden nicht angezeigt.',
 
+    // Color settings
+    'content_colors' => 'Inhaltsfarben',
+    'content_colors_desc' => 'Legt Farben für alle Elemente in der Seitenorganisationshierarchie fest. Die Auswahl von Farben mit einer ähnlichen Helligkeit wie die Standardfarben wird zur Lesbarkeit empfohlen.',
+    'bookshelf_color' => 'Regalfarbe',
+    'book_color' => 'Buchfarbe',
+    'chapter_color' => 'Kapitelfarbe',
+    'page_color' => 'Seitenfarbe',
+    'page_draft_color' => 'Seitenentwurfsfarbe',
+
     // Registration Settings
     'reg_settings' => 'Registrierungseinstellungen',
     'reg_enable' => 'Registrierung erlauben?',
     'reg_enable_toggle' => 'Registrierung erlauben',
     'reg_enable_desc' => 'Wenn die Registrierung erlaubt ist, kann sich der Benutzer als Anwendungsbenutzer anmelden. Bei der Registrierung erhält er eine einzige, voreingestellte Benutzerrolle.',
     'reg_default_role' => 'Standard-Benutzerrolle nach Registrierung',
+    'reg_enable_external_warning' => 'Die obige Option wird ignoriert, während eine externe LDAP oder SAML Authentifizierung aktiv ist. Benutzerkonten für nicht existierende Mitglieder werden automatisch erzeugt, wenn die Authentifizierung gegen das verwendete externe System erfolgreich ist.',
     'reg_email_confirmation' => 'Bestätigung per E-Mail',
     'reg_email_confirmation_toggle' => 'Bestätigung per E-Mail erforderlich',
     'reg_confirm_email_desc' => 'Falls die Einschränkung für Domains genutzt wird, ist die Bestätigung per E-Mail zwingend erforderlich und der untenstehende Wert wird ignoriert.',
@@ -61,11 +71,53 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'maint' => 'Wartung',
     'maint_image_cleanup' => 'Bilder bereinigen',
     'maint_image_cleanup_desc' => "Überprüft Seiten- und Versionsinhalte auf ungenutzte und mehrfach vorhandene Bilder. Erstelle vor dem Start ein Backup Deiner Datenbank und Bilder.",
-    'maint_image_cleanup_ignore_revisions' => 'Bilder in Versionen ignorieren',
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Reinigung starten',
     'maint_image_cleanup_warning' => ':count eventuell unbenutze Bilder wurden gefunden. Möchtest Du diese Bilder löschen?',
     'maint_image_cleanup_success' => ':count eventuell unbenutze Bilder wurden gefunden und gelöscht.',
     'maint_image_cleanup_nothing_found' => 'Keine unbenutzen Bilder gefunden. Nichts zu löschen!',
+    'maint_send_test_email' => 'Test Email versenden',
+    'maint_send_test_email_desc' => 'Dies sendet eine Test E-Mail an die in deinem Profil angegebene E-Mail-Adresse.',
+    'maint_send_test_email_run' => 'Sende eine Test E-Mail',
+    'maint_send_test_email_success' => 'E-Mail wurde an :address gesendet',
+    'maint_send_test_email_mail_subject' => 'Test E-Mail',
+    'maint_send_test_email_mail_greeting' => 'E-Mail-Versand scheint zu funktionieren!',
+    'maint_send_test_email_mail_text' => 'Glückwunsch! Da du diese E-Mail Benachrichtigung erhalten hast, scheinen deine E-Mail-Einstellungen korrekt konfiguriert zu sein.',
+    'maint_recycle_bin_desc' => 'Gelöschte Regale, Bücher, Kapitel & Seiten werden in den Papierkorb verschoben, so dass sie wiederhergestellt oder dauerhaft gelöscht werden können. Ältere Gegenstände im Papierkorb können, in Abhängigkeit von der Systemkonfiguration, nach einer Weile automatisch entfernt werden.',
+    'maint_recycle_bin_open' => 'Papierkorb öffnen',
+
+    // Recycle Bin
+    'recycle_bin' => 'Papierkorb',
+    'recycle_bin_desc' => 'Hier können Sie gelöschte Elemente wiederherstellen oder sie dauerhaft aus dem System entfernen. Diese Liste ist nicht gefiltert, im Gegensatz zu ähnlichen Aktivitätslisten im System, wo Berechtigungsfilter angewendet werden.',
+    'recycle_bin_deleted_item' => 'Gelöschtes Element',
+    'recycle_bin_deleted_by' => 'Gelöscht von',
+    'recycle_bin_deleted_at' => 'Löschzeitpunkt',
+    'recycle_bin_permanently_delete' => 'Dauerhaft löschen',
+    'recycle_bin_restore' => 'Wiederherstellen',
+    'recycle_bin_contents_empty' => 'Der Papierkorb ist derzeit leer',
+    'recycle_bin_empty' => 'Papierkorb leeren',
+    'recycle_bin_empty_confirm' => 'Dies wird alle Gegenstände im Papierkorb dauerhaft entfernen, einschließlich der Inhalte, die darin enthalten sind. Sind Sie sicher, dass Sie den Papierkorb leeren möchten?',
+    'recycle_bin_destroy_confirm' => 'Diese Aktion wird dieses Element zusammen mit allen unten aufgeführten Unterelementen dauerhaft aus dem System löschen und Sie werden nicht in der Lage sein, diesen Inhalt wiederherzustellen. Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten?',
+    'recycle_bin_destroy_list' => 'Zu löschende Elemente',
+    'recycle_bin_restore_list' => 'Zu wiederherzustellende Elemente',
+    'recycle_bin_restore_confirm' => 'Mit dieser Aktion wird das gelöschte Element einschließlich aller untergeordneten Elemente an seinen ursprünglichen Ort wiederherstellen. Wenn der ursprüngliche Ort gelöscht wurde und sich nun im Papierkorb befindet, muss auch das übergeordnete Element wiederhergestellt werden.',
+    'recycle_bin_restore_deleted_parent' => 'Das übergeordnete Elements wurde ebenfalls gelöscht. Dieses Element wird weiterhin als gelöscht zählen, bis auch das übergeordnete Element wiederhergestellt wurde.',
+    'recycle_bin_destroy_notification' => ':count Elemente wurden aus dem Papierkorb gelöscht.',
+    'recycle_bin_restore_notification' => ':count Elemente wurden aus dem Papierkorb wiederhergestellt.',
+
+    // Audit Log
+    'audit' => 'Audit-Protokoll',
+    'audit_desc' => 'Dieses Audit-Protokoll zeigt eine Liste der Aktivitäten an, welche vom System protokolliert werden. Im Gegensatz zu den anderen Aktivitätslisten im System, bei denen Berechtigungen angewendet werden, ist diese Liste ungefiltert.',
+    'audit_event_filter' => 'Ereignisfilter',
+    'audit_event_filter_no_filter' => 'Kein Filter',
+    'audit_deleted_item' => 'Gelöschtes Objekt',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'Benutzer',
+    'audit_table_event' => 'Ereignis',
+    'audit_table_related' => 'Verknüpftes Element oder Detail',
+    'audit_table_date' => 'Aktivitätsdatum',
+    'audit_date_from' => 'Zeitraum von',
+    'audit_date_to' => 'Zeitraum bis',
 
     // Role Settings
     'roles' => 'Rollen',
@@ -88,9 +140,11 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'role_manage_roles' => 'Rollen und Rollen-Berechtigungen verwalten',
     'role_manage_entity_permissions' => 'Alle Buch-, Kapitel- und Seiten-Berechtigungen verwalten',
     'role_manage_own_entity_permissions' => 'Nur Berechtigungen eigener Bücher, Kapitel und Seiten verwalten',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => 'Seitenvorlagen verwalten',
+    'role_access_api' => 'Systemzugriffs-API',
     'role_manage_settings' => 'Globaleinstellungen verwalten',
     'role_asset' => 'Berechtigungen',
+    'roles_system_warning' => 'Beachten Sie, dass der Zugriff auf eine der oben genannten drei Berechtigungen einem Benutzer erlauben kann, seine eigenen Berechtigungen oder die Rechte anderer im System zu ändern. Weisen Sie nur Rollen, mit diesen Berechtigungen, vertrauenswürdigen Benutzern zu.',
     'role_asset_desc' => 'Diese Berechtigungen gelten für den Standard-Zugriff innerhalb des Systems. Berechtigungen für Bücher, Kapitel und Seiten überschreiben diese Berechtigungenen.',
     'role_asset_admins' => 'Administratoren erhalten automatisch Zugriff auf alle Inhalte, aber diese Optionen können Oberflächenoptionen ein- oder ausblenden.',
     'role_all' => 'Alle',
@@ -106,6 +160,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'user_profile' => 'Benutzerprofil',
     'users_add_new' => 'Benutzer hinzufügen',
     'users_search' => 'Benutzer suchen',
+    'users_latest_activity' => 'Neueste Aktivitäten',
     'users_details' => 'Benutzerdetails',
     'users_details_desc' => 'Legen Sie für diesen Benutzer einen Anzeigenamen und eine E-Mail-Adresse fest. Die E-Mail-Adresse wird bei der Anmeldung verwendet.',
     'users_details_desc_no_email' => 'Legen Sie für diesen Benutzer einen Anzeigenamen fest, damit andere ihn erkennen können.',
@@ -113,17 +168,20 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'users_role_desc' => 'Wählen Sie aus, welchen Rollen dieser Benutzer zugeordnet werden soll. Wenn ein Benutzer mehreren Rollen zugeordnet ist, werden die Berechtigungen dieser Rollen gestapelt und er erhält alle Fähigkeiten der zugewiesenen Rollen.',
     'users_password' => 'Benutzerpasswort',
     'users_password_desc' => 'Legen Sie ein Passwort fest, mit dem Sie sich anmelden möchten. Diese muss mindestens 5 Zeichen lang sein.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
+    'users_send_invite_text' => 'Du kannst diesem Benutzer eine Einladungs-E-Mail senden, die es ihm erlaubt, sein eigenes Passwort zu setzen, andernfalls kannst du sein Passwort selbst setzen.',
+    'users_send_invite_option' => 'Benutzer-Einladungs-E-Mail senden',
     'users_external_auth_id' => 'Externe Authentifizierungs-ID',
-    'users_external_auth_id_desc' => 'Dies ist die ID, die verwendet wird, um diesen Benutzer bei der Kommunikation mit Ihrem LDAP-System abzugleichen.',
+    'users_external_auth_id_desc' => 'Dies ist die ID, die verwendet wird, um diesen Benutzer bei der Kommunikation mit deinem externen Authentifizierungssystem abzugleichen.',
     'users_password_warning' => 'Fülle die folgenden Felder nur aus, wenn Du Dein Passwort ändern möchtest:',
     'users_system_public' => 'Dieser Benutzer repräsentiert alle unangemeldeten Benutzer, die diese Seite betrachten. Er kann nicht zum Anmelden benutzt werden, sondern wird automatisch zugeordnet.',
     'users_delete' => 'Benutzer löschen',
     'users_delete_named' => 'Benutzer ":userName" löschen',
     'users_delete_warning' => 'Der Benutzer ":userName" wird aus dem System gelöscht.',
     'users_delete_confirm' => 'Bist Du sicher, dass Du diesen Benutzer löschen möchtest?',
-    'users_delete_success' => 'Benutzer erfolgreich gelöscht.',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Benutzer bearbeiten',
     'users_edit_profile' => 'Profil bearbeiten',
     'users_edit_success' => 'Benutzer erfolgreich aktualisisert',
@@ -137,6 +195,32 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'users_social_disconnect' => 'Social-Media-Konto lösen',
     'users_social_connected' => ':socialAccount-Konto wurde erfolgreich mit dem Profil verknüpft.',
     'users_social_disconnected' => ':socialAccount-Konto wurde erfolgreich vom Profil gelöst.',
+    'users_api_tokens' => 'API-Token',
+    'users_api_tokens_none' => 'Für diesen Benutzer wurden keine API-Token erstellt',
+    'users_api_tokens_create' => 'Token erstellen',
+    'users_api_tokens_expires' => 'Endet',
+    'users_api_tokens_docs' => 'API Dokumentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Neuen API-Token erstellen',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Gebe deinem Token einen aussagekräftigen Namen als spätere Erinnerung an seinen Verwendungszweck.',
+    'user_api_token_expiry' => 'Ablaufdatum',
+    'user_api_token_expiry_desc' => 'Lege ein Datum fest, an dem dieser Token abläuft. Nach diesem Datum funktionieren Anfragen, die mit diesem Token gestellt werden, nicht mehr. Wenn du dieses Feld leer lässt, wird ein Ablaufdatum von 100 Jahren in der Zukunft festgelegt.',
+    'user_api_token_create_secret_message' => 'Unmittelbar nach der Erstellung dieses Tokens wird eine "Token ID" & ein "Token Kennwort" generiert und angezeigt. Das Kennwort wird nur ein einziges Mal angezeigt. Stelle also sicher, dass du den Inhalt an einen sicheren Ort kopierst, bevor du fortfährst.',
+    'user_api_token_create_success' => 'API-Token erfolgreich erstellt',
+    'user_api_token_update_success' => 'API-Token erfolgreich aktualisiert',
+    'user_api_token' => 'API-Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'Dies ist ein nicht editierbarer, vom System generierter Identifikator für diesen Token, welcher bei API-Anfragen angegeben werden muss.',
+    'user_api_token_secret' => 'Token Kennwort',
+    'user_api_token_secret_desc' => 'Dies ist ein systemgeneriertes Kennwort für diesen Token, das bei API-Anfragen zur Verfügung gestellt werden muss. Es wird nur dieses eine Mal angezeigt, deshalb kopiere diesen an einen sicheren und geschützten Ort.',
+    'user_api_token_created' => 'Token erstellt :timeAgo',
+    'user_api_token_updated' => 'Token aktualisiert :timeAgo',
+    'user_api_token_delete' => 'Lösche Token',
+    'user_api_token_delete_warning' => 'Dies löscht den API-Token mit dem Namen \':tokenName\' vollständig aus dem System.',
+    'user_api_token_delete_confirm' => 'Bist du sicher, dass du diesen API-Token löschen möchtest?',
+    'user_api_token_delete_success' => 'API-Token erfolgreich gelöscht',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -144,26 +228,32 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bulgarisch',
+        'cs' => 'Česky',
+        'da' => 'Dänisch',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slowenisch',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 1cf2176d5283129f916a755b3df083f52a1dff3e..24c1e8addb8e224e4ce848427a3da45a3ff49e70 100644 (file)
@@ -30,19 +30,19 @@ return [
     'digits'               => ':attribute muss :digits Stellen haben.',
     'digits_between'       => ':attribute muss zwischen :min und :max Stellen haben.',
     'email'                => ':attribute muss eine valide E-Mail-Adresse sein.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ':attribute muss mit einem der folgenden Werte: :values enden',
     'filled'               => ':attribute ist erforderlich.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute muss größer als :value sein.',
+        'file'    => ':attribute muss mindestens größer als :value Kilobytes sein.',
+        'string'  => ':attribute muss mehr als :value Zeichen haben.',
+        'array'   => ':attribute muss mehr als :value Elemente haben.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => ':attribute muss größer-gleich :value sein.',
+        'file'    => ':attribute muss größer-gleich :value Kilobytes sein.',
+        'string'  => ':attribute muss mindestens :value Zeichen haben.',
+        'array'   => ':attribute muss :value Elemente oder mehr haben.',
     ],
     'exists'               => ':attribute ist ungültig.',
     'image'                => ':attribute muss ein Bild sein.',
@@ -50,20 +50,20 @@ return [
     'in'                   => ':attribute ist ungültig.',
     'integer'              => ':attribute muss eine Zahl sein.',
     'ip'                   => ':attribute muss eine valide IP-Adresse sein.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ipv4'                 => ':attribute muss eine gültige IPv4 Adresse sein.',
+    'ipv6'                 => ':attribute muss eine gültige IPv6-Adresse sein.',
+    'json'                 => ':attribute muss ein gültiger JSON-String sein.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute muss kleiner als :value sein.',
+        'file'    => ':attribute muss kleiner als :value Kilobytes sein.',
+        'string'  => ':attribute muss weniger als :value Zeichen haben.',
+        'array'   => ':attribute muss weniger als :value Elemente haben.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute muss kleiner oder gleich :value sein.',
+        'file'    => ':attribute muss kleiner oder gleich :value Kilobytes sein.',
+        'string'  => ':attribute muss :value oder weniger Zeichen haben.',
+        'array'   => ':attribute darf höchstens :value Elemente haben.',
     ],
     'max'                  => [
         'numeric' => ':attribute darf nicht größer als :max sein.',
@@ -80,7 +80,7 @@ return [
     ],
     'no_double_extension'  => ':attribute darf nur eine gültige Dateiendung',
     'not_in'               => ':attribute ist ungültig.',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => ':attribute ist kein gültiges Format.',
     'numeric'              => ':attribute muss eine Zahl sein.',
     'regex'                => ':attribute ist in einem ungültigen Format.',
     'required'             => ':attribute ist erforderlich.',
@@ -90,6 +90,7 @@ return [
     'required_without'     => ':attribute ist erforderlich, wenn :values nicht vorhanden ist.',
     'required_without_all' => ':attribute ist erforderlich, wenn :values nicht vorhanden sind.',
     'same'                 => ':attribute und :other müssen übereinstimmen.',
+    'safe_url'             => 'Der angegebene Link ist möglicherweise nicht sicher.',
     'size'                 => [
         'numeric' => ':attribute muss :size sein.',
         'file'    => ':attribute muss :size Kilobytes groß sein.',
index 4cac54b2a706efa35cb8873ebc247c20420e65b5..fe937b061930262a060465699446647adab763d9 100644 (file)
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'commented on',
+    'permissions_update'          => 'updated permissions',
 ];
index 6961e049b3dc2ad15d25d2a1369f4dc6988e2a6b..d64fce93a62d90889b2297a9e4f6482ad9046475 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Reset Password',
     'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.',
     'reset_password_send_button' => 'Send Reset Link',
-    'reset_password_sent_success' => 'A password reset link has been sent to :email.',
+    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
     'reset_password_success' => 'Your password has been successfully reset.',
     'email_reset_subject' => 'Reset your :appName password',
     'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.',
index 1807217a375d2f0a77f5fe10ea80113ee57ea490..e87bd11a5e343173fadf78e62a042e1ca0579a4a 100644 (file)
@@ -33,11 +33,13 @@ return [
     'copy' => 'Copy',
     'reply' => 'Reply',
     'delete' => 'Delete',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Search',
     'search_clear' => 'Clear Search',
     'reset' => 'Reset',
     'remove' => 'Remove',
     'add' => 'Add',
+    'fullscreen' => 'Fullscreen',
 
     // Sort Options
     'sort_options' => 'Sort Options',
@@ -65,6 +67,8 @@ return [
     'profile_menu' => 'Profile Menu',
     'view_profile' => 'View Profile',
     'edit_profile' => 'Edit Profile',
+    'dark_mode' => 'Dark Mode',
+    'light_mode' => 'Light Mode',
 
     // Layout tabs
     'tab_info' => 'Info',
index d8e8981fb5fcf6ba8d15993453d4f8f2d07df970..48a0a32faa38c4821a9d71dda9a5fb4f97d35232 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Load More',
     'image_image_name' => 'Image Name',
     'image_delete_used' => 'This image is used in the pages below.',
-    'image_delete_confirm' => 'Click delete again to confirm you want to delete this image.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Select Image',
     'image_dropzone' => 'Drop images or click here to upload',
     'images_deleted' => 'Images Deleted',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Edit Code',
     'code_language' => 'Code Language',
     'code_content' => 'Code Content',
+    'code_session_history' => 'Session History',
     'code_save' => 'Save Code',
 ];
index 6bbc723b0abfc1e6b1f69e270bbb9d2afdbe851b..6c0980fab380d430f50d12ca8928f69be522b203 100644 (file)
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => 'Created :timeLength by :user',
     'meta_updated' => 'Updated :timeLength',
     'meta_updated_name' => 'Updated :timeLength by :user',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Entity Select',
     'images' => 'Images',
     'my_recent_drafts' => 'My Recent Drafts',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Once enabled, These permissions will take priority over any set role permissions.',
     'permissions_enable' => 'Enable Custom Permissions',
     'permissions_save' => 'Save Permissions',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Search Results',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'No pages matched this search',
     'search_for_term' => 'Search for :term',
     'search_more' => 'More Results',
-    'search_filters' => 'Search Filters',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Content Type',
     'search_exact_matches' => 'Exact Matches',
     'search_tags' => 'Tag Searches',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Create New Chapter',
     'chapters_delete' => 'Delete Chapter',
     'chapters_delete_named' => 'Delete Chapter :chapterName',
-    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages will be removed and added directly to the parent book.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?',
     'chapters_edit' => 'Edit Chapter',
     'chapters_edit_named' => 'Edit Chapter :chapterName',
@@ -207,6 +210,7 @@ return [
     'pages_revisions' => 'Page Revisions',
     'pages_revisions_named' => 'Page Revisions for :pageName',
     'pages_revision_named' => 'Page Revision for :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Created By',
     'pages_revisions_date' => 'Revision Date',
     'pages_revisions_number' => '#',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Upload File',
     'attachments_link' => 'Attach Link',
     'attachments_set_link' => 'Set Link',
-    'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Drop files or click here to attach a file',
     'attachments_no_files' => 'No files have been uploaded',
     'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Link to file',
     'attachments_link_url_hint' => 'Url of site or file',
     'attach' => 'Attach',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Edit File',
     'attachments_edit_file_name' => 'File Name',
     'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
index c3b47744d31f5fc63081ec6d50136511721941c1..79024e482ed69efa633116592f9b7c83a0bcc93a 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'Email has already been confirmed, Try logging in.',
     'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.',
     'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.',
+    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
     'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
     'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
     'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
     'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
+    'saml_already_logged_in' => 'Already logged in',
+    'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
+    'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
+    'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
+    'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
     'social_no_action_defined' => 'No action defined',
     'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
     'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'The file upload has timed out.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'Attachment not found',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Page Not Found',
     'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
+    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
     'return_home' => 'Return to home',
     'error_occurred' => 'An Error Occurred',
     'app_down' => ':appName is down right now',
     'back_soon' => 'It will be back up soon.',
 
+    // API errors
+    'api_no_authorization_found' => 'No authorization token found on the request',
+    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
+    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
+    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
+    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
+    'api_user_token_expired' => 'The authorization token used has expired',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+
 ];
index f41ca7868f66f2937e8ad49e7e2daee1eff3c569..b408f3c2fdaf1e80e9cdafa36ae9507db9fbda48 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Passwords must be at least eight characters and match the confirmation.',
     'user' => "We can't find a user with that e-mail address.",
-    'token' => 'This password reset token is invalid.',
+    'token' => 'The password reset token is invalid for this email address.',
     'sent' => 'We have e-mailed your password reset link!',
     'reset' => 'Your password has been reset!',
 
index 56e0868e4b80dc4830a8966f7122a0c4ae969357..414650d21bac0c5187afc841de93f0f0ea40aaed 100755 (executable)
@@ -33,7 +33,7 @@ return [
     'app_logo' => 'Application Logo',
     'app_logo_desc' => 'This image should be 43px in height. <br>Large images will be scaled down.',
     'app_primary_color' => 'Application Primary Color',
-    'app_primary_color_desc' => 'This should be a hex value. <br>Leave empty to reset to the default color.',
+    'app_primary_color_desc' => 'Sets the primary color for the application including the banner, buttons, and links.',
     'app_homepage' => 'Application Homepage',
     'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
     'app_homepage_select' => 'Select a page',
@@ -41,12 +41,22 @@ return [
     'app_disable_comments_toggle' => 'Disable comments',
     'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
 
+    // Color settings
+    'content_colors' => 'Content Colors',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
     // Registration Settings
     'reg_settings' => 'Registration',
     'reg_enable' => 'Enable Registration',
     'reg_enable_toggle' => 'Enable registration',
     'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
     'reg_default_role' => 'Default user role after registration',
+    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
     'reg_email_confirmation' => 'Email Confirmation',
     'reg_email_confirmation_toggle' => 'Require email confirmation',
     'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.',
@@ -58,7 +68,7 @@ return [
     'maint' => 'Maintenance',
     'maint_image_cleanup' => 'Cleanup Images',
     'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Run Cleanup',
     'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
     'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
@@ -70,6 +80,41 @@ return [
     'maint_send_test_email_mail_subject' => 'Test Email',
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
 
     // Role Settings
     'roles' => 'Roles',
@@ -93,8 +138,10 @@ return [
     'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions',
     'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
     'role_manage_page_templates' => 'Manage page templates',
+    'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Manage app settings',
     'role_asset' => 'Asset Permissions',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => 'All',
@@ -110,6 +157,7 @@ return [
     'user_profile' => 'User Profile',
     'users_add_new' => 'Add New User',
     'users_search' => 'Search Users',
+    'users_latest_activity' => 'Latest Activity',
     'users_details' => 'User Details',
     'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
     'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
@@ -120,14 +168,17 @@ return [
     'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
     'users_send_invite_option' => 'Send user invite email',
     'users_external_auth_id' => 'External Authentication ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
     'users_password_warning' => 'Only fill the below if you would like to change your password.',
     'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
     'users_delete' => 'Delete User',
     'users_delete_named' => 'Delete user :userName',
     'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
     'users_delete_confirm' => 'Are you sure you want to delete this user?',
-    'users_delete_success' => 'Users successfully removed',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Edit User',
     'users_edit_profile' => 'Edit Profile',
     'users_edit_success' => 'User successfully updated',
@@ -141,6 +192,32 @@ return [
     'users_social_disconnect' => 'Disconnect Account',
     'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
     'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
+    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -148,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 76b57a2a3b58ddb8ef41e0562c5187359cc6e542..578ea999fc31618997be7834012fb30aed3d7b1f 100644 (file)
@@ -90,6 +90,7 @@ return [
     'required_without'     => 'The :attribute field is required when :values is not present.',
     'required_without_all' => 'The :attribute field is required when none of :values are present.',
     'same'                 => 'The :attribute and :other must match.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
         'numeric' => 'The :attribute must be :size.',
         'file'    => 'The :attribute must be :size kilobytes.',
index aa90d9cd3e91832e3c33c73ea625c7be200afdff..b67781e18969774d39ed646fcabfb5c25335ea66 100644 (file)
@@ -7,42 +7,43 @@ return [
 
     // Pages
     'page_create'                 => 'página creada',
-    'page_create_notification'    => 'Página creada exitosamente',
+    'page_create_notification'    => 'Página creada correctamente',
     'page_update'                 => 'página actualizada',
-    'page_update_notification'    => 'Página actualizada exitosamente',
-    'page_delete'                 => 'página borrada',
-    'page_delete_notification'    => 'Página borrada exitosamente',
+    'page_update_notification'    => 'Página actualizada correctamente',
+    'page_delete'                 => 'página eliminada',
+    'page_delete_notification'    => 'Página eliminada correctamente',
     'page_restore'                => 'página restaurada',
-    'page_restore_notification'   => 'Página restaurada exitosamente',
+    'page_restore_notification'   => 'Página restaurada correctamente',
     'page_move'                   => 'página movida',
 
     // Chapters
     'chapter_create'              => 'capítulo creado',
-    'chapter_create_notification' => 'Capítulo creado exitosamente',
+    'chapter_create_notification' => 'Capítulo creado correctamente',
     'chapter_update'              => 'capítulo actualizado',
-    'chapter_update_notification' => 'Capítulo actualizado exitosamente',
-    'chapter_delete'              => 'capítulo borrado',
-    'chapter_delete_notification' => 'Capítulo borrado exitosamente',
+    'chapter_update_notification' => 'Capítulo actualizado correctamente',
+    'chapter_delete'              => 'capítulo eliminado',
+    'chapter_delete_notification' => 'Capítulo eliminado correctamente',
     'chapter_move'                => 'capítulo movido',
 
     // Books
     'book_create'                 => 'libro creado',
-    'book_create_notification'    => 'Libro creado exitosamente',
+    'book_create_notification'    => 'Libro creado correctamente',
     'book_update'                 => 'libro actualizado',
-    'book_update_notification'    => 'Libro actualizado exitosamente',
-    'book_delete'                 => 'libro borrado',
-    'book_delete_notification'    => 'Libro borrado exitosamente',
+    'book_update_notification'    => 'Libro actualizado correctamente',
+    'book_delete'                 => 'libro eliminado',
+    'book_delete_notification'    => 'Libro eliminado correctamente',
     'book_sort'                   => 'libro ordenado',
-    'book_sort_notification'      => 'Libro reordenado exitosamente',
+    'book_sort_notification'      => 'Libro reordenado correctamente',
 
     // Bookshelves
     'bookshelf_create'            => 'estante creado',
-    'bookshelf_create_notification'    => 'Estante creado exitosamente',
+    'bookshelf_create_notification'    => 'Estante creado correctamente',
     'bookshelf_update'                 => 'estante actualizado',
-    'bookshelf_update_notification'    => 'Estante actualizado exitosamente',
-    'bookshelf_delete'                 => 'estante borrado',
-    'bookshelf_delete_notification'    => 'Estante borrado exitosamente',
+    'bookshelf_update_notification'    => 'Estante actualizado correctamente',
+    'bookshelf_delete'                 => 'estante eliminado',
+    'bookshelf_delete_notification'    => 'Estante eliminado correctamente',
 
     // Other
     'commented_on'                => 'comentada el',
+    'permissions_update'          => 'permisos actualizados',
 ];
index 3636507fee72669b19fcc12aa023e1eaad476e22..25fc5b650d2d62eafd641de2a786879b8adad640 100644 (file)
@@ -6,15 +6,15 @@
  */
 return [
 
-    'failed' => 'Las credenciales no concuerdan con nuestros registros.',
-    'throttle' => 'Demasiados intentos fallidos de conexión. Por favor intente nuevamente en :seconds segundos.',
+    'failed' => 'Estas credenciales no coinciden con nuestros registros.',
+    'throttle' => 'Demasiados intentos de inicio de sesión. Por favor, inténtalo de nuevo en :seconds segundos.',
 
     // Login & Register
     'sign_up' => 'Registrarse',
     'log_in' => 'Acceder',
     'log_in_with' => 'Acceder con :socialDriver',
     'sign_up_with' => 'Registrarse con :socialDriver',
-    'logout' => 'Salir',
+    'logout' => 'Cerrar sesión',
 
     'name' => 'Nombre',
     'username' => 'Usuario',
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Resetear Contraseña',
     'reset_password_send_instructions' => 'Introduzca su correo electrónico a continuación y le será enviado un correo con un link para la restauración',
     'reset_password_send_button' => 'Enviar Enlace de Reseteo',
-    'reset_password_sent_success' => 'Un enlace para resetear la contraseña ha sido enviado a :email.',
+    'reset_password_sent' => 'Un enlace para cambiar la contraseña será enviado a su dirección de correo electrónico si existe en nuestro sistema.',
     'reset_password_success' => 'Su password ha sido reseteado de manera éxitosa.',
     'email_reset_subject' => 'Resetee la contraseña de :appName',
     'email_reset_text' => 'Está recibiendo este correo electrónico debido a que recibimos una solicitud de reseteo de contraseña de su cuenta.',
index a1c16cf95377c1b38f216fb38a9d970e00ec1517..9c6575a5381a3769119162dc292c9accbd52236d 100644 (file)
@@ -33,11 +33,13 @@ return [
     'copy' => 'Copiar',
     'reply' => 'Responder',
     'delete' => 'Borrar',
+    'delete_confirm' => 'Confirmar borrado',
     'search' => 'Buscar',
     'search_clear' => 'Limpiar búsqueda',
     'reset' => 'Resetear',
     'remove' => 'Remover',
     'add' => 'Añadir',
+    'fullscreen' => 'Pantalla completa',
 
     // Sort Options
     'sort_options' => 'Opciones de ordenación',
@@ -65,6 +67,8 @@ return [
     'profile_menu' => 'Menú de Perfil',
     'view_profile' => 'Ver Perfil',
     'edit_profile' => 'Editar Perfil',
+    'dark_mode' => 'Modo Oscuro',
+    'light_mode' => 'Modo Claro',
 
     // Layout tabs
     'tab_info' => 'Información',
index f9251991216e07493e3438a2502a30bf5b264bc2..fb4929ad4682313af337e76df503b64342dfdb61 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Cargar más',
     'image_image_name' => 'Nombre de imagen',
     'image_delete_used' => 'Esta imagen está siendo utilizada en las páginas mostradas a continuación.',
-    'image_delete_confirm' => 'Haga click de nuevo para confirmar que quiere borrar esta imagen.',
+    'image_delete_confirm_text' => '¿Estás seguro de que quieres eliminar esta imagen?',
     'image_select_image' => 'Seleccionar Imagen',
     'image_dropzone' => 'Arrastre las imágenes o hacer click aquí para Subir',
     'images_deleted' => 'Imágenes borradas',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Editar Código',
     'code_language' => 'Lenguaje del Código',
     'code_content' => 'Contenido del Código',
+    'code_session_history' => 'Historial de la sesión',
     'code_save' => 'Guardar Código',
 ];
index db87efda7eeb7655ea90f9ad4d05f12fe784074a..5b1d3fcbab62cedd8c58580f60eade38039049e4 100644 (file)
@@ -18,10 +18,11 @@ return [
     'create_now' => 'Crear uno ahora',
     'revisions' => 'Revisiones',
     'meta_revision' => 'Revisión #:revisionCount',
-    'meta_created' => 'Creado el :timeLength',
-    'meta_created_name' => 'Creado el :timeLength por :user',
-    'meta_updated' => 'Actualizado el :timeLength',
-    'meta_updated_name' => 'Actualizado el :timeLength por :user',
+    'meta_created' => 'Creado :timeLength',
+    'meta_created_name' => 'Creado :timeLength por :user',
+    'meta_updated' => 'Actualizado :timeLength',
+    'meta_updated_name' => 'Actualizado :timeLength por :user',
+    'meta_owned_name' => 'Propiedad de :user',
     'entity_select' => 'Seleccione entidad',
     'images' => 'Imágenes',
     'my_recent_drafts' => 'Mis borradores recientes',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Una vez habilitado, estos permisos tendrán prioridad por encima de cualquier permiso establecido.',
     'permissions_enable' => 'Habilitar permisos personalizados',
     'permissions_save' => 'Guardar permisos',
+    'permissions_owner' => 'Propietario',
 
     // Search
     'search_results' => 'Resultados de búsqueda',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Ninguna página encontrada para la búsqueda',
     'search_for_term' => 'Búsqueda por :term',
     'search_more' => 'Más Resultados',
-    'search_filters' => 'Filtros de Búsqueda',
+    'search_advanced' => 'Búsqueda Avanzada',
+    'search_terms' => 'Términos de búsqueda',
     'search_content_type' => 'Tipo de Contenido',
     'search_exact_matches' => 'Coincidencias Exactas',
     'search_tags' => 'Búsquedas Etiquetadas',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Crear nuevo capítulo',
     'chapters_delete' => 'Borrar capítulo',
     'chapters_delete_named' => 'Borrar capítulo :chapterName',
-    'chapters_delete_explain' => 'Esto borrará el capítulo con el nombre \':chapterName\', todas las páginas serán eliminadas y agregadas directamente al libro padre.',
+    'chapters_delete_explain' => 'Esto eliminará el capítulo con el nombre \':chapterName\'. También se eliminarán todas las páginas que existen dentro de este capítulo.',
     'chapters_delete_confirm' => '¿Está seguro de borrar este capítulo?',
     'chapters_edit' => 'Editar capítulo',
     'chapters_edit_named' => 'Editar capítulo :chapterName',
@@ -181,7 +184,7 @@ return [
     'pages_edit_draft' => 'Editar borrador de página',
     'pages_editing_draft' => 'Editando borrador',
     'pages_editing_page' => 'Editando página',
-    'pages_edit_draft_save_at' => 'Borrador guardado el ',
+    'pages_edit_draft_save_at' => 'Borrador guardado ',
     'pages_edit_delete_draft' => 'Borrar borrador',
     'pages_edit_discard_draft' => 'Descartar borrador',
     'pages_edit_set_changelog' => 'Ajustar Log de cambios',
@@ -207,6 +210,7 @@ return [
     'pages_revisions' => 'Revisiones de página',
     'pages_revisions_named' => 'Revisiones de página para :pageName',
     'pages_revision_named' => 'Revisión de página para :pageName',
+    'pages_revision_restored_from' => 'Restaurado de #:id; :summary',
     'pages_revisions_created_by' => 'Creado por',
     'pages_revisions_date' => 'Fecha de revisión',
     'pages_revisions_number' => '#',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Subir Archivo',
     'attachments_link' => 'Adjuntar Enlace',
     'attachments_set_link' => 'Ajustar Enlace',
-    'attachments_delete_confirm' => 'Haga click en borrar nuevamente para confirmar que quiere borrar este adjunto.',
+    'attachments_delete' => '¿Está seguro de que quiere eliminar este archivo adjunto?',
     'attachments_dropzone' => 'Arrastre ficheros aquí o haga click aquí para adjuntar un fichero',
     'attachments_no_files' => 'No se han subido ficheros',
     'attachments_explain_link' => 'Puede agregar un enlace si prefiere no subir un archivo. Puede ser un enlace a otra página o un enlace a un fichero en la nube.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Enlace a fichero',
     'attachments_link_url_hint' => 'Url del sitio o fichero',
     'attach' => 'Adjuntar',
+    'attachments_insert_link' => 'Añadir enlace al adjunto en la página',
     'attachments_edit_file' => 'Editar fichero',
     'attachments_edit_file_name' => 'Nombre del fichero',
     'attachments_edit_drop_upload' => 'Arrastre a los ficheros o haga click aquí para subir y sobreescribir',
index 77d5520f4868208e282bb70cbe2a93e83c513b9e..f83ec4b80d77755158d06acbd5dc5d8cf2c25681 100644 (file)
@@ -5,18 +5,24 @@
 return [
 
     // Permissions
-    'permission' => 'No tiene permisos para visualizar la página solicitada.',
-    'permissionJson' => 'No tiene permisos para ejecutar la acción solicitada.',
+    'permission' => 'No tienes permisos para visualizar la página solicitada.',
+    'permissionJson' => 'No tienes permisos para ejecutar la acción solicitada.',
 
     // Auth
     'error_user_exists_different_creds' => 'Un usuario con el correo electrónico :email ya existe pero con credenciales diferentes.',
     'email_already_confirmed' => 'El correo electrónico ya ha sido confirmado, intente acceder a la aplicación.',
     'email_confirmation_invalid' => 'Este token de confirmación no es válido o ya ha sido usado, intente registrar uno nuevamente.',
     'email_confirmation_expired' => 'El token de confirmación ha expirado, un nuevo email de confirmacón ha sido enviado.',
+    'email_confirmation_awaiting' => 'La dirección de correo electrónico de la cuenta en uso debe ser confirmada',
     'ldap_fail_anonymous' => 'El acceso con LDAP ha fallado usando binding anónimo',
     'ldap_fail_authed' => 'El acceso LDAP ha fallado usando el dn & contraseña enviados',
     'ldap_extension_not_installed' => 'La extensión LDAP PHP no se encuentra instalada',
     'ldap_cannot_connect' => 'No se puede conectar con el servidor ldap, la conexión inicial ha fallado',
+    'saml_already_logged_in' => 'Ya estás conectado',
+    'saml_user_not_registered' => 'El usuario :name no está registrado y el registro automático está deshabilitado',
+    'saml_no_email_address' => 'No se pudo encontrar una dirección de correo electrónico, para este usuario, en los datos proporcionados por el sistema de autenticación externo',
+    'saml_invalid_response_id' => 'La solicitud del sistema de autenticación externo no está reconocida por un proceso iniciado por esta aplicación. Navegar hacia atrás después de un inicio de sesión podría causar este problema.',
+    'saml_fail_authed' => 'El inicio de sesión con :system falló, el sistema no proporcionó una autorización correcta',
     'social_no_action_defined' => 'Acción no definida',
     'social_login_bad_response' => "Se ha recibido un error durante el acceso con :socialAccount error: \n:error",
     'social_account_in_use' => 'la cuenta :socialAccount ya se encuentra en uso, intente acceder a través de la opción :socialAccount .',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'La carga del archivo ha caducado.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Página no coincidente durante la subida del adjunto ',
     'attachment_not_found' => 'No se encontró el adjunto',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Página no encontrada',
     'sorry_page_not_found' => 'Lo sentimos, la página a la que intenta acceder no pudo ser encontrada.',
+    'sorry_page_not_found_permission_warning' => 'Si esperaba que esta página existiera, puede que no tenga permiso para verla.',
     'return_home' => 'Volver a la página de inicio',
     'error_occurred' => 'Ha ocurrido un error',
     'app_down' => 'La aplicación :appName se encuentra caída en este momento',
     'back_soon' => 'Volverá a estar operativa pronto.',
 
+    // API errors
+    'api_no_authorization_found' => 'No se encontró ningún token de autorización en la solicitud',
+    'api_bad_authorization_format' => 'Se ha encontrado un token de autorización en la solicitud pero el formato era incorrecto',
+    'api_user_token_not_found' => 'No se ha encontrado un token API que corresponda con el token de autorización proporcionado',
+    'api_incorrect_token_secret' => 'El secreto proporcionado para el token API usado es incorrecto',
+    'api_user_no_api_permission' => 'El propietario del token API usado no tiene permiso para hacer llamadas API',
+    'api_user_token_expired' => 'El token de autorización usado ha caducado',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error al enviar un email de prueba:',
+
 ];
index 443f072c4356fa7bed62243fb052b0bc758bed4b..15e42d1ec9a2646ff36e2448b8df61098e1f52e0 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'La contraseña debe ser como mínimo de seis caracteres y coincidir con la confirmación.',
     'user' => "No podemos encontrar un usuario con esta dirección de correo electrónico.",
-    'token' => 'El token de reseteo de la contraseña es inválido.',
+    'token' => 'El token de modificación de contraseña no es válido para esta dirección de correo electrónico.',
     'sent' => '¡Hemos enviado a su cuenta de e-mail un enlace para restaurar su contraseña!',
     'reset' => '¡Su contraseña ha sido restaurada!',
 
index f448bdd22e9834e626d73c49682012e6df2d416d..d6488eded53f98c67521d2b7d9a8005a3f636d84 100644 (file)
@@ -13,12 +13,12 @@ return [
 
     // App Settings
     'app_customization' => 'Personalización',
-    'app_features_security' => 'Características & Seguridad',
+    'app_features_security' => 'Características y seguridad',
     'app_name' => 'Nombre de la aplicación',
-    'app_name_desc' => 'Este nombre se muestra en la cabecera y en cualquier correo electrónico',
-    'app_name_header' => 'Mostrar el nombre de la aplicación en la cabecera',
-    'app_public_access' => 'Acceso Público',
-    'app_public_access_desc' => 'Activando esta opción permitirá que usuarios sin iniciar sesión puedan ver el contenido de tu aplicación Bookstack.',
+    'app_name_desc' => 'Este nombre se muestra en la cabecera y en cualquier correo electrónico enviado por el sistema.',
+    'app_name_header' => 'Mostrar nombre en la cabecera',
+    'app_public_access' => 'Acceso público',
+    'app_public_access_desc' => 'Activar esta opción permitirá a los visitantes que no hayan iniciado sesión, poder ver el contenido de tu BookStack.',
     'app_public_access_desc_guest' => 'El acceso público para visitantes puede ser controlado a través del usuario "Guest".',
     'app_public_access_toggle' => 'Permitir acceso público',
     'app_public_viewing' => '¿Permitir acceso público?',
@@ -41,28 +41,80 @@ return [
     'app_disable_comments_toggle' => 'Deshabilitar comentarios',
     'app_disable_comments_desc' => 'Deshabilita los comentarios en todas las páginas de la aplicación. <br> Los comentarios existentes no se muestran.',
 
+    // Color settings
+    'content_colors' => 'Colores del contenido',
+    'content_colors_desc' => 'Establece los colores para todos los elementos en la jerarquía de la organización de la página. Se recomienda elegir colores con un brillo similar al predeterminado para mayor legibilidad.',
+    'bookshelf_color' => 'Color del estante',
+    'book_color' => 'Color del libro',
+    'chapter_color' => 'Color del capítulo',
+    'page_color' => 'Color de la página',
+    'page_draft_color' => 'Color del borrador de página',
+
     // Registration Settings
     'reg_settings' => 'Registro',
     'reg_enable' => 'Habilitar Registro',
     'reg_enable_toggle' => 'Habilitar registro',
     'reg_enable_desc' => 'Cuando se habilita el registro los usuarios podrán registrarse como usuarios de la aplicación. Al registrarse se les asigna un rol único por defecto.',
     'reg_default_role' => 'Rol de usuario por defecto después del registro',
+    'reg_enable_external_warning' => 'La opción anterior no se utiliza mientras la autenticación LDAP o SAML externa esté activa. Las cuentas de usuario para los miembros no existentes se crearán automáticamente si la autenticación en el sistema externo en uso es exitosa.',
     'reg_email_confirmation' => 'Confirmación por Email',
     'reg_email_confirmation_toggle' => 'Requerir confirmación por Email',
     'reg_confirm_email_desc' => 'Si se emplea la restricción por dominio, entonces se requerirá la confirmación por correo electrónico y esta opción será ignorada.',
     'reg_confirm_restrict_domain' => 'Restricción de Dominio',
-    'reg_confirm_restrict_domain_desc' => 'Introduzca una lista separada por comas de los dominio a los que les gustaría restringir el registro de usuarios. A los usuarios les será enviado un correo electrónico para confirmar la dirección antes de que se le permita interactuar con la aplicación. <br> Tenga en cuenta que los usuarios podrán cambiar sus direcciones de correo electrónico después de registrarse exitosamente.',
+    'reg_confirm_restrict_domain_desc' => 'Introduzca una lista separada por comas de los dominios para cuentas de correo a los que se les permitirá el registro de usuarios. A los usuarios les será enviado un correo electrónico para confirmar la dirección antes de que se le permita interactuar con la aplicación. <br> Tenga en cuenta que los usuarios podrán cambiar sus direcciones de correo electrónico después de registrarse exitosamente.',
     'reg_confirm_restrict_domain_placeholder' => 'Ninguna restricción establecida',
 
     // Maintenance settings
     'maint' => 'Mantenimiento',
     'maint_image_cleanup' => 'Limpiar imágenes',
     'maint_image_cleanup_desc' => "Analiza las páginas y sus revisiones para comprobar qué imágenes y dibujos están siendo utilizadas y cuales no son necesarias. Asegúrate de crear una copia completa de la base de datos y de las imágenes antes de lanzar esta opción.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignorar imágenes en revisiones',
+    'maint_delete_images_only_in_revisions' => 'Elimina también imágenes que sólo existen en antiguas revisiones de páginas',
     'maint_image_cleanup_run' => 'Lanzar limpieza',
     'maint_image_cleanup_warning' => 'Se han encontrado :count imágenes posiblemente no utilizadas . ¿Estás seguro de querer borrar estas imágenes?',
     'maint_image_cleanup_success' => '¡Se han encontrado y borrado :count imágenes posiblemente no utilizadas!',
     'maint_image_cleanup_nothing_found' => '¡No se han encontrado imágenes sin utilizar, no se han borrado imágenes!',
+    'maint_send_test_email' => 'Enviar un correo electrónico de prueba',
+    'maint_send_test_email_desc' => 'Esto envía un correo electrónico de prueba a la dirección de correo electrónico especificada en tu perfil.',
+    'maint_send_test_email_run' => 'Enviar correo electrónico de prueba',
+    'maint_send_test_email_success' => 'Correo electrónico enviado a :address',
+    'maint_send_test_email_mail_subject' => 'Probar correo electrónico',
+    'maint_send_test_email_mail_greeting' => '¡El envío de correos electrónicos parece funcionar!',
+    'maint_send_test_email_mail_text' => '¡Enhorabuena! Al recibir esta notificación de correo electrónico, tu configuración de correo electrónico parece estar ajustada correctamente.',
+    'maint_recycle_bin_desc' => 'Los estantes, libros, capítulos y páginas eliminados se envían a la papelera de reciclaje para que puedan ser restauradas o eliminadas permanentemente. Los elementos más antiguos en la papelera de reciclaje pueden ser eliminados automáticamente después de un tiempo dependiendo de la configuración del sistema.',
+    'maint_recycle_bin_open' => 'Abrir papelera de reciclaje',
+
+    // Recycle Bin
+    'recycle_bin' => 'Papelera de Reciclaje',
+    'recycle_bin_desc' => 'Aquí puede restaurar elementos que hayan sido eliminados o elegir eliminarlos permanentemente del sistema. Esta lista no está filtrada a diferencia de las listas de actividad similares en el sistema donde se aplican los filtros de permisos.',
+    'recycle_bin_deleted_item' => 'Elemento Eliminado',
+    'recycle_bin_deleted_by' => 'Eliminado por',
+    'recycle_bin_deleted_at' => 'Fecha de eliminación',
+    'recycle_bin_permanently_delete' => 'Eliminar permanentemente',
+    'recycle_bin_restore' => 'Restaurar',
+    'recycle_bin_contents_empty' => 'La papelera de reciclaje está vacía',
+    'recycle_bin_empty' => 'Vaciar Papelera de reciclaje',
+    'recycle_bin_empty_confirm' => 'Esto destruirá permanentemente todos los elementos de la papelera de reciclaje, incluyendo el contenido existente en cada elemento. ¿Está seguro de que desea vaciar la papelera de reciclaje?',
+    'recycle_bin_destroy_confirm' => 'Esta acción eliminará permanentemente este elemento del sistema, junto con los elementos secundarios listados a continuación, y no podrá restaurar este contenido de nuevo. ¿Está seguro de que desea eliminar permanentemente este elemento?',
+    'recycle_bin_destroy_list' => 'Elementos a eliminar',
+    'recycle_bin_restore_list' => 'Elementos a restaurar',
+    'recycle_bin_restore_confirm' => 'Esta acción restaurará el elemento eliminado, incluyendo cualquier elemento secundario, a su ubicación original. Si la ubicación original ha sido eliminada, y ahora está en la papelera de reciclaje, el elemento padre también tendrá que ser restaurado.',
+    'recycle_bin_restore_deleted_parent' => 'El padre de este elemento también ha sido eliminado. Estos permanecerán eliminados hasta que el padre también sea restaurado.',
+    'recycle_bin_destroy_notification' => 'Eliminados :count artículos de la papelera de reciclaje.',
+    'recycle_bin_restore_notification' => 'Restaurados :count artículos desde la papelera de reciclaje.',
+
+    // Audit Log
+    'audit' => 'Registro de Auditoría',
+    'audit_desc' => 'Este registro de auditoría muestra una lista de actividades registradas en el sistema. Esta lista no está filtrada a diferencia de las listas de actividad similares en el sistema donde se aplican los filtros de permisos.',
+    'audit_event_filter' => 'Filtro de eventos',
+    'audit_event_filter_no_filter' => 'Sin filtro',
+    'audit_deleted_item' => 'Elemento eliminado',
+    'audit_deleted_item_name' => 'Nombre: :name',
+    'audit_table_user' => 'Usuario',
+    'audit_table_event' => 'Evento',
+    'audit_table_related' => 'Elemento o detalle relacionados',
+    'audit_table_date' => 'Fecha de la actividad',
+    'audit_date_from' => 'Rango de fecha desde',
+    'audit_date_to' => 'Rango de fecha hasta',
 
     // Role Settings
     'roles' => 'Roles',
@@ -86,8 +138,10 @@ return [
     'role_manage_entity_permissions' => 'Gestionar todos los permisos de libros, capítulos y páginas',
     'role_manage_own_entity_permissions' => 'Gestionar permisos en libros, capítulos y páginas propias',
     'role_manage_page_templates' => 'Administrar plantillas',
+    'role_access_api' => 'API de sistema de acceso',
     'role_manage_settings' => 'Gestionar ajustes de la aplicación',
     'role_asset' => 'Permisos de contenido',
+    'roles_system_warning' => 'Tenga en cuenta que el acceso a cualquiera de los tres permisos anteriores puede permitir a un usuario alterar sus propios privilegios o los privilegios de otros en el sistema. Sólo asignar roles con estos permisos a usuarios de confianza.',
     'role_asset_desc' => 'Estos permisos controlan el acceso por defecto a los contenidos del sistema. Los permisos de Libros, Capítulos y Páginas sobreescribiran estos permisos.',
     'role_asset_admins' => 'A los administradores se les asigna automáticamente permisos para acceder a todo el contenido pero estas opciones podrían mostrar u ocultar opciones de la interfaz.',
     'role_all' => 'Todo',
@@ -103,6 +157,7 @@ return [
     'user_profile' => 'Perfil de Usuario',
     'users_add_new' => 'Agregar Nuevo Usuario',
     'users_search' => 'Buscar usuarios',
+    'users_latest_activity' => 'Actividad Reciente',
     'users_details' => 'Detalles de Usuario',
     'users_details_desc' => 'Ajusta un nombre público y email para este usuario. El email será empleado para acceder a la aplicación.',
     'users_details_desc_no_email' => 'Ajusta un nombre público para este usuario para que pueda ser reconocido por otros.',
@@ -113,14 +168,17 @@ return [
     'users_send_invite_text' => 'Puede enviar una invitación a este usuario por correo electrónico que le permitirá ajustar su propia contraseña, o puede usted ajustar su contraseña.',
     'users_send_invite_option' => 'Enviar un correo electrónico de invitación',
     'users_external_auth_id' => 'ID externo de autenticación',
-    'users_external_auth_id_desc' => 'Esta es la ID usada para asociar este usuario con LDAP.',
+    'users_external_auth_id_desc' => 'Esta es la ID usada para asociar este usuario con el sistema de autenticación externo.',
     'users_password_warning' => 'Solo debe rellenar este campo si desea cambiar su contraseña.',
     'users_system_public' => 'Este usuario representa cualquier usuario invitado que visita la aplicación. No puede utilizarse para acceder pero es asignado automáticamente.',
     'users_delete' => 'Borrar usuario',
     'users_delete_named' => 'Borrar usuario :userName',
     'users_delete_warning' => 'Se borrará completamente el usuario con el nombre \':userName\' del sistema.',
     'users_delete_confirm' => '¿Está seguro que desea borrar este usuario?',
-    'users_delete_success' => 'Usuarios removidos éxitosamente',
+    'users_migrate_ownership' => 'Cambiar Propietario',
+    'users_migrate_ownership_desc' => 'Seleccione un usuario aquí si desea que otro usuario se convierta en el dueño de todos los elementos que actualmente son propiedad de este usuario.',
+    'users_none_selected' => 'Usuario no seleccionado',
+    'users_delete_success' => 'El usuario se ha eliminado correctamente',
     'users_edit' => 'Editar Usuario',
     'users_edit_profile' => 'Editar perfil',
     'users_edit_success' => 'Usuario actualizado',
@@ -134,6 +192,32 @@ return [
     'users_social_disconnect' => 'Desconectar cuenta',
     'users_social_connected' => 'La cuenta :socialAccount ha sido añadida éxitosamente a su perfil.',
     'users_social_disconnected' => 'La cuenta :socialAccount ha sido desconectada éxitosamente de su perfil.',
+    'users_api_tokens' => 'Tokens API',
+    'users_api_tokens_none' => 'No se han creado tokens API para este usuario',
+    'users_api_tokens_create' => 'Crear token',
+    'users_api_tokens_expires' => 'Expira',
+    'users_api_tokens_docs' => 'Documentación API',
+
+    // API Tokens
+    'user_api_token_create' => 'Crear token API',
+    'user_api_token_name' => 'Nombre',
+    'user_api_token_name_desc' => 'Dale a tu token un nombre legible como un recordatorio futuro de su propósito.',
+    'user_api_token_expiry' => 'Fecha de expiración',
+    'user_api_token_expiry_desc' => 'Establece una fecha en la que este token expira. Después de esta fecha, las solicitudes realizadas usando este token ya no funcionarán. Dejar este campo en blanco fijará un vencimiento de 100 años en el futuro.',
+    'user_api_token_create_secret_message' => 'Inmediatamente después de crear este token se generarán y mostrarán sus correspondientes "Token ID" y "Token Secret". El "Token Secret" sólo se mostrará una vez, así que asegúrese de copiar el valor a un lugar seguro antes de proceder.',
+    'user_api_token_create_success' => 'Token API creado correctamente',
+    'user_api_token_update_success' => 'Token API actualizado correctamente',
+    'user_api_token' => 'Token API',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'Este es un identificador no editable generado por el sistema y único para este token que necesitará ser proporcionado en solicitudes de API.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'Esta es una clave no editable generada por el sistema que necesitará ser proporcionada en solicitudes de API. Solo se monstraré esta vez así que guarde su valor en un lugar seguro.',
+    'user_api_token_created' => 'Token creado :timeAgo',
+    'user_api_token_updated' => 'Token actualizado :timeAgo',
+    'user_api_token_delete' => 'Borrar token',
+    'user_api_token_delete_warning' => 'Esto eliminará completamente este token API con el nombre \':tokenName\' del sistema.',
+    'user_api_token_delete_confirm' => '¿Está seguro de que desea borrar este API token?',
+    'user_api_token_delete_success' => 'Token API borrado correctamente',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Danés',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index c30e168fc03b2c8a3a64b2dcffc626f5661b48c3..f6f9a1ee12bf65c98841af1aca9efb17ccad5455 100644 (file)
@@ -30,19 +30,19 @@ return [
     'digits'               => ':attribute debe ser de :digits dígitos.',
     'digits_between'       => ':attribute debe ser un valor entre :min y :max dígios.',
     'email'                => ':attribute debe ser un correo electrónico válido.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => 'El :attribute debe terminar con uno de los siguientes: :values',
     'filled'               => 'El campo :attribute es requerido.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => 'El :attribute debe ser mayor que :value.',
+        'file'    => 'El :attribute debe ser mayor que :value kilobytes.',
+        'string'  => 'El :attribute debe ser mayor que :value caracteres.',
+        'array'   => 'El :attribute debe tener más de :value elementos.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => 'El :attribute debe ser mayor o igual que :value.',
+        'file'    => 'El :attribute debe ser mayor o igual que :value kilobytes.',
+        'string'  => 'El :attribute debe ser mayor o igual que :value caracteres.',
+        'array'   => 'El :attribute debe tener :value o más elementos.',
     ],
     'exists'               => 'El :attribute seleccionado es inválido.',
     'image'                => 'El :attribute debe ser una imagen.',
@@ -50,20 +50,20 @@ return [
     'in'                   => 'El selected :attribute es inválio.',
     'integer'              => 'El :attribute debe ser un entero.',
     'ip'                   => 'El :attribute debe ser una dirección IP válida.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ipv4'                 => 'El :attribute debe ser una dirección IPv4 válida.',
+    'ipv6'                 => 'El :attribute debe ser una dirección IPv6 válida.',
+    'json'                 => 'El :attribute debe ser una cadena JSON válida.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => 'El :attribute debe ser menor que :value.',
+        'file'    => 'El :attribute debe ser menor que :value kilobytes.',
+        'string'  => 'El :attribute debe ser menor que :value caracteres.',
+        'array'   => 'El :attribute debe tener menos de :value elementos.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => 'El :attribute debe ser menor o igual que :value.',
+        'file'    => 'El :attribute debe ser menor o igual que :value kilobytes.',
+        'string'  => 'El :attribute debe ser menor o igual que :value caracteres.',
+        'array'   => 'El :attribute no debe tener más de :value elementos.',
     ],
     'max'                  => [
         'numeric' => 'El :attribute no puede ser mayor que :max.',
@@ -80,7 +80,7 @@ return [
     ],
     'no_double_extension'  => 'El :attribute solo debe tener una extensión de archivo.',
     'not_in'               => 'El :attribute seleccionado es inválio.',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => 'El formato de :attribute es inválido.',
     'numeric'              => 'El :attribute debe ser numérico.',
     'regex'                => 'El formato de :attribute es inválido',
     'required'             => 'El :attribute es requerido.',
@@ -90,6 +90,7 @@ return [
     'required_without'     => 'El :attribute es requerido cuando no se encuentre entre los valores :values.',
     'required_without_all' => 'El :attribute es requerido cuando ninguno de los valores :values están presentes.',
     'same'                 => 'El :attribute y :other deben coincidir.',
+    'safe_url'             => 'El enlace proporcionado puede no ser seguro.',
     'size'                 => [
         'numeric' => ':attribute debe ser :size.',
         'file'    => ':attribute debe ser :size kilobytes.',
index f8f9e84367568979b8faaca2fda33af9b7d6ebb9..7c9e22450756469b593720860fe374a5542df9fc 100644 (file)
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'comentado',
+    'permissions_update'          => 'permisos actualizados',
 ];
index aa54fa71f6541352370f9876027eb91fc5cd719c..2f957f46d6f18d108cc5453f4b8faec60483efd4 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Restablecer la contraseña',
     'reset_password_send_instructions' => 'Introduzca su correo electrónico a continuación y se le enviará un correo electrónico con un enlace para la restauración',
     'reset_password_send_button' => 'Enviar enlace de restauración',
-    'reset_password_sent_success' => 'Se envió un enlace para restablecer la contraseña a :email.',
+    'reset_password_sent' => 'Si la dirección de correo electrónico :email existe en el sistema, se enviará un enlace para restablecer la contraseña.',
     'reset_password_success' => 'Su contraseña se restableció con éxito.',
     'email_reset_subject' => 'Restauración de la contraseña de para la aplicación :appName',
     'email_reset_text' => 'Ud. esta recibiendo este correo electrónico debido a que recibimos una solicitud de restauración de la contraseña de su cuenta.',
index 3d45ce6cb514d8f46845cc73cefec760fba56010..c6bef56bce6884c58635641f6d105495a37e4d3b 100644 (file)
@@ -33,11 +33,13 @@ return [
     'copy' => 'Copiar',
     'reply' => 'Responder',
     'delete' => 'Borrar',
+    'delete_confirm' => 'Confirmar eliminación',
     'search' => 'Buscar',
     'search_clear' => 'Limpiar búsqueda',
     'reset' => 'Restablecer',
     'remove' => 'Remover',
     'add' => 'Agregar',
+    'fullscreen' => 'Pantalla completa',
 
     // Sort Options
     'sort_options' => 'Opciones de Orden',
@@ -65,6 +67,8 @@ return [
     'profile_menu' => 'Menu del Perfil',
     'view_profile' => 'Ver Perfil',
     'edit_profile' => 'Editar Perfil',
+    'dark_mode' => 'Modo Oscuro',
+    'light_mode' => 'Modo Claro',
 
     // Layout tabs
     'tab_info' => 'Información',
index d205afbc115d94ea6ebe03b1415cb52f841b5113..f7be885898a4ac7b6d5374b2bd55f5afac2f7286 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Cargar más',
     'image_image_name' => 'Nombre de imagen',
     'image_delete_used' => 'Esta imagen esta siendo utilizada en las páginas a continuación.',
-    'image_delete_confirm' => 'Haga click de nuevo para confirmar que quiere borrar esta imagen.',
+    'image_delete_confirm_text' => '¿Está seguro que quiere eliminar esta imagen?',
     'image_select_image' => 'Seleccionar Imagen',
     'image_dropzone' => 'Arrastre las imágenes o hacer click aquí para Subir',
     'images_deleted' => 'Imágenes borradas',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Editar Código',
     'code_language' => 'Lenguaje del Código',
     'code_content' => 'Contenido del Código',
+    'code_session_history' => 'Historial de la sesión',
     'code_save' => 'Guardar Código',
 ];
index 700e873c31d1d0c5786c14867b7b5370116bdc5f..093641a362346804a0a82b0205d0897e7b853aca 100644 (file)
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => 'Creado el  :timeLength por :user',
     'meta_updated' => 'Actualizado el :timeLength',
     'meta_updated_name' => 'Actualizado el :timeLength por :user',
+    'meta_owned_name' => 'Propiedad de :user',
     'entity_select' => 'Seleccione entidad',
     'images' => 'Imágenes',
     'my_recent_drafts' => 'Mis borradores recientes',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'una vez habilitado, Estos permisos tendrán prioridad por encima de cualquier permiso establecido.',
     'permissions_enable' => 'Habilitar permisos custom',
     'permissions_save' => 'Guardar permisos',
+    'permissions_owner' => 'Propietario',
 
     // Search
     'search_results' => 'Buscar resultados',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Ninguna página encontrada para la búsqueda',
     'search_for_term' => 'Busqueda por :term',
     'search_more' => 'Más resultados',
-    'search_filters' => 'Filtros de búsqueda',
+    'search_advanced' => 'Búsqueda Avanzada',
+    'search_terms' => 'Términos de búsqueda',
     'search_content_type' => 'Tipo de contenido',
     'search_exact_matches' => 'Coincidencias exactas',
     'search_tags' => 'Búsquedas de etiquetas',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Crear nuevo capítulo',
     'chapters_delete' => 'Borrar capítulo',
     'chapters_delete_named' => 'Borrar capítulo :chapterName',
-    'chapters_delete_explain' => 'Esto borrará el capítulo con el nombre \':chapterName\', todas las páginas serán removidas y agregadas directamente al libro padre.',
+    'chapters_delete_explain' => 'Esto eliminará el capítulo con el nombre \':chapterName\'. También se eliminarán todas las páginas que existen dentro de este capítulo.',
     'chapters_delete_confirm' => '¿Está seguro de borrar este capítulo?',
     'chapters_edit' => 'Editar capítulo',
     'chapters_edit_named' => 'Editar capítulo :chapterName',
@@ -207,6 +210,7 @@ return [
     'pages_revisions' => 'Revisiones de página',
     'pages_revisions_named' => 'Revisiones de página para :pageName',
     'pages_revision_named' => 'Revisión de ágina para :pageName',
+    'pages_revision_restored_from' => 'Restaurado de #:id; :summary',
     'pages_revisions_created_by' => 'Creado por',
     'pages_revisions_date' => 'Fecha de revisión',
     'pages_revisions_number' => '#',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Archivo adjuntado',
     'attachments_link' => 'Adjuntar enlace',
     'attachments_set_link' => 'Establecer enlace',
-    'attachments_delete_confirm' => 'Presione en borrar nuevamente para confirmar que quiere borrar este elemento adjunto.',
+    'attachments_delete' => '¿Está seguro que desea eliminar el archivo adjunto?',
     'attachments_dropzone' => 'Arrastre archivos aquí o presione aquí para adjuntar un archivo',
     'attachments_no_files' => 'No se adjuntó ningún archivo',
     'attachments_explain_link' => 'Usted puede agregar un enlace o si lo prefiere puede agregar un archivo. Esto puede ser un enlace a otra página o un enlace a un archivo en la nube.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Enlace a archivo',
     'attachments_link_url_hint' => 'URL del sitio o archivo',
     'attach' => 'Adjuntar',
+    'attachments_insert_link' => 'Agregar el Enlace del adjunto a la Página',
     'attachments_edit_file' => 'Editar archivo',
     'attachments_edit_file_name' => 'Nombre del archivo',
     'attachments_edit_drop_upload' => 'Arrastre los archivos o presione aquí para subir o sobreescribir',
index fb3c5e9138daab4e6141ec0ff7bfb0b65dc3f082..41719a5bcb3c035c553eada751bd4e93eebf8a48 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'El email ya ha sido confirmado, Intente loguearse en la aplicación.',
     'email_confirmation_invalid' => 'Este token de confirmación no e válido o ya ha sido usado,Intente registrar uno nuevamente.',
     'email_confirmation_expired' => 'El token de confirmación ha expirado, Un nuevo email de confirmacón ha sido enviado.',
+    'email_confirmation_awaiting' => 'La dirección de correo electrónico de la cuenta en uso debe ser confirmada',
     'ldap_fail_anonymous' => 'El acceso con LDAP ha fallado usando binding anónimo',
     'ldap_fail_authed' => 'El acceso LDAP usando el dn & password detallados',
     'ldap_extension_not_installed' => 'La extensión LDAP PHP no se encuentra instalada',
     'ldap_cannot_connect' => 'No se puede conectar con el servidor ldap, la conexión inicial ha fallado',
+    'saml_already_logged_in' => 'Ya estás conectado',
+    'saml_user_not_registered' => 'El usuario :name no está registrado y el registro automático está deshabilitado',
+    'saml_no_email_address' => 'No se pudo encontrar una dirección de correo electrónico, para este usuario, en los datos proporcionados por el sistema de autenticación externo',
+    'saml_invalid_response_id' => 'La solicitud del sistema de autenticación externo no está reconocida por un proceso iniciado por esta aplicación. Navegar hacia atrás después de un inicio de sesión podría causar este problema.',
+    'saml_fail_authed' => 'El inicio de sesión con :system falló, el sistema no proporcionó una autorización correcta',
     'social_no_action_defined' => 'Acción no definida',
     'social_login_bad_response' => "SE recibió un Error durante el acceso con :socialAccount : \n:error",
     'social_account_in_use' => 'la cuenta :socialAccount ya se encuentra en uso, intente loguearse a través de la opcón :socialAccount .',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'La carga del archivo ha caducado.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Página no coincidente durante la subida del adjunto ',
     'attachment_not_found' => 'No se encuentra el objeto adjunto',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Página no encontrada',
     'sorry_page_not_found' => 'Lo sentimos, la página que intenta acceder no pudo ser encontrada.',
+    'sorry_page_not_found_permission_warning' => 'Si esperaba que esta página existiera, puede que no tenga permiso para verla.',
     'return_home' => 'Volver al home',
     'error_occurred' => 'Ha ocurrido un error',
     'app_down' => 'La aplicación :appName se encuentra caída en este momento',
     'back_soon' => 'Volverá a estar operativa en corto tiempo.',
 
+    // API errors
+    'api_no_authorization_found' => 'No se encontró ningún token de autorización en la solicitud',
+    'api_bad_authorization_format' => 'Se ha encontrado un token de autorización en la solicitud pero el formato era incorrecto',
+    'api_user_token_not_found' => 'No se ha encontrado un token API que corresponda con el token de autorización proporcionado',
+    'api_incorrect_token_secret' => 'El secreto proporcionado para el token API usado es incorrecto',
+    'api_user_no_api_permission' => 'El propietario del token API usado no tiene permiso para hacer llamadas API',
+    'api_user_token_expired' => 'El token de autorización usado ha caducado',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error al enviar un email de prueba:',
+
 ];
index e38e55ed80e619d998d1a8fc353614e272551be2..f9e24939b2a130c19385f7eb6c69544dcbd68845 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'La contraseña debe ser como mínimo de seis caracteres y coincidir con la confirmación.',
     'user' => "No podemos encontrar un usuario con esta dirección de correo electrónico.",
-    'token' => 'Este token de restablecimiento de contraseña no es válido.',
+    'token' => 'El token para restablecer la contraseña no es válido para esta dirección de correo electrónico.',
     'sent' => '¡Hemos enviado a su cuenta de correo electrónico un enlace para restaurar su contraseña!',
     'reset' => '¡Su contraseña fue restaurada!',
 
index e9aad712896cb43d303c8f2525dab00d4dc9fbb8..8ab0941839a77c03e0334d7da1f5ad18fb1a3883 100644 (file)
@@ -41,12 +41,22 @@ return [
     'app_disable_comments_toggle' => 'Deshabilitar comentarios',
     'app_disable_comments_desc' => 'Deshabilitar comentarios en todas las páginas de la aplicación. Los comentarios existentes no se muestran.',
 
+    // Color settings
+    'content_colors' => 'Colores del contenido',
+    'content_colors_desc' => 'Establece los colores para todos los elementos en la jerarquía de la organización de la página. Se recomienda elegir colores con un brillo similar al predeterminado para mayor legibilidad.',
+    'bookshelf_color' => 'Color del estante',
+    'book_color' => 'Color del libro',
+    'chapter_color' => 'Color del capítulo',
+    'page_color' => 'Color de la página',
+    'page_draft_color' => 'Color del borrador de página',
+
     // Registration Settings
     'reg_settings' => 'Ajustes de registro',
     'reg_enable' => 'Habilitar Registro',
     'reg_enable_toggle' => 'Habilitar registro',
     'reg_enable_desc' => 'Cuando se habilita el registro, el usuario podrá crear su usuario en la aplicación. Con el regsitro, se le otorga un rol de usuario único y por defecto.',
     'reg_default_role' => 'Rol de usuario por defecto despúes del registro',
+    'reg_enable_external_warning' => 'La opción anterior no se utiliza mientras la autenticación LDAP o SAML externa esté activa. Las cuentas de usuario para los miembros no existentes se crearán automáticamente si la autenticación en el sistema externo en uso es exitosa.',
     'reg_email_confirmation' => 'Confirmación de correo electrónico',
     'reg_email_confirmation_toggle' => 'Requerir confirmación de correo electrónico',
     'reg_confirm_email_desc' => 'Si se utiliza la restricción por dominio, entonces se requerirá la confirmación por correo electrónico y se ignorará el valor a continuación.',
@@ -58,11 +68,53 @@ return [
     'maint' => 'Mantenimiento',
     'maint_image_cleanup' => 'Limpiar imágenes',
     'maint_image_cleanup_desc' => "Analizar contenido de páginas y revisiones para detectar cuáles imágenes y dibujos están en uso y cuáles son redundantes. Asegúrese de crear un respaldo completo de imágenes y base de datos antes de ejecutar esta tarea.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignorar imágenes en revisión',
+    'maint_delete_images_only_in_revisions' => 'Elimina también imágenes que sólo existen en antiguas revisiones de páginas',
     'maint_image_cleanup_run' => 'Ejecutar limpieza',
     'maint_image_cleanup_warning' => 'Se encontraron :count imágenes pontencialmente sin uso. Está seguro de que quiere eliminarlas?',
     'maint_image_cleanup_success' => 'Se encontraron y se eliminaron :count imágenes pontencialmente sin uso!',
     'maint_image_cleanup_nothing_found' => 'No se encotraron imágenes sin usar, Nada eliminado!',
+    'maint_send_test_email' => 'Enviar un correo electrónico de prueba',
+    'maint_send_test_email_desc' => 'Esto envía un correo electrónico de prueba a la dirección de correo electrónico especificada en tu perfil.',
+    'maint_send_test_email_run' => 'Enviar correo electrónico de prueba',
+    'maint_send_test_email_success' => 'Correo electrónico enviado a :address',
+    'maint_send_test_email_mail_subject' => 'Probar correo electrónico',
+    'maint_send_test_email_mail_greeting' => '¡El envío de correos electrónicos parece funcionar!',
+    'maint_send_test_email_mail_text' => '¡Enhorabuena! Al recibir esta notificación de correo electrónico, tu configuración de correo electrónico parece estar ajustada correctamente.',
+    'maint_recycle_bin_desc' => 'Los estantes, libros, capítulos y páginas eliminados se envían a la papelera de reciclaje para que puedan ser restauradas o eliminadas permanentemente. Los elementos más antiguos en la papelera de reciclaje pueden ser eliminados automáticamente después de un tiempo dependiendo de la configuración del sistema.',
+    'maint_recycle_bin_open' => 'Abrir papelera de reciclaje',
+
+    // Recycle Bin
+    'recycle_bin' => 'Papelera de Reciclaje',
+    'recycle_bin_desc' => 'Aquí puede restaurar elementos que hayan sido eliminados o elegir eliminarlos permanentemente del sistema. Esta lista no está filtrada a diferencia de las listas de actividad similares en el sistema donde se aplican los filtros de permisos.',
+    'recycle_bin_deleted_item' => 'Elemento Eliminado',
+    'recycle_bin_deleted_by' => 'Eliminado por',
+    'recycle_bin_deleted_at' => 'Fecha de eliminación',
+    'recycle_bin_permanently_delete' => 'Eliminar permanentemente',
+    'recycle_bin_restore' => 'Restaurar',
+    'recycle_bin_contents_empty' => 'La papelera de reciclaje está vacía',
+    'recycle_bin_empty' => 'Vaciar Papelera de reciclaje',
+    'recycle_bin_empty_confirm' => 'Esto destruirá permanentemente todos los elementos de la papelera de reciclaje, incluyendo el contenido existente en cada elemento. ¿Está seguro de que desea vaciar la papelera de reciclaje?',
+    'recycle_bin_destroy_confirm' => 'Esta acción eliminará permanentemente este elemento del sistema, junto con los elementos secundarios listados a continuación, y no podrá restaurar este contenido de nuevo. ¿Está seguro de que desea eliminar permanentemente este elemento?',
+    'recycle_bin_destroy_list' => 'Elementos a eliminar',
+    'recycle_bin_restore_list' => 'Elementos a restaurar',
+    'recycle_bin_restore_confirm' => 'Esta acción restaurará el elemento eliminado, incluyendo cualquier elemento secundario, a su ubicación original. Si la ubicación original ha sido eliminada, y ahora está en la papelera de reciclaje, el elemento padre también tendrá que ser restaurado.',
+    'recycle_bin_restore_deleted_parent' => 'El padre de este elemento también ha sido eliminado. Estos permanecerán eliminados hasta que el padre también sea restaurado.',
+    'recycle_bin_destroy_notification' => 'Eliminados :count artículos de la papelera de reciclaje.',
+    'recycle_bin_restore_notification' => 'Restaurados :count artículos desde la papelera de reciclaje.',
+
+    // Audit Log
+    'audit' => 'Registro de Auditoría',
+    'audit_desc' => 'Este registro de auditoría muestra una lista de actividades rastreadas en el sistema. Esta lista no tiene filtrado a diferencia de listas de actividad similares en el sistema en los que se aplican filtros de permisos.',
+    'audit_event_filter' => 'Filtro de Eventos',
+    'audit_event_filter_no_filter' => 'Sin Filtro',
+    'audit_deleted_item' => 'Elemento borrado',
+    'audit_deleted_item_name' => 'Nombre: :name',
+    'audit_table_user' => 'Usuario',
+    'audit_table_event' => 'Evento',
+    'audit_table_related' => 'Elemento o detalle relacionados',
+    'audit_table_date' => 'Fecha de la Actividad',
+    'audit_date_from' => 'Inicio del Rango de Fecha',
+    'audit_date_to' => 'Final del Rango de Fecha',
 
     // Role Settings
     'roles' => 'Roles',
@@ -87,9 +139,11 @@ return [
     'role_manage_own_entity_permissions' => 'Gestionar permisos en libro
     s propios, capítulos y páginas',
     'role_manage_page_templates' => 'Gestionar las plantillas de páginas',
+    'role_access_api' => 'API de sistema de acceso',
     'role_manage_settings' => 'Gestionar ajustes de activos',
     'role_asset' => 'Permisos de activos',
-    'role_asset_desc' => 'Estos permisos controlan el acceso por defecto a los activos del sistema. Permisos a Libros, Capítulos y Páginas sobreescribiran estos permisos.',
+    'roles_system_warning' => 'Tenga en cuenta que el acceso a cualquiera de los tres permisos anteriores puede permitir a un usuario modificar sus propios privilegios o los privilegios de otros usuarios en el sistema. Asignar roles con estos permisos sólo a usuarios de comfianza.',
+    'role_asset_desc' => 'Estos permisos controlan el acceso por defecto a los activos del sistema. Permisos definidos en Libros, Capítulos y Páginas ignorarán estos permisos.',
     'role_asset_admins' => 'Los administradores reciben automáticamente acceso a todo el contenido pero estas opciones pueden mostrar u ocultar opciones de UI.',
     'role_all' => 'Todo',
     'role_own' => 'Propio',
@@ -104,6 +158,7 @@ return [
     'user_profile' => 'Perfil de usuario',
     'users_add_new' => 'Agregar nuevo usuario',
     'users_search' => 'Buscar usuarios',
+    'users_latest_activity' => 'Actividad Reciente',
     'users_details' => 'Detalles del usuario',
     'users_details_desc' => 'Asigne un nombre de visualización y una dirección de correo electrónico para este usuario. La dirección de correo electrónico se usará pra ingresar a la aplicación.',
     'users_details_desc_no_email' => 'Asigne un nombre de visualización a este usuario para que los demás puedan reconocerlo.',
@@ -114,14 +169,17 @@ return [
     'users_send_invite_text' => 'Puede optar por enviar a este usuario un correo electrónico de invitación que les permita establecer su propia contraseña; de lo contrario, puede establecerla contraseña usted mismo.',
     'users_send_invite_option' => 'Enviar correo electrónico de invitación al usuario.',
     'users_external_auth_id' => 'ID externo de autenticación',
-    'users_external_auth_id_desc' => 'Esta es la ID usada para asociar este usuario con LDAP.',
+    'users_external_auth_id_desc' => 'Esta es la ID usada para asociar este usuario con el sistema de autenticación externo.',
     'users_password_warning' => 'Solo rellene a continuación si desea cambiar su password:',
     'users_system_public' => 'Este usuario representa cualquier usuario invitado que visita la aplicación. No puede utilizarse para hacer login sino que es asignado automáticamente.',
     'users_delete' => 'Borrar usuario',
     'users_delete_named' => 'Borrar usuario :userName',
     'users_delete_warning' => 'Se borrará completamente el usuario con el nombre \':userName\' del sistema.',
     'users_delete_confirm' => '¿Está seguro que desea borrar este usuario?',
-    'users_delete_success' => 'Usuarios removidos exitosamente',
+    'users_migrate_ownership' => 'Cambiar Propietario',
+    'users_migrate_ownership_desc' => 'Seleccione un usuario aquí si desea que otro usuario se convierta en el dueño de todos los elementos que actualmente son propiedad de este usuario.',
+    'users_none_selected' => 'Usuario no seleccionado',
+    'users_delete_success' => 'El usuario se ha eliminado correctamente',
     'users_edit' => 'Editar Usuario',
     'users_edit_profile' => 'Editar perfil',
     'users_edit_success' => 'Usuario actualizado',
@@ -135,6 +193,32 @@ return [
     'users_social_disconnect' => 'Desconectar cuenta',
     'users_social_connected' => 'La cuenta :socialAccount ha sido exitosamente añadida a su perfil.',
     'users_social_disconnected' => 'La cuenta :socialAccount ha sido desconectada exitosamente de su perfil.',
+    'users_api_tokens' => 'Tokens API',
+    'users_api_tokens_none' => 'No se han creado tokens API para este usuario',
+    'users_api_tokens_create' => 'Crear token',
+    'users_api_tokens_expires' => 'Expira',
+    'users_api_tokens_docs' => 'Documentación API',
+
+    // API Tokens
+    'user_api_token_create' => 'Crear token API',
+    'user_api_token_name' => 'Nombre',
+    'user_api_token_name_desc' => 'Dale a tu token un nombre legible como un recordatorio futuro de su propósito.',
+    'user_api_token_expiry' => 'Fecha de expiración',
+    'user_api_token_expiry_desc' => 'Establece una fecha en la que este token expira. Después de esta fecha, las solicitudes realizadas usando este token ya no funcionarán. Dejar este campo en blanco fijará un vencimiento de 100 años en el futuro.',
+    'user_api_token_create_secret_message' => 'Luego de crear este token, inmediatamente se generará y mostrará el "Token ID" y el "Token Secret" correspondientes. El "Token Secret" se mostrará por única vez, asegúrese de copiar el valor a un lugar seguro antes de continuar.',
+    'user_api_token_create_success' => 'Token API creado correctamente',
+    'user_api_token_update_success' => 'Token API actualizado correctamente',
+    'user_api_token' => 'Token API',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'Este es un identificador no editable generado por el sistema y único para este token que necesitará ser proporcionado en solicitudes de API.',
+    'user_api_token_secret' => 'Clave de Token',
+    'user_api_token_secret_desc' => 'Esta es una clave no editable generada por el sistema que necesitará ser proporcionada en solicitudes de API. Solo se monstraré esta vez así que guarde su valor en un lugar seguro.',
+    'user_api_token_created' => 'Token creado :timeAgo',
+    'user_api_token_updated' => 'Token actualizado :timeAgo',
+    'user_api_token_delete' => 'Borrar token',
+    'user_api_token_delete_warning' => 'Esto eliminará completamente este token API con el nombre \':tokenName\' del sistema.',
+    'user_api_token_delete_confirm' => '¿Está seguro de que desea borrar este API token?',
+    'user_api_token_delete_success' => 'Token API borrado correctamente',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -142,26 +226,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Danés',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index cd360c8eae64e9f987a379366f40b10cf7bbfcc5..7afd2bb461fbbe5c16852b70525c8e849ab03bdb 100644 (file)
@@ -90,6 +90,7 @@ return [
     'required_without'     => ':attribute es requerido cuando no se encuentre entre los valores :values.',
     'required_without_all' => ':attribute es requerido cuando ninguno de los valores :values están presentes.',
     'same'                 => ':attribute y :other deben coincidir.',
+    'safe_url'             => 'El enlace proporcionado puede no ser seguro.',
     'size'                 => [
         'numeric' => ':attribute debe ser :size.',
         'file'    => ':attribute debe ser :size kilobytes.',
diff --git a/resources/lang/fa/activities.php b/resources/lang/fa/activities.php
new file mode 100644 (file)
index 0000000..4cac54b
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'created page',
+    'page_create_notification'    => 'Page Successfully Created',
+    'page_update'                 => 'updated page',
+    'page_update_notification'    => 'Page Successfully Updated',
+    'page_delete'                 => 'deleted page',
+    'page_delete_notification'    => 'Page Successfully Deleted',
+    'page_restore'                => 'restored page',
+    'page_restore_notification'   => 'Page Successfully Restored',
+    'page_move'                   => 'moved page',
+
+    // Chapters
+    'chapter_create'              => 'created chapter',
+    'chapter_create_notification' => 'Chapter Successfully Created',
+    'chapter_update'              => 'updated chapter',
+    'chapter_update_notification' => 'Chapter Successfully Updated',
+    'chapter_delete'              => 'deleted chapter',
+    'chapter_delete_notification' => 'Chapter Successfully Deleted',
+    'chapter_move'                => 'moved chapter',
+
+    // Books
+    'book_create'                 => 'created book',
+    'book_create_notification'    => 'Book Successfully Created',
+    'book_update'                 => 'updated book',
+    'book_update_notification'    => 'Book Successfully Updated',
+    'book_delete'                 => 'deleted book',
+    'book_delete_notification'    => 'Book Successfully Deleted',
+    'book_sort'                   => 'sorted book',
+    'book_sort_notification'      => 'Book Successfully Re-sorted',
+
+    // Bookshelves
+    'bookshelf_create'            => 'created Bookshelf',
+    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
+    'bookshelf_update'                 => 'updated bookshelf',
+    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
+    'bookshelf_delete'                 => 'deleted bookshelf',
+    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+
+    // Other
+    'commented_on'                => 'commented on',
+];
diff --git a/resources/lang/fa/auth.php b/resources/lang/fa/auth.php
new file mode 100644 (file)
index 0000000..d64fce9
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'These credentials do not match our records.',
+    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
+
+    // Login & Register
+    'sign_up' => 'Sign up',
+    'log_in' => 'Log in',
+    'log_in_with' => 'Login with :socialDriver',
+    'sign_up_with' => 'Sign up with :socialDriver',
+    'logout' => 'Logout',
+
+    'name' => 'Name',
+    'username' => 'Username',
+    'email' => 'Email',
+    'password' => 'Password',
+    'password_confirm' => 'Confirm Password',
+    'password_hint' => 'Must be over 7 characters',
+    'forgot_password' => 'Forgot Password?',
+    'remember_me' => 'Remember Me',
+    'ldap_email_hint' => 'Please enter an email to use for this account.',
+    'create_account' => 'Create Account',
+    'already_have_account' => 'Already have an account?',
+    'dont_have_account' => 'Don\'t have an account?',
+    'social_login' => 'Social Login',
+    'social_registration' => 'Social Registration',
+    'social_registration_text' => 'Register and sign in using another service.',
+
+    'register_thanks' => 'Thanks for registering!',
+    'register_confirm' => 'Please check your email and click the confirmation button to access :appName.',
+    'registrations_disabled' => 'Registrations are currently disabled',
+    'registration_email_domain_invalid' => 'That email domain does not have access to this application',
+    'register_success' => 'Thanks for signing up! You are now registered and signed in.',
+
+
+    // Password Reset
+    'reset_password' => 'Reset Password',
+    'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.',
+    'reset_password_send_button' => 'Send Reset Link',
+    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_success' => 'Your password has been successfully reset.',
+    'email_reset_subject' => 'Reset your :appName password',
+    'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.',
+    'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Confirm your email on :appName',
+    'email_confirm_greeting' => 'Thanks for joining :appName!',
+    'email_confirm_text' => 'Please confirm your email address by clicking the button below:',
+    'email_confirm_action' => 'Confirm Email',
+    'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.',
+    'email_confirm_success' => 'Your email has been confirmed!',
+    'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.',
+
+    'email_not_confirmed' => 'Email Address Not Confirmed',
+    'email_not_confirmed_text' => 'Your email address has not yet been confirmed.',
+    'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.',
+    'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.',
+    'email_not_confirmed_resend_button' => 'Resend Confirmation Email',
+
+    // User Invite
+    'user_invite_email_subject' => 'You have been invited to join :appName!',
+    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
+    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
+    'user_invite_email_action' => 'Set Account Password',
+    'user_invite_page_welcome' => 'Welcome to :appName!',
+    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
+    'user_invite_page_confirm_button' => 'Confirm Password',
+    'user_invite_success' => 'Password set, you now have access to :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/fa/common.php b/resources/lang/fa/common.php
new file mode 100644 (file)
index 0000000..e87bd11
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Cancel',
+    'confirm' => 'Confirm',
+    'back' => 'Back',
+    'save' => 'Save',
+    'continue' => 'Continue',
+    'select' => 'Select',
+    'toggle_all' => 'Toggle All',
+    'more' => 'More',
+
+    // Form Labels
+    'name' => 'Name',
+    'description' => 'Description',
+    'role' => 'Role',
+    'cover_image' => 'Cover image',
+    'cover_image_description' => 'This image should be approx 440x250px.',
+    
+    // Actions
+    'actions' => 'Actions',
+    'view' => 'View',
+    'view_all' => 'View All',
+    'create' => 'Create',
+    'update' => 'Update',
+    'edit' => 'Edit',
+    'sort' => 'Sort',
+    'move' => 'Move',
+    'copy' => 'Copy',
+    'reply' => 'Reply',
+    'delete' => 'Delete',
+    'delete_confirm' => 'Confirm Deletion',
+    'search' => 'Search',
+    'search_clear' => 'Clear Search',
+    'reset' => 'Reset',
+    'remove' => 'Remove',
+    'add' => 'Add',
+    'fullscreen' => 'Fullscreen',
+
+    // Sort Options
+    'sort_options' => 'Sort Options',
+    'sort_direction_toggle' => 'Sort Direction Toggle',
+    'sort_ascending' => 'Sort Ascending',
+    'sort_descending' => 'Sort Descending',
+    'sort_name' => 'Name',
+    'sort_created_at' => 'Created Date',
+    'sort_updated_at' => 'Updated Date',
+
+    // Misc
+    'deleted_user' => 'Deleted User',
+    'no_activity' => 'No activity to show',
+    'no_items' => 'No items available',
+    'back_to_top' => 'Back to top',
+    'toggle_details' => 'Toggle Details',
+    'toggle_thumbnails' => 'Toggle Thumbnails',
+    'details' => 'Details',
+    'grid_view' => 'Grid View',
+    'list_view' => 'List View',
+    'default' => 'Default',
+    'breadcrumb' => 'Breadcrumb',
+
+    // Header
+    'profile_menu' => 'Profile Menu',
+    'view_profile' => 'View Profile',
+    'edit_profile' => 'Edit Profile',
+    'dark_mode' => 'Dark Mode',
+    'light_mode' => 'Light Mode',
+
+    // Layout tabs
+    'tab_info' => 'Info',
+    'tab_content' => 'Content',
+
+    // Email Content
+    'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
+    'email_rights' => 'All rights reserved',
+];
diff --git a/resources/lang/fa/components.php b/resources/lang/fa/components.php
new file mode 100644 (file)
index 0000000..48a0a32
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Image Select',
+    'image_all' => 'All',
+    'image_all_title' => 'View all images',
+    'image_book_title' => 'View images uploaded to this book',
+    'image_page_title' => 'View images uploaded to this page',
+    'image_search_hint' => 'Search by image name',
+    'image_uploaded' => 'Uploaded :uploadedDate',
+    'image_load_more' => 'Load More',
+    'image_image_name' => 'Image Name',
+    'image_delete_used' => 'This image is used in the pages below.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_select_image' => 'Select Image',
+    'image_dropzone' => 'Drop images or click here to upload',
+    'images_deleted' => 'Images Deleted',
+    'image_preview' => 'Image Preview',
+    'image_upload_success' => 'Image uploaded successfully',
+    'image_update_success' => 'Image details successfully updated',
+    'image_delete_success' => 'Image successfully deleted',
+    'image_upload_remove' => 'Remove',
+
+    // Code Editor
+    'code_editor' => 'Edit Code',
+    'code_language' => 'Code Language',
+    'code_content' => 'Code Content',
+    'code_session_history' => 'Session History',
+    'code_save' => 'Save Code',
+];
diff --git a/resources/lang/fa/entities.php b/resources/lang/fa/entities.php
new file mode 100644 (file)
index 0000000..f64867a
--- /dev/null
@@ -0,0 +1,316 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Recently Created',
+    'recently_created_pages' => 'Recently Created Pages',
+    'recently_updated_pages' => 'Recently Updated Pages',
+    'recently_created_chapters' => 'Recently Created Chapters',
+    'recently_created_books' => 'Recently Created Books',
+    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_update' => 'Recently Updated',
+    'recently_viewed' => 'Recently Viewed',
+    'recent_activity' => 'Recent Activity',
+    'create_now' => 'Create one now',
+    'revisions' => 'Revisions',
+    'meta_revision' => 'Revision #:revisionCount',
+    'meta_created' => 'Created :timeLength',
+    'meta_created_name' => 'Created :timeLength by :user',
+    'meta_updated' => 'Updated :timeLength',
+    'meta_updated_name' => 'Updated :timeLength by :user',
+    'entity_select' => 'Entity Select',
+    'images' => 'Images',
+    'my_recent_drafts' => 'My Recent Drafts',
+    'my_recently_viewed' => 'My Recently Viewed',
+    'no_pages_viewed' => 'You have not viewed any pages',
+    'no_pages_recently_created' => 'No pages have been recently created',
+    'no_pages_recently_updated' => 'No pages have been recently updated',
+    'export' => 'Export',
+    'export_html' => 'Contained Web File',
+    'export_pdf' => 'PDF File',
+    'export_text' => 'Plain Text File',
+
+    // Permissions and restrictions
+    'permissions' => 'Permissions',
+    'permissions_intro' => 'Once enabled, These permissions will take priority over any set role permissions.',
+    'permissions_enable' => 'Enable Custom Permissions',
+    'permissions_save' => 'Save Permissions',
+
+    // Search
+    'search_results' => 'Search Results',
+    'search_total_results_found' => ':count result found|:count total results found',
+    'search_clear' => 'Clear Search',
+    'search_no_pages' => 'No pages matched this search',
+    'search_for_term' => 'Search for :term',
+    'search_more' => 'More Results',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
+    'search_content_type' => 'Content Type',
+    'search_exact_matches' => 'Exact Matches',
+    'search_tags' => 'Tag Searches',
+    'search_options' => 'Options',
+    'search_viewed_by_me' => 'Viewed by me',
+    'search_not_viewed_by_me' => 'Not viewed by me',
+    'search_permissions_set' => 'Permissions set',
+    'search_created_by_me' => 'Created by me',
+    'search_updated_by_me' => 'Updated by me',
+    'search_date_options' => 'Date Options',
+    'search_updated_before' => 'Updated before',
+    'search_updated_after' => 'Updated after',
+    'search_created_before' => 'Created before',
+    'search_created_after' => 'Created after',
+    'search_set_date' => 'Set Date',
+    'search_update' => 'Update Search',
+
+    // Shelves
+    'shelf' => 'Shelf',
+    'shelves' => 'Shelves',
+    'x_shelves' => ':count Shelf|:count Shelves',
+    'shelves_long' => 'Bookshelves',
+    'shelves_empty' => 'No shelves have been created',
+    'shelves_create' => 'Create New Shelf',
+    'shelves_popular' => 'Popular Shelves',
+    'shelves_new' => 'New Shelves',
+    'shelves_new_action' => 'New Shelf',
+    'shelves_popular_empty' => 'The most popular shelves will appear here.',
+    'shelves_new_empty' => 'The most recently created shelves will appear here.',
+    'shelves_save' => 'Save Shelf',
+    'shelves_books' => 'Books on this shelf',
+    'shelves_add_books' => 'Add books to this shelf',
+    'shelves_drag_books' => 'Drag books here to add them to this shelf',
+    'shelves_empty_contents' => 'This shelf has no books assigned to it',
+    'shelves_edit_and_assign' => 'Edit shelf to assign books',
+    'shelves_edit_named' => 'Edit Bookshelf :name',
+    'shelves_edit' => 'Edit Bookshelf',
+    'shelves_delete' => 'Delete Bookshelf',
+    'shelves_delete_named' => 'Delete Bookshelf :name',
+    'shelves_delete_explain' => "This will delete the bookshelf with the name ':name'. Contained books will not be deleted.",
+    'shelves_delete_confirmation' => 'Are you sure you want to delete this bookshelf?',
+    'shelves_permissions' => 'Bookshelf Permissions',
+    'shelves_permissions_updated' => 'Bookshelf Permissions Updated',
+    'shelves_permissions_active' => 'Bookshelf Permissions Active',
+    'shelves_copy_permissions_to_books' => 'Copy Permissions to Books',
+    'shelves_copy_permissions' => 'Copy Permissions',
+    'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this bookshelf to all books contained within. Before activating, ensure any changes to the permissions of this bookshelf have been saved.',
+    'shelves_copy_permission_success' => 'Bookshelf permissions copied to :count books',
+
+    // Books
+    'book' => 'Book',
+    'books' => 'Books',
+    'x_books' => ':count Book|:count Books',
+    'books_empty' => 'No books have been created',
+    'books_popular' => 'Popular Books',
+    'books_recent' => 'Recent Books',
+    'books_new' => 'New Books',
+    'books_new_action' => 'New Book',
+    'books_popular_empty' => 'The most popular books will appear here.',
+    'books_new_empty' => 'The most recently created books will appear here.',
+    'books_create' => 'Create New Book',
+    'books_delete' => 'Delete Book',
+    'books_delete_named' => 'Delete Book :bookName',
+    'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.',
+    'books_delete_confirmation' => 'Are you sure you want to delete this book?',
+    'books_edit' => 'Edit Book',
+    'books_edit_named' => 'Edit Book :bookName',
+    'books_form_book_name' => 'Book Name',
+    'books_save' => 'Save Book',
+    'books_permissions' => 'Book Permissions',
+    'books_permissions_updated' => 'Book Permissions Updated',
+    'books_empty_contents' => 'No pages or chapters have been created for this book.',
+    'books_empty_create_page' => 'Create a new page',
+    'books_empty_sort_current_book' => 'Sort the current book',
+    'books_empty_add_chapter' => 'Add a chapter',
+    'books_permissions_active' => 'Book Permissions Active',
+    'books_search_this' => 'Search this book',
+    'books_navigation' => 'Book Navigation',
+    'books_sort' => 'Sort Book Contents',
+    'books_sort_named' => 'Sort Book :bookName',
+    'books_sort_name' => 'Sort by Name',
+    'books_sort_created' => 'Sort by Created Date',
+    'books_sort_updated' => 'Sort by Updated Date',
+    'books_sort_chapters_first' => 'Chapters First',
+    'books_sort_chapters_last' => 'Chapters Last',
+    'books_sort_show_other' => 'Show Other Books',
+    'books_sort_save' => 'Save New Order',
+
+    // Chapters
+    'chapter' => 'Chapter',
+    'chapters' => 'Chapters',
+    'x_chapters' => ':count Chapter|:count Chapters',
+    'chapters_popular' => 'Popular Chapters',
+    'chapters_new' => 'New Chapter',
+    'chapters_create' => 'Create New Chapter',
+    'chapters_delete' => 'Delete Chapter',
+    'chapters_delete_named' => 'Delete Chapter :chapterName',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages will be removed and added directly to the parent book.',
+    'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?',
+    'chapters_edit' => 'Edit Chapter',
+    'chapters_edit_named' => 'Edit Chapter :chapterName',
+    'chapters_save' => 'Save Chapter',
+    'chapters_move' => 'Move Chapter',
+    'chapters_move_named' => 'Move Chapter :chapterName',
+    'chapter_move_success' => 'Chapter moved to :bookName',
+    'chapters_permissions' => 'Chapter Permissions',
+    'chapters_empty' => 'No pages are currently in this chapter.',
+    'chapters_permissions_active' => 'Chapter Permissions Active',
+    'chapters_permissions_success' => 'Chapter Permissions Updated',
+    'chapters_search_this' => 'Search this chapter',
+
+    // Pages
+    'page' => 'Page',
+    'pages' => 'Pages',
+    'x_pages' => ':count Page|:count Pages',
+    'pages_popular' => 'Popular Pages',
+    'pages_new' => 'New Page',
+    'pages_attachments' => 'Attachments',
+    'pages_navigation' => 'Page Navigation',
+    'pages_delete' => 'Delete Page',
+    'pages_delete_named' => 'Delete Page :pageName',
+    'pages_delete_draft_named' => 'Delete Draft Page :pageName',
+    'pages_delete_draft' => 'Delete Draft Page',
+    'pages_delete_success' => 'Page deleted',
+    'pages_delete_draft_success' => 'Draft page deleted',
+    'pages_delete_confirm' => 'Are you sure you want to delete this page?',
+    'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?',
+    'pages_editing_named' => 'Editing Page :pageName',
+    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_save_draft' => 'Save Draft',
+    'pages_edit_draft' => 'Edit Page Draft',
+    'pages_editing_draft' => 'Editing Draft',
+    'pages_editing_page' => 'Editing Page',
+    'pages_edit_draft_save_at' => 'Draft saved at ',
+    'pages_edit_delete_draft' => 'Delete Draft',
+    'pages_edit_discard_draft' => 'Discard Draft',
+    'pages_edit_set_changelog' => 'Set Changelog',
+    'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made',
+    'pages_edit_enter_changelog' => 'Enter Changelog',
+    'pages_save' => 'Save Page',
+    'pages_title' => 'Page Title',
+    'pages_name' => 'Page Name',
+    'pages_md_editor' => 'Editor',
+    'pages_md_preview' => 'Preview',
+    'pages_md_insert_image' => 'Insert Image',
+    'pages_md_insert_link' => 'Insert Entity Link',
+    'pages_md_insert_drawing' => 'Insert Drawing',
+    'pages_not_in_chapter' => 'Page is not in a chapter',
+    'pages_move' => 'Move Page',
+    'pages_move_success' => 'Page moved to ":parentName"',
+    'pages_copy' => 'Copy Page',
+    'pages_copy_desination' => 'Copy Destination',
+    'pages_copy_success' => 'Page successfully copied',
+    'pages_permissions' => 'Page Permissions',
+    'pages_permissions_success' => 'Page permissions updated',
+    'pages_revision' => 'Revision',
+    'pages_revisions' => 'Page Revisions',
+    'pages_revisions_named' => 'Page Revisions for :pageName',
+    'pages_revision_named' => 'Page Revision for :pageName',
+    'pages_revisions_created_by' => 'Created By',
+    'pages_revisions_date' => 'Revision Date',
+    'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'Revision #:id',
+    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_changelog' => 'Changelog',
+    'pages_revisions_changes' => 'Changes',
+    'pages_revisions_current' => 'Current Version',
+    'pages_revisions_preview' => 'Preview',
+    'pages_revisions_restore' => 'Restore',
+    'pages_revisions_none' => 'This page has no revisions',
+    'pages_copy_link' => 'Copy Link',
+    'pages_edit_content_link' => 'Edit Content',
+    'pages_permissions_active' => 'Page Permissions Active',
+    'pages_initial_revision' => 'Initial publish',
+    'pages_initial_name' => 'New Page',
+    'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.',
+    'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count users have started editing this page',
+        'start_b' => ':userName has started editing this page',
+        'time_a' => 'since the page was last updated',
+        'time_b' => 'in the last :minCount minutes',
+        'message' => ':start :time. Take care not to overwrite each other\'s updates!',
+    ],
+    'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content',
+    'pages_specific' => 'Specific Page',
+    'pages_is_template' => 'Page Template',
+
+    // Editor Sidebar
+    'page_tags' => 'Page Tags',
+    'chapter_tags' => 'Chapter Tags',
+    'book_tags' => 'Book Tags',
+    'shelf_tags' => 'Shelf Tags',
+    'tag' => 'Tag',
+    'tags' =>  'Tags',
+    'tag_name' =>  'Tag Name',
+    'tag_value' => 'Tag Value (Optional)',
+    'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.",
+    'tags_add' => 'Add another tag',
+    'tags_remove' => 'Remove this tag',
+    'attachments' => 'Attachments',
+    'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.',
+    'attachments_explain_instant_save' => 'Changes here are saved instantly.',
+    'attachments_items' => 'Attached Items',
+    'attachments_upload' => 'Upload File',
+    'attachments_link' => 'Attach Link',
+    'attachments_set_link' => 'Set Link',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
+    'attachments_dropzone' => 'Drop files or click here to attach a file',
+    'attachments_no_files' => 'No files have been uploaded',
+    'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
+    'attachments_link_name' => 'Link Name',
+    'attachment_link' => 'Attachment link',
+    'attachments_link_url' => 'Link to file',
+    'attachments_link_url_hint' => 'Url of site or file',
+    'attach' => 'Attach',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
+    'attachments_edit_file' => 'Edit File',
+    'attachments_edit_file_name' => 'File Name',
+    'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
+    'attachments_order_updated' => 'Attachment order updated',
+    'attachments_updated_success' => 'Attachment details updated',
+    'attachments_deleted' => 'Attachment deleted',
+    'attachments_file_uploaded' => 'File successfully uploaded',
+    'attachments_file_updated' => 'File successfully updated',
+    'attachments_link_attached' => 'Link successfully attached to page',
+    'templates' => 'Templates',
+    'templates_set_as_template' => 'Page is a template',
+    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
+    'templates_replace_content' => 'Replace page content',
+    'templates_append_content' => 'Append to page content',
+    'templates_prepend_content' => 'Prepend to page content',
+
+    // Profile View
+    'profile_user_for_x' => 'User for :time',
+    'profile_created_content' => 'Created Content',
+    'profile_not_created_pages' => ':userName has not created any pages',
+    'profile_not_created_chapters' => ':userName has not created any chapters',
+    'profile_not_created_books' => ':userName has not created any books',
+    'profile_not_created_shelves' => ':userName has not created any shelves',
+
+    // Comments
+    'comment' => 'Comment',
+    'comments' => 'Comments',
+    'comment_add' => 'Add Comment',
+    'comment_placeholder' => 'Leave a comment here',
+    'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments',
+    'comment_save' => 'Save Comment',
+    'comment_saving' => 'Saving comment...',
+    'comment_deleting' => 'Deleting comment...',
+    'comment_new' => 'New Comment',
+    'comment_created' => 'commented :createDiff',
+    'comment_updated' => 'Updated :updateDiff by :username',
+    'comment_deleted_success' => 'Comment deleted',
+    'comment_created_success' => 'Comment added',
+    'comment_updated_success' => 'Comment updated',
+    'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
+    'comment_in_reply_to' => 'In reply to :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Are you sure you want to delete this revision?',
+    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_delete_success' => 'Revision deleted',
+    'revision_cannot_delete_latest' => 'Cannot delete the latest revision.'
+];
\ No newline at end of file
diff --git a/resources/lang/fa/errors.php b/resources/lang/fa/errors.php
new file mode 100644 (file)
index 0000000..79024e4
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'You do not have permission to access the requested page.',
+    'permissionJson' => 'You do not have permission to perform the requested action.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.',
+    'email_already_confirmed' => 'Email has already been confirmed, Try logging in.',
+    'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.',
+    'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.',
+    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+    'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
+    'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
+    'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
+    'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
+    'saml_already_logged_in' => 'Already logged in',
+    'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
+    'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
+    'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
+    'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
+    'social_no_action_defined' => 'No action defined',
+    'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
+    'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
+    'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.',
+    'social_account_existing' => 'This :socialAccount is already attached to your profile.',
+    'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
+    'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ',
+    'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.',
+    'social_driver_not_found' => 'Social driver not found',
+    'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
+    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+
+    // System
+    'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
+    'cannot_get_image_from_url' => 'Cannot get image from :url',
+    'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
+    'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'uploaded'  => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'image_upload_error' => 'An error occurred uploading the image',
+    'image_upload_type_error' => 'The image type being uploaded is invalid',
+    'file_upload_timeout' => 'The file upload has timed out.',
+
+    // Attachments
+    'attachment_not_found' => 'Attachment not found',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
+    'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage',
+
+    // Entities
+    'entity_not_found' => 'Entity not found',
+    'bookshelf_not_found' => 'Bookshelf not found',
+    'book_not_found' => 'Book not found',
+    'page_not_found' => 'Page not found',
+    'chapter_not_found' => 'Chapter not found',
+    'selected_book_not_found' => 'The selected book was not found',
+    'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
+    'guests_cannot_save_drafts' => 'Guests cannot save drafts',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
+    'users_cannot_delete_guest' => 'You cannot delete the guest user',
+
+    // Roles
+    'role_cannot_be_edited' => 'This role cannot be edited',
+    'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
+    'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
+    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+
+    // Comments
+    'comment_list' => 'An error occurred while fetching the comments.',
+    'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.',
+    'comment_add' => 'An error occurred while adding / updating the comment.',
+    'comment_delete' => 'An error occurred while deleting the comment.',
+    'empty_comment' => 'Cannot add an empty comment.',
+
+    // Error pages
+    '404_page_not_found' => 'Page Not Found',
+    'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
+    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'return_home' => 'Return to home',
+    'error_occurred' => 'An Error Occurred',
+    'app_down' => ':appName is down right now',
+    'back_soon' => 'It will be back up soon.',
+
+    // API errors
+    'api_no_authorization_found' => 'No authorization token found on the request',
+    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
+    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
+    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
+    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
+    'api_user_token_expired' => 'The authorization token used has expired',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+
+];
diff --git a/resources/lang/fa/pagination.php b/resources/lang/fa/pagination.php
new file mode 100644 (file)
index 0000000..85bd12f
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; Previous',
+    'next'     => 'Next &raquo;',
+
+];
diff --git a/resources/lang/fa/passwords.php b/resources/lang/fa/passwords.php
new file mode 100644 (file)
index 0000000..b408f3c
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+    'password' => 'Passwords must be at least eight characters and match the confirmation.',
+    'user' => "We can't find a user with that e-mail address.",
+    'token' => 'The password reset token is invalid for this email address.',
+    'sent' => 'We have e-mailed your password reset link!',
+    'reset' => 'Your password has been reset!',
+
+];
diff --git a/resources/lang/fa/settings.php b/resources/lang/fa/settings.php
new file mode 100644 (file)
index 0000000..2bd314c
--- /dev/null
@@ -0,0 +1,229 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Settings',
+    'settings_save' => 'Save Settings',
+    'settings_save_success' => 'Settings saved',
+
+    // App Settings
+    'app_customization' => 'Customization',
+    'app_features_security' => 'Features & Security',
+    'app_name' => 'Application Name',
+    'app_name_desc' => 'This name is shown in the header and in any system-sent emails.',
+    'app_name_header' => 'Show name in header',
+    'app_public_access' => 'Public Access',
+    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
+    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
+    'app_public_access_toggle' => 'Allow public access',
+    'app_public_viewing' => 'Allow public viewing?',
+    'app_secure_images' => 'Higher Security Image Uploads',
+    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.',
+    'app_editor' => 'Page Editor',
+    'app_editor_desc' => 'Select which editor will be used by all users to edit pages.',
+    'app_custom_html' => 'Custom HTML Head Content',
+    'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the <head> section of every page. This is handy for overriding styles or adding analytics code.',
+    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_logo' => 'Application Logo',
+    'app_logo_desc' => 'This image should be 43px in height. <br>Large images will be scaled down.',
+    'app_primary_color' => 'Application Primary Color',
+    'app_primary_color_desc' => 'Sets the primary color for the application including the banner, buttons, and links.',
+    'app_homepage' => 'Application Homepage',
+    'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
+    'app_homepage_select' => 'Select a page',
+    'app_disable_comments' => 'Disable Comments',
+    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
+
+    // Color settings
+    'content_colors' => 'Content Colors',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
+    // Registration Settings
+    'reg_settings' => 'Registration',
+    'reg_enable' => 'Enable Registration',
+    'reg_enable_toggle' => 'Enable registration',
+    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_default_role' => 'Default user role after registration',
+    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    'reg_email_confirmation' => 'Email Confirmation',
+    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.',
+    'reg_confirm_restrict_domain' => 'Domain Restriction',
+    'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
+    'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
+
+    // Maintenance settings
+    'maint' => 'Maintenance',
+    'maint_image_cleanup' => 'Cleanup Images',
+    'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
+    'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+    'maint_image_cleanup_run' => 'Run Cleanup',
+    'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
+    'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
+    'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
+    'maint_send_test_email' => 'Send a Test Email',
+    'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
+    'maint_send_test_email_run' => 'Send test email',
+    'maint_send_test_email_success' => 'Email sent to :address',
+    'maint_send_test_email_mail_subject' => 'Test Email',
+    'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
+    'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
+    // Role Settings
+    'roles' => 'Roles',
+    'role_user_roles' => 'User Roles',
+    'role_create' => 'Create New Role',
+    'role_create_success' => 'Role successfully created',
+    'role_delete' => 'Delete Role',
+    'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
+    'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
+    'role_delete_no_migration' => "Don't migrate users",
+    'role_delete_sure' => 'Are you sure you want to delete this role?',
+    'role_delete_success' => 'Role successfully deleted',
+    'role_edit' => 'Edit Role',
+    'role_details' => 'Role Details',
+    'role_name' => 'Role Name',
+    'role_desc' => 'Short Description of Role',
+    'role_external_auth_id' => 'External Authentication IDs',
+    'role_system' => 'System Permissions',
+    'role_manage_users' => 'Manage users',
+    'role_manage_roles' => 'Manage roles & role permissions',
+    'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions',
+    'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
+    'role_manage_page_templates' => 'Manage page templates',
+    'role_access_api' => 'Access system API',
+    'role_manage_settings' => 'Manage app settings',
+    'role_asset' => 'Asset Permissions',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
+    'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
+    'role_all' => 'All',
+    'role_own' => 'Own',
+    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_save' => 'Save Role',
+    'role_update_success' => 'Role successfully updated',
+    'role_users' => 'Users in this role',
+    'role_users_none' => 'No users are currently assigned to this role',
+
+    // Users
+    'users' => 'Users',
+    'user_profile' => 'User Profile',
+    'users_add_new' => 'Add New User',
+    'users_search' => 'Search Users',
+    'users_details' => 'User Details',
+    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
+    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_role' => 'User Roles',
+    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
+    'users_password' => 'User Password',
+    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
+    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
+    'users_send_invite_option' => 'Send user invite email',
+    'users_external_auth_id' => 'External Authentication ID',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+    'users_password_warning' => 'Only fill the below if you would like to change your password.',
+    'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
+    'users_delete' => 'Delete User',
+    'users_delete_named' => 'Delete user :userName',
+    'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
+    'users_delete_confirm' => 'Are you sure you want to delete this user?',
+    'users_delete_success' => 'Users successfully removed',
+    'users_edit' => 'Edit User',
+    'users_edit_profile' => 'Edit Profile',
+    'users_edit_success' => 'User successfully updated',
+    'users_avatar' => 'User Avatar',
+    'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.',
+    'users_preferred_language' => 'Preferred Language',
+    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_social_accounts' => 'Social Accounts',
+    'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.',
+    'users_social_connect' => 'Connect Account',
+    'users_social_disconnect' => 'Disconnect Account',
+    'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
+    'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token Created :timeAgo',
+    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'Slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/fa/validation.php b/resources/lang/fa/validation.php
new file mode 100644 (file)
index 0000000..76b57a2
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => 'The :attribute must be accepted.',
+    'active_url'           => 'The :attribute is not a valid URL.',
+    'after'                => 'The :attribute must be a date after :date.',
+    'alpha'                => 'The :attribute may only contain letters.',
+    'alpha_dash'           => 'The :attribute may only contain letters, numbers, dashes and underscores.',
+    'alpha_num'            => 'The :attribute may only contain letters and numbers.',
+    'array'                => 'The :attribute must be an array.',
+    'before'               => 'The :attribute must be a date before :date.',
+    'between'              => [
+        'numeric' => 'The :attribute must be between :min and :max.',
+        'file'    => 'The :attribute must be between :min and :max kilobytes.',
+        'string'  => 'The :attribute must be between :min and :max characters.',
+        'array'   => 'The :attribute must have between :min and :max items.',
+    ],
+    'boolean'              => 'The :attribute field must be true or false.',
+    'confirmed'            => 'The :attribute confirmation does not match.',
+    'date'                 => 'The :attribute is not a valid date.',
+    'date_format'          => 'The :attribute does not match the format :format.',
+    'different'            => 'The :attribute and :other must be different.',
+    'digits'               => 'The :attribute must be :digits digits.',
+    'digits_between'       => 'The :attribute must be between :min and :max digits.',
+    'email'                => 'The :attribute must be a valid email address.',
+    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'filled'               => 'The :attribute field is required.',
+    'gt'                   => [
+        'numeric' => 'The :attribute must be greater than :value.',
+        'file'    => 'The :attribute must be greater than :value kilobytes.',
+        'string'  => 'The :attribute must be greater than :value characters.',
+        'array'   => 'The :attribute must have more than :value items.',
+    ],
+    'gte'                  => [
+        'numeric' => 'The :attribute must be greater than or equal :value.',
+        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be greater than or equal :value characters.',
+        'array'   => 'The :attribute must have :value items or more.',
+    ],
+    'exists'               => 'The selected :attribute is invalid.',
+    'image'                => 'The :attribute must be an image.',
+    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'in'                   => 'The selected :attribute is invalid.',
+    'integer'              => 'The :attribute must be an integer.',
+    'ip'                   => 'The :attribute must be a valid IP address.',
+    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
+    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
+    'json'                 => 'The :attribute must be a valid JSON string.',
+    'lt'                   => [
+        'numeric' => 'The :attribute must be less than :value.',
+        'file'    => 'The :attribute must be less than :value kilobytes.',
+        'string'  => 'The :attribute must be less than :value characters.',
+        'array'   => 'The :attribute must have less than :value items.',
+    ],
+    'lte'                  => [
+        'numeric' => 'The :attribute must be less than or equal :value.',
+        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be less than or equal :value characters.',
+        'array'   => 'The :attribute must not have more than :value items.',
+    ],
+    'max'                  => [
+        'numeric' => 'The :attribute may not be greater than :max.',
+        'file'    => 'The :attribute may not be greater than :max kilobytes.',
+        'string'  => 'The :attribute may not be greater than :max characters.',
+        'array'   => 'The :attribute may not have more than :max items.',
+    ],
+    'mimes'                => 'The :attribute must be a file of type: :values.',
+    'min'                  => [
+        'numeric' => 'The :attribute must be at least :min.',
+        'file'    => 'The :attribute must be at least :min kilobytes.',
+        'string'  => 'The :attribute must be at least :min characters.',
+        'array'   => 'The :attribute must have at least :min items.',
+    ],
+    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'not_in'               => 'The selected :attribute is invalid.',
+    'not_regex'            => 'The :attribute format is invalid.',
+    'numeric'              => 'The :attribute must be a number.',
+    'regex'                => 'The :attribute format is invalid.',
+    'required'             => 'The :attribute field is required.',
+    'required_if'          => 'The :attribute field is required when :other is :value.',
+    'required_with'        => 'The :attribute field is required when :values is present.',
+    'required_with_all'    => 'The :attribute field is required when :values is present.',
+    'required_without'     => 'The :attribute field is required when :values is not present.',
+    'required_without_all' => 'The :attribute field is required when none of :values are present.',
+    'same'                 => 'The :attribute and :other must match.',
+    'size'                 => [
+        'numeric' => 'The :attribute must be :size.',
+        'file'    => 'The :attribute must be :size kilobytes.',
+        'string'  => 'The :attribute must be :size characters.',
+        'array'   => 'The :attribute must contain :size items.',
+    ],
+    'string'               => 'The :attribute must be a string.',
+    'timezone'             => 'The :attribute must be a valid zone.',
+    'unique'               => 'The :attribute has already been taken.',
+    'url'                  => 'The :attribute format is invalid.',
+    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Password confirmation required',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 56db4abff0490f3c68e97b24933aa925e2b2b586..8b62bd0528a390770f9e1fe6b9694ef88867f317 100644 (file)
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'a commenté',
+    'permissions_update'          => 'mettre à jour les autorisations',
 ];
index 99b41c070d3a06265764f91fe591bf4863ba1b2b..07252420a030a8b87fc3e1b3eb9de8e41c389f78 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Réinitialiser le mot de passe',
     'reset_password_send_instructions' => 'Entrez votre adresse e-mail ci-dessous et un e-mail avec un lien de réinitialisation de mot de passe vous sera envoyé.',
     'reset_password_send_button' => 'Envoyer un lien de réinitialisation',
-    'reset_password_sent_success' => 'Un lien de réinitialisation a été envoyé à :email.',
+    'reset_password_sent' => 'Un lien de réinitialisation du mot de passe sera envoyé à :email si cette adresse e-mail est trouvée dans le système.',
     'reset_password_success' => 'Votre mot de passe a été réinitialisé avec succès.',
     'email_reset_subject' => 'Réinitialisez votre mot de passe pour :appName',
     'email_reset_text' => 'Vous recevez cet e-mail parce que nous avons reçu une demande de réinitialisation pour votre compte.',
index 3fb19a303949f83dff8d8bbf9ff6f403c598bc41..6571db60c6d37485f5476e9123da9b5ccfb00b43 100644 (file)
@@ -33,11 +33,13 @@ return [
     'copy' => 'Copier',
     'reply' => 'Répondre',
     'delete' => 'Supprimer',
+    'delete_confirm' => 'Confirmer la suppression',
     'search' => 'Chercher',
     'search_clear' => 'Réinitialiser la recherche',
     'reset' => 'Réinitialiser',
     'remove' => 'Enlever',
     'add' => 'Ajouter',
+    'fullscreen' => 'Plein écran',
 
     // Sort Options
     'sort_options' => 'Options de tri',
@@ -65,9 +67,11 @@ return [
     'profile_menu' => 'Menu du profil',
     'view_profile' => 'Voir le profil',
     'edit_profile' => 'Modifier le profil',
+    'dark_mode' => 'Mode sombre',
+    'light_mode' => 'Mode clair',
 
     // Layout tabs
-    'tab_info' => 'Info',
+    'tab_info' => 'Informations',
     'tab_content' => 'Contenu',
 
     // Email Content
index 2f6ff8bf9c906413e5a679c4943b6364d9ea8633..6cce4f80418f1e5967daceee7b8e72b30b0f28a6 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Charger plus',
     'image_image_name' => 'Nom de l\'image',
     'image_delete_used' => 'Cette image est utilisée dans les pages ci-dessous.',
-    'image_delete_confirm' => 'Confirmez que vous souhaitez bien supprimer cette image.',
+    'image_delete_confirm_text' => 'Êtes-vous sûr de vouloir supprimer cette image ?',
     'image_select_image' => 'Sélectionner l\'image',
     'image_dropzone' => 'Glissez les images ici ou cliquez pour les ajouter',
     'images_deleted' => 'Images supprimées',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Editer le code',
     'code_language' => 'Langage du code',
     'code_content' => 'Contenu du code',
+    'code_session_history' => 'Historique de session',
     'code_save' => 'Enregistrer le code',
 ];
index a6c665f9c1f5904a6437832d62763bb71e19bbf7..72711bcb57ea7312fd59bc840c5c8986a033c951 100644 (file)
@@ -15,13 +15,14 @@ return [
     'recently_update' => 'Mis à jour récemment',
     'recently_viewed' => 'Vus récemment',
     'recent_activity' => 'Activité récente',
-    'create_now' => 'En créer un maintenant',
+    'create_now' => 'En créer une maintenant',
     'revisions' => 'Révisions',
     'meta_revision' => 'Révision #:revisionCount',
     'meta_created' => 'Créé :timeLength',
     'meta_created_name' => 'Créé :timeLength par :user',
     'meta_updated' => 'Mis à jour :timeLength',
     'meta_updated_name' => 'Mis à jour :timeLength par :user',
+    'meta_owned_name' => 'Possédé par :user',
     'entity_select' => 'Sélectionner l\'entité',
     'images' => 'Images',
     'my_recent_drafts' => 'Mes brouillons récents',
@@ -35,10 +36,11 @@ return [
     'export_text' => 'Document texte',
 
     // Permissions and restrictions
-    'permissions' => 'Permissions',
+    'permissions' => 'Autorisations',
     'permissions_intro' => 'Une fois activées ces permissions prendront la priorité sur tous les sets de permissions préexistants.',
     'permissions_enable' => 'Activer les permissions personnalisées',
     'permissions_save' => 'Enregistrer les permissions',
+    'permissions_owner' => 'Propriétaire',
 
     // Search
     'search_results' => 'Résultats de recherche',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Aucune page correspondant à cette recherche',
     'search_for_term' => 'recherche pour :term',
     'search_more' => 'Plus de résultats',
-    'search_filters' => 'Filtres de recherche',
+    'search_advanced' => 'Recherche avancée',
+    'search_terms' => 'Mot-clé',
     'search_content_type' => 'Type de contenu',
     'search_exact_matches' => 'Correspondances exactes',
     'search_tags' => 'Recherche par tags',
@@ -87,7 +90,7 @@ return [
     'shelves_edit' => 'Modifier l\'étagère',
     'shelves_delete' => 'Supprimer l\'étagère',
     'shelves_delete_named' => 'Supprimer l\'étagère :name',
-    'shelves_delete_explain' => "Ceci va supprimer l\\'étagère nommée \\':bookName\\'. Les livres contenus dans cette étagère ne seront pas supprimés.",
+    'shelves_delete_explain' => "Ceci va supprimer l'étagère nommée ':name'. Les livres contenus dans cette étagère ne seront pas supprimés.",
     'shelves_delete_confirmation' => 'Êtes-vous sûr(e) de vouloir supprimer cette étagère ?',
     'shelves_permissions' => 'Permissions de l\'étagère',
     'shelves_permissions_updated' => 'Permissions de l\'étagère mises à jour',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Créer un nouveau chapitre',
     'chapters_delete' => 'Supprimer le chapitre',
     'chapters_delete_named' => 'Supprimer le chapitre :chapterName',
-    'chapters_delete_explain' => 'Ceci va supprimer le chapitre \':chapterName\', toutes les pages seront déplacées dans le livre parent.',
+    'chapters_delete_explain' => 'Ceci supprimera le chapitre portant le nom \':chapterName\'. Toutes les pages qui existent dans ce chapitre seront également supprimées.',
     'chapters_delete_confirm' => 'Etes-vous sûr(e) de vouloir supprimer ce chapitre ?',
     'chapters_edit' => 'Modifier le chapitre',
     'chapters_edit_named' => 'Modifier le chapitre :chapterName',
@@ -162,7 +165,7 @@ return [
     // Pages
     'page' => 'Page',
     'pages' => 'Pages',
-    'x_pages' => ':count Page|:count Pages',
+    'x_pages' => ':count Page|:count pages',
     'pages_popular' => 'Pages populaires',
     'pages_new' => 'Nouvelle page',
     'pages_attachments' => 'Fichiers joints',
@@ -176,7 +179,7 @@ return [
     'pages_delete_confirm' => 'Êtes-vous sûr(e) de vouloir supprimer cette page ?',
     'pages_delete_draft_confirm' => 'Êtes-vous sûr(e) de vouloir supprimer ce brouillon ?',
     'pages_editing_named' => 'Modification de la page :pageName',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Options du brouillon',
     'pages_edit_save_draft' => 'Enregistrer le brouillon',
     'pages_edit_draft' => 'Modifier le brouillon',
     'pages_editing_draft' => 'Modification du brouillon',
@@ -207,11 +210,12 @@ return [
     'pages_revisions' => 'Révisions de la page',
     'pages_revisions_named' => 'Révisions pour :pageName',
     'pages_revision_named' => 'Révision pour :pageName',
+    'pages_revision_restored_from' => 'Restauré à partir de #:id; :summary',
     'pages_revisions_created_by' => 'Créé par',
     'pages_revisions_date' => 'Date de révision',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_numbered' => 'Révision #:id',
+    'pages_revisions_numbered_changes' => 'Modification #:id',
     'pages_revisions_changelog' => 'Journal des changements',
     'pages_revisions_changes' => 'Changements',
     'pages_revisions_current' => 'Version courante',
@@ -243,11 +247,11 @@ return [
     'shelf_tags' => 'Mots-clés de l\'étagère',
     'tag' => 'Mot-clé',
     'tags' =>  'Mots-clés',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'Nom du tag',
     'tag_value' => 'Valeur du mot-clé (Optionnel)',
     'tags_explain' => "Ajouter des mots-clés pour catégoriser votre contenu.",
     'tags_add' => 'Ajouter un autre mot-clé',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Supprimer le tag',
     'attachments' => 'Fichiers joints',
     'attachments_explain' => 'Ajouter des fichiers ou des liens pour les afficher sur votre page. Ils seront affichés dans la barre latérale',
     'attachments_explain_instant_save' => 'Ces changements sont enregistrés immédiatement.',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Uploader un fichier',
     'attachments_link' => 'Attacher un lien',
     'attachments_set_link' => 'Définir un lien',
-    'attachments_delete_confirm' => 'Cliquer une seconde fois sur supprimer pour valider la suppression.',
+    'attachments_delete' => 'Êtes-vous sûr de vouloir supprimer la pièce jointe ?',
     'attachments_dropzone' => 'Glissez des fichiers ou cliquez ici pour attacher des fichiers',
     'attachments_no_files' => 'Aucun fichier ajouté',
     'attachments_explain_link' => 'Vous pouvez attacher un lien si vous ne souhaitez pas uploader un fichier.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Lien sur un fichier',
     'attachments_link_url_hint' => 'URL du site ou du fichier',
     'attach' => 'Attacher',
+    'attachments_insert_link' => 'Ajouter un lien de pièce jointe à la page',
     'attachments_edit_file' => 'Modifier le fichier',
     'attachments_edit_file_name' => 'Nom du fichier',
     'attachments_edit_drop_upload' => 'Glissez un fichier ou cliquer pour mettre à jour le fichier',
@@ -275,7 +280,7 @@ return [
     'attachments_link_attached' => 'Lien attaché à la page avec succès',
     'templates' => 'Modèles',
     'templates_set_as_template' => 'La page est un modèle',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
+    'templates_explain_set_as_template' => 'Vous pouvez définir cette page comme modèle pour que son contenu soit utilisé lors de la création d\'autres pages. Les autres utilisateurs pourront utiliser ce modèle s\'ils ont les permissions pour cette page.',
     'templates_replace_content' => 'Remplacer le contenu de la page',
     'templates_append_content' => 'Ajouter après le contenu de la page',
     'templates_prepend_content' => 'Ajouter devant le contenu de la page',
index 11da312a44223b5d75bdd435885cda610a0d6b2b..c1c14fe31f36ed8313026824792f19e2577cda8f 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'Cet e-mail a déjà été validé, vous pouvez vous connecter.',
     'email_confirmation_invalid' => 'Cette confirmation est invalide. Veuillez essayer de vous inscrire à nouveau.',
     'email_confirmation_expired' => 'Le jeton de confirmation est périmé. Un nouvel e-mail vous a été envoyé.',
+    'email_confirmation_awaiting' => 'L\'adresse e-mail du compte utilisé doit être confirmée',
     'ldap_fail_anonymous' => 'L\'accès LDAP anonyme n\'a pas abouti',
     'ldap_fail_authed' => 'L\'accès LDAP n\'a pas abouti avec cet utilisateur et ce mot de passe',
     'ldap_extension_not_installed' => 'L\'extension LDAP PHP n\'est pas installée',
     'ldap_cannot_connect' => 'Impossible de se connecter au serveur LDAP, la connexion initiale a échoué',
+    'saml_already_logged_in' => 'Déjà connecté',
+    'saml_user_not_registered' => 'L\'utilisateur :name n\'est pas enregistré et l\'enregistrement automatique est désactivé',
+    'saml_no_email_address' => 'Impossible de trouver une adresse e-mail, pour cet utilisateur, dans les données fournies par le système d\'authentification externe',
+    'saml_invalid_response_id' => 'La requête du système d\'authentification externe n\'est pas reconnue par un processus démarré par cette application. Naviguer après une connexion peut causer ce problème.',
+    'saml_fail_authed' => 'Connexion avec :system échoue, le système n\'a pas fourni l\'autorisation réussie',
     'social_no_action_defined' => 'Pas d\'action définie',
     'social_login_bad_response' => "Erreur pendant la tentative de connexion à :socialAccount : \n:error",
     'social_account_in_use' => 'Ce compte :socialAccount est déjà utilisé. Essayez de vous connecter via :socialAccount.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'Le téléchargement du fichier a expiré.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page incorrecte durant la mise à jour du fichier joint',
     'attachment_not_found' => 'Fichier joint non trouvé',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Page non trouvée',
     'sorry_page_not_found' => 'Désolé, cette page n\'a pas pu être trouvée.',
+    'sorry_page_not_found_permission_warning' => 'Si vous vous attendiez à ce que cette page existe, il se peut que vous n\'ayez pas l\'autorisation de la consulter.',
     'return_home' => 'Retour à l\'accueil',
     'error_occurred' => 'Une erreur est survenue',
     'app_down' => ':appName n\'est pas en service pour le moment',
     'back_soon' => 'Nous serons bientôt de retour.',
 
+    // API errors
+    'api_no_authorization_found' => 'Aucun jeton d\'autorisation trouvé pour la demande',
+    'api_bad_authorization_format' => 'Un jeton d\'autorisation a été trouvé pour la requête, mais le format semble incorrect',
+    'api_user_token_not_found' => 'Aucun jeton API correspondant n\'a été trouvé pour le jeton d\'autorisation fourni',
+    'api_incorrect_token_secret' => 'Le secret fourni pour le jeton d\'API utilisé est incorrect',
+    'api_user_no_api_permission' => 'Le propriétaire du jeton API utilisé n\'a pas la permission de passer des appels API',
+    'api_user_token_expired' => 'Le jeton d\'autorisation utilisé a expiré',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Erreur émise lors de l\'envoi d\'un e-mail de test :',
+
 ];
index 3852f5bf1492baaa63355999e9c112daacf6baae..b0ff20e282989ec735bf278ba97baaf6d1477b2f 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Les mots de passe doivent faire au moins 6 caractères et correspondre à la confirmation.',
     'user' => "Nous n'avons pas trouvé d'utilisateur avec cette adresse.",
-    'token' => 'Le jeton de réinitialisation est invalide.',
+    'token' => 'Le mot de passe reset du token n\'est pas valide pour cette adresse e-mail.',
     'sent' => 'Nous vous avons envoyé un lien de réinitialisation de mot de passe !',
     'reset' => 'Votre mot de passe a été réinitialisé !',
 
index fc25fb22c5797e28eaa47dce65838335b74caf7c..89e2f0ca5691bf4aaa725f3ac5b7156258f0a85b 100644 (file)
@@ -29,7 +29,7 @@ return [
     'app_editor_desc' => 'Sélectionnez l\'éditeur qui sera utilisé pour modifier les pages.',
     'app_custom_html' => 'HTML personnalisé dans l\'en-tête',
     'app_custom_html_desc' => 'Le contenu inséré ici sera ajouté en bas de la balise <head> de toutes les pages. Vous pouvez l\'utiliser pour ajouter du CSS personnalisé ou un tracker analytique.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'Le contenu de la tête HTML personnalisée est désactivé sur cette page de paramètres pour garantir que les modifications les plus récentes peuvent être annulées.',
     'app_logo' => 'Logo de l\'Application',
     'app_logo_desc' => 'Cette image doit faire 43px de hauteur. <br>Les images plus larges seront réduites.',
     'app_primary_color' => 'Couleur principale de l\'application',
@@ -41,12 +41,22 @@ return [
     'app_disable_comments_toggle' => 'Désactiver les commentaires',
     'app_disable_comments_desc' => 'Désactive les commentaires sur toutes les pages de l\'application. Les commentaires existants ne sont pas affichés.',
 
+    // Color settings
+    'content_colors' => 'Couleur du contenu',
+    'content_colors_desc' => 'Définit les couleurs pour tous les éléments de la hiérarchie d\'organisation des pages. Choisir les couleurs avec une luminosité similaire aux couleurs par défaut est recommandé pour la lisibilité.',
+    'bookshelf_color' => 'Couleur de l\'étagère',
+    'book_color' => 'Couleur du livre',
+    'chapter_color' => 'Couleur du chapitre',
+    'page_color' => 'Couleur de la page',
+    'page_draft_color' => 'Couleur du brouillon',
+
     // Registration Settings
     'reg_settings' => 'Préférence pour l\'inscription',
     'reg_enable' => 'Activer l\'inscription',
     'reg_enable_toggle' => 'Activer l\'inscription',
     'reg_enable_desc' => 'Lorsque l\'inscription est activée, l\'utilisateur pourra s\'enregistrer en tant qu\'utilisateur de l\'application. Lors de l\'inscription, ils se voient attribuer un rôle par défaut.',
     'reg_default_role' => 'Rôle par défaut lors de l\'inscription',
+    'reg_enable_external_warning' => 'L\'option ci-dessus est ignorée lorsque l\'authentification externe LDAP ou SAML est activée. Les comptes utilisateur pour les membres non existants seront créés automatiquement si l\'authentification, par rapport au système externe utilisé, est réussie.',
     'reg_email_confirmation' => 'Confirmation de l\'e-mail',
     'reg_email_confirmation_toggle' => 'Obliger la confirmation par e-mail ?',
     'reg_confirm_email_desc' => 'Si la restriction de domaine est activée, la confirmation sera automatiquement obligatoire et cette valeur sera ignorée.',
@@ -58,11 +68,53 @@ return [
     'maint' => 'Maintenance',
     'maint_image_cleanup' => 'Nettoyer les images',
     'maint_image_cleanup_desc' => "Scan le contenu des pages et des révisions pour vérifier les images et les dessins en cours d'utilisation et lesquels sont redondant. Veuillez à faire une sauvegarde de la base de données et des images avant de lancer ceci.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignorer les images dans les révisions',
+    'maint_delete_images_only_in_revisions' => 'Supprimer également les images qui n\'existent que dans les anciennes révisions de page',
     'maint_image_cleanup_run' => 'Lancer le nettoyage',
     'maint_image_cleanup_warning' => ':count images potentiellement inutilisées trouvées. Etes-vous sûr de vouloir supprimer ces images ?',
     'maint_image_cleanup_success' => ':count images potentiellement inutilisées trouvées et supprimées !',
     'maint_image_cleanup_nothing_found' => 'Aucune image inutilisée trouvée, rien à supprimer !',
+    'maint_send_test_email' => 'Envoyer un email de test',
+    'maint_send_test_email_desc' => 'Ceci envoie un e-mail de test à votre adresse e-mail spécifiée dans votre profil.',
+    'maint_send_test_email_run' => 'Envoyer un email de test',
+    'maint_send_test_email_success' => 'Email envoyé à :address',
+    'maint_send_test_email_mail_subject' => 'Email de test',
+    'maint_send_test_email_mail_greeting' => 'La livraison d\'email semble fonctionner !',
+    'maint_send_test_email_mail_text' => 'Félicitations ! Lorsque vous avez reçu cette notification par courriel, vos paramètres d\'email semblent être configurés correctement.',
+    'maint_recycle_bin_desc' => 'Les étagères, livres, chapitres et pages supprimés sont envoyés dans la corbeille afin qu\'ils puissent être restaurés ou supprimés définitivement. Les éléments plus anciens de la corbeille peuvent être supprimés automatiquement après un certain temps selon la configuration du système.',
+    'maint_recycle_bin_open' => 'Ouvrir la corbeille',
+
+    // Recycle Bin
+    'recycle_bin' => 'Corbeille',
+    'recycle_bin_desc' => 'Ici, vous pouvez restaurer les éléments qui ont été supprimés ou choisir de les effacer définitivement du système. Cette liste n\'est pas filtrée contrairement aux listes d\'activités similaires dans le système pour lesquelles les filtres d\'autorisation sont appliqués.',
+    'recycle_bin_deleted_item' => 'Élément supprimé',
+    'recycle_bin_deleted_by' => 'Supprimé par',
+    'recycle_bin_deleted_at' => 'Date de suppression',
+    'recycle_bin_permanently_delete' => 'Supprimer définitivement',
+    'recycle_bin_restore' => 'Restaurer',
+    'recycle_bin_contents_empty' => 'La corbeille est vide',
+    'recycle_bin_empty' => 'Vider la Corbeille',
+    'recycle_bin_empty_confirm' => 'Cela détruira définitivement tous les éléments de la corbeille, y compris le contenu contenu de chaque élément. Êtes-vous sûr de vouloir vider la corbeille ?',
+    'recycle_bin_destroy_confirm' => 'Cette action supprimera définitivement cet élément, ainsi que tous les éléments enfants listés ci-dessous du système et vous ne pourrez pas restaurer ce contenu. Êtes-vous sûr de vouloir supprimer définitivement cet élément ?',
+    'recycle_bin_destroy_list' => 'Éléments à détruire',
+    'recycle_bin_restore_list' => 'Éléments à restaurer',
+    'recycle_bin_restore_confirm' => 'Cette action restaurera l\'élément supprimé, y compris tous les éléments enfants, à leur emplacement d\'origine. Si l\'emplacement d\'origine a été supprimé depuis et est maintenant dans la corbeille, l\'élément parent devra également être restauré.',
+    'recycle_bin_restore_deleted_parent' => 'Le parent de cet élément a également été supprimé. Ceux-ci resteront supprimés jusqu\'à ce que ce parent soit également restauré.',
+    'recycle_bin_destroy_notification' => ':count éléments totaux supprimés de la corbeille.',
+    'recycle_bin_restore_notification' => ':count éléments totaux restaurés de la corbeille.',
+
+    // Audit Log
+    'audit' => 'Journal d\'audit',
+    'audit_desc' => 'Ce journal d\'audit affiche une liste des activités suivies dans le système. Cette liste n\'est pas filtrée contrairement aux listes d\'activités similaires dans le système où les filtres d\'autorisation sont appliqués.',
+    'audit_event_filter' => 'Filtres d\'événement',
+    'audit_event_filter_no_filter' => 'Pas de filtre',
+    'audit_deleted_item' => 'Élément supprimé',
+    'audit_deleted_item_name' => 'Nom: :name',
+    'audit_table_user' => 'Utilisateur',
+    'audit_table_event' => 'Evènement',
+    'audit_table_related' => 'Élément ou détail lié',
+    'audit_table_date' => 'Date d\'activation',
+    'audit_date_from' => 'À partir du',
+    'audit_date_to' => 'Jusqu\'au',
 
     // Role Settings
     'roles' => 'Rôles',
@@ -85,9 +137,11 @@ return [
     'role_manage_roles' => 'Gérer les rôles et permissions',
     'role_manage_entity_permissions' => 'Gérer les permissions sur les livres, chapitres et pages',
     'role_manage_own_entity_permissions' => 'Gérer les permissions de ses propres livres, chapitres et pages',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => 'Gérer les modèles de page',
+    'role_access_api' => 'Accès à l\'API du système',
     'role_manage_settings' => 'Gérer les préférences de l\'application',
     'role_asset' => 'Permissions des ressources',
+    'roles_system_warning' => 'Sachez que l\'accès à l\'une des trois permissions ci-dessus peut permettre à un utilisateur de modifier ses propres privilèges ou les privilèges des autres utilisateurs du système. Attribuer uniquement des rôles avec ces permissions à des utilisateurs de confiance.',
     'role_asset_desc' => 'Ces permissions contrôlent l\'accès par défaut des ressources dans le système. Les permissions dans les livres, les chapitres et les pages ignoreront ces permissions',
     'role_asset_admins' => 'Les administrateurs ont automatiquement accès à tous les contenus mais les options suivantes peuvent afficher ou masquer certaines options de l\'interface.',
     'role_all' => 'Tous',
@@ -103,6 +157,7 @@ return [
     'user_profile' => 'Profil d\'utilisateur',
     'users_add_new' => 'Ajouter un nouvel utilisateur',
     'users_search' => 'Chercher les utilisateurs',
+    'users_latest_activity' => 'Dernière activité',
     'users_details' => 'Informations de l\'utilisateur',
     'users_details_desc' => 'Définissez un nom et une adresse e-mail pour cet utilisateur. L\'adresse e-mail sera utilisée pour se connecter à l\'application.',
     'users_details_desc_no_email' => 'Définissez un nom d\'affichage pour cet utilisateur afin que les autres puissent le reconnaître.',
@@ -110,17 +165,20 @@ return [
     'users_role_desc' => 'Sélectionnez les rôles auxquels cet utilisateur sera affecté. Si un utilisateur est affecté à plusieurs rôles, les permissions de ces rôles s\'empileront et ils recevront toutes les capacités des rôles affectés.',
     'users_password' => 'Mot de passe de l\'utilisateur',
     'users_password_desc' => 'Définissez un mot de passe utilisé pour vous connecter à l\'application. Il doit comporter au moins 5 caractères.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
+    'users_send_invite_text' => 'Vous pouvez choisir d\'envoyer à cet utilisateur un email d\'invitation qui lui permet de définir son propre mot de passe, sinon vous pouvez définir son mot de passe vous-même.',
+    'users_send_invite_option' => 'Envoyer l\'e-mail d\'invitation',
     'users_external_auth_id' => 'Identifiant d\'authentification externe',
-    'users_external_auth_id_desc' => 'Il s\'agit de l\'identifiant utilisé pour appairer cet utilisateur lors de la communication avec votre système LDAP.',
+    'users_external_auth_id_desc' => 'C\'est l\'ID utilisé pour correspondre à cet utilisateur lors de la communication avec votre système d\'authentification externe.',
     'users_password_warning' => 'Remplissez ce formulaire uniquement si vous souhaitez changer de mot de passe:',
     'users_system_public' => 'Cet utilisateur représente les invités visitant votre instance. Il est assigné automatiquement aux invités.',
     'users_delete' => 'Supprimer un utilisateur',
     'users_delete_named' => 'Supprimer l\'utilisateur :userName',
     'users_delete_warning' => 'Ceci va supprimer \':userName\' du système.',
     'users_delete_confirm' => 'Êtes-vous sûr(e) de vouloir supprimer cet utilisateur ?',
-    'users_delete_success' => 'Utilisateurs supprimés avec succès',
+    'users_migrate_ownership' => 'Migré propriété',
+    'users_migrate_ownership_desc' => 'Sélectionnez un utilisateur ici si vous voulez qu\'un autre utilisateur devienne le propriétaire de tous les éléments actuellement détenus par cet utilisateur.',
+    'users_none_selected' => 'Aucun utilisateur n\'a été séléctionné',
+    'users_delete_success' => 'Utilisateur supprimé avec succès',
     'users_edit' => 'Modifier l\'utilisateur',
     'users_edit_profile' => 'Modifier le profil',
     'users_edit_success' => 'Utilisateur mis à jour avec succès',
@@ -134,6 +192,32 @@ return [
     'users_social_disconnect' => 'Déconnecter le compte',
     'users_social_connected' => 'Votre compte :socialAccount a été ajouté avec succès.',
     'users_social_disconnected' => 'Votre compte :socialAccount a été déconnecté avec succès',
+    'users_api_tokens' => 'Jetons de l\'API',
+    'users_api_tokens_none' => 'Aucun jeton API n\'a été créé pour cet utilisateur',
+    'users_api_tokens_create' => 'Créer un jeton',
+    'users_api_tokens_expires' => 'Expiré',
+    'users_api_tokens_docs' => 'Documentation de l\'API',
+
+    // API Tokens
+    'user_api_token_create' => 'Créer un nouveau jeton API',
+    'user_api_token_name' => 'Nom',
+    'user_api_token_name_desc' => 'Donnez à votre jeton un nom lisible pour l\'identifier plus tard.',
+    'user_api_token_expiry' => 'Date d\'expiration',
+    'user_api_token_expiry_desc' => 'Définissez une date à laquelle ce jeton expire. Après cette date, les demandes effectuées à l\'aide de ce jeton ne fonctionneront plus. Le fait de laisser ce champ vide entraînera une expiration dans 100 ans.',
+    'user_api_token_create_secret_message' => 'Immédiatement après la création de ce jeton, un "ID de jeton" "et" Secret de jeton "sera généré et affiché. Le secret ne sera affiché qu\'une seule fois, alors assurez-vous de copier la valeur dans un endroit sûr et sécurisé avant de continuer.',
+    'user_api_token_create_success' => 'L\'API token a été créé avec succès',
+    'user_api_token_update_success' => 'L\'API token a été mis à jour avec succès',
+    'user_api_token' => 'Token API',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'Il s\'agit d\'un identifiant généré par le système non modifiable pour ce jeton qui devra être fourni dans les demandes d\'API.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'Il s\'agit d\'un secret généré par le système pour ce jeton, qui devra être fourni dans les demandes d\'API. Cela ne sera affiché qu\'une seule fois, alors copiez cette valeur dans un endroit sûr et sécurisé.',
+    'user_api_token_created' => 'Jeton créé :timeAgo',
+    'user_api_token_updated' => 'Jeton mis à jour :timeAgo',
+    'user_api_token_delete' => 'Supprimer le Token',
+    'user_api_token_delete_warning' => 'Cela supprimera complètement le jeton d\'API avec le nom \':tokenName\'.',
+    'user_api_token_delete_confirm' => 'Souhaitez-vous vraiment effacer l\'API Token ?',
+    'user_api_token_delete_success' => 'L\'API token a été supprimé avec succès',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bulgare',
+        'cs' => 'Česky',
+        'da' => 'Danois',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'Hébreu',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norvegien',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index f59d5c50313c3fb1ea3533b0d27be85e62b70ffb..60d8d34acfb02193039909d432e6d86760ceba52 100644 (file)
@@ -90,6 +90,7 @@ return [
     'required_without'     => ':attribute est requis si:values n\'est pas présent.',
     'required_without_all' => ':attribute est requis si aucun des valeurs :values n\'est présente.',
     'same'                 => ':attribute et :other doivent être identiques.',
+    'safe_url'             => 'Le lien fourni peut ne pas être sûr.',
     'size'                 => [
         'numeric' => ':attribute doit avoir la taille :size.',
         'file'    => ':attribute doit peser :size kilobytes.',
diff --git a/resources/lang/he/activities.php b/resources/lang/he/activities.php
new file mode 100644 (file)
index 0000000..0babc38
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'created page',
+    'page_create_notification'    => 'הדף נוצר בהצלחה',
+    'page_update'                 => 'updated page',
+    'page_update_notification'    => 'הדף עודכן בהצלחה',
+    'page_delete'                 => 'deleted page',
+    'page_delete_notification'    => 'הדף הוסר בהצלחה',
+    'page_restore'                => 'restored page',
+    'page_restore_notification'   => 'הדף שוחזר בהצלחה',
+    'page_move'                   => 'moved page',
+
+    // Chapters
+    'chapter_create'              => 'created chapter',
+    'chapter_create_notification' => 'הפרק נוצר בהצלחה',
+    'chapter_update'              => 'updated chapter',
+    'chapter_update_notification' => 'הפרק עודכן בהצלחה',
+    'chapter_delete'              => 'deleted chapter',
+    'chapter_delete_notification' => 'הפרק הוסר בהצלחה',
+    'chapter_move'                => 'moved chapter',
+
+    // Books
+    'book_create'                 => 'created book',
+    'book_create_notification'    => 'הספר נוצר בהצלחה',
+    'book_update'                 => 'updated book',
+    'book_update_notification'    => 'הספר עודכן בהצלחה',
+    'book_delete'                 => 'deleted book',
+    'book_delete_notification'    => 'הספר הוסר בהצלחה',
+    'book_sort'                   => 'sorted book',
+    'book_sort_notification'      => 'הספר מוין מחדש בהצלחה',
+
+    // Bookshelves
+    'bookshelf_create'            => 'created Bookshelf',
+    'bookshelf_create_notification'    => 'מדף הספרים נוצר בהצלחה',
+    'bookshelf_update'                 => 'updated bookshelf',
+    'bookshelf_update_notification'    => 'מדף הספרים עודכן בהצלחה',
+    'bookshelf_delete'                 => 'deleted bookshelf',
+    'bookshelf_delete_notification'    => 'מדף הספרים הוסר בהצלחה',
+
+    // Other
+    'commented_on'                => 'commented on',
+    'permissions_update'          => 'updated permissions',
+];
diff --git a/resources/lang/he/auth.php b/resources/lang/he/auth.php
new file mode 100644 (file)
index 0000000..733c84f
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'פרטי ההתחברות אינם תואמים את הנתונים שלנו',
+    'throttle' => 'נסיונות התחברות רבים מדי, יש להמתין :seconds שניות ולנסות שנית',
+
+    // Login & Register
+    'sign_up' => 'הרשמה',
+    'log_in' => 'התחבר',
+    'log_in_with' => 'התחבר באמצעות :socialDriver',
+    'sign_up_with' => 'הרשם באמצעות :socialDriver',
+    'logout' => 'התנתק',
+
+    'name' => 'שם',
+    'username' => 'שם משתמש',
+    'email' => 'אי-מייל',
+    'password' => 'סיסמא',
+    'password_confirm' => 'אימות סיסמא',
+    'password_hint' => 'חייבת להיות יותר מ-5 תווים',
+    'forgot_password' => 'שכחת סיסמא?',
+    'remember_me' => 'זכור אותי',
+    'ldap_email_hint' => 'אנא ציין כתובת אי-מייל לשימוש בחשבון זה',
+    'create_account' => 'צור חשבון',
+    'already_have_account' => 'יש לך כבר חשבון?',
+    'dont_have_account' => 'אין לך חשבון?',
+    'social_login' => 'התחברות באמצעות אתר חברתי',
+    'social_registration' => 'הרשמה באמצעות אתר חברתי',
+    'social_registration_text' => 'הרשם והתחבר באמצעות שירות אחר',
+
+    'register_thanks' => 'תודה על הרשמתך!',
+    'register_confirm' => 'יש לבדוק את תיבת המייל שלך ולאשר את ההרשמה על מנת להשתמש ב:appName',
+    'registrations_disabled' => 'הרשמה כרגע מבוטלת',
+    'registration_email_domain_invalid' => 'לא ניתן להרשם באמצעות המייל שסופק',
+    'register_success' => 'תודה על הרשמתך! ניתן כעת להתחבר',
+
+
+    // Password Reset
+    'reset_password' => 'איפוס סיסמא',
+    'reset_password_send_instructions' => 'יש להזין את כתובת המייל למטה ואנו נשלח אלייך הוראות לאיפוס הסיסמא',
+    'reset_password_send_button' => 'שלח קישור לאיפוס סיסמא',
+    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_success' => 'סיסמתך עודכנה בהצלחה',
+    'email_reset_subject' => 'איפוס סיסמא ב :appName',
+    'email_reset_text' => 'קישור זה נשלח עקב בקשה לאיפוס סיסמא בחשבון שלך',
+    'email_reset_not_requested' => 'אם לא ביקשת לאפס את סיסמתך, אפשר להתעלם ממייל זה',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'אמת אי-מייל ב :appName',
+    'email_confirm_greeting' => 'תודה שהצטרפת אל :appName!',
+    'email_confirm_text' => 'יש לאמת את כתובת המייל של על ידי לחיצה על הכפור למטה:',
+    'email_confirm_action' => 'אמת כתובת אי-מייל',
+    'email_confirm_send_error' => 'נדרש אימות אי-מייל אך שליחת האי-מייל אליך נכשלה. יש ליצור קשר עם מנהל המערכת כדי לוודא שאכן ניתן לשלוח מיילים.',
+    'email_confirm_success' => 'האי-מייל שלך אושר!',
+    'email_confirm_resent' => 'אימות נשלח לאי-מייל שלך, יש לבדוק בתיבת הדואר הנכנס',
+
+    'email_not_confirmed' => 'כתובת המייל לא אומתה',
+    'email_not_confirmed_text' => 'כתובת המייל שלך טרם אומתה',
+    'email_not_confirmed_click_link' => 'יש ללחוץ על הקישור אשר נשלח אליך לאחר ההרשמה',
+    'email_not_confirmed_resend' => 'אם אינך מוצא את המייל, ניתן לשלוח בשנית את האימות על ידי לחיצה על הכפתור למטה',
+    'email_not_confirmed_resend_button' => 'שלח שוב מייל אימות',
+
+    // User Invite
+    'user_invite_email_subject' => 'You have been invited to join :appName!',
+    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
+    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
+    'user_invite_email_action' => 'Set Account Password',
+    'user_invite_page_welcome' => 'Welcome to :appName!',
+    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
+    'user_invite_page_confirm_button' => 'Confirm Password',
+    'user_invite_success' => 'Password set, you now have access to :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/he/common.php b/resources/lang/he/common.php
new file mode 100644 (file)
index 0000000..8a6311a
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'ביטול',
+    'confirm' => 'אישור',
+    'back' => 'אחורה',
+    'save' => 'שמור',
+    'continue' => 'המשך',
+    'select' => 'בחר',
+    'toggle_all' => 'סמן הכל',
+    'more' => 'עוד',
+
+    // Form Labels
+    'name' => 'שם',
+    'description' => 'תיאור',
+    'role' => 'תפקיד',
+    'cover_image' => 'תמונת נושא',
+    'cover_image_description' => 'התמונה צריכה להיות בסביבות 440x250px',
+    
+    // Actions
+    'actions' => 'פעולות',
+    'view' => 'הצג',
+    'view_all' => 'הצג הכל',
+    'create' => 'צור',
+    'update' => 'עדכן',
+    'edit' => 'ערוך',
+    'sort' => 'מיין',
+    'move' => 'הזז',
+    'copy' => 'העתק',
+    'reply' => 'השב',
+    'delete' => 'מחק',
+    'delete_confirm' => 'Confirm Deletion',
+    'search' => 'חיפוש',
+    'search_clear' => 'נקה חיפוש',
+    'reset' => 'איפוס',
+    'remove' => 'הסר',
+    'add' => 'הוסף',
+    'fullscreen' => 'Fullscreen',
+
+    // Sort Options
+    'sort_options' => 'Sort Options',
+    'sort_direction_toggle' => 'Sort Direction Toggle',
+    'sort_ascending' => 'Sort Ascending',
+    'sort_descending' => 'Sort Descending',
+    'sort_name' => 'שם',
+    'sort_created_at' => 'תאריך יצירה',
+    'sort_updated_at' => 'תאריך עדכון',
+
+    // Misc
+    'deleted_user' => 'משתמש שנמחק',
+    'no_activity' => 'אין פעילות להציג',
+    'no_items' => 'אין פריטים זמינים',
+    'back_to_top' => 'בחזרה ללמעלה',
+    'toggle_details' => 'הצג/הסתר פרטים',
+    'toggle_thumbnails' => 'הצג/הסתר תמונות',
+    'details' => 'פרטים',
+    'grid_view' => 'תצוגת רשת',
+    'list_view' => 'תצוגת רשימה',
+    'default' => 'ברירת מחדל',
+    'breadcrumb' => 'Breadcrumb',
+
+    // Header
+    'profile_menu' => 'Profile Menu',
+    'view_profile' => 'הצג פרופיל',
+    'edit_profile' => 'ערוך פרופיל',
+    'dark_mode' => 'Dark Mode',
+    'light_mode' => 'Light Mode',
+
+    // Layout tabs
+    'tab_info' => 'מידע',
+    'tab_content' => 'תוכן',
+
+    // Email Content
+    'email_action_help' => 'אם לא ניתן ללחות על כפתור ״:actionText״, יש להעתיק ולהדביק את הכתובת למטה אל דפדפן האינטרנט שלך:',
+    'email_rights' => 'כל הזכויות שמורות',
+];
diff --git a/resources/lang/he/components.php b/resources/lang/he/components.php
new file mode 100644 (file)
index 0000000..84ff7c3
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'בחירת תמונה',
+    'image_all' => 'הכל',
+    'image_all_title' => 'הצג את כל התמונות',
+    'image_book_title' => 'הצג תמונות שהועלו לספר זה',
+    'image_page_title' => 'הצג תמונות שהועלו לדף זה',
+    'image_search_hint' => 'חפש תמונה לפי שם',
+    'image_uploaded' => 'הועלה :uploadedDate',
+    'image_load_more' => 'טען עוד',
+    'image_image_name' => 'שם התמונה',
+    'image_delete_used' => 'תמונה זו בשימוש בדפים שמתחת',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_select_image' => 'בחר תמונה',
+    'image_dropzone' => 'גרור תמונות או לחץ כאן להעלאה',
+    'images_deleted' => 'התמונות נמחקו',
+    'image_preview' => 'תצוגה מקדימה',
+    'image_upload_success' => 'התמונה עלתה בהצלחה',
+    'image_update_success' => 'פרטי התמונה עודכנו בהצלחה',
+    'image_delete_success' => 'התמונה נמחקה בהצלחה',
+    'image_upload_remove' => 'מחק',
+
+    // Code Editor
+    'code_editor' => 'ערוך קוד',
+    'code_language' => 'שפת הקוד',
+    'code_content' => 'תוכן הקוד',
+    'code_session_history' => 'Session History',
+    'code_save' => 'שמור קוד',
+];
diff --git a/resources/lang/he/entities.php b/resources/lang/he/entities.php
new file mode 100644 (file)
index 0000000..59199bd
--- /dev/null
@@ -0,0 +1,319 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'נוצר לאחרונה',
+    'recently_created_pages' => 'דפים שנוצרו לאחרונה',
+    'recently_updated_pages' => 'דפים שעודכנו לאחרונה',
+    'recently_created_chapters' => 'פרקים שנוצרו לאחרונה',
+    'recently_created_books' => 'ספרים שנוצרו לאחרונה',
+    'recently_created_shelves' => 'מדפים שנוצרו לאחרונה',
+    'recently_update' => 'עודכן לאחרונה',
+    'recently_viewed' => 'נצפה לאחרונה',
+    'recent_activity' => 'פעילות לאחרונה',
+    'create_now' => 'צור אחד כעת',
+    'revisions' => 'עדכונים',
+    'meta_revision' => 'עדכון #:revisionCount',
+    'meta_created' => 'נוצר :timeLength',
+    'meta_created_name' => 'נוצר :timeLength על ידי :user',
+    'meta_updated' => 'עודכן :timeLength',
+    'meta_updated_name' => 'עודכן :timeLength על ידי :user',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'בחר יישות',
+    'images' => 'תמונות',
+    'my_recent_drafts' => 'הטיוטות האחרונות שלי',
+    'my_recently_viewed' => 'הנצפים לאחרונה שלי',
+    'no_pages_viewed' => 'לא צפית בדפים כלשהם',
+    'no_pages_recently_created' => 'לא נוצרו דפים לאחרונה',
+    'no_pages_recently_updated' => 'לא עודכנו דפים לאחרונה',
+    'export' => 'ייצוא',
+    'export_html' => 'דף אינטרנט',
+    'export_pdf' => 'קובץ PDF',
+    'export_text' => 'טקסט רגיל',
+
+    // Permissions and restrictions
+    'permissions' => 'הרשאות',
+    'permissions_intro' => 'ברגע שמסומן, הרשאות אלו יגברו על כל הרשאת תפקיד שקיימת',
+    'permissions_enable' => 'הפעל הרשאות מותאמות אישית',
+    'permissions_save' => 'שמור הרשאות',
+    'permissions_owner' => 'Owner',
+
+    // Search
+    'search_results' => 'תוצאות חיפוש',
+    'search_total_results_found' => ':count תוצאות נמצאו|:count סה״כ תוצאות',
+    'search_clear' => 'נקה חיפוש',
+    'search_no_pages' => 'לא נמצאו דפים התואמים לחיפוש',
+    'search_for_term' => 'חפש את :term',
+    'search_more' => 'תוצאות נוספות',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
+    'search_content_type' => 'סוג התוכן',
+    'search_exact_matches' => 'התאמות מדויקות',
+    'search_tags' => 'חפש בתגים',
+    'search_options' => 'אפשרויות',
+    'search_viewed_by_me' => 'נצפו על ידי',
+    'search_not_viewed_by_me' => 'שלא נצפו על ידי',
+    'search_permissions_set' => 'סט הרשאות',
+    'search_created_by_me' => 'שנוצרו על ידי',
+    'search_updated_by_me' => 'שעודכנו על ידי',
+    'search_date_options' => 'אפשרויות תאריך',
+    'search_updated_before' => 'שעודכנו לפני',
+    'search_updated_after' => 'שעודכנו לאחר',
+    'search_created_before' => 'שנוצרו לפני',
+    'search_created_after' => 'שנוצרו לאחר',
+    'search_set_date' => 'הגדר תאריך',
+    'search_update' => 'עדכן חיפוש',
+
+    // Shelves
+    'shelf' => 'מדף',
+    'shelves' => 'מדפים',
+    'x_shelves' => ':count מדף|:count מדפים',
+    'shelves_long' => 'מדפים',
+    'shelves_empty' => 'לא נוצרו מדפים',
+    'shelves_create' => 'צור מדף חדש',
+    'shelves_popular' => 'מדפים פופולרים',
+    'shelves_new' => 'מדפים חדשים',
+    'shelves_new_action' => 'מדף חדש',
+    'shelves_popular_empty' => 'המדפים הפופולריים ביותר יופיעו כאן',
+    'shelves_new_empty' => 'המדפים שנוצרו לאחרונה יופיעו כאן',
+    'shelves_save' => 'שמור מדף',
+    'shelves_books' => 'ספרים במדף זה',
+    'shelves_add_books' => 'הוסף ספרים למדף זה',
+    'shelves_drag_books' => 'גרור ספרים לכאן על מנת להוסיף אותם למדף',
+    'shelves_empty_contents' => 'במדף זה לא קיימים ספרים',
+    'shelves_edit_and_assign' => 'עריכת מדף להוספת ספרים',
+    'shelves_edit_named' => 'עריכת מדף :name',
+    'shelves_edit' => 'ערוך מדף',
+    'shelves_delete' => 'מחק מדף',
+    'shelves_delete_named' => 'מחיקת דף :name',
+    'shelves_delete_explain' => "פעולה זו תמחק את המדף :name - הספרים שמופיעים בו ימחקו גם כן!",
+    'shelves_delete_confirmation' => 'האם ברצונך למחוק את המדף?',
+    'shelves_permissions' => 'הרשאות מדף',
+    'shelves_permissions_updated' => 'הרשאות מדף עודכנו',
+    'shelves_permissions_active' => 'הרשאות מדף פעילות',
+    'shelves_copy_permissions_to_books' => 'העתק הרשאות מדף אל הספרים',
+    'shelves_copy_permissions' => 'העתק הרשאות',
+    'shelves_copy_permissions_explain' => 'פעולה זו תעתיק את כל הרשאות המדף לכל הספרים המשוייכים למדף זה. לפני הביצוע, יש לוודא שכל הרשאות המדף אכן נשמרו.',
+    'shelves_copy_permission_success' => 'הרשאות המדף הועתקו אל :count ספרים',
+
+    // Books
+    'book' => 'ספר',
+    'books' => 'ספרים',
+    'x_books' => ':count ספר|:count ספרים',
+    'books_empty' => 'לא נוצרו ספרים',
+    'books_popular' => 'ספרים פופולריים',
+    'books_recent' => 'ספרים אחרונים',
+    'books_new' => 'ספרים חדשים',
+    'books_new_action' => 'ספר חדש',
+    'books_popular_empty' => 'הספרים הפופולריים יופיעו כאן',
+    'books_new_empty' => 'הספרים שנוצרו לאחרונה יופיעו כאן',
+    'books_create' => 'צור ספר חדש',
+    'books_delete' => 'מחק ספר',
+    'books_delete_named' => 'מחק ספר :bookName',
+    'books_delete_explain' => 'פעולה זו תמחק את הספר :bookName, כל הדפים והפרקים ימחקו גם כן.',
+    'books_delete_confirmation' => 'האם ברצונך למחוק את הספר הזה?',
+    'books_edit' => 'ערוך ספר',
+    'books_edit_named' => 'עריכת ספר :bookName',
+    'books_form_book_name' => 'שם הספר',
+    'books_save' => 'שמור ספר',
+    'books_permissions' => 'הרשאות ספר',
+    'books_permissions_updated' => 'הרשאות הספר עודכנו',
+    'books_empty_contents' => 'לא נוצרו פרקים או דפים עבור ספר זה',
+    'books_empty_create_page' => 'צור דף חדש',
+    'books_empty_sort_current_book' => 'מיין את הספר הנוכחי',
+    'books_empty_add_chapter' => 'הוסף פרק',
+    'books_permissions_active' => 'הרשאות ספר פעילות',
+    'books_search_this' => 'חפש בספר זה',
+    'books_navigation' => 'ניווט בספר',
+    'books_sort' => 'מיין את תוכן הספר',
+    'books_sort_named' => 'מיין את הספר :bookName',
+    'books_sort_name' => 'מיין לפי שם',
+    'books_sort_created' => 'מיין לפי תאריך יצירה',
+    'books_sort_updated' => 'מיין לפי תאריך עדכון',
+    'books_sort_chapters_first' => 'פרקים בהתחלה',
+    'books_sort_chapters_last' => 'פרקים בסוף',
+    'books_sort_show_other' => 'הצג ספרים אחרונים',
+    'books_sort_save' => 'שמור את הסדר החדש',
+
+    // Chapters
+    'chapter' => 'פרק',
+    'chapters' => 'פרקים',
+    'x_chapters' => ':count פרק|:count פרקים',
+    'chapters_popular' => 'פרקים פופולריים',
+    'chapters_new' => 'פרק חדש',
+    'chapters_create' => 'צור פרק חדש',
+    'chapters_delete' => 'מחק פרק',
+    'chapters_delete_named' => 'מחק את פרק :chapterName',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_confirm' => 'האם ברצונך למחוק פרק זה?',
+    'chapters_edit' => 'ערוך פרק',
+    'chapters_edit_named' => 'ערוך פרק :chapterName',
+    'chapters_save' => 'שמור פרק',
+    'chapters_move' => 'העבר פרק',
+    'chapters_move_named' => 'העבר פרק :chapterName',
+    'chapter_move_success' => 'הפרק הועבר אל :bookName',
+    'chapters_permissions' => 'הרשאות פרק',
+    'chapters_empty' => 'לא נמצאו דפים בפרק זה.',
+    'chapters_permissions_active' => 'הרשאות פרק פעילות',
+    'chapters_permissions_success' => 'הרשאות פרק עודכנו',
+    'chapters_search_this' => 'חפש בפרק זה',
+
+    // Pages
+    'page' => 'דף',
+    'pages' => 'דפים',
+    'x_pages' => ':count דף|:count דפים',
+    'pages_popular' => 'דפים פופולריים',
+    'pages_new' => 'דף חדש',
+    'pages_attachments' => 'קבצים מצורפים',
+    'pages_navigation' => 'ניווט בדף',
+    'pages_delete' => 'מחק דף',
+    'pages_delete_named' => 'מחק דף :pageName',
+    'pages_delete_draft_named' => 'מחק טיוטת דף :pageName',
+    'pages_delete_draft' => 'מחק טיוטת דף',
+    'pages_delete_success' => 'הדף נמחק',
+    'pages_delete_draft_success' => 'טיוטת דף נמחקה',
+    'pages_delete_confirm' => 'האם ברצונך למחוק דף זה?',
+    'pages_delete_draft_confirm' => 'האם ברצונך למחוק את טיוטת הדף הזה?',
+    'pages_editing_named' => 'עריכת דף :pageName',
+    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_save_draft' => 'שמור טיוטה',
+    'pages_edit_draft' => 'ערוך טיוטת דף',
+    'pages_editing_draft' => 'עריכת טיוטה',
+    'pages_editing_page' => 'עריכת דף',
+    'pages_edit_draft_save_at' => 'טיוטה נשמרה ב ',
+    'pages_edit_delete_draft' => 'מחק טיוטה',
+    'pages_edit_discard_draft' => 'התעלם מהטיוטה',
+    'pages_edit_set_changelog' => 'הגדר יומן שינויים',
+    'pages_edit_enter_changelog_desc' => 'ציין תיאור קצר אודות השינויים שביצעת',
+    'pages_edit_enter_changelog' => 'הכנס יומן שינויים',
+    'pages_save' => 'שמור דף',
+    'pages_title' => 'כותרת דף',
+    'pages_name' => 'שם הדף',
+    'pages_md_editor' => 'עורך',
+    'pages_md_preview' => 'תצוגה מקדימה',
+    'pages_md_insert_image' => 'הכנס תמונה',
+    'pages_md_insert_link' => 'הכנס קישור ליישות',
+    'pages_md_insert_drawing' => 'הכנס סרטוט',
+    'pages_not_in_chapter' => 'דף אינו חלק מפרק',
+    'pages_move' => 'העבר דף',
+    'pages_move_success' => 'הדף הועבר אל ":parentName"',
+    'pages_copy' => 'העתק דף',
+    'pages_copy_desination' => 'העתק יעד',
+    'pages_copy_success' => 'הדף הועתק בהצלחה',
+    'pages_permissions' => 'הרשאות דף',
+    'pages_permissions_success' => 'הרשאות הדף עודכנו',
+    'pages_revision' => 'נוסח',
+    'pages_revisions' => 'נוסחי דף',
+    'pages_revisions_named' => 'נוסחי דף עבור :pageName',
+    'pages_revision_named' => 'נוסח דף עבור :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions_created_by' => 'נוצר על ידי',
+    'pages_revisions_date' => 'תאריך נוסח',
+    'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'נוסח #:id',
+    'pages_revisions_numbered_changes' => 'שינויי נוסח #:id',
+    'pages_revisions_changelog' => 'יומן שינויים',
+    'pages_revisions_changes' => 'שינויים',
+    'pages_revisions_current' => 'גירסא נוכחית',
+    'pages_revisions_preview' => 'תצוגה מקדימה',
+    'pages_revisions_restore' => 'שחזר',
+    'pages_revisions_none' => 'לדף זה אין נוסחים',
+    'pages_copy_link' => 'העתק קישור',
+    'pages_edit_content_link' => 'ערוך תוכן',
+    'pages_permissions_active' => 'הרשאות דף פעילות',
+    'pages_initial_revision' => 'פרסום ראשוני',
+    'pages_initial_name' => 'דף חדש',
+    'pages_editing_draft_notification' => 'הינך עורך טיוטה אשר נשמרה לאחרונה ב :timeDiff',
+    'pages_draft_edited_notification' => 'דף זה עודכן מאז, מומלץ להתעלם מהטיוטה הזו.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count משתמשים החלו לערוך דף זה',
+        'start_b' => ':userName החל לערוך דף זה',
+        'time_a' => 'מאז שהדף עודכן לאחרונה',
+        'time_b' => 'ב :minCount דקות האחרונות',
+        'message' => ':start :time. יש לשים לב לא לדרוס שינויים של משתמשים אחרים!',
+    ],
+    'pages_draft_discarded' => 'הסקיצה נמחקה, העורך עודכן עם תוכן הדף העכשוי',
+    'pages_specific' => 'דף ספציפי',
+    'pages_is_template' => 'Page Template',
+
+    // Editor Sidebar
+    'page_tags' => 'תגיות דף',
+    'chapter_tags' => 'תגיות פרק',
+    'book_tags' => 'תגיות ספר',
+    'shelf_tags' => 'תגיות מדף',
+    'tag' => 'תגית',
+    'tags' =>  'תגיות',
+    'tag_name' =>  'Tag Name',
+    'tag_value' => 'ערך התגית (אופציונאלי)',
+    'tags_explain' => "הכנס תגיות על מנת לסדר את התוכן שלך. \n  ניתן לציין ערך לתגית על מנת לבצע סידור יסודי יותר",
+    'tags_add' => 'הוסף תגית נוספת',
+    'tags_remove' => 'Remove this tag',
+    'attachments' => 'קבצים מצורפים',
+    'attachments_explain' => 'צרף קבצים או קישורים על מנת להציגם בדף שלך. צירופים אלו יהיו זמינים בתפריט הצדדי של הדף',
+    'attachments_explain_instant_save' => 'שינויים נשמרים באופן מיידי',
+    'attachments_items' => 'פריטים שצורפו',
+    'attachments_upload' => 'העלה קובץ',
+    'attachments_link' => 'צרף קישור',
+    'attachments_set_link' => 'הגדר קישור',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
+    'attachments_dropzone' => 'גרור לכאן קבצים או לחץ על מנת לצרף קבצים',
+    'attachments_no_files' => 'לא הועלו קבצים',
+    'attachments_explain_link' => 'ניתן לצרף קישור במקום העלאת קובץ, קישור זה יכול להוביל לדף אחר או לכל קובץ באינטרנט',
+    'attachments_link_name' => 'שם הקישור',
+    'attachment_link' => 'כתובת הקישור',
+    'attachments_link_url' => 'קישור לקובץ',
+    'attachments_link_url_hint' => 'כתובת האתר או הקובץ',
+    'attach' => 'צרף',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
+    'attachments_edit_file' => 'ערוך קובץ',
+    'attachments_edit_file_name' => 'שם הקובץ',
+    'attachments_edit_drop_upload' => 'גרור קבצים או לחץ כאן על מנת להעלות קבצים במקום הקבצים הקיימים',
+    'attachments_order_updated' => 'סדר הקבצים עודכן',
+    'attachments_updated_success' => 'פרטי הקבצים עודכנו',
+    'attachments_deleted' => 'קובץ מצורף הוסר',
+    'attachments_file_uploaded' => 'הקובץ עלה בהצלחה',
+    'attachments_file_updated' => 'הקובץ עודכן בהצלחה',
+    'attachments_link_attached' => 'הקישור צורף לדף בהצלחה',
+    'templates' => 'Templates',
+    'templates_set_as_template' => 'Page is a template',
+    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
+    'templates_replace_content' => 'Replace page content',
+    'templates_append_content' => 'Append to page content',
+    'templates_prepend_content' => 'Prepend to page content',
+
+    // Profile View
+    'profile_user_for_x' => 'משתמש במערכת כ :time',
+    'profile_created_content' => 'תוכן שנוצר',
+    'profile_not_created_pages' => 'המשתמש :userName לא יצר דפים',
+    'profile_not_created_chapters' => 'המשתמש :userName לא יצר פרקים',
+    'profile_not_created_books' => 'המשתמש :userName לא יצר ספרים',
+    'profile_not_created_shelves' => 'המשתמש :userName לא יצר מדפים',
+
+    // Comments
+    'comment' => 'תגובה',
+    'comments' => 'תגובות',
+    'comment_add' => 'הוסף תגובה',
+    'comment_placeholder' => 'השאר תגובה כאן',
+    'comment_count' => '{0} אין תגובות|{1} 1 תגובה|[2,*] :count תגובות',
+    'comment_save' => 'שמור תגובה',
+    'comment_saving' => 'שומר תגובה...',
+    'comment_deleting' => 'מוחק תגובה...',
+    'comment_new' => 'תגובה חדשה',
+    'comment_created' => 'הוגב :createDiff',
+    'comment_updated' => 'עודכן :updateDiff על ידי :username',
+    'comment_deleted_success' => 'התגובה נמחקה',
+    'comment_created_success' => 'התגובה נוספה',
+    'comment_updated_success' => 'התגובה עודכנה',
+    'comment_delete_confirm' => 'האם ברצונך למחוק תגובה זו?',
+    'comment_in_reply_to' => 'בתגובה ל :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'האם ברצונך למחוק נוסח זה?',
+    'revision_restore_confirm' => 'האם ברצונך לשחזר נוסח זה? תוכן הדף הנוכחי יעודכן לנוסח זה.',
+    'revision_delete_success' => 'נוסח נמחק',
+    'revision_cannot_delete_latest' => 'לא ניתן למחוק את הנוסח האחרון'
+];
\ No newline at end of file
diff --git a/resources/lang/he/errors.php b/resources/lang/he/errors.php
new file mode 100644 (file)
index 0000000..f6a01d3
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'אין לך הרשאות על מנת לצפות בדף המבוקש.',
+    'permissionJson' => 'אין לך הרשאות על מנת לבצע את הפעולה המבוקשת.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'משתמש עם המייל :email כבר קיים אך עם פרטי הזדהות שונים',
+    'email_already_confirmed' => 'המייל כבר אומת, אנא נסה להתחבר',
+    'email_confirmation_invalid' => 'מפתח האימות אינו תקין או שכבר נעשה בו שימוש, אנא נסה להרשם שנית',
+    'email_confirmation_expired' => 'מפתח האימות פג-תוקף, מייל אימות חדש נשלח שוב.',
+    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+    'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
+    'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
+    'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
+    'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
+    'saml_already_logged_in' => 'Already logged in',
+    'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
+    'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
+    'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
+    'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
+    'social_no_action_defined' => 'לא הוגדרה פעולה',
+    'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
+    'social_account_in_use' => 'החשבון :socialAccount כבר בשימוש. אנא נסה להתחבר באמצעות אפשרות :socialAccount',
+    'social_account_email_in_use' => 'המייל :email כבר נמצא בשימוש. אם כבר יש ברשותך חשבון ניתן להתחבר באמצעות :socialAccount ממסך הגדרות הפרופיל שלך.',
+    'social_account_existing' => 'ה - :socialAccount כבר מחובר לחשבון שלך.',
+    'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
+    'social_account_not_used' => 'החשבון :socialAccount אינו מחובר למשתמש כלשהוא. אנא חבר אותו לחשבונך במסך הגדרות הפרופיל שלך.',
+    'social_account_register_instructions' => 'אם אין ברשותך חשבון, תוכל להרשם באמצעות :socialAccount',
+    'social_driver_not_found' => 'Social driver not found',
+    'social_driver_not_configured' => 'הגדרות ה :socialAccount אינן מוגדרות כראוי',
+    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+
+    // System
+    'path_not_writable' => 'לא ניתן להעלות את :filePath אנא ודא שניתן לכתוב למיקום זה',
+    'cannot_get_image_from_url' => 'לא ניתן לקבל תמונה מ :url',
+    'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
+    'server_upload_limit' => 'השרת אינו מרשה העלאת קבצים במשקל זה. אנא נסה להעלות קובץ קטן יותר.',
+    'uploaded'  => 'השרת אינו מרשה העלאת קבצים במשקל זה. אנא נסה להעלות קובץ קטן יותר.',
+    'image_upload_error' => 'התרחשה שגיאה במהלך העלאת התמונה',
+    'image_upload_type_error' => 'התמונה שהועלתה אינה תקינה',
+    'file_upload_timeout' => 'The file upload has timed out.',
+
+    // Attachments
+    'attachment_not_found' => 'קובץ מצורף לא נמצא',
+
+    // Pages
+    'page_draft_autosave_fail' => 'שגיאה בשמירת הטיוטה. אנא ודא כי חיבור האינטרנט תקין לפני שמירת דף זה.',
+    'page_custom_home_deletion' => 'לא ניתן למחוק דף אשר מוגדר כדף הבית',
+
+    // Entities
+    'entity_not_found' => 'פריט לא נמצא',
+    'bookshelf_not_found' => 'מדף הספרים לא נמצא',
+    'book_not_found' => 'ספר לא נמצא',
+    'page_not_found' => 'דף לא נמצא',
+    'chapter_not_found' => 'פרק לא נמצא',
+    'selected_book_not_found' => 'הספר שנבחר לא נמצא',
+    'selected_book_chapter_not_found' => 'הפרק או הספר שנבחר לא נמצאו',
+    'guests_cannot_save_drafts' => 'אורחים אינם יכולים לשמור סקיצות',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'אינך יכול למחוק את המנהל היחיד',
+    'users_cannot_delete_guest' => 'אינך יכול למחוק את משתמש האורח',
+
+    // Roles
+    'role_cannot_be_edited' => 'לא ניתן לערוך תפקיד זה',
+    'role_system_cannot_be_deleted' => 'תפקיד זה הינו תפקיד מערכת ולא ניתן למחיקה',
+    'role_registration_default_cannot_delete' => 'לא ניתן למחוק תפקיד זה מכיוון שהוא מוגדר כתפקיד ברירת המחדל בעת הרשמה',
+    'role_cannot_remove_only_admin' => 'משתמש זה הינו המשתמש היחיד המשוייך לפקיד המנהל. נסה לשייך את תפקיד המנהל למשתמש נוסף לפני הסרה כאן',
+
+    // Comments
+    'comment_list' => 'התרחשה שגיאה בעת שליפת התגובות',
+    'cannot_add_comment_to_draft' => 'אינך יכול להוסיף תגובות לסקיצה זו',
+    'comment_add' => 'התרחשה שגיאה בעת הוספה / עדכון התגובה',
+    'comment_delete' => 'התרחשה שגיאה בעת מחיקת התגובה',
+    'empty_comment' => 'לא ניתן להוסיף תגובה ריקה',
+
+    // Error pages
+    '404_page_not_found' => 'דף לא קיים',
+    'sorry_page_not_found' => 'מצטערים, הדף שחיפשת אינו קיים',
+    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'return_home' => 'בחזרה לדף הבית',
+    'error_occurred' => 'התרחשה שגיאה',
+    'app_down' => ':appName כרגע אינו זמין',
+    'back_soon' => 'מקווים שיחזור במהרה',
+
+    // API errors
+    'api_no_authorization_found' => 'No authorization token found on the request',
+    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
+    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
+    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
+    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
+    'api_user_token_expired' => 'The authorization token used has expired',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+
+];
diff --git a/resources/lang/he/pagination.php b/resources/lang/he/pagination.php
new file mode 100644 (file)
index 0000000..8f7c5c4
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; הקודם',
+    'next'     => 'הבא &raquo;',
+
+];
diff --git a/resources/lang/he/passwords.php b/resources/lang/he/passwords.php
new file mode 100644 (file)
index 0000000..17fad68
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+    'password' => 'הסיסמא חייבת להיות בעלת 6 תווים ולהתאים לאימות',
+    'user' => "לא ניתן למצוא משתמש עם המייל שסופק",
+    'token' => 'The password reset token is invalid for this email address.',
+    'sent' => 'נשלח אליך אי-מייל עם קישור לאיפוס הסיסמא',
+    'reset' => 'איפוס הסיסמא הושלם בהצלחה!',
+
+];
diff --git a/resources/lang/he/settings.php b/resources/lang/he/settings.php
new file mode 100755 (executable)
index 0000000..a0632d0
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'הגדרות',
+    'settings_save' => 'שמור הגדרות',
+    'settings_save_success' => 'ההגדרות נשמרו',
+
+    // App Settings
+    'app_customization' => 'התאמה אישית',
+    'app_features_security' => 'מאפיינים ואבטחה',
+    'app_name' => 'שם היישום',
+    'app_name_desc' => 'השם הזה יופיע בכותרת ובכל אי-מייל הנשלח מהמערכת',
+    'app_name_header' => 'הצג שם בחלק העליון',
+    'app_public_access' => 'גישה ציבורית',
+    'app_public_access_desc' => 'הפעלת אפשרות זו תאפשר למשתמשים אשר אינם רשומים לגשת לתוכן שלך',
+    'app_public_access_desc_guest' => 'הגדרות הרשאה למשתמשים ציבוריים ניתנות לשינוי דרך משתמש מסוג ״אורח״',
+    'app_public_access_toggle' => 'אפשר גישה ציבורית',
+    'app_public_viewing' => 'לאפשר גישה ציבורית?',
+    'app_secure_images' => 'העלאת תמונות מאובטחת',
+    'app_secure_images_toggle' => 'אפשר העלאת תמונות מאובטחת',
+    'app_secure_images_desc' => 'משיקולי ביצועים, כל התמונות הינן ציבוריות. אפשרות זו מוסיפה מחרוזת אקראית שקשה לנחש לכל כתובת של תמונה. אנא ודא שאפשרות הצגת תוכן התיקייה מבוטל.',
+    'app_editor' => 'עורך הדפים',
+    'app_editor_desc' => 'בחר באמצעות איזה עורך תתבצע עריכת הדפים',
+    'app_custom_html' => 'HTML מותאם אישית לחלק העליון',
+    'app_custom_html_desc' => 'כל קוד שיתווסף כאן, יופיע בתחתית תגית ה head של כל דף. חלק זה שימושי על מנת להגדיר עיצובי CSS והתקנת קוד Analytics',
+    'app_custom_html_disabled_notice' => 'קוד HTML מותאם מבוטל בדף ההגדרות על מנת לוודא ששינויים שגורמים לבעיה יוכלו להיות מבוטלים לאחר מכן',
+    'app_logo' => 'לוגו היישום',
+    'app_logo_desc' => 'תמונה זו צריכה להיות בגובה 43 פיקסלים. תמונות גדולות יותר יוקטנו.',
+    'app_primary_color' => 'צבע עיקרי ליישום',
+    'app_primary_color_desc' => 'ערך זה צריך להיות מסוג hex. <br> יש להשאיר ריק לשימוש בצבע ברירת המחדל',
+    'app_homepage' => 'דף הבית של היישום',
+    'app_homepage_desc' => 'אנא בחר דף להצגה בדף הבית במקום דף ברירת המחדל. הרשאות הדף לא יחולו בדפים הנבחרים.',
+    'app_homepage_select' => 'בחר דף',
+    'app_disable_comments' => 'ביטול תגובות',
+    'app_disable_comments_toggle' => 'בטל תגובות',
+    'app_disable_comments_desc' => 'מבטל את התגובות לאורך כל היישום, תגובות קיימות לא יוצגו.',
+
+    // Color settings
+    'content_colors' => 'Content Colors',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
+    // Registration Settings
+    'reg_settings' => 'הרשמה',
+    'reg_enable' => 'אפשר הרשמה',
+    'reg_enable_toggle' => 'אפשר להרשם',
+    'reg_enable_desc' => 'כאשר אפשר להרשם משתמשים יוכלו להכנס באופן עצמאי. בעת ההרשמה המשתמש יקבל הרשאה יחידה כברירת מחדל.',
+    'reg_default_role' => 'הרשאה כברירת מחדל',
+    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    'reg_email_confirmation' => 'אימות כתובת אי-מייל',
+    'reg_email_confirmation_toggle' => 'יש לאמת את כתובת המייל',
+    'reg_confirm_email_desc' => 'אם מופעלת הגבלה לדומיין ספציפי אז אימות המייל לא יבוצע',
+    'reg_confirm_restrict_domain' => 'הגבלה לדומיין ספציפי',
+    'reg_confirm_restrict_domain_desc' => 'הכנס דומיינים מופרדים בפסיק אשר עבורם תוגבל ההרשמה. משתמשים יקבלו אי-מייל על מנת לאמת את כתובת המייל שלהם. <br> לתשומת לבך: משתמש יוכל לשנות את כתובת המייל שלו לאחר ההרשמה',
+    'reg_confirm_restrict_domain_placeholder' => 'אין הגבלה לדומיין',
+
+    // Maintenance settings
+    'maint' => 'תחזוקה',
+    'maint_image_cleanup' => 'ניקוי תמונות',
+    'maint_image_cleanup_desc' => "סורק את הדפים והגרסאות על מנת למצוא אילו תמונות לא בשימוש. יש לוודא גיבוי מלא של מסד הנתונים והתמונות לפני הרצה",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_run' => 'הפעל ניקוי תמונות',
+    'maint_image_cleanup_warning' => 'נמצאו כ :count תמונות אשר לא בשימוש האם ברצונך להמשיך?',
+    'maint_image_cleanup_success' => ':count תמונות שלא בשימוש נמחקו',
+    'maint_image_cleanup_nothing_found' => 'לא נמצאו תמונות אשר לא בשימוש, לא נמחקו קבצים כלל.',
+    'maint_send_test_email' => 'Send a Test Email',
+    'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
+    'maint_send_test_email_run' => 'Send test email',
+    'maint_send_test_email_success' => 'Email sent to :address',
+    'maint_send_test_email_mail_subject' => 'Test Email',
+    'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
+    'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
+    // Role Settings
+    'roles' => 'תפקידים',
+    'role_user_roles' => 'תפקידי משתמשים',
+    'role_create' => 'צור תפקיד משתמש חדש',
+    'role_create_success' => 'התפקיד נוצר בהצלחה',
+    'role_delete' => 'מחק תפקיד',
+    'role_delete_confirm' => 'פעולה זו תמחק את התפקיד: :roleName',
+    'role_delete_users_assigned' => 'לתפקיד :userCount יש משתמשים אשר משויכים אליו. אם ברצונך להעבירם לתפקיד אחר אנא בחר תפקיד מלמטה',
+    'role_delete_no_migration' => "אל תעביר משתמשים לתפקיד",
+    'role_delete_sure' => 'האם אתה בטוח שברצונך למחוק את התפקיד?',
+    'role_delete_success' => 'התפקיד נמחק בהצלחה',
+    'role_edit' => 'ערוך תפקיד',
+    'role_details' => 'פרטי תפקיד',
+    'role_name' => 'שם התפקיד',
+    'role_desc' => 'תיאור קצר של התפקיד',
+    'role_external_auth_id' => 'External Authentication IDs',
+    'role_system' => 'הרשאות מערכת',
+    'role_manage_users' => 'ניהול משתמשים',
+    'role_manage_roles' => 'ניהול תפקידים והרשאות תפקידים',
+    'role_manage_entity_permissions' => 'נהל הרשאות ספרים, פרקים ודפים',
+    'role_manage_own_entity_permissions' => 'נהל הרשאות על ספרים, פרקים ודפים בבעלותך',
+    'role_manage_page_templates' => 'Manage page templates',
+    'role_access_api' => 'Access system API',
+    'role_manage_settings' => 'ניהול הגדרות יישום',
+    'role_asset' => 'הרשאות משאבים',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => 'הרשאות אלו שולטות בגישת ברירת המחדל למשאבים בתוך המערכת. הרשאות של ספרים, פרקים ודפים יגברו על הרשאות אלו.',
+    'role_asset_admins' => 'מנהלים מקבלים הרשאה מלאה לכל התוכן אך אפשרויות אלו עלולות להציג או להסתיר אפשרויות בממשק',
+    'role_all' => 'הכל',
+    'role_own' => 'שלי',
+    'role_controlled_by_asset' => 'נשלטים על ידי המשאב אליו הועלו',
+    'role_save' => 'שמור תפקיד',
+    'role_update_success' => 'התפקיד עודכן בהצלחה',
+    'role_users' => 'משתמשים משוייכים לתפקיד זה',
+    'role_users_none' => 'אין משתמשים המשוייכים לתפקיד זה',
+
+    // Users
+    'users' => 'משתמשים',
+    'user_profile' => 'פרופיל משתמש',
+    'users_add_new' => 'הוסף משתמש חדש',
+    'users_search' => 'חפש משתמשים',
+    'users_latest_activity' => 'Latest Activity',
+    'users_details' => 'פרטי משתמש',
+    'users_details_desc' => 'הגדר שם לתצוגה ומייל עבור משתמש זה. כתובת המייל תשמש על מנת להתחבר למערכת',
+    'users_details_desc_no_email' => 'הגדר שם לתצוגה כדי שאחרים יוכלו לזהות',
+    'users_role' => 'תפקידי משתמשים',
+    'users_role_desc' => 'בחר אילו תפקידים ישויכו למשתמש זה. אם המשתמש משוייך למספר תפקידים, ההרשאות יהיו כלל ההרשאות של כל התפקידים',
+    'users_password' => 'סיסמא',
+    'users_password_desc' => 'הגדר סיסמא עבור גישה למערכת. על הסיסמא להיות באורך של 5 תווים לפחות',
+    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
+    'users_send_invite_option' => 'Send user invite email',
+    'users_external_auth_id' => 'זיהוי חיצוני - ID',
+    'users_external_auth_id_desc' => 'זיהוי זה יהיה בשימוש מול מערכת ה LDAP שלך',
+    'users_password_warning' => 'יש למלא רק אם ברצונך לשנות את הסיסמא.',
+    'users_system_public' => 'משתמש זה מייצג את כל האורחים שלא מזוהים אשר משתמשים במערכת. לא ניתן להתחבר למשתמש זה אך הוא מוגדר כברירת מחדל',
+    'users_delete' => 'מחק משתמש',
+    'users_delete_named' => 'מחק משתמש :userName',
+    'users_delete_warning' => 'פעולה זו תמחק את המשתמש \':userName\' מהמערכת',
+    'users_delete_confirm' => 'האם ברצונך למחוק משתמש זה?',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
+    'users_edit' => 'עריכת משתמש',
+    'users_edit_profile' => 'עריכת פרופיל',
+    'users_edit_success' => 'המשתמש עודכן בהצלחה',
+    'users_avatar' => 'תמונת משתמש',
+    'users_avatar_desc' => 'בחר תמונה אשר תייצג את המשתמש. על התמונה להיות ריבוע של 256px',
+    'users_preferred_language' => 'שפה מועדפת',
+    'users_preferred_language_desc' => 'אפשרות זו תשנע את השפה אשר מוצגת בממשק המערכת. פעולה זו לא תשנה את התוכן אשר נכתב על ידי המשתמשים.',
+    'users_social_accounts' => 'רשתות חברתיות',
+    'users_social_accounts_info' => 'כן ניתן לשייך חשבונות אחרים שלך לחיבור וזיהוי קל ומהיר. ניתוק חשבון אינו מנתק גישה קיימת למערכת. לביצוע ניתוק יש לשנות את ההגדרה בהגדרות של חשבון הרשת החברתית',
+    'users_social_connect' => 'חיבור החשבון',
+    'users_social_disconnect' => 'ניתוק חשבון',
+    'users_social_connected' => 'חשבון :socialAccount חובר בהצלחה לחשבון שלך',
+    'users_social_disconnected' => ':socialAccount נותק בהצלחה מהחשבון שלך',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
+    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'Slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/he/validation.php b/resources/lang/he/validation.php
new file mode 100644 (file)
index 0000000..7c02735
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => 'שדה :attribute חייב להיות מסומן.',
+    'active_url'           => 'שדה :attribute הוא לא כתובת אתר תקנית.',
+    'after'                => 'שדה :attribute חייב להיות תאריך אחרי :date.',
+    'alpha'                => 'שדה :attribute יכול להכיל אותיות בלבד.',
+    'alpha_dash'           => 'שדה :attribute יכול להכיל אותיות, מספרים ומקפים בלבד.',
+    'alpha_num'            => 'שדה :attribute יכול להכיל אותיות ומספרים בלבד.',
+    'array'                => 'שדה :attribute חייב להיות מערך.',
+    'before'               => 'שדה :attribute חייב להיות תאריך לפני :date.',
+    'between'              => [
+        'numeric' => 'שדה :attribute חייב להיות בין :min ל-:max.',
+        'file'    => 'שדה :attribute חייב להיות בין :min ל-:max קילובייטים.',
+        'string'  => 'שדה :attribute חייב להיות בין :min ל-:max תווים.',
+        'array'   => 'שדה :attribute חייב להיות בין :min ל-:max פריטים.',
+    ],
+    'boolean'              => 'שדה :attribute חייב להיות אמת או שקר.',
+    'confirmed'            => 'שדה האישור של :attribute לא תואם.',
+    'date'                 => 'שדה :attribute אינו תאריך תקני.',
+    'date_format'          => 'שדה :attribute לא תואם את הפורמט :format.',
+    'different'            => 'שדה :attribute ושדה :other חייבים להיות שונים.',
+    'digits'               => 'שדה :attribute חייב להיות בעל :digits ספרות.',
+    'digits_between'       => 'שדה :attribute חייב להיות בין :min ו-:max ספרות.',
+    'email'                => 'שדה :attribute חייב להיות כתובת אימייל תקנית.',
+    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'filled'               => 'שדה :attribute הוא חובה.',
+    'gt'                   => [
+        'numeric' => 'The :attribute must be greater than :value.',
+        'file'    => 'The :attribute must be greater than :value kilobytes.',
+        'string'  => 'The :attribute must be greater than :value characters.',
+        'array'   => 'The :attribute must have more than :value items.',
+    ],
+    'gte'                  => [
+        'numeric' => 'The :attribute must be greater than or equal :value.',
+        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be greater than or equal :value characters.',
+        'array'   => 'The :attribute must have :value items or more.',
+    ],
+    'exists'               => 'בחירת ה-:attribute אינה תקפה.',
+    'image'                => 'שדה :attribute חייב להיות תמונה.',
+    'image_extension'      => 'שדה :attribute חייב להיות מסוג תמונה נתמך',
+    'in'                   => 'בחירת ה-:attribute אינה תקפה.',
+    'integer'              => 'שדה :attribute חייב להיות מספר שלם.',
+    'ip'                   => 'שדה :attribute חייב להיות כתובת IP תקנית.',
+    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
+    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
+    'json'                 => 'The :attribute must be a valid JSON string.',
+    'lt'                   => [
+        'numeric' => 'The :attribute must be less than :value.',
+        'file'    => 'The :attribute must be less than :value kilobytes.',
+        'string'  => 'The :attribute must be less than :value characters.',
+        'array'   => 'The :attribute must have less than :value items.',
+    ],
+    'lte'                  => [
+        'numeric' => 'The :attribute must be less than or equal :value.',
+        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be less than or equal :value characters.',
+        'array'   => 'The :attribute must not have more than :value items.',
+    ],
+    'max'                  => [
+        'numeric' => 'שדה :attribute אינו יכול להיות גדול מ-:max.',
+        'file'    => 'שדה :attribute לא יכול להיות גדול מ-:max קילובייטים.',
+        'string'  => 'שדה :attribute לא יכול להיות גדול מ-:max characters.',
+        'array'   => 'שדה :attribute לא יכול להכיל יותר מ-:max פריטים.',
+    ],
+    'mimes'                => 'שדה :attribute צריך להיות קובץ מסוג: :values.',
+    'min'                  => [
+        'numeric' => 'שדה :attribute חייב להיות לפחות :min.',
+        'file'    => 'שדה :attribute חייב להיות לפחות :min קילובייטים.',
+        'string'  => 'שדה :attribute חייב להיות לפחות :min תווים.',
+        'array'   => 'שדה :attribute חייב להיות לפחות :min פריטים.',
+    ],
+    'no_double_extension'  => 'השדה :attribute חייב להיות בעל סיומת קובץ אחת בלבד.',
+    'not_in'               => 'בחירת ה-:attribute אינה תקפה.',
+    'not_regex'            => 'The :attribute format is invalid.',
+    'numeric'              => 'שדה :attribute חייב להיות מספר.',
+    'regex'                => 'שדה :attribute בעל פורמט שאינו תקין.',
+    'required'             => 'שדה :attribute הוא חובה.',
+    'required_if'          => 'שדה :attribute נחוץ כאשר :other הוא :value.',
+    'required_with'        => 'שדה :attribute נחוץ כאשר :values נמצא.',
+    'required_with_all'    => 'שדה :attribute נחוץ כאשר :values נמצא.',
+    'required_without'     => 'שדה :attribute נחוץ כאשר :values לא בנמצא.',
+    'required_without_all' => 'שדה :attribute נחוץ כאשר אף אחד מ-:values נמצאים.',
+    'same'                 => 'שדה :attribute ו-:other חייבים להיות זהים.',
+    'safe_url'             => 'The provided link may not be safe.',
+    'size'                 => [
+        'numeric' => 'שדה :attribute חייב להיות :size.',
+        'file'    => 'שדה :attribute חייב להיות :size קילובייטים.',
+        'string'  => 'שדה :attribute חייב להיות :size תווים.',
+        'array'   => 'שדה :attribute חייב להכיל :size פריטים.',
+    ],
+    'string'               => 'שדה :attribute חייב להיות מחרוזת.',
+    'timezone'             => 'שדה :attribute חייב להיות איזור תקני.',
+    'unique'               => 'שדה :attribute כבר תפוס.',
+    'url'                  => 'שדה :attribute בעל פורמט שאינו תקין.',
+    'uploaded'             => 'שדה :attribute ארעה שגיאה בעת ההעלאה.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'נדרש אימות סיסמא',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 575e9e509e0bdefd6dad5542d03a319bbd5455bc..8d22605e36c1c0549ebc99f83866163d44296c7b 100644 (file)
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'megjegyzést fűzött hozzá:',
+    'permissions_update'          => 'updated permissions',
 ];
index b55add879c2dbdc677f5262d7daa59f9ccd07614..a13c8300e2084722c9cd4f623c9bf08e75ed8b3d 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Jelszó visszaállítása',
     'reset_password_send_instructions' => 'Meg kell adni az email címet amire egy jelszó visszaállító hivatkozás lesz elküldve.',
     'reset_password_send_button' => 'Visszaállító hivatkozás elküldése',
-    'reset_password_sent_success' => 'Jelszó visszaállító hivatkozás elküldve :email címre.',
+    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
     'reset_password_success' => 'A jelszó sikeresen visszaállítva.',
     'email_reset_subject' => ':appName jelszó visszaállítása',
     'email_reset_text' => 'Ezt az emailt azért küldtük mert egy jelszó visszaállításra vonatkozó kérést kaptunk ebből a fiókból.',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => 'Megerősítő email újraküldése',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Ez egy meghívó :appName weboldalhoz!',
+    'user_invite_email_greeting' => 'Létre lett hozva egy fiók az :appName weboldalon.',
+    'user_invite_email_text' => 'Jelszó beállításához és hozzáféréshez a lenti gombra kell kattintani:',
+    'user_invite_email_action' => 'Fiók jelszó beállítása',
+    'user_invite_page_welcome' => ':appName üdvözöl!',
+    'user_invite_page_text' => 'A fiók véglegesítéséhez és a hozzáféréshez be kell állítani egy jelszót ami :appName weboldalon lesz használva a bejelentkezéshez.',
+    'user_invite_page_confirm_button' => 'Jelszó megerősítése',
+    'user_invite_success' => 'Jelszó beállítva, :appName most már elérhető!'
 ];
\ No newline at end of file
index 4bf5b503036ff8ee69d3542806680297baf53624..5070adccd770dc5e1380836809ac116947ca93ac 100644 (file)
@@ -33,17 +33,19 @@ return [
     'copy' => 'Másolás',
     'reply' => 'Válasz',
     'delete' => 'Törlés',
+    'delete_confirm' => 'Törlés megerősítése',
     'search' => 'Keresés',
     'search_clear' => 'Keresés törlése',
     'reset' => 'Visszaállítás',
     'remove' => 'Eltávolítás',
     'add' => 'Hozzáadás',
+    'fullscreen' => 'Teljes képernyő',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
+    'sort_options' => 'Rendezési beállítások',
+    'sort_direction_toggle' => 'Rendezési irány váltása',
+    'sort_ascending' => 'Növekvő sorrend',
+    'sort_descending' => 'Csökkenő sorrend',
     'sort_name' => 'Név',
     'sort_created_at' => 'Létrehozás dátuma',
     'sort_updated_at' => 'Frissítés dátuma',
@@ -59,12 +61,14 @@ return [
     'grid_view' => 'Rács nézet',
     'list_view' => 'Lista nézet',
     'default' => 'Alapértelmezés szerinti',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Morzsa',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Profil menü',
     'view_profile' => 'Profil megtekintése',
     'edit_profile' => 'Profil szerkesztése',
+    'dark_mode' => 'Sötét mód',
+    'light_mode' => 'Világos mód',
 
     // Layout tabs
     'tab_info' => 'Információ',
index 1f98df2dfe150dedf85fe7f35145df7782510edd..c1c57d27e76183b5139501cdb9658437d2630f32 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Több betöltése',
     'image_image_name' => 'Kép neve',
     'image_delete_used' => 'Ez a kép a lenti oldalakon van használatban.',
-    'image_delete_confirm' => 'A kép törléséhez ismét rá kell kattintani a törlésre.',
+    'image_delete_confirm_text' => 'Biztosan törölhető ez a kép?',
     'image_select_image' => 'Kép kiválasztása',
     'image_dropzone' => 'Képek feltöltése ejtéssel vagy kattintással',
     'images_deleted' => 'Képek törölve',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Kód szerkesztése',
     'code_language' => 'Kód nyelve',
     'code_content' => 'Kód tartalom',
+    'code_session_history' => 'Munkamenet előzményei',
     'code_save' => 'Kód mentése',
 ];
index 29f5822dc164ab5e3c681138cd9c0e531df4f915..c248d63a057714d33131d39f6ec4bfd9c34bb23c 100644 (file)
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => ':user hozta létre :timeLength',
     'meta_updated' => 'Frissítve :timeLength',
     'meta_updated_name' => ':user frissítette :timeLength',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Entitás kiválasztása',
     'images' => 'Képek',
     'my_recent_drafts' => 'Legutóbbi vázlataim',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Ha engedélyezett, ezek a jogosultságok elsőbbséget élveznek bármely beállított szerepkör jogosultsággal szemben.',
     'permissions_enable' => 'Egyéni jogosultságok engedélyezése',
     'permissions_save' => 'Jogosultságok mentése',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Keresési eredmények',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Nincsenek a keresésnek megfelelő oldalak',
     'search_for_term' => ':term keresése',
     'search_more' => 'További eredmények',
-    'search_filters' => 'Keresési szűrők',
+    'search_advanced' => 'Részletes keresés',
+    'search_terms' => 'Keresési kifejezések',
     'search_content_type' => 'Tartalomtípus',
     'search_exact_matches' => 'Pontos egyezések',
     'search_tags' => 'Címke keresések',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Új fejezet létrehozása',
     'chapters_delete' => 'Fejezet törlése',
     'chapters_delete_named' => ':chapterName fejezet törlése',
-    'chapters_delete_explain' => '\':chapterName\' nevű fejezet törölve lesz. Minden oldal el lesz távolítva és közvetlenül a szülő könyvhöz lesz hozzáadva.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'Biztosan törölhető ez a fejezet?',
     'chapters_edit' => 'Fejezet szerkesztése',
     'chapters_edit_named' => ':chapterName fejezet szerkesztése',
@@ -162,7 +165,7 @@ return [
     // Pages
     'page' => 'Oldal',
     'pages' => 'Oldalak',
-    'x_pages' => ':count oldal|:count oldalak',
+    'x_pages' => ':count oldal|:count oldal',
     'pages_popular' => 'Népszerű oldalak',
     'pages_new' => 'Új oldal',
     'pages_attachments' => 'Csatolmányok',
@@ -176,7 +179,7 @@ return [
     'pages_delete_confirm' => 'Biztosan törölhető ez az oldal?',
     'pages_delete_draft_confirm' => 'Biztosan törölhető ez a vázlatoldal?',
     'pages_editing_named' => ':pageName oldal szerkesztése',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Vázlatbeállítások',
     'pages_edit_save_draft' => 'Vázlat mentése',
     'pages_edit_draft' => 'Oldal vázlat szerkesztése',
     'pages_editing_draft' => 'Vázlat szerkesztése',
@@ -207,6 +210,7 @@ return [
     'pages_revisions' => 'Oldal változatai',
     'pages_revisions_named' => ':pageName oldal változatai',
     'pages_revision_named' => ':pageName oldal változata',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Létrehozta:',
     'pages_revisions_date' => 'Változat dátuma',
     'pages_revisions_number' => '#',
@@ -234,7 +238,7 @@ return [
     ],
     'pages_draft_discarded' => 'Vázlat elvetve, a szerkesztő frissítve lesz az oldal aktuális tartalmával',
     'pages_specific' => 'Egy bizonyos oldal',
-    'pages_is_template' => 'Page Template',
+    'pages_is_template' => 'Oldalsablon',
 
     // Editor Sidebar
     'page_tags' => 'Oldal címkék',
@@ -243,11 +247,11 @@ return [
     'shelf_tags' => 'Polc címkék',
     'tag' => 'Címke',
     'tags' =>  'Címkék',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'Címkenév',
     'tag_value' => 'Címke érték (nem kötelező)',
     'tags_explain' => "Címkék hozzáadása a tartalom jobb kategorizálásához.\nA mélyebb szervezettség megvalósításához hozzá lehet rendelni egy értéket a címkéhez.",
     'tags_add' => 'Másik címke hozzáadása',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Címke eltávolítása',
     'attachments' => 'Csatolmányok',
     'attachments_explain' => 'Az oldalon megjelenő fájlok feltöltése vagy hivatkozások csatolása. Az oldal oldalsávjában fognak megjelenni.',
     'attachments_explain_instant_save' => 'Az itt történt módosítások azonnal el lesznek mentve.',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Fájlfeltöltés',
     'attachments_link' => 'Hivatkozás csatolása',
     'attachments_set_link' => 'Hivatkozás beállítása',
-    'attachments_delete_confirm' => 'A csatolmány törléséhez ismét rá kell kattintani a törlésre.',
+    'attachments_delete' => 'Biztosan törölhető ez a melléklet?',
     'attachments_dropzone' => 'Fájlok csatolása ejtéssel vagy kattintással',
     'attachments_no_files' => 'Nincsenek fájlok feltöltve',
     'attachments_explain_link' => 'Fájl feltöltése helyett hozzá lehet kapcsolni egy hivatkozást. Ez egy hivatkozás lesz egy másik oldalra vagy egy fájlra a felhőben.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Hivatkozás fájlra',
     'attachments_link_url_hint' => 'Weboldal vagy fájl webcíme',
     'attach' => 'Csatolás',
+    'attachments_insert_link' => 'Melléklet hivatkozás hozzáadása oldalhoz',
     'attachments_edit_file' => 'Fájl szerkesztése',
     'attachments_edit_file_name' => 'Fájl neve',
     'attachments_edit_drop_upload' => 'Feltöltés és felülírás ejtéssel vagy kattintással',
@@ -273,12 +278,12 @@ return [
     'attachments_file_uploaded' => 'Fájl sikeresen feltöltve',
     'attachments_file_updated' => 'Fájl sikeresen frissítve',
     'attachments_link_attached' => 'Hivatkozás sikeresen hozzácsatolva az oldalhoz',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Sablonok',
+    'templates_set_as_template' => 'Az oldal egy sablon',
+    'templates_explain_set_as_template' => 'Ez az oldal sablonnak lett beállítva, így a tartalma felhasználható más oldalak létrehozásakor. Más felhasználók is használhatják ezt a sablont ha megtekintési jogosultságuk van ehhez az oldalhoz.',
+    'templates_replace_content' => 'Oldal tartalmának cseréje',
+    'templates_append_content' => 'Hozzáfűzés az oldal tartalmához',
+    'templates_prepend_content' => 'Hozzáadás az oldal tartalmának elejéhez',
 
     // Profile View
     'profile_user_for_x' => 'Felhasználó ez óta: :time',
index d2456a222119192511a2c1a364ac17025812adf4..64229a19f3d3283025093b3c2a159890c82bcb21 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'Az email cím már meg van erősítve, meg lehet próbálni a bejelentkezést.',
     'email_confirmation_invalid' => 'A megerősítő vezérjel nem érvényes vagy használva volt. Meg kell próbálni újraregisztrálni.',
     'email_confirmation_expired' => 'A megerősítő vezérjel lejárt. Egy új megerősítő email lett elküldve.',
+    'email_confirmation_awaiting' => 'A használatban lévő fiók email címét meg kell erősíteni',
     'ldap_fail_anonymous' => 'Nem sikerült az LDAP elérése névtelen csatlakozással',
     'ldap_fail_authed' => 'Az LDAP hozzáférés nem sikerült a megadott DN és jelszó beállításokkal',
     'ldap_extension_not_installed' => 'LDAP PHP kiterjesztés nincs telepítve',
     'ldap_cannot_connect' => 'Nem lehet kapcsolódni az LDAP kiszolgálóhoz, a kezdeti kapcsolatfelvétel nem sikerült',
+    'saml_already_logged_in' => 'Már bejelentkezett',
+    'saml_user_not_registered' => ':name felhasználó nincs regisztrálva és az automatikus regisztráció le van tiltva',
+    'saml_no_email_address' => 'Ehhez a felhasználóhoz nem található email cím a külső hitelesítő rendszer által átadott adatokban',
+    'saml_invalid_response_id' => 'A külső hitelesítő rendszerből érkező kérést nem ismerte fel az alkalmazás által indított folyamat. Bejelentkezés után az előző oldalra történő visszalépés okozhatja ezt a hibát.',
+    'saml_fail_authed' => 'Bejelentkezés :system használatával sikertelen, a rendszer nem biztosított sikeres hitelesítést',
     'social_no_action_defined' => 'Nincs művelet meghatározva',
     'social_login_bad_response' => "Hiba történt :socialAccount bejelentkezés közben:\n:error",
     'social_account_in_use' => ':socialAccount fiók már használatban van. :socialAccount opción keresztül érdemes megpróbálni a bejelentkezést.',
@@ -27,7 +33,7 @@ return [
     'social_account_register_instructions' => ':socialAccount beállítása használatával is lehet fiókot regisztrálni, ha még nem volt fiók létrehozva.',
     'social_driver_not_found' => 'Közösségi meghajtó nem található',
     'social_driver_not_configured' => ':socialAccount közösségi beállítások nem megfelelőek.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Ez a meghívó hivatkozás lejárt. Helyette meg lehet próbálni új jelszót megadni a fiókhoz.',
 
     // System
     'path_not_writable' => ':filePath elérési út nem tölthető fel. Ellenőrizni kell, hogy az útvonal a kiszolgáló számára írható.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'A fáj feltöltése időtúllépést okozott.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Oldal eltárás csatolmány frissítése közben',
     'attachment_not_found' => 'Csatolmány nem található',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Oldal nem található',
     'sorry_page_not_found' => 'Sajnáljuk, a keresett oldal nem található.',
+    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
     'return_home' => 'Vissza a kezdőlapra',
     'error_occurred' => 'Hiba örtént',
     'app_down' => ':appName jelenleg nem üzemel',
     'back_soon' => 'Hamarosan újra elérhető lesz.',
 
+    // API errors
+    'api_no_authorization_found' => 'A kérésben nem található hitelesítési vezérjel',
+    'api_bad_authorization_format' => 'A kérésben hitelesítési vezérjel található de a formátuma érvénytelennek tűnik',
+    'api_user_token_not_found' => 'A megadott hitelesítési vezérjelhez nem található egyező API vezérjel',
+    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
+    'api_user_no_api_permission' => 'A használt API vezérjel tulajdonosának nincs jogosultsága API hívások végrehajtásához',
+    'api_user_token_expired' => 'A használt hitelesítési vezérjel lejárt',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Hiba történt egy teszt email küldésekor:',
+
 ];
index bacc08b08dfce1f0a5476187ecb10b1ab92062c9..e06c6f6e35d25ef06dfac4b47117fb086c0a000e 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'A jelszónak legalább hat karakterből kell állnia és egyeznie kell a megerősítéssel.',
     'user' => "Nem található felhasználó ezzel az e-mail címmel.",
-    'token' => 'Ez a jelszó visszaállító vezérjel érvénytelen.',
+    'token' => 'The password reset token is invalid for this email address.',
     'sent' => 'E-mailben elküldtük a jelszó visszaállító hivatkozást!',
     'reset' => 'A jelszó visszaállítva!',
 
index 058784dc7e59d19f44a28231713e0025b56c0338..9b148a61e2e11e0c8e343abf98db57555f416697 100644 (file)
@@ -29,7 +29,7 @@ return [
     'app_editor_desc' => 'Annak kiválasztása, hogy a felhasználók melyik szerkesztőt használhatják az oldalak szerkesztéséhez.',
     'app_custom_html' => 'Egyéni HTML fejléc tartalom',
     'app_custom_html_desc' => 'Az itt hozzáadott bármilyen tartalom be lesz illesztve minden oldal <head> szekciójának aljára. Ez hasznos a stílusok felülírásához van analitikai kódok hozzáadásához.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'Az egyéni HTML fejléc tartalom le van tiltva ezen a beállítási oldalon, hogy az esetleg hibásan megadott módosításokat vissza lehessen állítani.',
     'app_logo' => 'Alkalmazás logó',
     'app_logo_desc' => 'A képnek 43px magasnak kell lennie.<br>A nagy képek át lesznek méretezve.',
     'app_primary_color' => 'Alkalmazás elsődleges színe',
@@ -41,12 +41,22 @@ return [
     'app_disable_comments_toggle' => 'Megjegyzések letiltása',
     'app_disable_comments_desc' => 'Megjegyzések letiltása az alkalmazás összes oldalán.<br>A már létező megjegyzések el lesznek rejtve.',
 
+    // Color settings
+    'content_colors' => 'Tartalomszínek',
+    'content_colors_desc' => 'Beállítja az elemek színét az oldalszervezési hierarchiában. Az olvashatóság szempontjából javasolt az alapértelmezés szerinti színhez hasonló fényerősséget választani.',
+    'bookshelf_color' => 'Polc színe',
+    'book_color' => 'Könyv színe',
+    'chapter_color' => 'Fejezet színe',
+    'page_color' => 'Oldal színe',
+    'page_draft_color' => 'Oldalvázlat színe',
+
     // Registration Settings
     'reg_settings' => 'Regisztráció',
     'reg_enable' => 'Regisztráció engedélyezése',
     'reg_enable_toggle' => 'Regisztráció engedélyezése',
     'reg_enable_desc' => 'Ha a regisztráció engedélyezett, akkor a felhasználó képes lesz bejelentkezni mint az alkalmazás egy felhasználója. Regisztráció után egy egyszerű, alapértelmezés szerinti felhasználói szerepkör lesz hozzárendelve.',
     'reg_default_role' => 'Regisztráció utáni alapértelmezett felhasználói szerepkör',
+    'reg_enable_external_warning' => 'A fenti beállítási lehetőség nincs használatban, ha külső LDAP vagy SAML hitelesítés aktív. A nem létező tagok felhasználói fiókjai automatikusan létrejönnek ha a használatban lévő külső rendszeren sikeres a hitelesítés.',
     'reg_email_confirmation' => 'Email megerősítés',
     'reg_email_confirmation_toggle' => 'Email megerősítés szükséges',
     'reg_confirm_email_desc' => 'Ha a tartomány korlátozás be van állítva, akkor email megerősítés szükséges és ez a beállítás figyelmen kívül lesz hagyva.',
@@ -58,11 +68,53 @@ return [
     'maint' => 'Karbantartás',
     'maint_image_cleanup' => 'Képek tisztítása',
     'maint_image_cleanup_desc' => "Végigolvassa az oldalakat és a tartalmak változatait, hogy leellenőrizze jelenleg mely képek és rajzok vannak használatban, és mely képek szerepelnek többször. A futtatása előtt feltétlen készíteni kell egy teljes adatbázis és lemezkép mentést.",
-    'maint_image_cleanup_ignore_revisions' => 'Képek figyelmen kívül hagyása a változatokban',
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Tisztítás futtatása',
     'maint_image_cleanup_warning' => ':count potenciálisan nem használt képet találtam. Biztosan törölhetőek ezek a képek?',
     'maint_image_cleanup_success' => ':count potenciálisan nem használt kép megtalálva és törölve!',
     'maint_image_cleanup_nothing_found' => 'Nincsenek nem használt képek, semmi sem lett törölve!',
+    'maint_send_test_email' => 'Teszt e-mail küldése',
+    'maint_send_test_email_desc' => 'Ez elküld egy teszt emailt a profilban megadott email címre.',
+    'maint_send_test_email_run' => 'Teszt e-mail küldése',
+    'maint_send_test_email_success' => 'Email elküldve :address címre',
+    'maint_send_test_email_mail_subject' => 'Teszt e-mail',
+    'maint_send_test_email_mail_greeting' => 'Az email kézbesítés működőképesnek tűnik!',
+    'maint_send_test_email_mail_text' => 'Gratulálunk! Mivel ez az email figyelmeztetés megérkezett az email beállítások megfelelőek.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Lomtár megnyitása',
+
+    // Recycle Bin
+    'recycle_bin' => 'Lomtár',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Törölt elem',
+    'recycle_bin_deleted_by' => 'Törölte',
+    'recycle_bin_deleted_at' => 'Törlés ideje',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Visszaállítás',
+    'recycle_bin_contents_empty' => 'A lomtár jelenleg üres',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Eseményszűrő',
+    'audit_event_filter_no_filter' => 'Nincs szűrő',
+    'audit_deleted_item' => 'Törölt elem',
+    'audit_deleted_item_name' => 'Név: :name',
+    'audit_table_user' => 'Felhasználó',
+    'audit_table_event' => 'Esemény',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
 
     // Role Settings
     'roles' => 'Szerepkörök',
@@ -85,9 +137,11 @@ return [
     'role_manage_roles' => 'Szerepkörök és szerepkör engedélyek kezelése',
     'role_manage_entity_permissions' => 'Minden könyv, fejezet és oldalengedély kezelése',
     'role_manage_own_entity_permissions' => 'Saját könyv, fejezet és oldalak engedélyeinek kezelése',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => 'Oldalsablonok kezelése',
+    'role_access_api' => 'Hozzáférés a rendszer API-hoz',
     'role_manage_settings' => 'Alkalmazás beállításainak kezelése',
     'role_asset' => 'Eszköz jogosultságok',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Ezek a jogosultság vezérlik a alapértelmezés szerinti hozzáférést a rendszerben található eszközökhöz. A könyvek, fejezetek és oldalak jogosultságai felülírják ezeket a jogosultságokat.',
     'role_asset_admins' => 'Az adminisztrátorok automatikusan hozzáférést kapnak minden tartalomhoz, de ezek a beállítások megjeleníthetnek vagy elrejthetnek felhasználói felület beállításokat.',
     'role_all' => 'Összes',
@@ -103,6 +157,7 @@ return [
     'user_profile' => 'Felhasználói profil',
     'users_add_new' => 'Új felhasználó hozzáadása',
     'users_search' => 'Felhasználók keresése',
+    'users_latest_activity' => 'Latest Activity',
     'users_details' => 'Felhasználó részletei',
     'users_details_desc' => 'Egy megjelenítendő név és email cím beállítása ennek a felhasználónak. Az email cím az alkalmazásba történő bejelentkezéshez lesz használva.',
     'users_details_desc_no_email' => 'Egy megjelenítendő név beállítása ennek a felhasználónak amiről mások felismerik.',
@@ -110,17 +165,20 @@ return [
     'users_role_desc' => 'A felhasználó melyik szerepkörhöz lesz rendelve. Ha a felhasználó több szerepkörhöz van rendelve, akkor ezeknek a szerepköröknek a jogosultságai összeadódnak, és a a felhasználó a hozzárendelt szerepkörök minden képességét megkapja.',
     'users_password' => 'Felhasználó jelszava',
     'users_password_desc' => 'Az alkalmazásba bejelentkezéshez használható jelszó beállítása. Legalább 5 karakter hosszúnak kell lennie.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
+    'users_send_invite_text' => 'Lehetséges egy meghívó emailt küldeni ennek a felhasználónak ami lehetővé teszi, hogy beállíthassa a saját jelszavát. Máskülönben a jelszót az erre jogosult felhasználónak kell beállítania.',
+    'users_send_invite_option' => 'Felhasználó meghívó levél küldése',
     'users_external_auth_id' => 'Külső hitelesítés azonosítója',
-    'users_external_auth_id_desc' => 'Ez az azonosító lesz használva a felhasználó ellenőrzéséhez mikor az LDAP rendszerrel kommunikál.',
+    'users_external_auth_id_desc' => 'Ez az azonosító lesz használva a felhasználó ellenőrzéséhez mikor a külső hitelesítő rendszerrel kommunikál.',
     'users_password_warning' => 'A lenti mezőket csak a jelszó módosításához kell kitölteni.',
     'users_system_public' => 'Ez a felhasználó bármelyik, a példányt megtekintő felhasználót képviseli. Nem lehet vele bejelentkezni de automatikusan hozzá lesz rendelve.',
     'users_delete' => 'Felhasználó törlése',
     'users_delete_named' => ':userName felhasználó törlése',
     'users_delete_warning' => '\':userName\' felhasználó teljesen törölve lesz a rendszerből.',
     'users_delete_confirm' => 'Biztosan törölhető ez a felhasználó?',
-    'users_delete_success' => 'Felhasználó sikeresen eltávolítva',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Felhasználó szerkesztése',
     'users_edit_profile' => 'Profil szerkesztése',
     'users_edit_success' => 'Felhasználó sikeresen frissítve',
@@ -134,6 +192,32 @@ return [
     'users_social_disconnect' => 'Fiók lecsatlakoztatása',
     'users_social_connected' => ':socialAccount fiók sikeresen csatlakoztatva a profilhoz.',
     'users_social_disconnected' => ':socialAccount fiók sikeresen lecsatlakoztatva a profilról.',
+    'users_api_tokens' => 'API vezérjelek',
+    'users_api_tokens_none' => 'Ehhez a felhasználóhoz nincsenek létrehozva API vezérjelek',
+    'users_api_tokens_create' => 'Vezérjel létrehozása',
+    'users_api_tokens_expires' => 'Lejárat',
+    'users_api_tokens_docs' => 'API dokumentáció',
+
+    // API Tokens
+    'user_api_token_create' => 'API vezérjel létrehozása',
+    'user_api_token_name' => 'Név',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Lejárati dátum',
+    'user_api_token_expiry_desc' => 'Dátum megadása ameddig a vezérjel érvényes. Ez után a dátum után az ezzel a vezérjellel történő kérések nem fognak működni. Üresen hagyva a lejárati idő 100 évre lesz beállítva.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API vezérjel sikeresen létrehozva',
+    'user_api_token_update_success' => 'API vezérjel sikeresen frissítve',
+    'user_api_token' => 'API vezérjel',
+    'user_api_token_id' => 'Vezérjel azonosító',
+    'user_api_token_id_desc' => 'Ez egy nem szerkeszthető, a rendszer által létrehozott azonosító ehhez a vezérjelhez amire API kérésekben lehet szükség.',
+    'user_api_token_secret' => 'Vezérjel titkos kódja',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Vezérjel létrehozva :timeAgo',
+    'user_api_token_updated' => 'Vezérjel frissítve :timeAgo',
+    'user_api_token_delete' => 'Vezérjel törlése',
+    'user_api_token_delete_warning' => '\':tokenName\' nevű API vezérjel teljesen törölve lesz a rendszerből.',
+    'user_api_token_delete_confirm' => 'Biztosan törölhető ez az API vezérjel?',
+    'user_api_token_delete_success' => 'API vezérjel sikeresen törölve',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 023f9f0a4bcb6385d57ef6074c801f46b5cf6c06..7c378e983cc58cf629c05005e11c9c1b938ce98d 100644 (file)
@@ -30,16 +30,16 @@ return [
     'digits'               => ':attribute :digits számból kell álljon.',
     'digits_between'       => ':attribute hosszának :min és :max számjegy között kell lennie.',
     'email'                => ':attribute érvényes email cím kell legyen.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ':attribute attribútumnak a következők egyikével kell végződnie: :values',
     'filled'               => ':attribute mező kötelező.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute nagyobb kell, hogy legyen, mint :value.',
+        'file'    => ':attribute nagyobb kell, hogy legyen, mint :value kilobájt.',
+        'string'  => ':attribute nagyobb kell legyen mint :value karakter.',
+        'array'   => ':attribute több, mint :value elemet kell, hogy tartalmazzon.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
+        'numeric' => ':attribute attribútumnak :value értéknél nagyobbnak vagy vele egyenlőnek kell lennie.',
         'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
         'string'  => 'The :attribute must be greater than or equal :value characters.',
         'array'   => 'The :attribute must have :value items or more.',
@@ -80,7 +80,7 @@ return [
     ],
     'no_double_extension'  => ':attribute csak egy fájlkiterjesztéssel rendelkezhet.',
     'not_in'               => 'A kiválasztott :attribute érvénytelen.',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => ':attribute formátuma érvénytelen.',
     'numeric'              => ':attribute szám kell legyen.',
     'regex'                => ':attribute formátuma érvénytelen.',
     'required'             => ':attribute mező kötelező.',
@@ -90,6 +90,7 @@ return [
     'required_without'     => ':attribute mező kötelező ha :values nincs beállítva.',
     'required_without_all' => ':attribute mező kötelező ha egyik :values sincs beállítva.',
     'same'                 => ':attribute és :other értékének egyeznie kell.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
         'numeric' => ':attribute :size méretű kell legyen.',
         'file'    => ':attribute :size kilobájt méretű kell legyen.',
index c66651489394fc5b0ea34c8eda4d37729a93583c..24b484181ef75f34c16350b0ee454163dd0a1e73 100755 (executable)
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'ha commentato in',
+    'permissions_update'          => 'updated permissions',
 ];
index 234af2eeba723d1839cc3c94ff44f034b2c2ccab..a1c4b704831450ac3f61ce34cf13674e1c5b723f 100755 (executable)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Reimposta Password',
     'reset_password_send_instructions' => 'Inserisci il tuo indirizzo sotto e ti verrà inviata una mail contenente un link per resettare la tua password.',
     'reset_password_send_button' => 'Invia Link Reset',
-    'reset_password_sent_success' => 'Un link di reset è stato mandato a :email.',
+    'reset_password_sent' => 'Un link di reset della password verrà inviato a :email se la mail verrà trovata nel sistema.',
     'reset_password_success' => 'La tua password è stata resettata correttamente.',
     'email_reset_subject' => 'Reimposta la password di :appName',
     'email_reset_text' => 'Stai ricevendo questa mail perché abbiamo ricevuto una richiesta di reset della password per il tuo account.',
index 1873a100c6dfda4ec9dc0c48144948af009e9fbd..9676962bdc7f94c5f04b03a1a82e7b4b522da686 100755 (executable)
@@ -33,11 +33,13 @@ return [
     'copy' => 'Copia',
     'reply' => 'Rispondi',
     'delete' => 'Elimina',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Cerca',
     'search_clear' => 'Pulisci Ricerca',
     'reset' => 'Azzera',
     'remove' => 'Rimuovi',
     'add' => 'Aggiungi',
+    'fullscreen' => 'Schermo intero',
 
     // Sort Options
     'sort_options' => 'Opzioni Ordinamento',
@@ -65,6 +67,8 @@ return [
     'profile_menu' => 'Menu del profilo',
     'view_profile' => 'Visualizza Profilo',
     'edit_profile' => 'Modifica Profilo',
+    'dark_mode' => 'Modalità Scura',
+    'light_mode' => 'Modalità Chiara',
 
     // Layout tabs
     'tab_info' => 'Info',
index 360409646e3040033dd88acad388bd8a5870604d..aa1c8f4a68fea41b80bb5b590ec11ba72944b226 100755 (executable)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Carica Altre',
     'image_image_name' => 'Nome Immagine',
     'image_delete_used' => 'Questa immagine è usata nelle pagine elencate.',
-    'image_delete_confirm' => 'Clicca elimina nuovamente per confermare.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Seleziona Immagine',
     'image_dropzone' => 'Rilascia immagini o clicca qui per caricarle',
     'images_deleted' => 'Immagini Eliminate',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Modifica Codice',
     'code_language' => 'Linguaggio Codice',
     'code_content' => 'Contenuto Codice',
+    'code_session_history' => 'Cronologia Sessione',
     'code_save' => 'Salva Codice',
 ];
index 89beb9af580f6bb97a70475a0b956111f2e6acf1..2d8766655218455247a398dfb4f4a6afeb76d7c4 100755 (executable)
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => 'Creato :timeLength da :user',
     'meta_updated' => 'Aggiornato :timeLength',
     'meta_updated_name' => 'Aggiornato :timeLength da :user',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Selezione Entità',
     'images' => 'Immagini',
     'my_recent_drafts' => 'Bozze Recenti',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Una volta abilitati, questi permessi avranno la priorità su tutti gli altri.',
     'permissions_enable' => 'Abilita Permessi Custom',
     'permissions_save' => 'Salva Permessi',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Risultati Ricerca',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Nessuna pagina corrisponde alla ricerca',
     'search_for_term' => 'Ricerca per :term',
     'search_more' => 'Più Risultati',
-    'search_filters' => 'Filtri Ricerca',
+    'search_advanced' => 'Ricerca Avanzata',
+    'search_terms' => 'Termini Ricerca',
     'search_content_type' => 'Tipo di Contenuto',
     'search_exact_matches' => 'Corrispondenza Esatta',
     'search_tags' => 'Ricerche Tag',
@@ -95,7 +98,7 @@ return [
     'shelves_copy_permissions_to_books' => 'Copia Permessi ai Libri',
     'shelves_copy_permissions' => 'Copia Permessi',
     'shelves_copy_permissions_explain' => 'Verranno applicati tutti i permessi della libreria ai libri contenuti. Prima di attivarlo, assicurati che ogni permesso di questa libreria sia salvato.',
-    'shelves_copy_permission_success' => 'Permessi della libreria copiati in :count books',
+    'shelves_copy_permission_success' => 'Permessi della libreria copiati in :count libri',
 
     // Books
     'book' => 'Libro',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Crea un nuovo capitolo',
     'chapters_delete' => 'Elimina Capitolo',
     'chapters_delete_named' => 'Elimina il capitolo :chapterName',
-    'chapters_delete_explain' => 'Questo eliminerà il capitolo \':chapterName\'. Tutte le pagine verranno spostate nel libro.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'Sei sicuro di voler eliminare questo capitolo?',
     'chapters_edit' => 'Elimina Capitolo',
     'chapters_edit_named' => 'Modifica il capitolo :chapterName',
@@ -207,6 +210,7 @@ return [
     'pages_revisions' => 'Versioni Pagina',
     'pages_revisions_named' => 'Versioni della pagina :pageName',
     'pages_revision_named' => 'Versione della pagina :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Creata Da',
     'pages_revisions_date' => 'Data Versione',
     'pages_revisions_number' => '#',
@@ -255,15 +259,16 @@ return [
     'attachments_upload' => 'Carica File',
     'attachments_link' => 'Allega Link',
     'attachments_set_link' => 'Imposta Link',
-    'attachments_delete_confirm' => 'Clicca elimina nuovamente per confermare l\'eliminazione di questo allegato.',
+    'attachments_delete' => 'Sei sicuro di voler eliminare questo allegato?',
     'attachments_dropzone' => 'Rilascia file o clicca qui per allegare un file',
     'attachments_no_files' => 'Nessun file è stato caricato',
-    'attachments_explain_link' => 'Puoi allegare un link se preferisci non caricare un file. Questo può essere un link a un\'altra pagina o a un file in un cloud.',
+    'attachments_explain_link' => 'Puoi allegare un link se preferisci non caricare un file. Questo può essere un link a un\'altra pagina o a un file nel cloud.',
     'attachments_link_name' => 'Nome Link',
     'attachment_link' => 'Link allegato',
     'attachments_link_url' => 'Link al file',
     'attachments_link_url_hint' => 'Url del sito o del file',
     'attach' => 'Allega',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Modifica File',
     'attachments_edit_file_name' => 'Nome File',
     'attachments_edit_drop_upload' => 'Rilascia file o clicca qui per caricare e sovrascrivere',
index 466aabcd5fb55f103d63214a25a85b671e201d59..3e48ad762a942488b3c1d8bf99269fa2e3fb092a 100755 (executable)
@@ -13,16 +13,22 @@ return [
     'email_already_confirmed' => 'La mail è già stata confermata, esegui il login.',
     'email_confirmation_invalid' => 'Questo token di conferma non è valido o già stato utilizzato, registrati nuovamente.',
     'email_confirmation_expired' => 'Il token di conferma è scaduto, è stata inviata una nuova mail di conferma.',
+    'email_confirmation_awaiting' => 'L\'indirizzo email per l\'account in uso deve essere confermato',
     'ldap_fail_anonymous' => 'Accesso LDAP fallito usando bind anonimo',
     'ldap_fail_authed' => 'Accesso LDAP fallito usando il dn e la password inseriti',
     'ldap_extension_not_installed' => 'L\'estensione PHP LDAP non è installata',
     'ldap_cannot_connect' => 'Impossibile connettersi al server ldap, connessione iniziale fallita',
+    'saml_already_logged_in' => 'Già loggato',
+    'saml_user_not_registered' => 'L\'utente :name non è registrato e la registrazione automatica è disabilitata',
+    'saml_no_email_address' => 'Impossibile trovare un indirizzo email per questo utente nei dati forniti dal sistema di autenticazione esterno',
+    'saml_invalid_response_id' => 'La richiesta dal sistema di autenticazione esterno non è riconosciuta da un processo iniziato da questa applicazione. Tornare indietro dopo un login potrebbe causare questo problema.',
+    'saml_fail_authed' => 'Accesso con :system non riuscito, il sistema non ha fornito l\'autorizzazione corretta',
     'social_no_action_defined' => 'Nessuna azione definita',
     'social_login_bad_response' => "Ricevuto error durante il login con :socialAccount : \n:error",
     'social_account_in_use' => 'Questo account :socialAccount è già utilizzato, prova a loggarti usando l\'opzione :socialAccount.',
     'social_account_email_in_use' => 'La mail :email è già in uso. Se hai già un account puoi connettere il tuo account :socialAccount dalle impostazioni del tuo profilo.',
     'social_account_existing' => 'Questo account :socialAccount è già connesso al tuo profilo.',
-    'social_account_already_used_existing' => 'Questo accoutn :socialAccount è già utilizzato da un altro utente.',
+    'social_account_already_used_existing' => 'Questo account :socialAccount è già utilizzato da un altro utente.',
     'social_account_not_used' => 'Questo account :socialAccount non è collegato a nessun utente. Collegalo nelle impostazioni del profilo. ',
     'social_account_register_instructions' => 'Se non hai ancora un account, puoi registrarti usando l\'opzione :socialAccount.',
     'social_driver_not_found' => 'Driver social non trovato',
@@ -30,17 +36,16 @@ return [
     'invite_token_expired' => 'Il link di invito è scaduto. Puoi provare a resettare la password del tuo account.',
 
     // System
-    'path_not_writable' => 'La path :filePath non può essere scritta. Controlla che abbia i permessi corretti.',
+    'path_not_writable' => 'Il percorso :filePath non è scrivibile. Controlla che abbia i permessi corretti.',
     'cannot_get_image_from_url' => 'Impossibile scaricare immagine da :url',
     'cannot_create_thumbs' => 'Il server non può creare thumbnail. Controlla che l\'estensione GD sia installata.',
     'server_upload_limit' => 'Il server non permette un upload di questa grandezza. Prova con un file più piccolo.',
     'uploaded'  => 'Il server non consente upload di questa grandezza. Prova un file più piccolo.',
     'image_upload_error' => 'C\'è stato un errore caricando l\'immagine',
-    'image_upload_type_error' => 'Il tipo di immagine in upload non è valido',
-    'file_upload_timeout' => 'Il caricamento del file è scaduto.',
+    'image_upload_type_error' => 'Il tipo di immagine caricata non è valido',
+    'file_upload_timeout' => 'Il caricamento del file è andato in timeout.',
 
     // Attachments
-    'attachment_page_mismatch' => 'La pagina non è corrisposta durante l\'aggiornamento dell\'allegato',
     'attachment_not_found' => 'Allegato non trovato',
 
     // Pages
@@ -58,7 +63,7 @@ return [
     'guests_cannot_save_drafts' => 'Gli ospiti non possono salvare bozze',
 
     // Users
-    'users_cannot_delete_only_admin' => 'Non puoi eliminare l\'unico adin',
+    'users_cannot_delete_only_admin' => 'Non puoi eliminare l\'unico admin',
     'users_cannot_delete_guest' => 'Non puoi eliminare l\'utente ospite',
 
     // Roles
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Pagina Non Trovata',
     'sorry_page_not_found' => 'La pagina che stavi cercando non è stata trovata.',
+    'sorry_page_not_found_permission_warning' => 'Se pensi che questa pagina possa esistere, potresti non avere i permessi per visualizzarla.',
     'return_home' => 'Ritorna alla home',
     'error_occurred' => 'C\'è Stato un errore',
     'app_down' => ':appName è offline',
     'back_soon' => 'Ritornerà presto.',
 
+    // API errors
+    'api_no_authorization_found' => 'No authorization token found on the request',
+    'api_bad_authorization_format' => 'Un token di autorizzazione è stato trovato nella richiesta, ma il formato sembra non corretto',
+    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
+    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
+    'api_user_no_api_permission' => 'Il proprietario del token API utilizzato non ha il permesso di effettuare chiamate API',
+    'api_user_token_expired' => 'The authorization token used has expired',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+
 ];
index 0f95a3e0672ee0be86bfa1d447db9559d93007c9..604e02fe2c6b0a47298624b3db025737864a26af 100755 (executable)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'La password deve avere almeno sei caratteri e corrispondere alla conferma.',
     'user' => "Non possiamo trovare un utente per quella mail.",
-    'token' => 'Questo token per reimpostare la password non è valido.',
+    'token' => 'The password reset token is invalid for this email address.',
     'sent' => 'Ti abbiamo inviato via mail il link per reimpostare la password!',
     'reset' => 'La tua password è stata resettata!',
 
index fe312796525388a826e4ba818cc2d62fe13d025f..f925de9620d3af06f896b8490fb7099c8ad72aae 100755 (executable)
@@ -41,12 +41,22 @@ return [
     'app_disable_comments_toggle' => 'Disabilita commenti',
     'app_disable_comments_desc' => 'Disabilita i commenti su tutte le pagine nell\'applicazione. I commenti esistenti non sono mostrati. ',
 
+    // Color settings
+    'content_colors' => 'Colori del contenuto',
+    'content_colors_desc' => 'Imposta i colori per tutti gli elementi nella gerarchia della pagina. È raccomandato scegliere colori con una luminosità simile a quelli di default per una maggiore leggibilità.',
+    'bookshelf_color' => 'Colore delle libreria',
+    'book_color' => 'Colore del libro',
+    'chapter_color' => 'Colore del capitolo',
+    'page_color' => 'Colore della Pagina',
+    'page_draft_color' => 'Colore della bozza',
+
     // Registration Settings
     'reg_settings' => 'Impostazioni Registrazione',
     'reg_enable' => 'Abilita Registrazione',
     'reg_enable_toggle' => 'Abilita registrazione',
-    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_enable_desc' => 'Quando la registrazione è abilitata, l\utente sarà in grado di registrarsi all\'applicazione. Al momento della registrazione gli verrà associato un ruolo utente predefinito.',
     'reg_default_role' => 'Ruolo predefinito dopo la registrazione',
+    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
     'reg_email_confirmation' => 'Conferma Email',
     'reg_email_confirmation_toggle' => 'Richiedi conferma email',
     'reg_confirm_email_desc' => 'Se la restrizione per dominio è usata la conferma della mail sarà richiesta e la scelta ignorata.',
@@ -57,12 +67,54 @@ return [
     // Maintenance settings
     'maint' => 'Manutenzione',
     'maint_image_cleanup' => 'Pulizia Immagini',
-    'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignora le immagini nelle revisioni',
+    'maint_image_cleanup_desc' => "Esegue la scansione del contenuto delle pagine e delle revisioni per verificare quali immagini e disegni sono attualmente in uso e quali immagini sono ridondanti. Assicurati di creare backup completo del database e delle immagini prima di eseguire la pulizia.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Esegui Pulizia',
     'maint_image_cleanup_warning' => ':count immagini potenzialmente inutilizzate sono state trovate. Sei sicuro di voler eliminare queste immagini?',
     'maint_image_cleanup_success' => ':count immagini potenzialmente inutilizzate trovate e eliminate!',
-    'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
+    'maint_image_cleanup_nothing_found' => 'Nessuna immagine non utilizzata trovata, Nulla è stato cancellato!',
+    'maint_send_test_email' => 'Invia un Email di Test',
+    'maint_send_test_email_desc' => 'Questo invia un\'email di prova al tuo indirizzo email specificato nel tuo profilo.',
+    'maint_send_test_email_run' => 'Invia email di test',
+    'maint_send_test_email_success' => 'Email inviata a :address',
+    'maint_send_test_email_mail_subject' => 'Email di Test',
+    'maint_send_test_email_mail_greeting' => 'L\'invio delle email sembra funzionare!',
+    'maint_send_test_email_mail_text' => 'Congratulazioni! Siccome hai ricevuto questa notifica email, le tue impostazioni sembrano essere configurate correttamente.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Apri il Cestino',
+
+    // Recycle Bin
+    'recycle_bin' => 'Cestino',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Cancellato da',
+    'recycle_bin_deleted_at' => 'Orario Cancellazione',
+    'recycle_bin_permanently_delete' => 'Elimina Definitivamente',
+    'recycle_bin_restore' => 'Ripristina',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Svuota Cestino',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'Utente',
+    'audit_table_event' => 'Evento',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
 
     // Role Settings
     'roles' => 'Ruoli',
@@ -86,8 +138,10 @@ return [
     'role_manage_entity_permissions' => 'Gestire tutti i permessi di libri, capitoli e pagine',
     'role_manage_own_entity_permissions' => 'Gestire i permessi sui propri libri, capitoli e pagine',
     'role_manage_page_templates' => 'Gestisci template pagine',
+    'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Gestire impostazioni app',
     'role_asset' => 'Permessi Entità',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Questi permessi controllano l\'accesso di default alle entità. I permessi nei Libri, Capitoli e Pagine sovrascriveranno questi.',
     'role_asset_admins' => 'Gli amministratori hanno automaticamente accesso a tutti i contenuti ma queste opzioni possono mostrare o nascondere le opzioni della UI.',
     'role_all' => 'Tutti',
@@ -103,6 +157,7 @@ return [
     'user_profile' => 'Profilo Utente',
     'users_add_new' => 'Aggiungi Nuovo Utente',
     'users_search' => 'Cerca Utenti',
+    'users_latest_activity' => 'Latest Activity',
     'users_details' => 'Dettagli Utente',
     'users_details_desc' => 'Imposta un nome e un indirizzo email per questo utente. L\'indirizzo email verrà utilizzato per accedere all\'applicazione.',
     'users_details_desc_no_email' => 'Imposta un nome per questo utente così gli altri possono riconoscerlo.',
@@ -113,14 +168,17 @@ return [
     'users_send_invite_text' => 'Puoi scegliere di inviare a questo utente un\'email di invito che permette loro di impostare la propria password altrimenti puoi impostare la password tu stesso.',
     'users_send_invite_option' => 'Invia email di invito',
     'users_external_auth_id' => 'ID Autenticazioni Esterna',
-    'users_external_auth_id_desc' => 'Questo è l\'ID utilizzato per abbinare questo utente quando si comunica con il sistema LDAP.',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
     'users_password_warning' => 'Riempi solo se desideri cambiare la tua password:',
     'users_system_public' => 'Questo utente rappresente qualsiasi ospite che visita il sito. Non può essere usato per effettuare il login ma è assegnato automaticamente.',
     'users_delete' => 'Elimina Utente',
     'users_delete_named' => 'Elimina l\'utente :userName',
     'users_delete_warning' => 'Questo eliminerà completamente l\'utente \':userName\' dal sistema.',
     'users_delete_confirm' => 'Sei sicuro di voler eliminare questo utente?',
-    'users_delete_success' => 'Utenti rimossi correttamente',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'Nessun utente selezionato',
+    'users_delete_success' => 'Utente rimosso con successo',
     'users_edit' => 'Modifica Utente',
     'users_edit_profile' => 'Modifica Profilo',
     'users_edit_success' => 'Utente aggiornato correttamente',
@@ -134,6 +192,32 @@ return [
     'users_social_disconnect' => 'Disconnetti Account',
     'users_social_connected' => 'L\'account :socialAccount è stato connesso correttamente al tuo profilo.',
     'users_social_disconnected' => 'L\'account :socialAccount è stato disconnesso correttamente dal tuo profilo.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Crea Token',
+    'users_api_tokens_expires' => 'Scade',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Crea Token API',
+    'user_api_token_name' => 'Nome',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Data di scadenza',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'Token API',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token Aggiornato :timeAgo',
+    'user_api_token_updated' => 'Token Aggiornato :timeAgo',
+    'user_api_token_delete' => 'Elimina Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Sei sicuri di voler eliminare questo token API?',
+    'user_api_token_delete_success' => 'Token API eliminato correttamente',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Danese',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Sloveno',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 3b85303d2b879477e83f4309db786bfde8b152ed..2e460cdc382715a74492197fe0d31da98364de5b 100755 (executable)
@@ -90,6 +90,7 @@ return [
     'required_without'     => 'Il campo :attribute è richiesto quando :values non è presente.',
     'required_without_all' => 'Il campo :attribute è richiesto quando nessuno dei :values sono presenti.',
     'same'                 => ':attribute e :other devono corrispondere.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
         'numeric' => 'Il campo :attribute deve essere :size.',
         'file'    => 'Il campo :attribute deve essere :size kilobytes.',
index de1ca1ac6c9cdea7475e8acc830ca5ed1856635c..b1995a654f5b8c85b8ef39cdbf9c9fb85762fcc5 100644 (file)
@@ -36,13 +36,14 @@ return [
     'book_sort_notification'      => '並び順を変更しました',
 
     // Bookshelves
-    'bookshelf_create'            => 'created Bookshelf',
-    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
-    'bookshelf_update'                 => 'updated bookshelf',
-    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
-    'bookshelf_delete'                 => 'deleted bookshelf',
-    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+    'bookshelf_create'            => '本棚を作成:',
+    'bookshelf_create_notification'    => '本棚を作成しました',
+    'bookshelf_update'                 => '本棚を更新:',
+    'bookshelf_update_notification'    => '本棚を更新しました',
+    'bookshelf_delete'                 => 'ブックが削除されました。',
+    'bookshelf_delete_notification'    => '本棚を削除しました',
 
     // Other
-    'commented_on'                => 'commented on',
+    'commented_on'                => 'コメントする',
+    'permissions_update'          => 'updated permissions',
 ];
index d0db7d080f05f5b8930484f9e7cbf0da38b91a70..6163a5fc1f739b171b332661d7112fb282a13d4e 100644 (file)
@@ -26,8 +26,8 @@ return [
     'remember_me' => 'ログイン情報を保存する',
     'ldap_email_hint' => 'このアカウントで使用するEメールアドレスを入力してください。',
     'create_account' => 'アカウント作成',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
+    'already_have_account' => 'すでにアカウントをお持ちですか?',
+    'dont_have_account' => '初めての登録ですか?',
     'social_login' => 'SNSログイン',
     'social_registration' => 'SNS登録',
     'social_registration_text' => '他のサービスで登録 / ログインする',
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'パスワードリセット',
     'reset_password_send_instructions' => '以下にEメールアドレスを入力すると、パスワードリセットリンクが記載されたメールが送信されます。',
     'reset_password_send_button' => 'リセットリンクを送信',
-    'reset_password_sent_success' => ':emailへリセットリンクを送信しました。',
+    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
     'reset_password_success' => 'パスワードがリセットされました。',
     'email_reset_subject' => ':appNameのパスワードをリセット',
     'email_reset_text' => 'このメールは、パスワードリセットがリクエストされたため送信されています。',
index feac9c460433576c99800535c91fb91f7e81f73c..7932dc7135bbf9fc5192b8e7df0405a67503d1dd 100644 (file)
@@ -33,11 +33,13 @@ return [
     'copy' => 'Copy',
     'reply' => '返信',
     'delete' => '削除',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => '検索',
     'search_clear' => '検索をクリア',
     'reset' => 'リセット',
     'remove' => '削除',
     'add' => '追加',
+    'fullscreen' => 'Fullscreen',
 
     // Sort Options
     'sort_options' => 'Sort Options',
@@ -65,6 +67,8 @@ return [
     'profile_menu' => 'Profile Menu',
     'view_profile' => 'プロフィール表示',
     'edit_profile' => 'プロフィール編集',
+    'dark_mode' => 'Dark Mode',
+    'light_mode' => 'Light Mode',
 
     // Layout tabs
     'tab_info' => 'Info',
index 65e8a7a79c9591ea0e187256cf6026597339bd1a..c4e44433787858f367e72133eba3dfab288b49e2 100644 (file)
@@ -15,19 +15,20 @@ return [
     'image_load_more' => 'さらに読み込む',
     'image_image_name' => '画像名',
     'image_delete_used' => 'この画像は以下のページで利用されています。',
-    'image_delete_confirm' => '削除してもよろしければ、再度ボタンを押して下さい。',
-    'image_select_image' => '選択',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_select_image' => '画像を選択',
     'image_dropzone' => '画像をドロップするか、クリックしてアップロード',
     'images_deleted' => '画像を削除しました',
     'image_preview' => '画像プレビュー',
     'image_upload_success' => '画像がアップロードされました',
     'image_update_success' => '画像が更新されました',
     'image_delete_success' => '画像が削除されました',
-    'image_upload_remove' => 'Remove',
+    'image_upload_remove' => '削除',
 
     // Code Editor
-    'code_editor' => 'ã\83\97ã\83­ã\82°ã\83©ã\83 ã\83\96ã\83­ã\83\83ã\82¯ç·¨é\9b\86',
+    'code_editor' => 'ã\82³ã\83¼ã\83\89ã\82\92ç·¨é\9b\86ã\81\99ã\82\8b',
     'code_language' => 'プログラミング言語の選択',
     'code_content' => 'プログラム内容',
+    'code_session_history' => 'セッション履歴',
     'code_save' => 'プログラムを保存',
 ];
index 4f1a855ff78a2a1414ce4082490716049f488edd..d220d5e38e756915d0fea34a9a64e0820159c05e 100644 (file)
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => '作成: :timeLength (:user)',
     'meta_updated' => '更新: :timeLength',
     'meta_updated_name' => '更新: :timeLength (:user)',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'エンティティ選択',
     'images' => '画像',
     'my_recent_drafts' => '最近の下書き',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'この設定は各ユーザの役割よりも優先して適用されます。',
     'permissions_enable' => 'カスタム権限設定を有効にする',
     'permissions_save' => '権限を保存',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => '検索結果',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'ページが見つかりませんでした。',
     'search_for_term' => ':term の検索結果',
     'search_more' => 'さらに表示',
-    'search_filters' => '検索フィルタ',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => '種類',
     'search_exact_matches' => '完全一致',
     'search_tags' => 'タグ検索',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'チャプターを作成',
     'chapters_delete' => 'チャプターを削除',
     'chapters_delete_named' => 'チャプター「:chapterName」を削除',
-    'chapters_delete_explain' => 'チャプター「:chapterName」を削除すると、チャプター内のすべてのページはブック内に直接追加されます。',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'チャプターを削除してよろしいですか?',
     'chapters_edit' => 'チャプターを編集',
     'chapters_edit_named' => 'チャプター「:chapterName」を編集',
@@ -207,6 +210,7 @@ return [
     'pages_revisions' => '編集履歴',
     'pages_revisions_named' => ':pageName のリビジョン',
     'pages_revision_named' => ':pageName のリビジョン',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => '作成者',
     'pages_revisions_date' => '日付',
     'pages_revisions_number' => 'リビジョン',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'アップロード',
     'attachments_link' => 'リンクを添付',
     'attachments_set_link' => 'リンクを設定',
-    'attachments_delete_confirm' => 'もう一度クリックし、削除を確認してください。',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'ファイルをドロップするか、クリックして選択',
     'attachments_no_files' => 'ファイルはアップロードされていません',
     'attachments_explain_link' => 'ファイルをアップロードしたくない場合、他のページやクラウド上のファイルへのリンクを添付できます。',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'ファイルURL',
     'attachments_link_url_hint' => 'WebサイトまたはファイルへのURL',
     'attach' => '添付',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'ファイルを編集',
     'attachments_edit_file_name' => 'ファイル名',
     'attachments_edit_drop_upload' => 'ファイルをドロップするか、クリックしてアップロード',
index c42a773b66eea5c22c851df1eb2204f779f68377..983e07a3a524aed553fb875fa070aed5a466b0d1 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'Eメールは既に確認済みです。ログインしてください。',
     'email_confirmation_invalid' => 'この確認トークンは無効か、または既に使用済みです。登録を再試行してください。',
     'email_confirmation_expired' => '確認トークンは有効期限切れです。確認メールを再送しました。',
+    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
     'ldap_fail_anonymous' => '匿名バインドを用いたLDAPアクセスに失敗しました',
     'ldap_fail_authed' => '識別名, パスワードを用いたLDAPアクセスに失敗しました',
     'ldap_extension_not_installed' => 'LDAP PHP extensionがインストールされていません',
     'ldap_cannot_connect' => 'LDAPサーバに接続できませんでした',
+    'saml_already_logged_in' => '既にログインしています',
+    'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
+    'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
+    'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
+    'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
     'social_no_action_defined' => 'アクションが定義されていません',
     'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
     'social_account_in_use' => ':socialAccountアカウントは既に使用されています。:socialAccountのオプションからログインを試行してください。',
@@ -30,7 +36,7 @@ return [
     'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
 
     // System
-    'path_not_writable' => 'ã\83\95ã\82¡ã\82¤ã\83«ã\83\91ã\82¹ :filePath ã\81¸ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\81§ã\81\8dã\81¾ã\81\9bã\82\93ã\81§ã\81\97ã\81\9fã\80\82ã\82µã\83¼ã\83\90ä¸\8aã\81§ã\81®æ\9b¸ã\81\8dè¾¼ã\81¿ã\82\92許å\8f¯してください。',
+    'path_not_writable' => 'ã\83\95ã\82¡ã\82¤ã\83«ã\83\91ã\82¹ :filePath ã\81¸ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\81§ã\81\8dã\81¾ã\81\9bã\82\93ã\81§ã\81\97ã\81\9fã\80\82ã\82µã\83¼ã\83\90ä¸\8aã\81§ã\81®æ\9b¸ã\81\8dè¾¼ã\81¿ã\81\8c許å\8f¯ã\81\95ã\82\8cã\81¦ã\81\84ã\82\8bã\81\8b確èª\8dしてください。',
     'cannot_get_image_from_url' => ':url から画像を取得できませんでした。',
     'cannot_create_thumbs' => 'このサーバはサムネイルを作成できません。GD PHP extensionがインストールされていることを確認してください。',
     'server_upload_limit' => 'このサイズの画像をアップロードすることは許可されていません。ファイルサイズを小さくし、再試行してください。',
@@ -40,8 +46,7 @@ return [
     'file_upload_timeout' => 'ファイルのアップロードがタイムアウトしました。',
 
     // Attachments
-    'attachment_page_mismatch' => '添付を更新するページが一致しません',
-    'attachment_not_found' => 'Attachment not found',
+    'attachment_not_found' => '添付ファイルが見つかりません。',
 
     // Pages
     'page_draft_autosave_fail' => '下書きの保存に失敗しました。インターネットへ接続してください。',
@@ -69,7 +74,7 @@ return [
 
     // Comments
     'comment_list' => 'An error occurred while fetching the comments.',
-    'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.',
+    'cannot_add_comment_to_draft' => '下書きにコメントは追加できません。',
     'comment_add' => 'An error occurred while adding / updating the comment.',
     'comment_delete' => 'An error occurred while deleting the comment.',
     'empty_comment' => 'Cannot add an empty comment.',
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'ページが見つかりません',
     'sorry_page_not_found' => 'ページを見つけることができませんでした。',
+    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
     'return_home' => 'ホームに戻る',
     'error_occurred' => 'エラーが発生しました',
     'app_down' => ':appNameは現在停止しています',
     'back_soon' => '回復までしばらくお待ちください。',
 
+    // API errors
+    'api_no_authorization_found' => 'No authorization token found on the request',
+    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
+    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
+    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
+    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
+    'api_user_token_expired' => '認証トークンが期限切れです。',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+
 ];
index 3531c73c74a32df33ed17ec3fa805128d70b33bf..e92a35500a03601af42759f7d6b868a6dcff7d8b 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'パスワードは6文字以上である必要があります。',
     'user' => "このEメールアドレスに一致するユーザが見つかりませんでした。",
-    'token' => 'このパスワードリセットトークンは無効です。',
+    'token' => 'The password reset token is invalid for this email address.',
     'sent' => 'パスワードリセットリンクを送信しました。',
     'reset' => 'パスワードはリセットされました。',
 
index 34eb469e96d1984ad9faadcb0d63d92f0ee43203..31ab22804ebc02b53041461f566850218a3b2efb 100644 (file)
@@ -17,7 +17,7 @@ return [
     'app_name' => 'アプリケーション名',
     'app_name_desc' => 'この名前はヘッダーやEメール内で表示されます。',
     'app_name_header' => 'ヘッダーにアプリケーション名を表示する',
-    'app_public_access' => 'Public Access',
+    'app_public_access' => 'パブリック・アクセス',
     'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
     'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
     'app_public_access_toggle' => 'Allow public access',
@@ -36,17 +36,27 @@ return [
     'app_primary_color_desc' => '16進数カラーコードで入力します。空にした場合、デフォルトの色にリセットされます。',
     'app_homepage' => 'Application Homepage',
     'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
-    'app_homepage_select' => 'Select a page',
+    'app_homepage_select' => 'ページを選択',
     'app_disable_comments' => 'コメントを無効にする',
-    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_toggle' => 'コメントを無効にする',
     'app_disable_comments_desc' => 'アプリケーション内のすべてのページのコメントを無効にします。既存のコメントは表示されません。',
 
+    // Color settings
+    'content_colors' => 'コンテンツの色',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
     // Registration Settings
     'reg_settings' => '登録設定',
     'reg_enable' => 'Enable Registration',
     'reg_enable_toggle' => 'Enable registration',
     'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
     'reg_default_role' => '新規登録時のデフォルト役割',
+    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
     'reg_email_confirmation' => 'Email Confirmation',
     'reg_email_confirmation_toggle' => 'Require email confirmation',
     'reg_confirm_email_desc' => 'ドメイン制限を有効にしている場合はEメール認証が必須となり、この項目は無視されます。',
@@ -55,14 +65,56 @@ return [
     'reg_confirm_restrict_domain_placeholder' => '制限しない',
 
     // Maintenance settings
-    'maint' => 'Maintenance',
+    'maint' => 'メンテナンス',
     'maint_image_cleanup' => 'Cleanup Images',
     'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
-    'maint_image_cleanup_run' => 'Run Cleanup',
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_run' => 'クリーンアップを実行',
     'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
     'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
     'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
+    'maint_send_test_email' => 'テストメールを送信',
+    'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
+    'maint_send_test_email_run' => 'Send test email',
+    'maint_send_test_email_success' => 'Email sent to :address',
+    'maint_send_test_email_mail_subject' => 'テストメール',
+    'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
+    'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
 
     // Role Settings
     'roles' => '役割',
@@ -86,8 +138,10 @@ return [
     'role_manage_entity_permissions' => '全てのブック, チャプター, ページに対する権限の管理',
     'role_manage_own_entity_permissions' => '自身のブック, チャプター, ページに対する権限の管理',
     'role_manage_page_templates' => 'Manage page templates',
+    'role_access_api' => 'Access system API',
     'role_manage_settings' => 'アプリケーション設定の管理',
     'role_asset' => 'アセット権限',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => '各アセットに対するデフォルトの権限を設定します。ここで設定した権限が優先されます。',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => '全て',
@@ -103,24 +157,28 @@ return [
     'user_profile' => 'ユーザプロフィール',
     'users_add_new' => 'ユーザを追加',
     'users_search' => 'ユーザ検索',
+    'users_latest_activity' => 'Latest Activity',
     'users_details' => 'User Details',
     'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
     'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
     'users_role' => 'ユーザ役割',
     'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
-    'users_password' => 'User Password',
+    'users_password' => 'ユーザー パスワード',
     'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
     'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
     'users_send_invite_option' => 'Send user invite email',
     'users_external_auth_id' => '外部認証ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
     'users_password_warning' => 'パスワードを変更したい場合のみ入力してください',
     'users_system_public' => 'このユーザはアプリケーションにアクセスする全てのゲストを表します。ログインはできませんが、自動的に割り当てられます。',
     'users_delete' => 'ユーザを削除',
     'users_delete_named' => 'ユーザ「:userName」を削除',
     'users_delete_warning' => 'ユーザ「:userName」を完全に削除します。',
     'users_delete_confirm' => '本当にこのユーザを削除してよろしいですか?',
-    'users_delete_success' => 'ユーザを削除しました',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'ユーザ編集',
     'users_edit_profile' => 'プロフィール編集',
     'users_edit_success' => 'ユーザを更新しました',
@@ -134,6 +192,32 @@ return [
     'users_social_disconnect' => 'アカウントを接続解除',
     'users_social_connected' => '「:socialAccount」がプロフィールに接続されました。',
     'users_social_disconnected' => '「:socialAccount」がプロフィールから接続解除されました。',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
+    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 231bdfa0b8f34f6ac7098f2ba1304d4ce465e6c5..61057f17ee00d6e3aedfddaf453af17d99f0f5a3 100644 (file)
@@ -90,6 +90,7 @@ return [
     'required_without'     => ':valuesが設定されていない場合、:attributeは必須です。',
     'required_without_all' => ':valuesが設定されていない場合、:attributeは必須です。',
     'same'                 => ':attributeと:otherは一致している必要があります。',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
         'numeric' => ':attributeは:sizeである必要があります。',
         'file'    => ':attributeは:sizeキロバイトである必要があります。',
index 5baeed3c8e456ded7d9eb7bb5a1bd941adf6cd63..fda7e4ef35ebb21aeca7c763841254356f5c75c5 100644 (file)
@@ -6,43 +6,44 @@
 return [
 
     // Pages
-    'page_create'                 => '페이지 생성',
-    'page_create_notification'    => '페이지를 만들었습니다.',
-    'page_update'                 => '페이지 업데이트',
-    'page_update_notification'    => '페이지를 업데이트하였습니다.',
-    'page_delete'                 => '페이지 삭제',
-    'page_delete_notification'    => '페이지를 삭제하였습니다.',
-    'page_restore'                => '페이지 복원',
-    'page_restore_notification'   => '페이지를 복원하였습니다.',
-    'page_move'                   => '페이지 이동',
+    'page_create'                 => '문서 만들기',
+    'page_create_notification'    => '문서 만듦',
+    'page_update'                 => '문서 수정',
+    'page_update_notification'    => '문서 수정함',
+    'page_delete'                 => '삭제 된 페이지',
+    'page_delete_notification'    => '문서 지움',
+    'page_restore'                => '문서 복원',
+    'page_restore_notification'   => '문서 복원함',
+    'page_move'                   => '문서 이동됨',
 
     // Chapters
     'chapter_create'              => '챕터 만들기',
-    'chapter_create_notification' => '챕터를 만들었습니다.',
-    'chapter_update'              => '챕터 업데이트',
-    'chapter_update_notification' => '챕터를 업데이트하였습니다.',
-    'chapter_delete'              => 'ì±\95í\84° ì\82­ì \9c',
-    'chapter_delete_notification' => 'ì±\94í\84°ë¥¼ ì\82­ì \9cí\95\98ì\98\80ì\8aµë\8b\88ë\8b¤.',
-    'chapter_move'                => '챕터 이동',
+    'chapter_create_notification' => '챕터 만듦',
+    'chapter_update'              => '챕터 바꾸기',
+    'chapter_update_notification' => '챕터 바꿈',
+    'chapter_delete'              => 'ì\82­ì \9cë\90\9c ì±\95í\84°',
+    'chapter_delete_notification' => 'ì±\95í\84° ì§\80ì\9b\80',
+    'chapter_move'                => '챕터 이동',
 
     // Books
-    'book_create'                 => '책 만들기',
-    'book_create_notification'    => 'ì±\85ì\9d\84 ë§\8cë\93¤ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'book_update'                 => '책 업데이트',
-    'book_update_notification'    => 'ì±\85ì\9d\84 ì\97\85ë\8d°ì\9d´í\8a¸í\95\98ì\98\80ì\8aµë\8b\88ë\8b¤.',
-    'book_delete'                 => 'ì±\85 ì\82­ì \9c',
-    'book_delete_notification'    => 'ì±\85ì\9d\84 ì\82­ì \9cí\95\98ì\98\80ì\8aµë\8b\88ë\8b¤.',
-    'book_sort'                   => '책 정렬',
-    'book_sort_notification'      => 'ì±\85ì\9d\84 ì \95ë ¬í\95\98ì\98\80ì\8aµë\8b\88ë\8b¤.',
+    'book_create'                 => '책 만들기',
+    'book_create_notification'    => 'ì±\85ì\9e\90 ë§\8cë\93¦',
+    'book_update'                 => '책자 바꾸기',
+    'book_update_notification'    => 'ì±\85ì\9e\90 ë°\94ê¿\88',
+    'book_delete'                 => 'ì\82­ì \9c ë\90\9c ì±\85ì\9e\90',
+    'book_delete_notification'    => 'ì±\85ì\9e\90 ì§\80ì\9b\80',
+    'book_sort'                   => '책 정렬',
+    'book_sort_notification'      => 'ì±\85ì\9e\90 ì \95ë ¬í\95¨',
 
     // Bookshelves
-    'bookshelf_create'            => 'created Bookshelf',
-    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
-    'bookshelf_update'                 => 'updated bookshelf',
-    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
-    'bookshelf_delete'                 => 'deleted bookshelf',
-    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+    'bookshelf_create'            => '서가 만들기',
+    'bookshelf_create_notification'    => '서가 만듦',
+    'bookshelf_update'                 => '서가 바꾸기',
+    'bookshelf_update_notification'    => '서가 바꿈',
+    'bookshelf_delete'                 => '삭제된 서가',
+    'bookshelf_delete_notification'    => '서가 지움',
 
     // Other
-    'commented_on'                => 'commented on',
+    'commented_on'                => '댓글 쓰기',
+    'permissions_update'          => 'updated permissions',
 ];
index 86b55dc2c860ade5a6c87983efc36779d0dc2a77..0bff8724c955ed3f840e29c17cb02108e0d3bd31 100644 (file)
@@ -6,72 +6,72 @@
  */
 return [
 
-    'failed' => 'ì\9d´ ì\9e\90격 ì¦\9dëª\85ì\9d\80 ë\93±ë¡\9dë\90\98ì\96´ ì\9e\88지 않습니다.',
-    'throttle' => '로그인 시도 횟수 제한을 초과했습니다. :seconds초 후에 다시 시도하십시오.',
+    'failed' => 'ì\9e\90격 ì¦\9dëª\85ì\9d´ ê¸°ë¡\9dê³¼ ì\9d¼ì¹\98í\95\98지 않습니다.',
+    'throttle' => '로그인 시도가 너무 많습니다. :seconds초 후에 다시 시도하세요.',
 
     // Login & Register
-    'sign_up' => '신규등록',
+    'sign_up' => '가입',
     'log_in' => '로그인',
-    'log_in_with' => ':socialDriver 로그인',
-    'sign_up_with' => ':socialDriver로 등록',
+    'log_in_with' => ':socialDriver 로그인',
+    'sign_up_with' => ':socialDriver로 가입',
     'logout' => '로그아웃',
 
     'name' => '이름',
-    'username' => '사용자이름',
-    'email' => '이메일',
+    'username' => '사용자 이름',
+    'email' => '메일 주소',
     'password' => '비밀번호',
-    'password_confirm' => '비밀번호 (확인)',
-    'password_hint' => '7자 이상이어야 합니다.',
-    'forgot_password' => 'ë¹\84ë°\80ë²\88í\98¸ë¥¼ ì\9e\8aì\9c¼ì\85¨ì\8aµë\8b\88ê¹\8c?',
-    'remember_me' => '자동로그인',
-    'ldap_email_hint' => '이 계정에서 사용하는 이메일을 입력해 주세요.',
-    'create_account' => 'ê³\84ì \95 ë§\8cë\93¤ê¸°',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
-    'social_login' => 'SNS로그인',
-    'social_registration' => 'SNS등록',
-    'social_registration_text' => '다른 서비스를 사용하여 등록하고 로그인.',
+    'password_confirm' => '비밀번호 확인',
+    'password_hint' => '일곱 글자를 넘어야 합니다.',
+    'forgot_password' => 'ë¹\84ë°\80ë²\88í\98¸ë¥¼ ì\9e\8aì\97\88ë\82\98ì\9a\94?',
+    'remember_me' => '로그인 유지',
+    'ldap_email_hint' => '이 계정에 대한 메일 주소를 입력하세요.',
+    'create_account' => 'ê°\80ì\9e\85',
+    'already_have_account' => '계정이 있나요?',
+    'dont_have_account' => '계정이 없나요?',
+    'social_login' => '소셜 로그인',
+    'social_registration' => '소셜 가입',
+    'social_registration_text' => '소셜 계정으로 가입하고 로그인합니다.',
 
-    'register_thanks' => '등록이완료되었습니다!',
-    'register_confirm' => 'ë\8b¹ì\8b ì\9d\98 ì\9d´ë©\94ì\9d¼ì\9d\84 í\99\95ì\9d¸í\95\98ì\8b í\9b\84 í\99\95ì\9d¸ ë²\84í\8a¼ì\9d\84 ë\88\8cë\9f¬ :appNameì\97\90 ì\95¡ì\84¸ì\8a¤í\95\98ì\8b­ì\8b\9cì\98¤.',
-    'registrations_disabled' => '현재 등록이 불가합니다.',
-    'registration_email_domain_invalid' => '해당 이메일 도메인으로 액세스 할 수 없습니다.',
-    'register_success' => '등록을 완료하고 로그인 할 수 있습니다!',
+    'register_thanks' => '가입해 주셔서 감사합니다!',
+    'register_confirm' => 'ë©\94ì\9d¼ì\9d\84 í\99\95ì\9d¸í\95\9c í\9b\84 ë²\84í\8a¼ì\9d\84 ë\88\8cë\9f¬ :appNameì\97\90 ì \91ê·¼í\95\98ì\84¸ì\9a\94.',
+    'registrations_disabled' => '가입할 수 없습니다.',
+    'registration_email_domain_invalid' => '이 메일 주소로는 이 사이트에 접근할 수 없습니다.',
+    'register_success' => '가입했습니다! 이제 로그인할 수 있습니다.',
 
 
     // Password Reset
-    'reset_password' => '암호 재설정',
-    'reset_password_send_instructions' => 'ë\8b¤ì\9d\8cì\97\90 ë©\94ì\9d¼ ì£¼ì\86\8c를 ì\9e\85ë ¥í\95\98ë©´ ë¹\84ë°\80ë²\88í\98¸ ì\9e¬ì\84¤ì \95 ë§\81í\81¬ê°\80 í\8f¬í\95¨ ë\90\9c ì\9d´ë©\94ì\9d¼ì\9d´ ì \84ì\86¡ë\90©니다.',
-    'reset_password_send_button' => '재설정 링크 보내기',
-    'reset_password_sent_success' => ':email로 재설정 링크를 보냈습니다.',
-    'reset_password_success' => '비밀번호가 재설정되었습니다.',
-    'email_reset_subject' => ':appName 암호를 재설정',
-    'email_reset_text' => '귀하의 계정에 대한 비밀번호 재설정 요청을 받았기 때문에 본 이메일이 발송되었습니다.',
-    'email_reset_not_requested' => 'ì\95\94í\98¸ ì\9e¬ì\84¤ì \95ì\9d\84 ì\9a\94ì²­í\95\98ì§\80 ì\95\8aì\9d\80 ê²½ì\9a° ë\8d\94 ì\9d´ì\83\81ì\9d\98 ì¡°ì¹\98ë\8a\94 í\95\84ì\9a\94í\95\98ì§\80 ì\95\8a습니다.',
+    'reset_password' => '비밀번호 바꾸기',
+    'reset_password_send_instructions' => 'ë©\94ì\9d¼ ì£¼ì\86\8c를 ì\9e\85ë ¥í\95\98ì\84¸ì\9a\94. ì\9d´ ì£¼ì\86\8cë¡\9c í\95´ë\8b¹ ê³¼ì \95ì\9d\84 ì\9c\84í\95\9c ë§\81í\81¬ë¥¼ ë³´ë\82¼ ê²\83ì\9e\85니다.',
+    'reset_password_send_button' => '메일 보내기',
+    'reset_password_sent' => '시스템에서 이메일 주소가 발견되면, 암호 재설정 링크가 :email로 전송된다.',
+    'reset_password_success' => '비밀번호를 바꿨습니다.',
+    'email_reset_subject' => ':appName 비밀번호 바꾸기',
+    'email_reset_text' => '비밀번호를 바꿉니다.',
+    'email_reset_not_requested' => 'ì\9b\90í\95\98ì§\80 ì\95\8aë\8a\94ë\8b¤ë©´ ì\9d´ ê³¼ì \95ì\9d\80 í\95\84ì\9a\94 ì\97\86습니다.',
 
 
     // Email Confirmation
-    'email_confirm_subject' => ':appName의 이메일 주소 확인',
-    'email_confirm_greeting' => ':appName에 가입 ​​해 주셔서 감사합니다!',
-    'email_confirm_text' => 'ë\8b¤ì\9d\8c ë²\84í\8a¼ì\9d\84 ë\88\8cë\9f¬ ì\9d´ë©\94ì\9d¼ ì£¼ì\86\8c를 í\99\95ì\9d¸í\95\98ì\8b­ì\8b\9cì\98¤',
-    'email_confirm_action' => '이메일 주소를 확인',
-    'email_confirm_send_error' => 'Eë©\94ì\9d¼ í\99\95ì\9d¸ì\9d´ í\95\84ì\9a\94í\95\98ì§\80ë§\8c ì\8b\9cì\8a¤í\85\9cì\97\90ì\84\9c ë©\94ì\9d¼ì\9d\84 ë³´ë\82¼ ì\88\98 ì\97\86ì\8aµë\8b\88ë\8b¤. ê´\80리ì\9e\90ì\97\90ê²\8c ë¬¸ì\9d\98í\95\98ì\97¬ ë©\94ì\9d¼ì\9d´ ì \9cë\8c\80ë¡\9c ì\84¤ì \95ë\90\98ì\96´ ì\9e\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95\98ì\8b­ì\8b\9cì\98¤.',
-    'email_confirm_success' => '메일 주소가 확인되었습니다.',
-    'email_confirm_resent' => '확인 메일을 다시 보냈습니다. 받은 편지함을 확인하십시오.',
+    'email_confirm_subject' => ':appName 메일 인증',
+    'email_confirm_greeting' => ':appName로 가입해 주셔서 감사합니다!',
+    'email_confirm_text' => 'ë\8b¤ì\9d\8c ë²\84í\8a¼ì\9d\84 ë\88\8cë\9f¬ ì\9d¸ì¦\9dí\95\98ì\84¸ì\9a\94:',
+    'email_confirm_action' => '메일 인증',
+    'email_confirm_send_error' => 'ë©\94ì\9d¼ì\9d\84 ë³´ë\82¼ ì\88\98 ì\97\86ì\97\88ì\8aµë\8b\88ë\8b¤.',
+    'email_confirm_success' => '인증했습니다!',
+    'email_confirm_resent' => '다시 보냈습니다. 메일함을 확인하세요.',
 
-    'email_not_confirmed' => '메일 주소가 확인되지 않습니다',
-    'email_not_confirmed_text' => '메일 주소 확인이 완료되지 않습니다.',
-    'email_not_confirmed_click_link' => 'ë\93±ë¡\9dì\8b\9c ë°\9bì\9d\80 ì\9d´ë©\94ì\9d¼ì\9d\84 í\99\95ì\9d¸í\95\98ê³  í\99\95ì\9d¸ ë§\81í\81¬ë¥¼ í\81´ë¦­í\95\98ì\8b­ì\8b\9cì\98¤.',
-    'email_not_confirmed_resend' => '메일이 없으면 아래 양식을 통해 다시 제출하십시오.',
-    'email_not_confirmed_resend_button' => '확인 메일을 다시 전송',
+    'email_not_confirmed' => '인증하지 않았습니다.',
+    'email_not_confirmed_text' => '인증을 완료하지 않았습니다.',
+    'email_not_confirmed_click_link' => 'ë©\94ì\9d¼ì\9d\84 í\99\95ì\9d¸í\95\98ê³  ì\9d¸ì¦\9d ë§\81í\81¬ë¥¼ í\81´ë¦­í\95\98ì\84¸ì\9a\94.',
+    'email_not_confirmed_resend' => '메일 주소가 없다면 다음을 입력하세요.',
+    'email_not_confirmed_resend_button' => '다시 보내기',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => ':appName에서 권유를 받았습니다.',
+    'user_invite_email_greeting' => ':appName에서 가입한 기록이 있습니다.',
+    'user_invite_email_text' => '다음 버튼을 눌러 확인하세요:',
+    'user_invite_email_action' => '비밀번호 설정',
+    'user_invite_page_welcome' => ':appName에 오신 것을 환영합니다!',
+    'user_invite_page_text' => ':appName에 로그인할 때 입력할 비밀번호를 설정하세요.',
+    'user_invite_page_confirm_button' => '비밀번호 확인',
+    'user_invite_success' => '암호가 설정되었고, 이제 :appName에 접근할 수 있습니다.'
 ];
\ No newline at end of file
index 31d3e50241934331925ffcb985bf8639842f36a7..43156260224a02beaf79a5c12bf81df0540189b1 100644 (file)
@@ -9,68 +9,72 @@ return [
     'confirm' => '확인',
     'back' => '뒤로',
     'save' => '저장',
-    'continue' => '계속하기',
+    'continue' => '계속',
     'select' => '선택',
-    'toggle_all' => 'Toggle All',
-    'more' => '더보기',
+    'toggle_all' => '모두 보기',
+    'more' => '더 보기',
 
     // Form Labels
     'name' => '이름',
     'description' => '설명',
-    'role' => '역할',
+    'role' => '권한',
     'cover_image' => '대표 이미지',
-    'cover_image_description' => '이 이미지는 약 440x250px 정도의 크기여야 합니다.',
+    'cover_image_description' => '이미지 규격은 440x250px 내외입니다.',
     
     // Actions
-    'actions' => 'Actions',
-    'view' => 'ë·°',
-    'view_all' => 'View All',
-    'create' => '생성',
-    'update' => '업데이트',
+    'actions' => '활동',
+    'view' => '보기',
+    'view_all' => '모두 보기',
+    'create' => '만들기',
+    'update' => '바꾸기',
     'edit' => '수정',
     'sort' => '정렬',
     'move' => '이동',
     'copy' => '복사',
-    'reply' => 'Reply',
+    'reply' => '답글',
     'delete' => '삭제',
+    'delete_confirm' => '삭제 요청 확인',
     'search' => '검색',
-    'search_clear' => '검색기록 삭제',
-    'reset' => '초기화',
+    'search_clear' => '검색 지우기',
+    'reset' => '리셋',
     'remove' => '제거',
     'add' => '추가',
+    'fullscreen' => '전체화면',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
-    'sort_name' => 'Name',
-    'sort_created_at' => 'Created Date',
-    'sort_updated_at' => 'Updated Date',
+    'sort_options' => '정렬 기준',
+    'sort_direction_toggle' => '순서 반전',
+    'sort_ascending' => '오름차 순서',
+    'sort_descending' => '내림차 순서',
+    'sort_name' => '제목',
+    'sort_created_at' => '만든 날짜',
+    'sort_updated_at' => '수정한 날짜',
 
     // Misc
-    'deleted_user' => '삭제ë\90\9c ì\82¬ì\9a©ì\9e\90',
-    'no_activity' => '활동내역이 없음',
-    'no_items' => '사용가능한 항목이 없음',
-    'back_to_top' => '맨위로',
-    'toggle_details' => '상세 토글',
-    'toggle_thumbnails' => 'ì\8d¸ë\82´ì\9d¼ í\86 ê¸\80',
-    'details' => 'ì\83\81ì\84¸',
-    'grid_view' => '그리ë\93\9c ë·°',
-    'list_view' => '리ì\8a¤í\8a¸ë·°',
-    'default' => '기본설정',
-    'breadcrumb' => 'Breadcrumb',
+    'deleted_user' => '삭제í\95\9c ì\82¬ì\9a©ì\9e\90',
+    'no_activity' => '활동 없음',
+    'no_items' => '항목 없음',
+    'back_to_top' => '맨 위로',
+    'toggle_details' => '내용 보기',
+    'toggle_thumbnails' => 'ì\84¬ë\84¤ì\9d¼ ë³´ê¸°',
+    'details' => 'ì \95ë³´',
+    'grid_view' => '격ì\9e\90 ë³´ê¸°',
+    'list_view' => '목ë¡\9d ë³´ê¸°',
+    'default' => '기본 설정',
+    'breadcrumb' => '탐색 경로',
 
     // Header
-    'profile_menu' => 'Profile Menu',
-    'view_profile' => '프로파일 보기',
-    'edit_profile' => '프로파일 수정하기',
+    'profile_menu' => '프로필',
+    'view_profile' => '프로필 보기',
+    'edit_profile' => '프로필 바꾸기',
+    'dark_mode' => '다크 모드',
+    'light_mode' => '라이트 모드',
 
     // Layout tabs
-    'tab_info' => 'Info',
-    'tab_content' => 'Content',
+    'tab_info' => '정보',
+    'tab_content' => '내용',
 
     // Email Content
-    'email_action_help' => '":actionText"버튼을 클릭하는 데 문제가 있으면 아래 URL을 복사하여 웹 브라우저에 붙여 넣으십시오:',
-    'email_rights' => 'All rights reserved',
+    'email_action_help' => ':actionText를 클릭할 수 없을 때는 웹 브라우저에서 다음 링크로 접속할 수 있습니다.',
+    'email_rights' => '모든 권리 소유',
 ];
index fd7839e2f2c38161e35d68982541bfab4a0d742c..9155b9490ee6f531db61753c5a9d7a8363c80926 100644 (file)
@@ -6,28 +6,29 @@ return [
 
     // Image Manager
     'image_select' => '이미지 선택',
-    'image_all' => '전체',
-    'image_all_title' => '모든 이미지 보기',
-    'image_book_title' => 'ì\9d´ ì±\85ì\97\90 ì\97\85ë¡\9cë\93\9cë\90\9c ì\9d´ë¯¸ì§\80 ë³´ê¸°',
-    'image_page_title' => '이 페이지에 업로드된 이미지 보기',
-    'image_search_hint' => '이미지 이름으로 검색',
-    'image_uploaded' => ':uploadedDate에 업로드됨',
-    'image_load_more' => 'ë\8d\94 ë\88ë\9f¬ì\98¤기',
+    'image_all' => '모든 이미지',
+    'image_all_title' => '모든 이미지',
+    'image_book_title' => 'ì\9d´ ì±\85ì\9e\90ì\97\90ì\84\9c ì\93°ê³  ì\9e\88ë\8a\94 ì\9d´ë¯¸ì§\80',
+    'image_page_title' => '이 문서에서 쓰고 있는 이미지',
+    'image_search_hint' => '이미지 이름 검색',
+    'image_uploaded' => '올림 :uploadedDate',
+    'image_load_more' => 'ë\8d\94 ë¡\9cë\93\9cí\95\98기',
     'image_image_name' => '이미지 이름',
-    'image_delete_used' => '이 이미지는 다음 페이지에서 이용되고 있습니다.',
-    'image_delete_confirm' => '삭제해도 괜찮으시면 다시 삭제 버튼을 눌러주세요.',
-    'image_select_image' => '선택',
-    'image_dropzone' => 'ì\97\85ë¡\9cë\93\9c를 ì\9c\84í\95´ ì\9d´ë¯¸ì§\80를 ê°\80ì ¸ì\99\80 ë\86\93ê±°ë\82\98 í\81´ë¦­í\95\98ì\84¸ì\9a\94',
-    'images_deleted' => '이미지 삭제',
-    'image_preview' => '이미지 미리보기',
-    'image_upload_success' => 'ì\9d´ë¯¸ì§\80 ì\97\85ë¡\9cë\93\9cê°\80 ì\99\84ë£\8cë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'image_update_success' => 'ì\9d´ë¯¸ì§\80 ì\83\81ì\84¸ì \95ë³´ê°\80 ì\97\85ë\8d°ì\9d´í\8a¸ ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'image_delete_success' => '이미지가 삭제되었습니다.',
-    'image_upload_remove' => 'ì \9cê±°',
+    'image_delete_used' => '이 이미지는 다음 문서들이 쓰고 있습니다.',
+    'image_delete_confirm_text' => '이 이미지를 정말 삭제하시겠습니까?',
+    'image_select_image' => 'ì\9d´ë¯¸ì§\80 ì\84 í\83\9d',
+    'image_dropzone' => 'ì\97¬ê¸°ì\97\90 ì\9d´ë¯¸ì§\80를 ë\93\9cë¡­í\95\98ê±°ë\82\98 ì\97¬ê¸°ë¥¼ í\81´ë¦­í\95\98ì\84¸ì\9a\94. ì\9d´ë¯¸ì§\80를 ì\98¬ë¦´ ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤.',
+    'images_deleted' => '이미지 삭제',
+    'image_preview' => '이미지 미리 보기',
+    'image_upload_success' => 'ì\9d´ë¯¸ì§\80 ì\98¬ë¦¼',
+    'image_update_success' => 'ì\9d´ë¯¸ì§\80 ì \95ë³´ ì\88\98ì \95í\95¨',
+    'image_delete_success' => '이미지 삭제함',
+    'image_upload_remove' => 'ì\82­ì \9c',
 
     // Code Editor
     'code_editor' => '코드 수정',
-    'code_language' => '코드 언어',
-    'code_content' => '코드 내용',
-    'code_save' => '코드 저장',
+    'code_language' => '언어',
+    'code_content' => '내용',
+    'code_session_history' => '세션 기록',
+    'code_save' => '저장',
 ];
index 4e2692bc57c00ae1b6a504ba9fe642a15ff813f4..71815cdd7c3ab544738f8a33fb904eadf2a8786a 100644 (file)
 return [
 
     // Shared
-    'recently_created' => '최근작성',
-    'recently_created_pages' => '최근 작성된 페이지',
-    'recently_updated_pages' => '최근 업데이트된 페이지',
-    'recently_created_chapters' => '최근 만들어진 챕터',
-    'recently_created_books' => '최근 만들어진 책',
-    'recently_created_shelves' => 'Recently Created Shelves',
-    'recently_update' => '최근 작성',
-    'recently_viewed' => '검색 기록',
-    'recent_activity' => '최근 활동',
-    'create_now' => '지금 만들기',
-    'revisions' => '변경이력',
-    'meta_revision' => '수정 #:revisionCount',
-    'meta_created' => '작성: :timeLength',
-    'meta_created_name' => '작성: :timeLength by :user',
-    'meta_updated' => '업데이트 :timeLength',
-    'meta_updated_name' => '업데이트 :timeLength by :user',
-    'entity_select' => '엔티티선택',
+    'recently_created' => '최근에 수정함',
+    'recently_created_pages' => '최근에 만든 문서',
+    'recently_updated_pages' => '최근에 수정한 문서',
+    'recently_created_chapters' => '최근에 만든 챕터',
+    'recently_created_books' => '최근에 만든 책자',
+    'recently_created_shelves' => '최근에 만든 서가',
+    'recently_update' => '최근에 수정함',
+    'recently_viewed' => '최근에 읽음',
+    'recent_activity' => '최근에 활동함',
+    'create_now' => '바로 만들기',
+    'revisions' => '수정본',
+    'meta_revision' => '판본 #:revisionCount',
+    'meta_created' => '만듦 :timeLength',
+    'meta_created_name' => '만듦 :timeLength, :user',
+    'meta_updated' => '수정함 :timeLength',
+    'meta_updated_name' => '수정함 :timeLength, :user',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => '항목 선택',
     'images' => '이미지',
-    'my_recent_drafts' => '내 최근 초안',
-    'my_recently_viewed' => '검색 기록',
-    'no_pages_viewed' => '조회한 페이지가 없습니다.',
-    'no_pages_recently_created' => '최근 만들어진 페이지가 없습니다',
-    'no_pages_recently_updated' => '최근 업데이트된 페이지가없습니다',
-    'export' => '내보내기',
-    'export_html' => 'html 내보내기',
+    'my_recent_drafts' => '내 최근의 초안 문서',
+    'my_recently_viewed' => '내가 읽은 문서',
+    'no_pages_viewed' => '문서 없음',
+    'no_pages_recently_created' => '문서 없음',
+    'no_pages_recently_updated' => '문서 없음',
+    'export' => '파일로 받기',
+    'export_html' => 'Contained Web(.html) 파일',
     'export_pdf' => 'PDF 파일',
-    'export_text' => '일반 텍스트 파일',
+    'export_text' => 'Plain Text(.txt) 파일',
 
     // Permissions and restrictions
     'permissions' => '권한',
-    'permissions_intro' => 'ì\9d´ ì\84¤ì \95ì\9d\80 ê°\81 ì\82¬ì\9a©ì\9e\90ì\9d\98 ì\97­í\95 ë³´ë\8b¤ ì\9a°ì\84 í\95\98ì\97¬ ì \81ì\9a©ë\90©ë\8b\88ë\8b¤.',
-    'permissions_enable' => '커ì\8a¤í\85\80 ê¶\8cí\95\9c í\99\9cì\84±í\99\94',
+    'permissions_intro' => 'í\95\9cë²\88 í\97\88ì\9a©í\95\98ë©´ ì\9d´ ì\84¤ì \95ì\9d\80 ì\82¬ì\9a©ì\9e\90 ê¶\8cí\95\9cì\97\90 ì\9a°ì\84 í\95©ë\8b\88ë\8b¤.',
+    'permissions_enable' => 'ì\84¤ì \95 í\97\88ì\9a©',
     'permissions_save' => '권한 저장',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => '검색 결과',
-    'search_total_results_found' => ':count 개의 결과를 찾았습니다.|총 :count 개의 결과를 찾았습니다.',
-    'search_clear' => '검색기록 초기화',
-    'search_no_pages' => '검색결과가 없습니다.',
-    'search_for_term' => ':term 을(를) 검색합니다.',
-    'search_more' => '결과 더보기',
-    'search_filters' => '검색 필터',
-    'search_content_type' => '컨텐츠 타입',
-    'search_exact_matches' => '정확히 일치합니다.',
-    'search_tags' => '테그 검색',
-    'search_options' => '설정',
-    'search_viewed_by_me' => '내가본것',
-    'search_not_viewed_by_me' => '내가안본것',
-    'search_permissions_set' => '권한 설정',
+    'search_total_results_found' => ':count개|총 :count개',
+    'search_clear' => '검색 지우기',
+    'search_no_pages' => '결과 없음',
+    'search_for_term' => ':term 검색',
+    'search_more' => '더 많은 결과',
+    'search_advanced' => '고급 검색',
+    'search_terms' => '용어 검색',
+    'search_content_type' => '형식',
+    'search_exact_matches' => '정확히 일치',
+    'search_tags' => '꼬리표 일치',
+    'search_options' => '선택',
+    'search_viewed_by_me' => '내가 읽음',
+    'search_not_viewed_by_me' => '내가 읽지 않음',
+    'search_permissions_set' => '권한 설정함',
     'search_created_by_me' => '내가 만듦',
-    'search_updated_by_me' => 'ë\82´ê°\80 ì\97\85ë\8d°ì\9d´í\8a¸함',
-    'search_date_options' => '날짜 설정',
-    'search_updated_before' => 'ì\9d´ì \84ì\97\90 ì\97\85ë\8d°ì\9d´í\8a¸함',
-    'search_updated_after' => 'ì\9d´í\9b\84ì\97\90 ì\97\85ë\8d°ì\9d´í\8a¸함',
-    'search_created_before' => '이전에 생성함',
-    'search_created_after' => '이후에 생성함',
+    'search_updated_by_me' => 'ë\82´ê°\80 ì\88\98ì \95함',
+    'search_date_options' => '날짜',
+    'search_updated_before' => 'ì\9d´ì \84ì\97\90 ì\88\98ì \95함',
+    'search_updated_after' => 'ì\9d´í\9b\84ì\97\90 ì\88\98ì \95함',
+    'search_created_before' => '이전에 만듦',
+    'search_created_after' => '이후에 만듦',
     'search_set_date' => '날짜 설정',
-    'search_update' => '검색 업데이트',
+    'search_update' => '검색',
 
     // Shelves
-    'shelf' => 'ì±\85ê½\83ì\9d´',
-    'shelves' => 'ì±\85ê½\83ì\9d´',
-    'x_shelves' => ':count Shelf|:count Shelves',
-    'shelves_long' => 'ì±\85ê½\83ì\9d´',
-    'shelves_empty' => '책꽃이가 만들어지지 않았습니다.',
-    'shelves_create' => 'ì\83\88ì±\85ê½\83ì\9d´ 만들기',
-    'shelves_popular' => '인기있는 책꽃이',
-    'shelves_new' => 'ì\83\88ë¡\9cì\9a´ ì±\85ê½\83ì\9d´',
-    'shelves_new_action' => 'New Shelf',
-    'shelves_popular_empty' => '인기있는 책꽃이가 여기에 나타납니다.',
-    'shelves_new_empty' => '가장 최근에 만들어진 책꽃이가 여기에 나타납니다.',
-    'shelves_save' => 'ì±\85ê½\83ì\9d´ ì \80ì\9e¥',
-    'shelves_books' => 'ì\9d´ ì±\85ê½\83ì\9d´ì\97\90 ì\9e\88ë\8a\94 ì±\85',
-    'shelves_add_books' => 'ì\9d´ ì±\85ê½\83ì\9d´ì\97\90 ì±\85ì\9d\84 ì¶\94ê°\80í\95©ë\8b\88ë\8b¤',
-    'shelves_drag_books' => 'ì\9d´ ì±\85ê½\83ì\9d´ì\97\90 ì±\85ì\9d\84 ì¶\94ê°\80í\95\98기 ì\9c\84í\95´ ë\81\8cì\96´ë\86\93ì\9c¼세요.',
-    'shelves_empty_contents' => '이책꽃이엔 등록된 책이 없습니다.',
-    'shelves_edit_and_assign' => 'ì±\85ì\9d\84 ë\93±ë¡\9dí\95\98기 ì\9c\84í\95´ ì±\85ê½\83ì\9d´ë¥¼ ì\88\98ì \95',
-    'shelves_edit_named' => ':name 책꽃이 수정',
-    'shelves_edit' => 'ì±\85ê½\83ì\9d´ ì\88\98ì \95',
-    'shelves_delete' => 'ì±\85ê½\83ì\9d´ ì\82­ì \9c',
-    'shelves_delete_named' => ':name ì±\85ê½\83ì\9d´ë¥¼ ì\82­ì \9cí\95©ë\8b\88ë\8b¤.',
-    'shelves_delete_explain' => "':name' 이름의 책꽃이가 삭제됩니다. 포함된 책은 삭제되지 않습니다.",
-    'shelves_delete_confirmation' => 'ì\9d´ ì±\85ê½\83ì\9d´ë¥¼ ì\82­ì \9cí\95\98ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'shelves_permissions' => 'ì±\85ê½\83ì\9d´ 권한',
-    'shelves_permissions_updated' => 'ì±\85ê½\83ì\9d´ ê¶\8cí\95\9cì\9d´ ì\97\85ë\8d°ì\9d´í\8a¸ ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'shelves_permissions_active' => 'ì±\85ê½\83ì\9d´ ê¶\8cí\95\9c í\99\9cì\84±í\99\94',
-    'shelves_copy_permissions_to_books' => '책에 권한을 복사합니다.',
-    'shelves_copy_permissions' => '권한 복사',
-    'shelves_copy_permissions_explain' => 'ì\9d´ ì±\85ê½\82ì\9d´ì\9d\98 í\98\84ì\9e¬ ê¶\8cí\95\9c ì\84¤ì \95ì\9d´ ì\95\88ì\97\90 í\8f¬í\95¨ ë\90\9c ëª¨ë\93  ì±\85ì\97\90 ì \81ì\9a©ë\90©ë\8b\88ë\8b¤. í\99\9cì\84±í\99\94í\95\98기 ì \84ì\97\90ì\9d´ ì±\85ê½\82ì\9d´ì\9d\98 ì\82¬ì\9a© ê¶\8cí\95\9cì\9d´ ë³\80ê²½ë\90\98ì\97\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95\98ì\8b­ì\8b\9cì\98¤.',
-    'shelves_copy_permission_success' => '책꽃이의 권한이 :count 개의 책에 복사되었습니다.',
+    'shelf' => 'ì\84\9cê°\80',
+    'shelves' => 'ì\84\9cê°\80',
+    'x_shelves' => '서가 :count개|총 :count개',
+    'shelves_long' => 'ì\84\9cê°\80',
+    'shelves_empty' => '만든 서가가 없습니다.',
+    'shelves_create' => 'ì\84\9cê°\80 만들기',
+    'shelves_popular' => '많이 읽은 서가',
+    'shelves_new' => 'ì\83\88ë¡\9cì\9a´ ì\84\9cê°\80',
+    'shelves_new_action' => '새로운 서가',
+    'shelves_popular_empty' => '많이 읽은 서가 목록',
+    'shelves_new_empty' => '새로운 서가 목록',
+    'shelves_save' => '저장',
+    'shelves_books' => 'ì\9d´ ì\84\9cê°\80ì\97\90 ì\9e\88ë\8a\94 ì±\85ì\9e\90ë\93¤',
+    'shelves_add_books' => 'ì\9d´ ì\84\9cê°\80ì\97\90 ì±\85ì\9e\90 ì¶\94ê°\80',
+    'shelves_drag_books' => 'ì\97¬ê¸°ì\97\90 ì±\85ì\9e\90를 ë\93\9cë¡­í\95\98세요.',
+    'shelves_empty_contents' => '이 서가에 책자가 없습니다.',
+    'shelves_edit_and_assign' => 'ì\84\9cê°\80 ë°\94꾸기ë¡\9c ì±\85ì\9e\90를 ì¶\94ê°\80í\95\98ì\84¸ì\9a\94.',
+    'shelves_edit_named' => ':name 바꾸기',
+    'shelves_edit' => 'ì\84\9cê°\80 ë°\94꾸기',
+    'shelves_delete' => 'ì\84\9cê°\80 ì\82­ì \9cí\95\98기',
+    'shelves_delete_named' => ':name ì\82­ì \9cí\95\98기',
+    'shelves_delete_explain' => ":name을 지웁니다. 책자는 지우지 않습니다.",
+    'shelves_delete_confirmation' => 'ì\9d´ ì\84\9cê°\80를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'shelves_permissions' => 'ì\84\9cê°\80 권한',
+    'shelves_permissions_updated' => 'ì\84\9cê°\80 ê¶\8cí\95\9c ë°\94ê¿\88',
+    'shelves_permissions_active' => 'ì\84\9cê°\80 ê¶\8cí\95\9c í\97\88ì\9a©í\95¨',
+    'shelves_copy_permissions_to_books' => '권한 맞춤',
+    'shelves_copy_permissions' => '실행',
+    'shelves_copy_permissions_explain' => 'ì\84\9cê°\80ì\9d\98 ëª¨ë\93  ì±\85ì\9e\90ì\97\90 ì\9d´ ê¶\8cí\95\9cì\9d\84 ì \81ì\9a©í\95©ë\8b\88ë\8b¤. ì\84\9cê°\80ì\9d\98 ê¶\8cí\95\9cì\9d\84 ì \80ì\9e¥í\96\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95\98ì\84¸ì\9a\94.',
+    'shelves_copy_permission_success' => '책자 :count개 권한 바꿈',
 
     // Books
-    'book' => 'ì±\85',
-    'books' => 'ì±\85ë\93¤',
-    'x_books' => ':count 책|:count 책들',
-    'books_empty' => '책이 만들어지지 않았습니다.',
-    'books_popular' => '인기있는 책',
-    'books_recent' => '최근 책',
-    'books_new' => '새로운 책',
-    'books_new_action' => 'New Book',
-    'books_popular_empty' => '가장 인기있는 책이 여기에 보입니다.',
-    'books_new_empty' => '가장 최근에 만든 책이 여기에 표시됩니다.',
-    'books_create' => 'ì\83\88ë¡\9cì\9a´ ì±\85 만들기',
-    'books_delete' => '책 삭제하기',
-    'books_delete_named' => ':bookName 책 삭제하기',
-    'books_delete_explain' => '\':bookName\' 이름의 책이 삭제됩니다. 모든 페이지와 챕터가 삭제됩니다.',
-    'books_delete_confirmation' => 'ì\9d´ ì±\85ì\9d\84 ì\82­ì \9c í\95\98ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'books_edit' => '책 수정',
-    'books_edit_named' => ':bookName 책 수정',
-    'books_form_book_name' => '책 이름',
-    'books_save' => 'ì±\85 ì \80ì\9e¥',
-    'books_permissions' => '책 권한',
-    'books_permissions_updated' => '책 권한이 업데이트 되었습니다.',
-    'books_empty_contents' => 'ì\9d´ ì±\85ì\97\90 ë\8c\80í\95\9c í\8e\98ì\9d´ì§\80 ë\98\90ë\8a\94 ì\9e¥ì\9d´ ì\9e\91ì\84±ë\90\98ì§\80 ì\95\8aì\95\98습니다.',
-    'books_empty_create_page' => '새로운 페이지 만들기',
-    'books_empty_sort_current_book' => '현제 책 정렬하기',
-    'books_empty_add_chapter' => '챕터 추가하기',
-    'books_permissions_active' => '책 권한 활성화',
-    'books_search_this' => '이책 찾기',
-    'books_navigation' => '책 네비게이션',
-    'books_sort' => '책 구성 정렬하기',
-    'books_sort_named' => ':bookName ì±\85 ì \95ë ¬í\95\98기',
-    'books_sort_name' => 'Sort by Name',
-    'books_sort_created' => 'Sort by Created Date',
-    'books_sort_updated' => 'Sort by Updated Date',
-    'books_sort_chapters_first' => 'Chapters First',
-    'books_sort_chapters_last' => 'Chapters Last',
-    'books_sort_show_other' => '다른책 보기',
-    'books_sort_save' => 'ì\83\88ë¡\9cì\9a´ ì\88\9cì\84\9c ì \80ì\9e¥',
+    'book' => 'ì\84\9cê³ ',
+    'books' => 'ì\84\9cê³ ',
+    'x_books' => '책자 :count개|총 :count개',
+    'books_empty' => '만든 책자가 없습니다.',
+    'books_popular' => '많이 읽은 책자',
+    'books_recent' => '최근에 읽은 책자',
+    'books_new' => '새로운 책',
+    'books_new_action' => '새로운 책자',
+    'books_popular_empty' => '많이 읽은 책자 목록',
+    'books_new_empty' => '새로운 책자 목록',
+    'books_create' => 'ì±\85ì\9e\90 만들기',
+    'books_delete' => '책 삭제하기',
+    'books_delete_named' => ':bookName(을)를 지웁니다.',
+    'books_delete_explain' => ':bookName에 있는 모든 챕터와 문서도 지웁니다.',
+    'books_delete_confirmation' => 'ì\9d´ ì±\85ì\9e\90를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'books_edit' => '책자 바꾸기',
+    'books_edit_named' => ':bookName(을)를 바꿉니다.',
+    'books_form_book_name' => '책 이름',
+    'books_save' => '저장',
+    'books_permissions' => '책 권한',
+    'books_permissions_updated' => '권한 저장함',
+    'books_empty_contents' => 'ì\9d´ ì±\85ì\9e\90ì\97\90 ì±\95í\84°ë\82\98 ë¬¸ì\84\9cê°\80 ì\97\86습니다.',
+    'books_empty_create_page' => '문서 만들기',
+    'books_empty_sort_current_book' => '읽고 있는 책자 정렬',
+    'books_empty_add_chapter' => '챕터 만들기',
+    'books_permissions_active' => '책자 권한 허용함',
+    'books_search_this' => '이 책자에서 검색',
+    'books_navigation' => '목차',
+    'books_sort' => '다른 책자들',
+    'books_sort_named' => ':bookName ì \95ë ¬',
+    'books_sort_name' => '제목',
+    'books_sort_created' => '만든 날짜',
+    'books_sort_updated' => '수정한 날짜',
+    'books_sort_chapters_first' => '챕터 우선',
+    'books_sort_chapters_last' => '문서 우선',
+    'books_sort_show_other' => '다른 책자들',
+    'books_sort_save' => 'ì \81ì\9a©',
 
     // Chapters
     'chapter' => '챕터',
     'chapters' => '챕터',
-    'x_chapters' => ':count 개 챕터|:count 챔터들',
-    'chapters_popular' => '인기있는 챕터',
-    'chapters_new' => '새로운 챕',
-    'chapters_create' => 'ì\83\88ë¡\9cì\9a´ ì±\95í\84° ë§\8cë\93¤ê¸°',
-    'chapters_delete' => '챕터 삭제',
-    'chapters_delete_named' => ':chapterName 챕터 지우기',
-    'chapters_delete_explain' => '\':chapterName\' 챕터를 지웁니다. 챕터에서 모든 페이지가 삭제되며 페이지가 상위 책에 추가됩니다.',
-    'chapters_delete_confirm' => 'ì \95ë§\90ë¡\9c ì±\95í\84°ë¥¼ ì§\80ì\9a°ì\8b\9cê² ì\8aµë\8b\88ë\94°?',
-    'chapters_edit' => '챕터 수정',
-    'chapters_edit_named' => ':chapterName 챕터 수정',
-    'chapters_save' => 'ì±\95í\84° ì \80ì\9e¥',
-    'chapters_move' => '챕터 이동',
-    'chapters_move_named' => ':chapterName ì±\95í\84° ì\9d´ë\8f\99',
-    'chapter_move_success' => ':bookName 으로 챕터를 이동하였습니다.',
+    'x_chapters' => '챕터 :count개|총 :count개',
+    'chapters_popular' => '많이 읽은 챕터',
+    'chapters_new' => '새로운 챕',
+    'chapters_create' => '챕터 만들기',
+    'chapters_delete' => '챕터 삭제하기',
+    'chapters_delete_named' => ':chapterName(을)를 지웁니다.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_confirm' => 'ì\9d´ ì±\95í\84°ë¥¼ ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'chapters_edit' => '챕터 바꾸기',
+    'chapters_edit_named' => ':chapterName 바꾸기',
+    'chapters_save' => '저장',
+    'chapters_move' => '챕터 이동하기',
+    'chapters_move_named' => ':chapterName ì\9d´ë\8f\99í\95\98기',
+    'chapter_move_success' => ':bookName(으)로 옮김',
     'chapters_permissions' => '챕터 권한',
-    'chapters_empty' => 'ì±\95í\84°ì\97\90 í\8f¬í\95¨ë\90\9c í\8e\98ì\9d´ì§\80가 없습니다.',
-    'chapters_permissions_active' => '챕터 권한 활동',
-    'chapters_permissions_success' => 'ì±\95í\84° ê¶\8cí\95\9c ì\88\98ì \95ë\90¨',
-    'chapters_search_this' => '이 챕터 찾기',
+    'chapters_empty' => 'ì\9d´ ì±\95í\84°ì\97\90 ë¬¸ì\84\9c가 없습니다.',
+    'chapters_permissions_active' => '문서 권한 허용함',
+    'chapters_permissions_success' => 'ê¶\8cí\95\9c ì \80ì\9e¥í\95¨',
+    'chapters_search_this' => '이 챕터에서 검색',
 
     // Pages
-    'page' => '페이지',
-    'pages' => '페이지들',
-    'x_pages' => ':count 개의 페이지|:count 개의 페이지들',
-    'pages_popular' => '인기있는 페이지',
-    'pages_new' => '새로운 페이지',
-    'pages_attachments' => '첨부파일',
-    'pages_navigation' => '페이지 네비게이션',
-    'pages_delete' => '페이지 지우기',
-    'pages_delete_named' => ':pageName 페이지 지우기',
-    'pages_delete_draft_named' => ':pageName 초안 페이지 지우기',
-    'pages_delete_draft' => '초안 페이지 지우기',
-    'pages_delete_success' => '페이지 삭제됨',
-    'pages_delete_draft_success' => '초안페이지 삭제됨',
-    'pages_delete_confirm' => 'ì \95ë§\90ë¡\9c ì\9d´ í\8e\98ì\9d´ì§\80를 ì§\80ì\9a°ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'pages_delete_draft_confirm' => 'ì \95ë§\90ë¡\9c ì´\88ì\95\88í\8e\98ì\9d´ì§\80를 ì§\80ì\9a°ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'pages_editing_named' => ':pageName 페이지 수정',
-    'pages_edit_draft_options' => 'Draft Options',
-    'pages_edit_save_draft' => '초안 저장',
-    'pages_edit_draft' => '페이지 초안 수정',
-    'pages_editing_draft' => '초안 수정중',
-    'pages_editing_page' => '페이지 수정중',
-    'pages_edit_draft_save_at' => '초안이 저장됨 ',
+    'page' => '문서',
+    'pages' => '문서',
+    'x_pages' => '문서 :count개|총 :count개',
+    'pages_popular' => '많이 읽은 문서',
+    'pages_new' => '새로운 문서',
+    'pages_attachments' => '첨부',
+    'pages_navigation' => '목차',
+    'pages_delete' => '문서 삭제하기',
+    'pages_delete_named' => ':pageName 삭제하기',
+    'pages_delete_draft_named' => ':pageName 초안 문서 삭제하기',
+    'pages_delete_draft' => '초안 문서 삭제하기',
+    'pages_delete_success' => '문서 지움',
+    'pages_delete_draft_success' => '초안 문서 지움',
+    'pages_delete_confirm' => 'ì\9d´ ë¬¸ì\84\9c를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'pages_delete_draft_confirm' => 'ì´\88ì\95\88 ë¬¸ì\84\9c를 ì\82­ì \9cí\95  ê±´ê°\80ì\9a\94?',
+    'pages_editing_named' => ':pageName 수정',
+    'pages_edit_draft_options' => '초안 문서 옵션',
+    'pages_edit_save_draft' => '초안으로 저장',
+    'pages_edit_draft' => '초안 문서 수정',
+    'pages_editing_draft' => '초안 문서 수정',
+    'pages_editing_page' => '문서 수정',
+    'pages_edit_draft_save_at' => '보관함: ',
     'pages_edit_delete_draft' => '초안 삭제',
-    'pages_edit_discard_draft' => '초안 버리기',
-    'pages_edit_set_changelog' => '변경내역 남기기',
-    'pages_edit_enter_changelog_desc' => '어떤 내용에 대하여 변경하셨나요?',
-    'pages_edit_enter_changelog' => '변경내역 입력',
-    'pages_save' => '페이지 저장',
-    'pages_title' => '페이지 제목',
-    'pages_name' => '페이지 이름',
-    'pages_md_editor' => '편집자',
-    'pages_md_preview' => 'Preview',
-    'pages_md_insert_image' => '이미지 삽입',
-    'pages_md_insert_link' => '전체링크 입력',
-    'pages_md_insert_drawing' => '드로잉 넣기',
-    'pages_not_in_chapter' => '페이지가 챕터에 있지않습니다.',
-    'pages_move' => '페이지 옮기기',
-    'pages_move_success' => '":parentName"로 페이지를 이동하였습니다.',
-    'pages_copy' => '페이지 복사',
-    'pages_copy_desination' => '경로(desination) 복사',
-    'pages_copy_success' => '페이지가 성공적으로 복사되었습니다',
-    'pages_permissions' => '페이지 권한',
-    'pages_permissions_success' => '페이지 권한이 업데이트 되었습니다.',
-    'pages_revision' => '변경이력',
-    'pages_revisions' => '페이지 변경이력',
-    'pages_revisions_named' => ':pageName페이지의 변경이력내역',
-    'pages_revision_named' => ':pageName페이지의 변경이력',
-    'pages_revisions_created_by' => 'Created By',
-    'pages_revisions_date' => '변경일',
-    'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
-    'pages_revisions_changelog' => '변경내역',
-    'pages_revisions_changes' => '변경사항 보기',
-    'pages_revisions_current' => '현재 버전',
-    'pages_revisions_preview' => '미리보기',
-    'pages_revisions_restore' => '되돌리기',
-    'pages_revisions_none' => '이 페이지에는 변경이력이 없습니다.',
-    'pages_copy_link' => '링크복사',
-    'pages_edit_content_link' => '링크 수정',
-    'pages_permissions_active' => '페이지 권한 활성화',
-    'pages_initial_revision' => '최초 작성',
-    'pages_initial_name' => '새 페이지',
-    'pages_editing_draft_notification' => ':timeDiff 전에 저장된 초안을 최근 편집하셨습니다.',
-    'pages_draft_edited_notification' => '이 페이지는 그 이후로 업데이트되었습니다. 이 초안을 폐기하는 것이 좋습니다.',
+    'pages_edit_discard_draft' => '폐기',
+    'pages_edit_set_changelog' => '수정본 설명',
+    'pages_edit_enter_changelog_desc' => '수정본 설명',
+    'pages_edit_enter_changelog' => '설명',
+    'pages_save' => '저장',
+    'pages_title' => '문서 제목',
+    'pages_name' => '문서 이름',
+    'pages_md_editor' => '에디터',
+    'pages_md_preview' => '미리 보기',
+    'pages_md_insert_image' => '이미지 추가',
+    'pages_md_insert_link' => '내부 링크',
+    'pages_md_insert_drawing' => '드로잉 추가',
+    'pages_not_in_chapter' => '챕터에 있는 문서가 아닙니다.',
+    'pages_move' => '문서 이동하기',
+    'pages_move_success' => ':parentName(으)로 옮김',
+    'pages_copy' => '문서 복제',
+    'pages_copy_desination' => '복제할 위치',
+    'pages_copy_success' => '복제함',
+    'pages_permissions' => '문서 권한',
+    'pages_permissions_success' => '문서 권한 바꿈',
+    'pages_revision' => '수정본',
+    'pages_revisions' => '문서 수정본',
+    'pages_revisions_named' => ':pageName 수정본',
+    'pages_revision_named' => ':pageName 수정본',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions_created_by' => '만든 사용자',
+    'pages_revisions_date' => '수정한 날짜',
+    'pages_revisions_number' => 'No.',
+    'pages_revisions_numbered' => '수정본 :id',
+    'pages_revisions_numbered_changes' => '수정본 :id에서 바꾼 부분',
+    'pages_revisions_changelog' => '설명',
+    'pages_revisions_changes' => '바꾼 부분',
+    'pages_revisions_current' => '현재 판본',
+    'pages_revisions_preview' => '미리 보기',
+    'pages_revisions_restore' => '복원',
+    'pages_revisions_none' => '수정본이 없습니다.',
+    'pages_copy_link' => '주소 복사',
+    'pages_edit_content_link' => '수정',
+    'pages_permissions_active' => '문서 권한 허용함',
+    'pages_initial_revision' => '처음 판본',
+    'pages_initial_name' => '제목 없음',
+    'pages_editing_draft_notification' => ':timeDiff에 초안 문서입니다.',
+    'pages_draft_edited_notification' => '최근에 수정한 문서이기 때문에 초안 문서를 폐기하는 편이 좋습니다.',
     'pages_draft_edit_active' => [
-        'start_a' => ':countëª\85ì\9d\98 ì\82¬ì\9a©ì\9e\90ê°\80 ì\9d´ í\8e\98ì\9d´ì§\80를 ì\88\98ì \95ì¤\91ì\9e\85니다.',
-        'start_b' => ':userName가(이) 페이지를 수정중입니다.',
-        'time_a' => '페이지가 마지막으로 업데이트 된 이후',
-        'time_b' => '지난 :minCount분 동안',
-        'message' => ':start :time. 서로의 업데이트를 덮어 쓰지 않도록 주의하십시오!',
+        'start_a' => ':countëª\85ì\9d´ ì\9d´ ë¬¸ì\84\9c를 ì\88\98ì \95í\95\98ê³  ì\9e\88ì\8aµ니다.',
+        'start_b' => ':userName이 이 문서를 수정하고 있습니다.',
+        'time_a' => '수정본이 생겼습니다.',
+        'time_b' => '(:minCount분 전)',
+        'message' => ':start :time. 다른 사용자의 수정본을 덮어쓰지 않도록 주의하세요.',
     ],
-    'pages_draft_discarded' => '초안이 삭제되었습니다. 편집기가 현재 페이지 작성자로 업데이트되었습니다.',
-    'pages_specific' => '특정 페이지',
-    'pages_is_template' => 'Page Template',
+    'pages_draft_discarded' => '초안 문서를 지웠습니다. 에디터에 현재 판본이 나타납니다.',
+    'pages_specific' => '특정한 문서',
+    'pages_is_template' => '템플릿',
 
     // Editor Sidebar
-    'page_tags' => '페이지 테그',
-    'chapter_tags' => '챕터 테그',
-    'book_tags' => '책 테그',
-    'shelf_tags' => 'ì±\85ê½\83ì\9d´ í\85\8cê·¸',
-    'tag' => '테그',
-    'tags' =>  '테그들',
-    'tag_name' =>  'Tag Name',
-    'tag_value' => '테그 값 (선택사항)',
-    'tags_explain' => "컨텐츠를 더 잘 분류하기 위해 테그를 추가하세요! \n 보다 상세한 구성을 위해 태그값을 할당 할 수 있습니다.",
-    'tags_add' => '다른 테그 추가',
-    'tags_remove' => 'Remove this tag',
-    'attachments' => '첨부',
-    'attachments_explain' => 'ì\9d¼ë¶\80 í\8c\8cì\9d¼ì\9d\84 ì\97\85ë¡\9cë\93\9cí\95\98ê±°ë\82\98 í\8e\98ì\9d´ì§\80ì\97\90 í\91\9cì\8b\9c í\95  ë§\81í\81¬ë¥¼ ì²¨ë¶\80í\95\98ì\8b­ì\8b\9cì\98¤. í\8e\98ì\9d´ì§\80 ì\82¬ì\9d´ë\93\9c ë°\94ì\97\90 í\91\9cì\8b\9cë\90©ë\8b\88ë\8b¤.',
-    'attachments_explain_instant_save' => 'ë³\80ê²½ ì\82¬í\95­ì\9d\80 ì¦\89ì\8b\9c ì \80ì\9e¥ë\90©ë\8b\88ë\8b¤.',
-    'attachments_items' => '첨부된 항목',
-    'attachments_upload' => '차일 업로드',
-    'attachments_link' => '링크 첨부',
+    'page_tags' => '문서 꼬리표',
+    'chapter_tags' => '챕터 꼬리표',
+    'book_tags' => '책자 꼬리표',
+    'shelf_tags' => 'ì\84\9cê°\80 ê¼¬ë¦¬í\91\9c',
+    'tag' => '꼬리표',
+    'tags' =>  '꼬리표',
+    'tag_name' =>  '꼬리표 이름',
+    'tag_value' => '리스트 값 (선택 사항)',
+    'tags_explain' => "태그로 문서를 분류하세요.",
+    'tags_add' => '그 추가',
+    'tags_remove' => '태그 삭제',
+    'attachments' => '첨부 파일',
+    'attachments_explain' => 'í\8c\8cì\9d¼ì\9d´ë\82\98 ë§\81í\81¬ë¥¼ ì²¨ë¶\80í\95\98ì\84¸ì\9a\94. ì \95ë³´ í\83­ì\97\90 ë\82\98í\83\80ë\82©ë\8b\88ë\8b¤.',
+    'attachments_explain_instant_save' => 'ì\97¬ê¸°ì\97\90ì\84\9c ë°\94ê¾¼ ë\82´ì\9a©ì\9d\80 ë°\94ë¡\9c ì \81ì\9a©í\95©ë\8b\88ë\8b¤.',
+    'attachments_items' => '첨부한 파일들',
+    'attachments_upload' => '파일 올리기',
+    'attachments_link' => '링크 첨부',
     'attachments_set_link' => '링크 설정',
-    'attachments_delete_confirm' => '삭제를 다시 클릭하면 첨부파일이 완전히 삭제됩니다.',
-    'attachments_dropzone' => '파일 놓기 또는 여기를 클릭하여 파일 첨부',
-    'attachments_no_files' => 'ì\97\85ë¡\9cë\93\9c ë\90\9c í\8c\8cì\9d¼ì\9d´ ì\97\86ì\8aµë\8b\88ë\8b¤.',
-    'attachments_explain_link' => 'í\8c\8cì\9d¼ì\9d\84 ì\97\85ë¡\9cë\93\9cí\95\98ì§\80 ì\95\8aì\9c¼ë ¤ë\8a\94 ê²½ì\9a° ë§\81í\81¬ë¥¼ ì²¨ë¶\80 í\95  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤. ì\9d´ ë§\81í\81¬ë\8a\94 ë\8b¤ë¥¸ í\8e\98ì\9d´ì§\80ì\97\90 ë\8c\80í\95\9c ë§\81í\81¬ì\9d´ê±°ë\82\98 í\81´ë\9d¼ì\9a°ë\93\9cì\97\90ì\9e\88ë\8a\94 í\8c\8cì\9d¼ì\97\90 ë\8c\80í\95\9c ë§\81í\81¬ ì\9d¼ 수 있습니다.',
+    'attachments_delete' => '이 첨부파일을 정말 삭제하시겠습니까?',
+    'attachments_dropzone' => '여기에 파일을 드롭하거나 여기를 클릭하세요.',
+    'attachments_no_files' => 'ì\98¬ë¦° í\8c\8cì\9d¼ ì\97\86ì\9d\8c',
+    'attachments_explain_link' => 'í\8c\8cì\9d¼ì\9d\84 ì\98¬ë¦¬ì§\80 ì\95\8aê³  ë§\81í\81¬ë¡\9c ì²¨ë¶\80í\95  수 있습니다.',
     'attachments_link_name' => '링크 이름',
-    'attachment_link' => '첨부 링크',
-    'attachments_link_url' => '파일로 첨부',
-    'attachments_link_url_hint' => 'Url, 사이트 또는 파일',
-    'attach' => '첨부',
+    'attachment_link' => '파일 주소',
+    'attachments_link_url' => '파일로 링크',
+    'attachments_link_url_hint' => '파일 주소',
+    'attach' => '파일 첨부',
+    'attachments_insert_link' => '페이지에 첨부파일 링크 추가',
     'attachments_edit_file' => '파일 수정',
-    'attachments_edit_file_name' => '파일이름',
-    'attachments_edit_drop_upload' => '파일을 놓거나 여기를 클릭하여 업로드 및 덮어 쓰기',
-    'attachments_order_updated' => '첨부 순서 업데이트',
-    'attachments_updated_success' => '첨부파일 상세내용 업데이트 성공',
-    'attachments_deleted' => '첨부파일 삭제',
-    'attachments_file_uploaded' => '파일이 성공적으로 업로드 되었습니다.',
-    'attachments_file_updated' => '파일이 성공적으로 업데이트 되었습니다.',
-    'attachments_link_attached' => '링크가 성공적으로 페이지에 첨부되었습니다.',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'attachments_edit_file_name' => '파일 이름',
+    'attachments_edit_drop_upload' => '여기에 파일을 드롭하거나 여기를 클릭하세요. 파일을 올리거나 덮어쓸 수 있습니다.',
+    'attachments_order_updated' => '첨부 순서 바꿈',
+    'attachments_updated_success' => '첨부 파일 정보 수정함',
+    'attachments_deleted' => '첨부 파일 삭제함',
+    'attachments_file_uploaded' => '파일 올림',
+    'attachments_file_updated' => '파일 바꿈',
+    'attachments_link_attached' => '링크 첨부함',
+    'templates' => '템플릿',
+    'templates_set_as_template' => '템플릿',
+    'templates_explain_set_as_template' => '템플릿은 보기 권한만 있어도 문서에 쓸 수 있습니다.',
+    'templates_replace_content' => '문서 대체',
+    'templates_append_content' => '문서 앞에 추가',
+    'templates_prepend_content' => '문서 뒤에 추가',
 
     // Profile View
-    'profile_user_for_x' => ':time 전에 작성',
-    'profile_created_content' => '생성한 컨텐츠',
-    'profile_not_created_pages' => ':userName가 작성한 페이지가 없습니다.',
-    'profile_not_created_chapters' => ':userName가 작성한 챕터가 없습니다.',
-    'profile_not_created_books' => ':userName가 작성한 책이 없습니다.',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_user_for_x' => ':time 전에 가입함',
+    'profile_created_content' => '활동한 이력',
+    'profile_not_created_pages' => ':userName(이)가 만든 문서 없음',
+    'profile_not_created_chapters' => ':userName(이)가 만든 챕터 없음',
+    'profile_not_created_books' => ':userName(이)가 만든 책자 없음',
+    'profile_not_created_shelves' => ':userName(이)가 만든 서가 없음',
 
     // Comments
-    'comment' => '코멘트',
-    'comments' => '코멘트들',
-    'comment_add' => '코멘트 추가',
-    'comment_placeholder' => 'ì\97¬ê¸°ì\97\90 ì½\94ë©\98í\8a¸ë¥¼ ë\82¨ê¸°ì\84¸ì\9a\94',
-    'comment_count' => '{0} 코멘트 없음|{1} 1개 코멘트|[2,*] :count개의 코멘트',
-    'comment_save' => '코멘트 저장',
-    'comment_saving' => 'ì½\94ë©\98í\8a¸ ì \80ì\9e¥중...',
-    'comment_deleting' => 'ì½\94ë©\98í\8a¸ ì\82­ì \9c중...',
-    'comment_new' => '새로운 코멘트',
-    'comment_created' => '코멘트를 작성하였습니다. :createDiff',
-    'comment_updated' => ':username이 코멘트를 수정하였습니다 :updateDiff',
-    'comment_deleted_success' => '코멘트 삭제성공',
-    'comment_created_success' => '코멘트 추가성공',
-    'comment_updated_success' => '코멘트 업데이트 성공',
-    'comment_delete_confirm' => 'ì \95ë§\90ë¡\9c ì½\94ë©\98í\8a¸ë¥¼ ì§\80ì\9a°ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'comment_in_reply_to' => ':commentId 응답',
+    'comment' => '댓글',
+    'comments' => '댓글',
+    'comment_add' => '댓글 쓰기',
+    'comment_placeholder' => 'ì\9d´ê³³ì\97\90 ë\8c\93ê¸\80ì\9d\84 ì\93°ì\84¸ì\9a\94...',
+    'comment_count' => '{0} 댓글 없음|{1} 댓글 1개|[2,*] 댓글 :count개',
+    'comment_save' => '등록',
+    'comment_saving' => 'ì \80ì\9e¥í\95\98ë\8a\94 중...',
+    'comment_deleting' => 'ì\82­ì \9cí\95\98ë\8a\94 중...',
+    'comment_new' => '새로운 댓글',
+    'comment_created' => '댓글 등록함 :createDiff',
+    'comment_updated' => ':username(이)가 댓글 수정함 :updateDiff',
+    'comment_deleted_success' => '댓글 지움',
+    'comment_created_success' => '댓글 등록함',
+    'comment_updated_success' => '댓글 수정함',
+    'comment_delete_confirm' => 'ì\9d´ ë\8c\93ê¸\80ì\9d\84 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'comment_in_reply_to' => ':commentId(을)를 향한 답글',
 
     // Revision
-    'revision_delete_confirm' => '해당 개정판을 지우시겠습니까??',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
-    'revision_delete_success' => '개정판 삭제성공',
-    'revision_cannot_delete_latest' => '최신버전은 지울수 없습니다.'
+    'revision_delete_confirm' => '이 수정본을 지울 건가요?',
+    'revision_restore_confirm' => '이 수정본을 되돌릴 건가요? 현재 판본을 바꿉니다.',
+    'revision_delete_success' => '수정본 지움',
+    'revision_cannot_delete_latest' => '현재 판본은 지울 수 없습니다.'
 ];
\ No newline at end of file
index 1a88dd4e31017b148e3ead49a0545d3dd9b3f119..093288c83a7f59f52c3db9dceec1475be5320bc4 100644 (file)
@@ -5,81 +5,98 @@
 return [
 
     // Permissions
-    'permission' => '요청한 페이지에 권한이 없습니다.',
-    'permissionJson' => '요청한 작업을 수행 할 권한이 없습니다.',
+    'permission' => '권한이 없습니다.',
+    'permissionJson' => '권한이 없습니다.',
 
     // Auth
-    'error_user_exists_different_creds' => '전자 메일 :email을 가진 사용자가 이미 존재하지만 자격 증명이 다릅니다.',
-    'email_already_confirmed' => '이메일이 이미 확인되었습니다. 로그인 해주세요.',
-    'email_confirmation_invalid' => '이 확인 토큰이 유효하지 않거나 이미 사용되었습니다. 다시 등록하세요.',
-    'email_confirmation_expired' => '확인 토큰이 만료되었습니다. 새 확인 이메일이 전송되었습니다.',
-    'ldap_fail_anonymous' => '익명 바인드를 이용한 LDAP 액세스에 실패하였습니다.',
-    'ldap_fail_authed' => '주어진 dn 및 비밀번호 세부 정보를 사용하여 LDAP 액세스하는 것이 실패했습니다.',
-    'ldap_extension_not_installed' => 'LDAP PHP 확장기능이 설치되지 않았습니다.',
-    'ldap_cannot_connect' => 'LDAP 서버에 연결할 수 없습니다. 초기 연결에 실패했습니다.',
-    'social_no_action_defined' => '동작이 정의되지 않았습니다.',
-    'social_login_bad_response' => ":socialAccount 로그인에 실패하였습니다 : \n:error",
-    'social_account_in_use' => '이 :socialAccount 계정이 이미 사용 중입니다. :socialAccount 옵션을 통해 로그인하십시오.',
-    'social_account_email_in_use' => ' 이메일 :email이 이미 사용 중입니다. 이미 계정이있는 경우 프로필 설정에서 :socialAccount 계정을 연결할 수 있습니다.',
-    'social_account_existing' => ':socialAccount가 이미 프로필에 첨부되어 있습니다.',
-    'social_account_already_used_existing' => '이 :socialAccount 계정은 이미 다른 사용자가 사용하고 있습니다.',
-    'social_account_not_used' => '이 :socialAccount 계정이 모든 사용자에게 연결되어 있지 않습니다. 프로필 설정에 첨부하십시오. ',
-    'social_account_register_instructions' => '아직 계정이없는 경우 :socialAccount 옵션을 사용하여 계정을 등록 할 수 있습니다.',
-    'social_driver_not_found' => '소셜 드라이버를 찾을 수 없음',
-    'social_driver_not_configured' => '귀하의 :socialAccount 소셜 설정이 올바르게 구성되지 않았습니다.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'error_user_exists_different_creds' => ':email(을)를 가진 다른 사용자가 있습니다.',
+    'email_already_confirmed' => '확인이 끝난 메일 주소입니다. 로그인하세요.',
+    'email_confirmation_invalid' => '이 링크는 더 이상 유효하지 않습니다. 다시 가입하세요.',
+    'email_confirmation_expired' => '이 링크는 더 이상 유효하지 않습니다. 메일을 다시 보냈습니다.',
+    'email_confirmation_awaiting' => '사용 중인 계정의 이메일 주소를 확인해 주어야 합니다.',
+    'ldap_fail_anonymous' => '익명 정보로 LDAP 서버에 접근할 수 없습니다.',
+    'ldap_fail_authed' => '이 정보로 LDAP 서버에 접근할 수 없습니다.',
+    'ldap_extension_not_installed' => 'PHP에 LDAP 확장 도구를 설치하세요.',
+    'ldap_cannot_connect' => 'LDAP 서버에 연결할 수 없습니다.',
+    'saml_already_logged_in' => '이미 로그인되어있습니다.',
+    'saml_user_not_registered' => '사용자 이름이 등록되지 않았으며 자동 계정 등록이 활성화되지 않았습니다.',
+    'saml_no_email_address' => '이 사용자에 대하여 외부 인증시스템에 의해 제공된 데이타 중 이메일 주소를 찾을 수 없습니다.',
+    'saml_invalid_response_id' => '이 응용프로그램에 의해 시작된 프로세스에 의하면 외부 인증시스템으로 온 요청이 인식되지 않습니다. 인증 후에 뒤로가기 기능을 사용했을 경우 이런 현상이 발생할 수 있습니다.',
+    'saml_fail_authed' => '시스템 로그인에 실패하였습니다. ( 해당 시스템이 인증성공값을 제공하지 않았습니다. )',
+    'social_no_action_defined' => '무슨 활동인지 알 수 없습니다.',
+    'social_login_bad_response' => ":socialAccount에 로그인할 수 없습니다. : \\n:error",
+    'social_account_in_use' => ':socialAccount(을)를 가진 사용자가 있습니다. :socialAccount로 로그인하세요.',
+    'social_account_email_in_use' => ':email(을)를 가진 사용자가 있습니다. 쓰고 있는 계정을 :socialAccount에 연결하세요.',
+    'social_account_existing' => ':socialAccount(와)과 연결 상태입니다.',
+    'social_account_already_used_existing' => ':socialAccount(와)과 연결한 다른 계정이 있습니다.',
+    'social_account_not_used' => ':socialAccount(와)과 연결한 계정이 없습니다. 쓰고 있는 계정을 연결하세요.',
+    'social_account_register_instructions' => '계정이 없어도 :socialAccount로 가입할 수 있습니다.',
+    'social_driver_not_found' => '가입할 수 없습니다.',
+    'social_driver_not_configured' => ':socialAccount가 유효하지 않습니다.',
+    'invite_token_expired' => '이 링크는 더 이상 유효하지 않습니다. 비밀번호를 바꾸세요.',
 
     // System
-    'path_not_writable' => '파일 경로 :filePath에 업로드 할 수 없습니다. 서버에 쓰기 기능이 활성화 되어있는지 확인하세요.',
-    'cannot_get_image_from_url' => ':url에서 이미지를 가져올 수 없습니다.',
-    'cannot_create_thumbs' => 'ì\84\9cë²\84ì\97\90ì\84\9c ì\8d¸ë\84¤ì\9d¼ì\9d\84 ì\83\9dì\84±í\95  ì\88\98 ì\97\86ì\8aµë\8b\88ë\8b¤. GD PHPí\99\95ì\9e¥ê¸°ë\8a¥ì\9d´ ì\84¤ì¹\98ë\90\98ì\96´ì\9e\88ë\8a\94ì§\80 í\99\95ì\9d¸하세요.',
-    'server_upload_limit' => 'í\95´ë\8b¹ í\81¬ê¸°ì\9d\98 í\8c\8cì\9d¼ì\9d\84 ì\97\85ë¡\9cë\93\9cí\95\98ë\8a\94ê²\83ì\9d´ ì\84\9cë²\84ì\97\90ì\84\9c ì \9cí\95\9cë\90©ë\8b\88ë\8b¤. í\8c\8cì\9d¼ ì\82¬ì\9d´ì¦\88를 ì\9e\91ê²\8c ì¤\84ì\9d´ê±°ë\82\98 ì\84\9cë²\84 ì\84¤ì \95ì\9d\84 ë³\80ê²½í\95\98ì\84¸ì\9a\94.',
-    'uploaded'  => 'í\95´ë\8b¹ í\81¬ê¸°ì\9d\98 í\8c\8cì\9d¼ì\9d\84 ì\97\85ë¡\9cë\93\9cí\95\98ë\8a\94ê²\83ì\9d´ ì\84\9cë²\84ì\97\90ì\84\9c ì \9cí\95\9cë\90©ë\8b\88ë\8b¤. í\8c\8cì\9d¼ ì\82¬ì\9d´ì¦\88를 ì\9e\91ê²\8c ì¤\84ì\9d´ê±°ë\82\98 ì\84\9cë²\84 ì\84¤ì \95ì\9d\84 ë³\80ê²½í\95\98ì\84¸ì\9a\94.',
-    'image_upload_error' => 'ì\9d´ë¯¸ì§\80를 ì\97\85ë¡\9cë\93\9cí\95\98ë\8a\94 ì¤\91ì\97\90 ì\98¤ë¥\98ê°\80 ë°\9cì\83\9dí\96\88습니다.',
-    'image_upload_type_error' => 'ì\97\85ë¡\9cë\93\9cì¤\91ì\9d¸ ì\9d´ë¯¸ì§\80 ì\9c í\98\95ì\9d´ ì\9e\98못ë\90\98ì\97\88ì\8aµ니다.',
-    'file_upload_timeout' => '파일 업로드가 시간 초과되었습니다.',
+    'path_not_writable' => ':filePath에 쓰는 것을 서버에서 허용하지 않습니다.',
+    'cannot_get_image_from_url' => ':url에서 이미지를 불러올 수 없습니다.',
+    'cannot_create_thumbs' => 'ì\84¬ë\84¤ì\9d¼ì\9d\84 ëª» ë§\8cë\93¤ì\97\88ì\8aµë\8b\88ë\8b¤. PHPì\97\90 GD í\99\95ì\9e¥ ë\8f\84구를 ì\84¤ì¹\98하세요.',
+    'server_upload_limit' => 'í\8c\8cì\9d¼ í\81¬ê¸°ê°\80 ì\84\9cë²\84ì\97\90ì\84\9c í\97\88ì\9a©í\95\98ë\8a\94 ì\88\98ì¹\98를 ë\84\98ì\8aµë\8b\88ë\8b¤.',
+    'uploaded'  => 'í\8c\8cì\9d¼ í\81¬ê¸°ê°\80 ì\84\9cë²\84ì\97\90ì\84\9c í\97\88ì\9a©í\95\98ë\8a\94 ì\88\98ì¹\98를 ë\84\98ì\8aµë\8b\88ë\8b¤.',
+    'image_upload_error' => 'ì\9d´ë¯¸ì§\80를 ì\98¬ë¦¬ë\8b¤ ë¬¸ì \9cê°\80 ì\83\9dê²¼습니다.',
+    'image_upload_type_error' => 'ì\9c í\9a¨í\95\98ì§\80 ì\95\8aì\9d\80 ì\9d´ë¯¸ì§\80 í\98\95ì\8b\9dì\9e\85니다.',
+    'file_upload_timeout' => '파일을 올리는 데 걸리는 시간이 서버에서 허용하는 수치를 넘습니다.',
 
     // Attachments
-    'attachment_page_mismatch' => '첨부 파일 업데이트 중 페이지 불일치하였습니다.',
-    'attachment_not_found' => '첨부 파일을 찾을 수 없습니다.',
+    'attachment_not_found' => '첨부 파일이 없습니다.',
 
     // Pages
-    'page_draft_autosave_fail' => '초안을 저장하지 못했습니다. 이 페이지를 저장하기 전에 인터넷에 연결되어 있는지 확인하십시오.',
-    'page_custom_home_deletion' => '홈페이지로 설정되어있는 페이지는 삭제할 수 없습니다.',
+    'page_draft_autosave_fail' => '초안 문서를 유실했습니다. 인터넷 연결 상태를 확인하세요.',
+    'page_custom_home_deletion' => '처음 페이지는 지울 수 없습니다.',
 
     // Entities
-    'entity_not_found' => '개체(Entity)를 찾을 수 없음.',
-    'bookshelf_not_found' => 'ì±\85ê½\82ì\9d´ë¥¼ ì°¾ì\9d\84 ì\88\98 ì\97\86ì\9d\8c.',
-    'book_not_found' => 'ì±\85ì\9d\84 ì°¾ì\9d\84 ì\88\98 ì\97\86ì\9d\8c.',
-    'page_not_found' => '페이지를 찾을 수 없음.',
-    'chapter_not_found' => '챕터를 찾을 수 없음.',
-    'selected_book_not_found' => '선택한 책을 찾을 수 없습니다.',
-    'selected_book_chapter_not_found' => '선택한 책 또는 챕터를 찾을 수 없습니다.',
-    'guests_cannot_save_drafts' => '게스트는 임시저장을 할 수 없습니다.',
+    'entity_not_found' => '항목이 없습니다.',
+    'bookshelf_not_found' => 'ì\84\9cê°\80ê°\80 ì\97\86ì\8aµë\8b\88ë\8b¤.',
+    'book_not_found' => 'ì±\85ì\9e\90ê°\80 ì\97\86ì\8aµë\8b\88ë\8b¤.',
+    'page_not_found' => '문서가 없습니다.',
+    'chapter_not_found' => '챕터가 없습니다.',
+    'selected_book_not_found' => '고른 책자가 없습니다.',
+    'selected_book_chapter_not_found' => '고른 책자나 챕터가 없습니다.',
+    'guests_cannot_save_drafts' => 'Guest는 초안 문서를 보관할 수 없습니다.',
 
     // Users
-    'users_cannot_delete_only_admin' => '어드민 계정은 삭제할 수 없습니다.',
-    'users_cannot_delete_guest' => '게스트 사용자는 삭제할 수 없습니다.',
+    'users_cannot_delete_only_admin' => 'Admin을 삭제할 수 없습니다.',
+    'users_cannot_delete_guest' => 'Guest를 삭제할 수 없습니다.',
 
     // Roles
-    'role_cannot_be_edited' => '역할을 수정할 수 없습니다.',
-    'role_system_cannot_be_deleted' => 'ì\9d´ ì\97­í\95 ì\9d\80 ì\8b\9cì\8a¤í\85\9c ì\97­í\95 ì\9e\85ë\8b\88ë\8b¤. ì\82­ì \9cí\95  수 없습니다.',
-    'role_registration_default_cannot_delete' => '이 역할은 기본 등록 역할로 설정되어있는 동안 삭제할 수 없습니다.',
-    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+    'role_cannot_be_edited' => '권한을 수정할 수 없습니다.',
+    'role_system_cannot_be_deleted' => 'ì\8b\9cì\8a¤í\85\9c ê¶\8cí\95\9cì\9d\84 ì§\80ì\9a¸ 수 없습니다.',
+    'role_registration_default_cannot_delete' => '가입한 사용자의 기본 권한을 지울 수 있어야 합니다.',
+    'role_cannot_remove_only_admin' => 'Admin을 가진 사용자가 적어도 한 명 있어야 합니다.',
 
     // Comments
-    'comment_list' => '댓글을 가져 오는 중에 오류가 발생했습니다.',
-    'cannot_add_comment_to_draft' => '초안에 주석을 추가 할 수 없습니다.',
-    'comment_add' => '댓글을 추가 / 업데이트하는 중에 오류가 발생했습니다.',
-    'comment_delete' => 'ë\8c\93ê¸\80ì\9d\84 ì\82­ì \9cí\95\98ë\8a\94 ì¤\91ì\97\90 ì\98¤ë¥\98ê°\80 ë°\9cì\83\9dí\96\88습니다.',
-    'empty_comment' => '빈 주석을 추가 할 수 없습니다.',
+    'comment_list' => '댓글을 가져오다 문제가 생겼습니다.',
+    'cannot_add_comment_to_draft' => '초안 문서에 댓글을 달 수 없습니다.',
+    'comment_add' => '댓글을 등록하다 문제가 생겼습니다.',
+    'comment_delete' => 'ë\8c\93ê¸\80ì\9d\84 ì§\80ì\9a°ë\8b¤ ë¬¸ì \9cê°\80 ì\83\9dê²¼습니다.',
+    'empty_comment' => '빈 댓글은 등록할 수 없습니다.',
 
     // Error pages
-    '404_page_not_found' => '페이지를 찾을 수 없습니다.',
-    'sorry_page_not_found' => '죄송합니다, 찾고 있던 페이지를 찾을 수 없습니다.',
-    'return_home' => 'home으로 가기',
-    'error_occurred' => '오류가 발생하였습니다.',
-    'app_down' => ':appName가 다운되었습니다.',
-    'back_soon' => '곧 복구될 예정입니다.',
+    '404_page_not_found' => '404 Not Found',
+    'sorry_page_not_found' => '문서를 못 찾았습니다.',
+    'sorry_page_not_found_permission_warning' => '이 페이지가 존재하기를 기대했다면, 볼 수 있는 권한이 없을 수 있다.',
+    'return_home' => '처음으로 돌아가기',
+    'error_occurred' => '문제가 생겼습니다.',
+    'app_down' => ':appName에 문제가 있는 것 같습니다',
+    'back_soon' => '곧 되돌아갑니다.',
+
+    // API errors
+    'api_no_authorization_found' => '요청에서 인증 토큰을 찾을 수 없다.',
+    'api_bad_authorization_format' => '요청에서 인증 토큰을 찾았지만, 형식이 잘못된 것 같다.',
+    'api_user_token_not_found' => '제공된 인증 토큰과 일치하는 API 토큰을 찾을 수 없다.',
+    'api_incorrect_token_secret' => '사용한 API 토큰에 대해 제공한 시크릿이 맞지 않는다.',
+    'api_user_no_api_permission' => '사용한 API 토큰의 소유자가, API 호출을 할 수 있는 권한이 없다.',
+    'api_user_token_expired' => '사용된 인증 토큰이 만료되었다.',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => '테스트 이메일 발송할 때 발생한 오류:',
 
 ];
index 26790e95ebed87e0d23cc176c324253b7e799b8b..f93902aefd42bb257a14df002c8d989faae5e071 100644 (file)
@@ -6,10 +6,10 @@
  */
 return [
 
-    'password' => 'ë¹\84ë°\80ë²\88í\98¸ë\8a\94 6ì\9e\90 ì\9d´ì\83\81ì\9d´ì\96´ì\95¼ í\95\98ë©° í\99\95ì\9d¸ê³¼ ì\9d¼ì¹\98í\95´ì\95¼ í\95©ë\8b\88ë\8b¤.',
-    'user' => "해당 이메일 주소의 사용자가 없습니다.",
-    'token' => '해당 비밀번호의 초기화 토큰이 만료되었습니다.',
-    'sent' => '페스워드 초기화 링크를 메일로 보냈습니다!',
-    'reset' => '비밀번호가 초기화 되었습니다!',
+    'password' => 'ì\97¬ë\8d\9f ê¸\80ì\9e\90를 ë\84\98ì\96´ì\95¼ í\95©ë\8b\88ë\8b¤.',
+    'user' => "메일 주소를 가진 사용자가 없습니다.",
+    'token' => '비밀번호 재설정 토큰이 이 이메일 주소에 유효하지 않습니다.',
+    'sent' => '메일을 보냈습니다.',
+    'reset' => '비밀번호를 바꿨습니다.',
 
 ];
index 346e7f6a8462d08ea8adc38b91b72f33d8f47770..9fec26956757fbb45c6d8ddd94545c5a25c38bfd 100755 (executable)
@@ -8,132 +8,216 @@ return [
 
     // Common Messages
     'settings' => '설정',
-    'settings_save' => 'ì\84¤ì \95 ì \80ì\9e¥',
-    'settings_save_success' => '설정이 저장되었습니다.',
+    'settings_save' => 'ì \81ì\9a©',
+    'settings_save_success' => '설정 적용함',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
-    'app_name' => '어플리케이션 이름',
-    'app_name_desc' => '해당 이름은 헤더와 모든 이메일에 표시됩니다.',
-    'app_name_header' => '헤더에 어플리케이션 이름을 표시하시겠습니까?',
-    'app_public_access' => 'Public Access',
-    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
-    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
-    'app_public_access_toggle' => 'Allow public access',
-    'app_public_viewing' => '공개 보기를 허용하시겠습니까?',
-    'app_secure_images' => '더 높은 보안 이미지 업로드를 사용하시겠습니까?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
-    'app_secure_images_desc' => '성능상의 이유로 모든 이미지를 공개합니다. 해당 옵션은 이미지 URL 앞에 추측하기 어려운 임의의 문자열을 추가합니다. 간편한 접근을 방지하기 위해 디렉토리 색인을 비활성화하십시오.',
-    'app_editor' => '페이지 에디터',
-    'app_editor_desc' => '모든 사용자가 페이지를 편집하는데 사용할 에디터를 선택하십시오.',
-    'app_custom_html' => '사용자 정의 HTML 헤드 컨텐츠',
-    'app_custom_html_desc' => '여기에 추가된 모든 내용은 모든 페이지의 <head> 섹션 아래쪽에 삽입됩니다. 이는 스타일 오버라이딩이나 분석 코드 삽입에 편리합니다.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
-    'app_logo' => '어플리케이션 로고',
-    'app_logo_desc' => '해당 이미지는 반드시 높이가 43픽셀이어야 합니다. <br>대용량 이미지는 축소됩니다.',
-    'app_primary_color' => '어플리케이션 기본 색상',
-    'app_primary_color_desc' => '해당 값은 16진수이어야 합니다. <br>입력하지 않으면 기본 색상으로 재설정됩니다.',
-    'app_homepage' => '어플리케이션 홈페이지',
-    'app_homepage_desc' => '기본 화면 대신에 홈페이지에 표시할 화면을 선택하십시오. 선택된 페이지에서는 페이지 권한이 무시됩니다.',
-    'app_homepage_select' => '페이지를 선택하십시오',
-    'app_disable_comments' => '주석 비활성화',
-    'app_disable_comments_toggle' => 'Disable comments',
-    'app_disable_comments_desc' => '어플리케이션의 모든 페이지에서 주석을 비활성화합니다. 기존의 주석은 표시되지 않습니다.',
+    'app_customization' => '맞춤',
+    'app_features_security' => '보안',
+    'app_name' => '사이트 제목',
+    'app_name_desc' => '메일을 보낼 때 이 제목을 씁니다.',
+    'app_name_header' => '사이트 헤더 사용',
+    'app_public_access' => '사이트 공개',
+    'app_public_access_desc' => '계정 없는 사용자가 문서를 볼 수 있습니다.',
+    'app_public_access_desc_guest' => '이들의 권한은 사용자 이름이 Guest인 사용자로 관리할 수 있습니다.',
+    'app_public_access_toggle' => '사이트 공개',
+    'app_public_viewing' => '공개할 건가요?',
+    'app_secure_images' => '이미지 주소 보호',
+    'app_secure_images_toggle' => '이미지 주소 보호',
+    'app_secure_images_desc' => '성능상의 문제로 이미지에 누구나 접근할 수 있기 때문에 이미지 주소를 무작위한 문자로 구성합니다. 폴더 색인을 끄세요.',
+    'app_editor' => '에디터',
+    'app_editor_desc' => '모든 사용자에게 적용합니다.',
+    'app_custom_html' => '헤드 작성',
+    'app_custom_html_desc' => '설정 페이지를 제외한 모든 페이지 head 태그 끝머리에 추가합니다.',
+    'app_custom_html_disabled_notice' => '문제가 생겨도 설정 페이지에서 되돌릴 수 있어요.',
+    'app_logo' => '사이트 로고',
+    'app_logo_desc' => '높이를 43px로 구성하세요. 큰 이미지는 축소합니다.',
+    'app_primary_color' => '사이트 색채',
+    'app_primary_color_desc' => '16진수로 구성하세요. 비웠을 때는 기본 색채로 설정합니다.',
+    'app_homepage' => '처음 페이지',
+    'app_homepage_desc' => '고른 페이지에 설정한 권한은 무시합니다.',
+    'app_homepage_select' => '문서 고르기',
+    'app_disable_comments' => '댓글 사용 안 함',
+    'app_disable_comments_toggle' => '댓글 사용 안 함',
+    'app_disable_comments_desc' => '모든 페이지에서 댓글을 숨깁니다.',
+
+    // Color settings
+    'content_colors' => '본문 색상',
+    'content_colors_desc' => '페이지에 있는 모든 요소에 대한 색상 지정하세요. 가독성을 위해 기본 색상과 유사한 밝기를 가진 색상으로 추천됩니다.',
+    'bookshelf_color' => '책선반 색상',
+    'book_color' => '책 색상',
+    'chapter_color' => '챕터 색상',
+    'page_color' => '페이지 색상',
+    'page_draft_color' => '초안 페이지 색상',
 
     // Registration Settings
-    'reg_settings' => '등록 설정',
-    'reg_enable' => 'Enable Registration',
-    'reg_enable_toggle' => 'Enable registration',
-    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
-    'reg_default_role' => '등록 후 기본 사용자 역할',
-    'reg_email_confirmation' => 'Email Confirmation',
-    'reg_email_confirmation_toggle' => 'Require email confirmation',
-    'reg_confirm_email_desc' => '도메인 제한이 사용되면 이메일 확인이 요구되며, 하단의 값은 무시됩니다.',
-    'reg_confirm_restrict_domain' => '도메인 등록 제한',
-    'reg_confirm_restrict_domain_desc' => '등록을 제한할 이메일 도메인의 목록을 쉼표로 구분하여 입력해주십시오. 사용자는 어플리케이션과의 상호작용을 허가받기 전에 이메일 주소를 확인하는 이메일을 받게 됩니다, <br> 등록이 완료된 후에는 이메일 주소를 변경할 수 있습니다.',
-    'reg_confirm_restrict_domain_placeholder' => '제한 없음 설정',
+    'reg_settings' => '가입',
+    'reg_enable' => '사이트 가입 허용',
+    'reg_enable_toggle' => '사이트 가입 허용',
+    'reg_enable_desc' => '가입한 사용자는 단일한 권한을 가집니다.',
+    'reg_default_role' => '가입한 사용자의 기본 권한',
+    'reg_enable_external_warning' => '외부 LDAP 또는 SAML 인증이 활성화되어 있는 동안에는 위의 옵션이 무시된다. 사용 중인 외부 시스템에 대해 인증이 성공하면, 존재하지 않는 회원에 대한 사용자 계정이 자동으로 생성된다.',
+    'reg_email_confirmation' => '메일 주소 확인',
+    'reg_email_confirmation_toggle' => '주소 확인 요구',
+    'reg_confirm_email_desc' => '도메인 차단을 쓰고 있으면 메일 주소를 확인해야 하고, 이 설정은 무시합니다.',
+    'reg_confirm_restrict_domain' => '도메인 차단',
+    'reg_confirm_restrict_domain_desc' => '쉼표로 분리해서 가입을 차단할 메일 주소 도메인을 쓰세요. 이 설정과 관계없이 사용자가 메일을 보내고, 가입한 사용자가 메일 주소를 바꿀 수 있습니다.',
+    'reg_confirm_restrict_domain_placeholder' => '없음',
 
     // Maintenance settings
-    'maint' => 'Maintenance',
+    'maint' => '데이터',
     'maint_image_cleanup' => '이미지 정리',
-    'maint_image_cleanup_desc' => "페이지를 스캔하여 현재 사용중인 이미지와 도면에서 수정된 내용 및 중복된 이미지를 확인합니다. 이를 실행하기 전에 전체 데이터베이스와 이미지의 백업을 작성했는지 확인하십시오.",
-    'maint_image_cleanup_ignore_revisions' => '수정본의 이미지를 무시합니다.',
-    'maint_image_cleanup_run' => '정리 실행',
-    'maint_image_cleanup_warning' => '잠재적으로 사용되지 않는 이미지를 찾았습니다. 해당 이미지들을 삭제하시겠습니까?',
-    'maint_image_cleanup_success' => ':잠재적으로 사용되지 않는 이미지들이 삭제되었습니다.',
-    'maint_image_cleanup_nothing_found' => '사용되지 않는 이미지를 찾을 수 없습니다. 아무것도 삭제되지 않았습니다.',
+    'maint_image_cleanup_desc' => "중복한 이미지를 찾습니다. 실행하기 전에 이미지를 백업하세요.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_run' => '실행',
+    'maint_image_cleanup_warning' => '이미지 :count개를 지울 건가요?',
+    'maint_image_cleanup_success' => '이미지 :count개 삭제함',
+    'maint_image_cleanup_nothing_found' => '삭제한 것 없음',
+    'maint_send_test_email' => '테스트 메일 보내기',
+    'maint_send_test_email_desc' => '프로필에 명시된 이메일주소로 테스트 메일이 전송됩니다.',
+    'maint_send_test_email_run' => '테스트 메일 보내기',
+    'maint_send_test_email_success' => '보낼 이메일 주소',
+    'maint_send_test_email_mail_subject' => '테스트 메일',
+    'maint_send_test_email_mail_greeting' => '이메일 전송이 성공하였습니다.',
+    'maint_send_test_email_mail_text' => '축하합니다! 이 메일을 받음으로 이메일 설정이 정상적으로 되었음을 확인하였습니다.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => '감사 기록',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => '이벤트 필터',
+    'audit_event_filter_no_filter' => '필터 없음',
+    'audit_deleted_item' => '삭제된 항목',
+    'audit_deleted_item_name' => '이름: :name',
+    'audit_table_user' => '사용자',
+    'audit_table_event' => '이벤트',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => '활동 날짜',
+    'audit_date_from' => '날짜 범위 시작',
+    'audit_date_to' => '날짜 범위 끝',
 
     // Role Settings
-    'roles' => '역할',
-    'role_user_roles' => '사용자 역할',
-    'role_create' => '신규 역할 생성',
-    'role_create_success' => '역할이 생성되었습니다.',
-    'role_delete' => '역할을 삭제합니다.',
-    'role_delete_confirm' => '\':roleName\'(이)라는 이름의 역할이 삭제됩니다.',
-    'role_delete_users_assigned' => '해당 역할에 :userCount 명의 사용자가 할당되어 있습니다. 이 역할로부터 사용자를 재할당하고 싶다면 아래에서 새 역할을 선택하십시오.',
-    'role_delete_no_migration' => "사용자 재배치 안함",
-    'role_delete_sure' => '이 역할을 삭제하시겠습니까?',
-    'role_delete_success' => '역할이 삭제되었습니다.',
-    'role_edit' => '역할 편집',
-    'role_details' => '역할 상세정보',
-    'role_name' => '역할명',
-    'role_desc' => 'ì\97­í\95 ì\97\90 ë\8c\80í\95\9c ê°\84ë\9eµí\95\9c ì\84¤ëª\85',
-    'role_external_auth_id' => '외부 인증 ID',
+    'roles' => '권한',
+    'role_user_roles' => '사용자 권한',
+    'role_create' => '권한 만들기',
+    'role_create_success' => '권한 만듦',
+    'role_delete' => '권한 제거',
+    'role_delete_confirm' => ':roleName(을)를 지웁니다.',
+    'role_delete_users_assigned' => '이 권한을 가진 사용자 :userCount명에 할당할 권한을 고르세요.',
+    'role_delete_no_migration' => "할당하지 않음",
+    'role_delete_sure' => '이 권한을 지울 건가요?',
+    'role_delete_success' => '권한 지움',
+    'role_edit' => '권한 수정',
+    'role_details' => '권한 정보',
+    'role_name' => '권한 이름',
+    'role_desc' => '설명',
+    'role_external_auth_id' => 'LDAP 확인',
     'role_system' => '시스템 권한',
     'role_manage_users' => '사용자 관리',
-    'role_manage_roles' => '역할 및 역할 권한 관리',
-    'role_manage_entity_permissions' => '모든 책, 챕터, 페이지 관리',
-    'role_manage_own_entity_permissions' => '보유한 책, 챕터, 페이지에 대한 권한 관리',
-    'role_manage_page_templates' => 'Manage page templates',
-    'role_manage_settings' => '어플리케이선 설정 관리',
-    'role_asset' => '자산 관리',
-    'role_asset_desc' => '해당 권한들은 시스템 내의 Assets 파일에 대한 기본적인 접근을 제어합니다.',
-    'role_asset_admins' => '관리자는 모든 컨텐츠에 대한 접근 권한을 자동으로 부여받지만, 해당 옵션들은 UI 옵션을 표시하거나 숨길 수 있습니다.',
-    'role_all' => '전체',
-    'role_own' => '보유한 것만',
-    'role_controlled_by_asset' => '업로드된 Assets 파일에 의해 제어됩니다.',
-    'role_save' => '역할 저장',
-    'role_update_success' => '역할이 업데이트되었습니다.',
-    'role_users' => '해당 역할의 사용자',
-    'role_users_none' => '현재 이 역할에 할당된 사용자가 없습니다.',
+    'role_manage_roles' => '권한 관리',
+    'role_manage_entity_permissions' => '문서별 권한 관리',
+    'role_manage_own_entity_permissions' => '직접 만든 문서별 권한 관리',
+    'role_manage_page_templates' => '템플릿 관리',
+    'role_access_api' => '시스템 접근 API',
+    'role_manage_settings' => '사이트 설정 관리',
+    'role_asset' => '권한 항목',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => '책자, 챕터, 문서별 권한은 이 설정에 우선합니다.',
+    'role_asset_admins' => 'Admin 권한은 어디든 접근할 수 있지만 이 설정은 사용자 인터페이스에서 해당 활동을 표시할지 결정합니다.',
+    'role_all' => '모든 항목',
+    'role_own' => '직접 만든 항목',
+    'role_controlled_by_asset' => '저마다 다름',
+    'role_save' => '저장',
+    'role_update_success' => '권한 저장함',
+    'role_users' => '이 권한을 가진 사용자들',
+    'role_users_none' => '그런 사용자가 없습니다.',
 
     // Users
     'users' => '사용자',
     'user_profile' => '사용자 프로필',
-    'users_add_new' => '사용자 추가',
+    'users_add_new' => '사용자 만들기',
     'users_search' => '사용자 검색',
-    'users_details' => 'User Details',
-    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
-    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
-    'users_role' => '사용자 역할',
-    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
-    'users_password' => 'User Password',
-    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
-    'users_external_auth_id' => '외부 인증 ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
-    'users_password_warning' => '비밀번호를 변경하시려면 다음을 입력하십시오:',
-    'users_system_public' => '이 사용자는 당신의 인스턴스를 방문하는 게스트 사용자를 나타냅니다. 로그인하는 데는 사용할 수 없지만 자동으로 할당됩니다.',
+    'users_latest_activity' => 'Latest Activity',
+    'users_details' => '사용자 정보',
+    'users_details_desc' => '메일 주소로 로그인합니다.',
+    'users_details_desc_no_email' => '사용자 이름을 바꿉니다.',
+    'users_role' => '사용자 권한',
+    'users_role_desc' => '고른 권한 모두를 적용합니다.',
+    'users_password' => '사용자 비밀번호',
+    'users_password_desc' => '여섯 글자를 넘어야 합니다.',
+    'users_send_invite_text' => '비밀번호 설정을 권유하는 메일을 보내거나 내가 정할 수 있습니다.',
+    'users_send_invite_option' => '메일 보내기',
+    'users_external_auth_id' => 'LDAP 확인',
+    'users_external_auth_id_desc' => '외부 인증 시스템과 통신할 때 사용자와 연결시키는 데 사용되는 ID 입니다.',
+    'users_password_warning' => '비밀번호를 바꿀 때만 쓰세요.',
+    'users_system_public' => '계정 없는 모든 사용자에 할당한 사용자입니다. 이 사용자로 로그인할 수 없어요.',
     'users_delete' => '사용자 삭제',
-    'users_delete_named' => '사용자 :userName 삭제',
-    'users_delete_warning' => '시스템에서 \':userName\'(이)라는 사용자가 완전히 삭제됩니다.',
-    'users_delete_confirm' => '이 사용자를 삭제하시겠습니까?',
-    'users_delete_success' => '사용자가 삭제되었습니다.',
-    'users_edit' => '사용자 편집',
-    'users_edit_profile' => '프로필 편집',
-    'users_edit_success' => '사용자가 업데이트되었습니다.',
-    'users_avatar' => '사용자 아바타',
-    'users_avatar_desc' => '해당 이미지는 256픽셀의 정사각형 이미지여야합니다.',
-    'users_preferred_language' => '선호하는 언어',
-    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_delete_named' => ':userName 삭제',
+    'users_delete_warning' => ':userName에 관한 데이터를 지웁니다.',
+    'users_delete_confirm' => '이 사용자를 지울 건가요?',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
+    'users_edit' => '사용자 수정',
+    'users_edit_profile' => '프로필 바꾸기',
+    'users_edit_success' => '프로필 바꿈',
+    'users_avatar' => '프로필 이미지',
+    'users_avatar_desc' => '이미지 규격은 256x256px 내외입니다.',
+    'users_preferred_language' => '언어',
+    'users_preferred_language_desc' => '문서 내용에는 아무런 영향을 주지 않습니다.',
     'users_social_accounts' => '소셜 계정',
-    'users_social_accounts_info' => 'ì\97¬ê¸°ì\97\90ì\84\9c ë\8b¤ë¥¸ ê³\84ì \95ì\9d\84 ì\97°ê²°í\95\98ì\97¬ ë\8d\94 ë¹ ë¥´ê³  ì\89½ê²\8c ë¡\9cê·¸ì\9d¸í\95  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤. ì\97¬ê¸°ì\97\90ì\84\9c ê³\84ì \95 ì\97°ê²°ì\9d\84 í\95´ì \9cí\95\98ë©´ ì\9d´ì \84ì\97\90 ì\8a¹ì\9d¸ë\90\9c ì \91ê·¼ì\9d´ ì \9cê³µë\90\98ì§\80 ì\95\8aì\8aµë\8b\88ë\8b¤ ì\97°ê²°ë\90\9c ì\86\8cì\85\9c ê³\84ì \95ì\9d\98 í\94\84ë¡\9cí\95\84 ì\84¤ì \95ì\97\90ì\84\9c ì \91ê·¼ ê¶\8cí\95\9cì\9d\84 ì·¨ì\86\8cí\95\98ì\8b­ì\8b\9cì\98¤.',
+    'users_social_accounts_info' => 'ë\8b¤ë¥¸ ê³\84ì \95ì\9c¼ë¡\9c ê°\84ë\8b¨í\95\98ê²\8c ë¡\9cê·¸ì\9d¸í\95\98ì\84¸ì\9a\94. ì\97¬ê¸°ì\97\90ì\84\9c ê³\84ì \95 ì\97°ê²°ì\9d\84 ë\81\8aë\8a\94 ê²\83ê³¼ ì\86\8cì\85\9c ê³\84ì \95ì\97\90ì\84\9c ì \91ê·¼ ê¶\8cí\95\9cì\9d\84 ì·¨ì\86\8cí\95\98ë\8a\94 ê²\83ì\9d\80 ë³\84ê°\9cì\9e\85ë\8b\88ë\8b¤.',
     'users_social_connect' => '계정 연결',
-    'users_social_disconnect' => '계정 연결 해제',
-    'users_social_connected' => ':socialAccount 계정이 당신의 프로필에 연결되었습니다.',
-    'users_social_disconnected' => ':socialAccount 계정이 당신의 프로필에서 연결해제되었습니다.',
+    'users_social_disconnect' => '계정 연결 끊기',
+    'users_social_connected' => ':socialAccount(와)과 연결했습니다.',
+    'users_social_disconnected' => ':socialAccount(와)과의 연결을 끊었습니다.',
+    'users_api_tokens' => 'API 토큰',
+    'users_api_tokens_none' => '이 사용자를 위해 생성된 API 토큰이 없습니다.',
+    'users_api_tokens_create' => '토큰 만들기',
+    'users_api_tokens_expires' => '만료',
+    'users_api_tokens_docs' => 'API 설명서',
+
+    // API Tokens
+    'user_api_token_create' => 'API 토큰 만들기',
+    'user_api_token_name' => '제목',
+    'user_api_token_name_desc' => '토큰이 의도한 목적을 향후에 상기시키기 위해 알아보기 쉬운 이름을 지정한다.',
+    'user_api_token_expiry' => '만료일',
+    'user_api_token_expiry_desc' => '이 토큰이 만료되는 날짜를 설정한다. 이 날짜가 지나면 이 토큰을 사용하여 만든 요청은 더 이상 작동하지 않는다. 이 칸을 공백으로 두면 100년 뒤가 만기가 된다.',
+    'user_api_token_create_secret_message' => '이 토큰을 만든 직후 "토큰 ID"와 "토큰 시크릿"이 생성되서 표시 된다. 시크릿은 한 번만 표시되므로 계속 진행하기 전에 안전하고 안심할 수 있는 곳에 값을 복사한다.',
+    'user_api_token_create_success' => 'API 토큰이 성공적으로 생성되었다.',
+    'user_api_token_update_success' => 'API 토큰이 성공적으로 갱신되었다.',
+    'user_api_token' => 'API 토큰',
+    'user_api_token_id' => '토큰 ID',
+    'user_api_token_id_desc' => '이 토큰은 API 요청으로 제공되어야 하는 편집 불가능한 시스템이 생성한 식별자다.',
+    'user_api_token_secret' => '토큰 시크릿',
+    'user_api_token_secret_desc' => '이것은 API 요청시 제공되어야 할 이 토큰에 대한 시스템에서 생성된 시크릿이다. 이 값은 한 번만 표시되므로 안전하고 한심할 수 있는 곳에 이 값을 복사한다.',
+    'user_api_token_created' => ':timeAgo 전에 토큰이 생성되었다.',
+    'user_api_token_updated' => ':timeAgo 전에 토큰이 갱신되었다.',
+    'user_api_token_delete' => '토큰 삭제',
+    'user_api_token_delete_warning' => '이렇게 하면 시스템에서 \':tokenName\'이라는 이름을 가진 이 API 토큰이 완전히 삭제된다.',
+    'user_api_token_delete_confirm' => '이 API 토큰을 삭제하시겠습니까?',
+    'user_api_token_delete_success' => 'API 토큰이 성공적으로 삭제되었다.',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => '히브리어',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 07ecdd021e4127cb52cbb819b96d8beee6b48bf9..64c4a4345b256d7d1c31f4dc9ea7466a3fc6e647 100644 (file)
 return [
 
     // Standard laravel validation lines
-    'accepted'             => ':attribute가 반드시 허용되어야 합니다.',
-    'active_url'           => ':attribute가 올바른 URL이 아닙니다.',
-    'after'                => ':attribute는 :date이후 날짜여야 합니다.',
-    'alpha'                => ':attribute는 문자만 포함해야 합니다.',
-    'alpha_dash'           => ':attribute는 문자, 숫자, 대시만 포함해야 합니다.',
-    'alpha_num'            => ':attribute는 문자와 숫자만 포함됩니다.',
-    'array'                => ':attribute는 배열이어야 합니다.',
-    'before'               => ':attribute는 :date이전 날짜여야 합니다.',
+    'accepted'             => ':attribute(을)를 허용하세요.',
+    'active_url'           => ':attribute(을)를 유효한 주소로 구성하세요.',
+    'after'                => ':attribute(을)를 :date 후로 설정하세요.',
+    'alpha'                => ':attribute(을)를 문자로만 구성하세요.',
+    'alpha_dash'           => ':attribute(을)를 문자, 숫자, -, _로만 구성하세요.',
+    'alpha_num'            => ':attribute(을)를 문자, 숫자로만 구성하세요.',
+    'array'                => ':attribute(을)를 배열로 구성하세요.',
+    'before'               => ':attribute(을)를 :date 전으로 설정하세요.',
     'between'              => [
-        'numeric' => ':attribute는 반드시 :min이상 :max이하여야 합니다.',
-        'file'    => ':attribute는 반드시 :min이상 :max kilobytes이하여야 합니다.',
-        'string'  => ':attribute는 반드시 :min이상 :max 문자 이하여야 합니다.',
-        'array'   => ':attribute는 반드시 :min이상 :max이하 항목이어야 합니다.',
+        'numeric' => ':attribute(을)를 :min~:max(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 :min~:max킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 :min~:max바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 :min~:max개로 구성하세요.',
     ],
-    'boolean'              => ':attribute 는 true혹은 false값만 가능합니다.',
-    'confirmed'            => ':attribute 확인이 일치하지 않습니다.',
-    'date'                 => ':attribute 는 잘못된 날짜입니다.',
-    'date_format'          => ':attribute 이 :format 포멧과 일치하지 않습니다.',
-    'different'            => ':attribute 와 :other는 반드시 달라야 합니다.',
-    'digits'               => ':attribute 는 반드시 :digits 숫자(digit)여야 합니다.',
-    'digits_between'       => ':attribute 는 반드시 :min이상 :max이하 숫자여야 합니다.',
-    'email'                => ':attribute 는 반드시 이메일 이어야 합니다.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
-    'filled'               => ':attribute 항목이 꼭 필요합니다.',
+    'boolean'              => ':attribute(을)를 true나 false로만 구성하세요.',
+    'confirmed'            => ':attribute(와)과 다릅니다.',
+    'date'                 => ':attribute(을)를 유효한 날짜로 구성하세요.',
+    'date_format'          => ':attribute(은)는 :format(와)과 다릅니다.',
+    'different'            => ':attribute(와)과 :other(을)를 다르게 구성하세요.',
+    'digits'               => ':attribute(을)를 :digits자리로 구성하세요.',
+    'digits_between'       => ':attribute(을)를 :min~:max자리로 구성하세요.',
+    'email'                => ':attribute(을)를 유효한 메일 주소로 구성하세요.',
+    'ends_with' => ':attribute(을)를 :values(으)로 끝나게 구성하세요.',
+    'filled'               => ':attribute(을)를 구성하세요.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute(을)를 :value(이)가 넘게 구성하세요.',
+        'file'    => ':attribute(을)를 :value킬로바이트가 넘게 구성하세요.',
+        'string'  => ':attribute(을)를 :value바이트가 넘게 구성하세요.',
+        'array'   => ':attribute(을)를 :value개가 넘게 구성하세요.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => ':attribute(을)를 적어도 :value(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 적어도 :value킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 적어도 :value바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 적어도 :value개로 구성하세요..',
     ],
-    'exists'               => '선택된 :attribute 은(는) 사용 불가합니다.',
-    'image'                => ':attribute 는 반드시 이미지여야 합니다.',
-    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
-    'in'                   => '선택된 :attribute 은(는) 사용 불가합니다.',
-    'integer'              => ':attribute 는 반드시(integer)여야 합니다.',
-    'ip'                   => ':attribute 는 반드시 IP주소 여야 합니다.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'exists'               => '고른 :attribute(이)가 유효하지 않습니다.',
+    'image'                => ':attribute(을)를 이미지로 구성하세요.',
+    'image_extension'      => ':attribute(을)를 유효한 이미지 확장자로 구성하세요.',
+    'in'                   => '고른 :attribute(이)가 유효하지 않습니다.',
+    'integer'              => ':attribute(을)를 정수로 구성하세요.',
+    'ip'                   => ':attribute(을)를 유효한 IP 주소로 구성하세요.',
+    'ipv4'                 => ':attribute(을)를 유효한 IPv4 주소로 구성하세요.',
+    'ipv6'                 => ':attribute(을)를 유효한 IPv6 주소로 구성하세요.',
+    'json'                 => ':attribute(을)를 유효한 JSON으로 구성하세요.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute(을)를 :value(이)가 안 되게 구성하세요.',
+        'file'    => ':attribute(을)를 :value킬로바이트가 안 되게 구성하세요.',
+        'string'  => ':attribute(을)를 :value바이트가 안 되게 구성하세요.',
+        'array'   => ':attribute(을)를 :value개가 안 되게 구성하세요.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute(을)를 많아야 :max(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 많아야 :max킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 많아야 :max바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 많아야 :max개로 구성하세요.',
     ],
     'max'                  => [
-        'numeric' => ':attribute :max 보다 크면 안됩니다.',
-        'file'    => ':attribute :max kilobytes보다 크면 안됩니다.',
-        'string'  => ':attribute :max 문자보다 길면 안됩니다.',
-        'array'   => ':attribute :max 를 초과하면 안됩니다.',
+        'numeric' => ':attribute(을)를 많아야 :max(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 많아야 :max킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 많아야 :max바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 많아야 :max개로 구성하세요.',
     ],
-    'mimes'                => ':attribute 은(는) 반드시 :values 타입이어야 합니다.',
+    'mimes'                => ':attribute(을)를 :values 형식으로 구성하세요.',
     'min'                  => [
-        'numeric' => ':attribute 은(는) 최소한 :min 이어야 합니다.',
-        'file'    => ':attribute 은(는) 최소한 :min kilobytes여야 합니다.',
-        'string'  => ':attribute 은(는) 최소한 :min 개 문자여야 합니다.',
-        'array'   => ':attribute 은(는) 적어도 :min 개의 항목이어야 합니다.',
+        'numeric' => ':attribute(을)를 적어도 :value(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 적어도 :value킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 적어도 :value바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 적어도 :value개로 구성하세요..',
     ],
-    'no_double_extension'  => 'The :attribute must only have a single file extension.',
-    'not_in'               => '선택된 :attribute 는 사용할 수 없습니다',
-    'not_regex'            => 'The :attribute format is invalid.',
-    'numeric'              => ':attribute 반드시 숫자여야 합니다.',
-    'regex'                => ':attribute 포멧이 잘못되었습니다.',
-    'required'             => ':attribute 항목은 필수입니다..',
-    'required_if'          => ':attribute 은(는) :other 가 :value 일때 필수항목입니다.',
-    'required_with'        => ':attribute 은(는) :values 가 있을때 필수항목입니다.',
-    'required_with_all'    => ':attribute 은(는) :values 가 있을때 필수항목입니다.',
-    'required_without'     => ':attribute 은(는) :values 가 없을때 필수항목입니다.',
-    'required_without_all' => ':attribute 은(는) :values 가 전혀 없을때 필수항목입니다.',
-    'same'                 => ':attribute 와 :other 은(는) 반드시 일치해야합니다.',
+    'no_double_extension'  => ':attribute(이)가 단일한 확장자를 가져야 합니다.',
+    'not_in'               => '고른 :attribute(이)가 유효하지 않습니다.',
+    'not_regex'            => ':attribute(은)는 유효하지 않은 형식입니다.',
+    'numeric'              => ':attribute(을)를 숫자로만 구성하세요.',
+    'regex'                => ':attribute(은)는 유효하지 않은 형식입니다.',
+    'required'             => ':attribute(을)를 구성하세요.',
+    'required_if'          => ':other(이)가 :value일 때 :attribute(을)를 구성해야 합니다.',
+    'required_with'        => ':values(이)가 있을 때 :attribute(을)를 구성해야 합니다.',
+    'required_with_all'    => ':values(이)가 모두 있을 때 :attribute(을)를 구성해야 합니다.',
+    'required_without'     => ':values(이)가 없을 때 :attribute(을)를 구성해야 합니다.',
+    'required_without_all' => ':values(이)가 모두 없을 때 :attribute(을)를 구성해야 합니다.',
+    'same'                 => ':attribute(와)과 :other(을)를 똑같이 구성하세요.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
-        'numeric' => ':attribute 은(는) :size 여야합니다.',
-        'file'    => ':attribute 은(는) :size kilobytes여야합니다.',
-        'string'  => ':attribute 은(는) :size 문자여야합니다.',
-        'array'   => ':attribute 은(는) :size 개 항목을 포함해야 합니다.',
+        'numeric' => ':attribute(을)를 :size(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 :size킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 :size바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 :size개로 구성하세요..',
     ],
-    'string'               => ':attribute 문자열이어야 합니다.',
-    'timezone'             => ':attribute 정상적인 지역(zone)이어야 합니다.',
-    'unique'               => ':attribute 은(는) 이미 사용중입니다..',
-    'url'                  => ':attribute 포멧이 사용 불가합니다.',
-    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+    'string'               => ':attribute(을)를 문자로 구성하세요.',
+    'timezone'             => ':attribute(을)를 유효한 시간대로 구성하세요.',
+    'unique'               => ':attribute(은)는 이미 있습니다.',
+    'url'                  => ':attribute(은)는 유효하지 않은 형식입니다.',
+    'uploaded'             => '파일 크기가 서버에서 허용하는 수치를 넘습니다.',
 
     // Custom validation lines
     'custom' => [
         'password-confirm' => [
-            'required_with' => '비밀번호 확인이 필요합니다.',
+            'required_with' => '같은 비밀번호를 다시 입력하세요.',
         ],
     ],
 
diff --git a/resources/lang/nb/activities.php b/resources/lang/nb/activities.php
new file mode 100644 (file)
index 0000000..e2516ab
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'opprettet side',
+    'page_create_notification'    => 'Siden ble opprettet',
+    'page_update'                 => 'oppdaterte side',
+    'page_update_notification'    => 'Siden ble oppdatert',
+    'page_delete'                 => 'slettet side',
+    'page_delete_notification'    => 'Siden ble slettet',
+    'page_restore'                => 'gjenopprettet side',
+    'page_restore_notification'   => 'Siden ble gjenopprettet',
+    'page_move'                   => 'flyttet side',
+
+    // Chapters
+    'chapter_create'              => 'opprettet kapittel',
+    'chapter_create_notification' => 'Kapittelet ble opprettet',
+    'chapter_update'              => 'oppdaterte kapittel',
+    'chapter_update_notification' => 'Kapittelet ble oppdatert',
+    'chapter_delete'              => 'slettet kapittel',
+    'chapter_delete_notification' => 'Kapittelet ble slettet',
+    'chapter_move'                => 'flyttet kapittel
+    ',
+
+    // Books
+    'book_create'                 => 'opprettet bok',
+    'book_create_notification'    => 'Boken ble opprettet',
+    'book_update'                 => 'oppdaterte bok',
+    'book_update_notification'    => 'Boken ble oppdatert',
+    'book_delete'                 => 'slettet bok',
+    'book_delete_notification'    => 'Boken ble slettet',
+    'book_sort'                   => 'sorterte bok',
+    'book_sort_notification'      => 'Boken ble omsortert',
+
+    // Bookshelves
+    'bookshelf_create'            => 'opprettet bokhylle',
+    'bookshelf_create_notification'    => 'Bokhyllen ble opprettet',
+    'bookshelf_update'                 => 'oppdaterte bokhylle',
+    'bookshelf_update_notification'    => 'Bokhyllen ble oppdatert',
+    'bookshelf_delete'                 => 'slettet bokhylle',
+    'bookshelf_delete_notification'    => 'Bokhyllen ble slettet',
+
+    // Other
+    'commented_on'                => 'kommenterte på',
+    'permissions_update'          => 'updated permissions',
+];
diff --git a/resources/lang/nb/auth.php b/resources/lang/nb/auth.php
new file mode 100644 (file)
index 0000000..ae145d2
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'Disse detaljene samsvarer ikke med det vi har på bok.',
+    'throttle' => 'For mange forsøk, prøv igjen om :seconds sekunder.',
+
+    // Login & Register
+    'sign_up' => 'Registrer deg',
+    'log_in' => 'Logg inn',
+    'log_in_with' => 'Logg inn med :socialDriver',
+    'sign_up_with' => 'Registrer med :socialDriver',
+    'logout' => 'Logg ut',
+
+    'name' => 'Navn',
+    'username' => 'Brukernavn',
+    'email' => 'E-post',
+    'password' => 'Passord',
+    'password_confirm' => 'Bekreft passord',
+    'password_hint' => 'Må inneholde 7 tegn',
+    'forgot_password' => 'Glemt passord?',
+    'remember_me' => 'Husk meg',
+    'ldap_email_hint' => 'Oppgi en e-post for denne kontoen.',
+    'create_account' => 'Opprett konto',
+    'already_have_account' => 'Har du allerede en konto?',
+    'dont_have_account' => 'Mangler du en konto?',
+    'social_login' => 'Sosiale kontoer',
+    'social_registration' => 'Registrer via sosiale kontoer',
+    'social_registration_text' => 'Bruk en annen tjeneste for å registrere deg.',
+
+    'register_thanks' => 'Takk for at du registrerte deg!',
+    'register_confirm' => 'Sjekk e-posten din for informasjon som gir deg tilgang til :appName.',
+    'registrations_disabled' => 'Registrering er deaktivert.',
+    'registration_email_domain_invalid' => 'Du kan ikke bruke det domenet for å registrere en konto.',
+    'register_success' => 'Takk for registreringen! Du kan nå logge inn på tjenesten.',
+
+
+    // Password Reset
+    'reset_password' => 'Nullstille passord',
+    'reset_password_send_instructions' => 'Oppgi e-posten som er koblet til kontoen din, så sender vi en epost hvor du kan nullstille passordet.',
+    'reset_password_send_button' => 'Send nullstillingslenke',
+    'reset_password_sent' => 'En nullstillingslenke ble sendt til :email om den eksisterer i systemet.',
+    'reset_password_success' => 'Passordet ble nullstilt.',
+    'email_reset_subject' => 'Nullstill ditt :appName passord',
+    'email_reset_text' => 'Du mottar denne eposten fordi det er blitt bedt om en nullstilling av passord på denne kontoen.',
+    'email_reset_not_requested' => 'Om det ikke var deg, så trenger du ikke foreta deg noe.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Bekreft epost-adressen for :appName',
+    'email_confirm_greeting' => 'Takk for at du registrerte deg for :appName!',
+    'email_confirm_text' => 'Bekreft e-posten din ved å trykke på knappen nedenfor:',
+    'email_confirm_action' => 'Bekreft e-post',
+    'email_confirm_send_error' => 'Bekreftelse er krevd av systemet, men systemet kan ikke sende disse. Kontakt admin for å løse problemet.',
+    'email_confirm_success' => 'E-posten din er bekreftet!',
+    'email_confirm_resent' => 'Bekreftelsespost ble sendt, sjekk innboksen din.',
+
+    'email_not_confirmed' => 'E-posten er ikke bekreftet.',
+    'email_not_confirmed_text' => 'Epost-adressen er ennå ikke bekreftet.',
+    'email_not_confirmed_click_link' => 'Trykk på lenken i e-posten du fikk vedrørende din registrering.',
+    'email_not_confirmed_resend' => 'Om du ikke finner den i innboksen eller søppelboksen, kan du få tilsendt ny ved å trykke på knappen under.',
+    'email_not_confirmed_resend_button' => 'Send bekreftelsespost på nytt',
+
+    // User Invite
+    'user_invite_email_subject' => 'Du har blitt invitert til :appName!',
+    'user_invite_email_greeting' => 'En konto har blitt opprettet for deg på :appName.',
+    'user_invite_email_text' => 'Trykk på knappen under for å opprette et sikkert passord:',
+    'user_invite_email_action' => 'Angi passord',
+    'user_invite_page_welcome' => 'Velkommen til :appName!',
+    'user_invite_page_text' => 'For å fullføre prosessen må du oppgi et passord som sikrer din konto på :appName for fremtidige besøk.',
+    'user_invite_page_confirm_button' => 'Bekreft passord',
+    'user_invite_success' => 'Passordet er angitt, du kan nå bruke :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/nb/common.php b/resources/lang/nb/common.php
new file mode 100644 (file)
index 0000000..5fab437
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Avbryt',
+    'confirm' => 'Bekreft',
+    'back' => 'Tilbake',
+    'save' => 'Lagre',
+    'continue' => 'Fortsett',
+    'select' => 'Velg',
+    'toggle_all' => 'Bytt alle',
+    'more' => 'Mer',
+
+    // Form Labels
+    'name' => 'Navn',
+    'description' => 'Beskrivelse',
+    'role' => 'Rolle',
+    'cover_image' => 'Bokomslag',
+    'cover_image_description' => 'Bildet bør være ca. 440x250px.',
+    
+    // Actions
+    'actions' => 'Handlinger',
+    'view' => 'Vis',
+    'view_all' => 'Vis alle',
+    'create' => 'Opprett',
+    'update' => 'Oppdater',
+    'edit' => 'Rediger',
+    'sort' => 'Sorter',
+    'move' => 'Flytt',
+    'copy' => 'Kopier',
+    'reply' => 'Svar',
+    'delete' => 'Slett',
+    'delete_confirm' => 'Bekreft sletting',
+    'search' => 'Søk',
+    'search_clear' => 'Nullstill søk',
+    'reset' => 'Nullstill',
+    'remove' => 'Fjern',
+    'add' => 'Legg til',
+    'fullscreen' => 'Fullskjerm',
+
+    // Sort Options
+    'sort_options' => 'Sorteringsalternativer',
+    'sort_direction_toggle' => 'Sorteringsretning',
+    'sort_ascending' => 'Stigende sortering',
+    'sort_descending' => 'Synkende sortering',
+    'sort_name' => 'Navn',
+    'sort_created_at' => 'Dato opprettet',
+    'sort_updated_at' => 'Dato oppdatert',
+
+    // Misc
+    'deleted_user' => 'Slett bruker',
+    'no_activity' => 'Ingen aktivitet å vise',
+    'no_items' => 'Ingen ting å vise',
+    'back_to_top' => 'Hopp til toppen',
+    'toggle_details' => 'Vis/skjul detaljer',
+    'toggle_thumbnails' => 'Vis/skjul miniatyrbilder',
+    'details' => 'Detaljer',
+    'grid_view' => 'Rutenettvisning',
+    'list_view' => 'Listevisning',
+    'default' => 'Standard',
+    'breadcrumb' => 'Brødsmuler',
+
+    // Header
+    'profile_menu' => 'Profilmeny',
+    'view_profile' => 'Vis profil',
+    'edit_profile' => 'Endre Profile',
+    'dark_mode' => 'Kveldsmodus',
+    'light_mode' => 'Dagmodus',
+
+    // Layout tabs
+    'tab_info' => 'Informasjon',
+    'tab_content' => 'Innhold',
+
+    // Email Content
+    'email_action_help' => 'Om du har problemer med å trykke på «:actionText»-knappen, bruk nettadressen under for å gå direkte dit:',
+    'email_rights' => 'Kopibeskyttet',
+];
diff --git a/resources/lang/nb/components.php b/resources/lang/nb/components.php
new file mode 100644 (file)
index 0000000..cfc28c4
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Velg bilde',
+    'image_all' => 'Alle',
+    'image_all_title' => 'Vis alle bilder',
+    'image_book_title' => 'Vis bilder som er lastet opp i denne boken',
+    'image_page_title' => 'Vis bilder lastet opp til denne siden',
+    'image_search_hint' => 'Søk på bilder etter navn',
+    'image_uploaded' => 'Opplastet :uploadedDate',
+    'image_load_more' => 'Last in flere',
+    'image_image_name' => 'Bildenavn',
+    'image_delete_used' => 'Dette bildet er brukt på sidene nedenfor.',
+    'image_delete_confirm_text' => 'Vil du slette dette bildet?',
+    'image_select_image' => 'Velg bilde',
+    'image_dropzone' => 'Dra og slipp eller trykk her for å laste opp bilder',
+    'images_deleted' => 'Bilder slettet',
+    'image_preview' => 'Hurtigvisning av bilder',
+    'image_upload_success' => 'Bilde ble lastet opp',
+    'image_update_success' => 'Bildedetaljer ble oppdatert',
+    'image_delete_success' => 'Bilde ble slettet',
+    'image_upload_remove' => 'Fjern',
+
+    // Code Editor
+    'code_editor' => 'Endre kode',
+    'code_language' => 'Kodespråk',
+    'code_content' => 'Kodeinnhold',
+    'code_session_history' => 'Sesjonshistorikk',
+    'code_save' => 'Lagre kode',
+];
diff --git a/resources/lang/nb/entities.php b/resources/lang/nb/entities.php
new file mode 100644 (file)
index 0000000..dfee93e
--- /dev/null
@@ -0,0 +1,319 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Nylig opprettet',
+    'recently_created_pages' => 'Nylig opprettede sider',
+    'recently_updated_pages' => 'Nylig oppdaterte sider',
+    'recently_created_chapters' => 'Nylig opprettede kapitler',
+    'recently_created_books' => 'Nylig opprettede bøker',
+    'recently_created_shelves' => 'Nylig opprettede bokhyller',
+    'recently_update' => 'Nylig oppdatert',
+    'recently_viewed' => 'Nylig vist',
+    'recent_activity' => 'Nylig aktivitet',
+    'create_now' => 'Opprett en nå',
+    'revisions' => 'Revisjoner',
+    'meta_revision' => 'Revisjon #:revisionCount',
+    'meta_created' => 'Opprettet :timeLength',
+    'meta_created_name' => 'Opprettet :timeLength av :user',
+    'meta_updated' => 'Oppdatert :timeLength',
+    'meta_updated_name' => 'Oppdatert :timeLength av :user',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'Velg entitet',
+    'images' => 'Bilder',
+    'my_recent_drafts' => 'Mine nylige utkast',
+    'my_recently_viewed' => 'Mine nylige visninger',
+    'no_pages_viewed' => 'Du har ikke sett på noen sider',
+    'no_pages_recently_created' => 'Ingen sider har nylig blitt opprettet',
+    'no_pages_recently_updated' => 'Ingen sider har nylig blitt oppdatert',
+    'export' => 'Eksporter',
+    'export_html' => 'Nettside med alt',
+    'export_pdf' => 'PDF Fil',
+    'export_text' => 'Tekstfil',
+
+    // Permissions and restrictions
+    'permissions' => 'Tilganger',
+    'permissions_intro' => 'Når disse er tillatt, vil disse tillatelsene ha prioritet over alle angitte rolletillatelser.',
+    'permissions_enable' => 'Aktiver egendefinerte tillatelser',
+    'permissions_save' => 'Lagre tillatelser',
+    'permissions_owner' => 'Owner',
+
+    // Search
+    'search_results' => 'Søkeresultater',
+    'search_total_results_found' => ':count resultater funnet|:count totalt',
+    'search_clear' => 'Nullstill søk',
+    'search_no_pages' => 'Ingen sider passer med søket',
+    'search_for_term' => 'Søk etter :term',
+    'search_more' => 'Flere resultater',
+    'search_advanced' => 'Avansert søk',
+    'search_terms' => 'Søkeord',
+    'search_content_type' => 'Innholdstype',
+    'search_exact_matches' => 'Eksakte ord',
+    'search_tags' => 'Søk på merker',
+    'search_options' => 'ALternativer',
+    'search_viewed_by_me' => 'Sett av meg',
+    'search_not_viewed_by_me' => 'Ikke sett av meg',
+    'search_permissions_set' => 'Tilganger er angitt',
+    'search_created_by_me' => 'Opprettet av meg',
+    'search_updated_by_me' => 'Oppdatert av meg',
+    'search_date_options' => 'Datoalternativer',
+    'search_updated_before' => 'Oppdatert før',
+    'search_updated_after' => 'Oppdatert etter',
+    'search_created_before' => 'Opprettet før',
+    'search_created_after' => 'Opprettet etter',
+    'search_set_date' => 'Angi dato',
+    'search_update' => 'Oppdater søk',
+
+    // Shelves
+    'shelf' => 'Hylle',
+    'shelves' => 'Hyller',
+    'x_shelves' => ':count hylle|:count hyller',
+    'shelves_long' => 'Bokhyller',
+    'shelves_empty' => 'Ingen bokhyller er opprettet',
+    'shelves_create' => 'Opprett ny bokhylle',
+    'shelves_popular' => 'Populære bokhyller',
+    'shelves_new' => 'Nye bokhyller',
+    'shelves_new_action' => 'Ny bokhylle',
+    'shelves_popular_empty' => 'De mest populære bokhyllene blir vist her.',
+    'shelves_new_empty' => 'Nylig opprettede bokhyller vises her.',
+    'shelves_save' => 'Lagre hylle',
+    'shelves_books' => 'Bøker på denne hyllen',
+    'shelves_add_books' => 'Legg til bøker på denne hyllen',
+    'shelves_drag_books' => 'Dra bøker hit for å stable dem i denne hylla',
+    'shelves_empty_contents' => 'INgen bøker er stablet i denne hylla',
+    'shelves_edit_and_assign' => 'Endre hylla for å legge til bøker',
+    'shelves_edit_named' => 'Endre hyllen :name',
+    'shelves_edit' => 'Endre bokhylle',
+    'shelves_delete' => 'Fjern bokhylle',
+    'shelves_delete_named' => 'Fjern bokhyllen :name',
+    'shelves_delete_explain' => "Dette vil fjerne bokhyllen ':name'. Bøkene vil ikke fjernes fra systemet.",
+    'shelves_delete_confirmation' => 'Er du helt sikker på at du vil skru ned hylla?',
+    'shelves_permissions' => 'Tilganger til hylla',
+    'shelves_permissions_updated' => 'Hyllas tilganger er oppdatert',
+    'shelves_permissions_active' => 'Hyllas tilganger er aktive',
+    'shelves_copy_permissions_to_books' => 'Kopier tilganger til bøkene på hylla',
+    'shelves_copy_permissions' => 'Kopier tilganger',
+    'shelves_copy_permissions_explain' => 'Dette vil angi gjeldende tillatelsesinnstillinger for denne bokhyllen på alle bøkene som finnes på den. Før du aktiverer, må du forsikre deg om at endringer i tillatelsene til denne bokhyllen er lagret.',
+    'shelves_copy_permission_success' => 'Tilgangene ble overført til :count bøker',
+
+    // Books
+    'book' => 'Bok',
+    'books' => 'Bøker',
+    'x_books' => ':count bok|:count bøker',
+    'books_empty' => 'Ingen bøker er skrevet',
+    'books_popular' => 'Populære bøker',
+    'books_recent' => 'Nylige bøker',
+    'books_new' => 'Nye bøker',
+    'books_new_action' => 'Ny bok',
+    'books_popular_empty' => 'De mest populære bøkene',
+    'books_new_empty' => 'Siste utgivelser vises her.',
+    'books_create' => 'Skriv ny bok',
+    'books_delete' => 'Brenn bok',
+    'books_delete_named' => 'Brenn boken :bookName',
+    'books_delete_explain' => 'Dette vil brenne boken «:bookName». Alle sider i boken vil fordufte for godt.',
+    'books_delete_confirmation' => 'Er du sikker på at du vil brenne boken?',
+    'books_edit' => 'Endre bok',
+    'books_edit_named' => 'Endre boken :bookName',
+    'books_form_book_name' => 'Boktittel',
+    'books_save' => 'Lagre bok',
+    'books_permissions' => 'Boktilganger',
+    'books_permissions_updated' => 'Boktilganger oppdatert',
+    'books_empty_contents' => 'Ingen sider eller kapitler finnes i denne boken.',
+    'books_empty_create_page' => 'Skriv en ny side',
+    'books_empty_sort_current_book' => 'Sorter innholdet i boken',
+    'books_empty_add_chapter' => 'Start på nytt kapittel',
+    'books_permissions_active' => 'Boktilganger er aktive',
+    'books_search_this' => 'Søk i boken',
+    'books_navigation' => 'Boknavigasjon',
+    'books_sort' => 'Sorter bokinnhold',
+    'books_sort_named' => 'Sorter boken :bookName',
+    'books_sort_name' => 'Sorter på navn',
+    'books_sort_created' => 'Sorter på opprettet dato',
+    'books_sort_updated' => 'Sorter på oppdatert dato',
+    'books_sort_chapters_first' => 'Kapitler først',
+    'books_sort_chapters_last' => 'Kapitler sist',
+    'books_sort_show_other' => 'Vis andre bøker',
+    'books_sort_save' => 'Lagre sortering',
+
+    // Chapters
+    'chapter' => 'Kapittel',
+    'chapters' => 'Kapitler',
+    'x_chapters' => ':count Kapittel|:count Kapitler',
+    'chapters_popular' => 'Populære kapittler',
+    'chapters_new' => 'Nytt kapittel',
+    'chapters_create' => 'Skriv nytt kapittel',
+    'chapters_delete' => 'Riv ut kapittel',
+    'chapters_delete_named' => 'Riv ut kapittelet :chapterName',
+    'chapters_delete_explain' => 'Du ønsker å rive ut kapittelet «:chapterName». Alle sidene vil bli flyttet ut av kapittelet og vil ligge direkte i boka.',
+    'chapters_delete_confirm' => 'Er du sikker på at du vil rive ut dette kapittelet?',
+    'chapters_edit' => 'Endre kapittel',
+    'chapters_edit_named' => 'Endre kapittelet :chapterName',
+    'chapters_save' => 'Lagre kapittel',
+    'chapters_move' => 'Flytt kapittel',
+    'chapters_move_named' => 'Flytt kapittelet :chapterName',
+    'chapter_move_success' => 'Kapittelet ble flyttet til :bookName',
+    'chapters_permissions' => 'Kapitteltilganger',
+    'chapters_empty' => 'Det finnes ingen sider i dette kapittelet.',
+    'chapters_permissions_active' => 'Kapitteltilganger er aktivert',
+    'chapters_permissions_success' => 'Kapitteltilgager er oppdatert',
+    'chapters_search_this' => 'Søk i dette kapittelet',
+
+    // Pages
+    'page' => 'Side',
+    'pages' => 'Sider',
+    'x_pages' => ':count side|:count sider',
+    'pages_popular' => 'Populære sider',
+    'pages_new' => 'Ny side',
+    'pages_attachments' => 'Vedlegg',
+    'pages_navigation' => 'Sidenavigasjon',
+    'pages_delete' => 'Riv ut side',
+    'pages_delete_named' => 'Riv ut siden :pageName',
+    'pages_delete_draft_named' => 'Kast sideutkast :pageName',
+    'pages_delete_draft' => 'Kast sideutkast',
+    'pages_delete_success' => 'Siden er revet ut',
+    'pages_delete_draft_success' => 'Sideutkast er kastet',
+    'pages_delete_confirm' => 'Er du sikker på at du vil rive ut siden?',
+    'pages_delete_draft_confirm' => 'Er du sikker på at du vil forkaste utkastet?',
+    'pages_editing_named' => 'Endrer :pageName',
+    'pages_edit_draft_options' => 'Utkastsalternativer',
+    'pages_edit_save_draft' => 'Lagre utkast',
+    'pages_edit_draft' => 'Endre utkast',
+    'pages_editing_draft' => 'Redigerer utkast',
+    'pages_editing_page' => 'Redigerer side',
+    'pages_edit_draft_save_at' => 'Ukast lagret under ',
+    'pages_edit_delete_draft' => 'Forkast utkast',
+    'pages_edit_discard_draft' => 'Gi opp utkast',
+    'pages_edit_set_changelog' => 'Angi endringslogg',
+    'pages_edit_enter_changelog_desc' => 'Gi en kort beskrivelse av endringene dine',
+    'pages_edit_enter_changelog' => 'Se endringslogg',
+    'pages_save' => 'Lagre side',
+    'pages_title' => 'Sidetittel',
+    'pages_name' => 'Sidenavn',
+    'pages_md_editor' => 'Tekstbehandler',
+    'pages_md_preview' => 'Forhåndsvisning',
+    'pages_md_insert_image' => 'Lim inn bilde',
+    'pages_md_insert_link' => 'Lim in lenke',
+    'pages_md_insert_drawing' => 'Lim inn tegning',
+    'pages_not_in_chapter' => 'Siden tilhører ingen kapittel',
+    'pages_move' => 'Flytt side',
+    'pages_move_success' => 'Siden ble flyttet til ":parentName"',
+    'pages_copy' => 'Kopier side',
+    'pages_copy_desination' => 'Destinasjon',
+    'pages_copy_success' => 'Siden ble flyttet',
+    'pages_permissions' => 'Sidetilganger',
+    'pages_permissions_success' => 'Sidens tilganger ble endret',
+    'pages_revision' => 'Revisjon',
+    'pages_revisions' => 'Sidens revisjoner',
+    'pages_revisions_named' => 'Revisjoner for :pageName',
+    'pages_revision_named' => 'Revisjoner for :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions_created_by' => 'Skrevet av',
+    'pages_revisions_date' => 'Revideringsdato',
+    'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'Revisjon #:id',
+    'pages_revisions_numbered_changes' => 'Endringer på revisjon #:id',
+    'pages_revisions_changelog' => 'Endringslogg',
+    'pages_revisions_changes' => 'Endringer',
+    'pages_revisions_current' => 'Siste versjon',
+    'pages_revisions_preview' => 'Forhåndsvisning',
+    'pages_revisions_restore' => 'Gjenopprett',
+    'pages_revisions_none' => 'Denne siden har ingen revisjoner',
+    'pages_copy_link' => 'Kopier lenke',
+    'pages_edit_content_link' => 'Endre innhold',
+    'pages_permissions_active' => 'Sidetilganger er aktive',
+    'pages_initial_revision' => 'Første publisering',
+    'pages_initial_name' => 'Ny side',
+    'pages_editing_draft_notification' => 'Du skriver på et utkast som sist ble lagret :timeDiff.',
+    'pages_draft_edited_notification' => 'Siden har blitt endret siden du startet. Det anbefales at du forkaster dine endringer.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count forfattere har begynt å endre denne siden.',
+        'start_b' => ':userName skriver på siden for øyeblikket',
+        'time_a' => 'siden sist siden ble oppdatert',
+        'time_b' => 'i løpet av de siste :minCount minuttene',
+        'message' => ':start :time. Prøv å ikke overskriv hverandres endringer!',
+    ],
+    'pages_draft_discarded' => 'Forkastet, viser nå siste endringer fra siden slik den er lagret.',
+    'pages_specific' => 'Bestemt side',
+    'pages_is_template' => 'Sidemal',
+
+    // Editor Sidebar
+    'page_tags' => 'Sidemerker',
+    'chapter_tags' => 'Kapittelmerker',
+    'book_tags' => 'Bokmerker',
+    'shelf_tags' => 'Hyllemerker',
+    'tag' => 'Merke',
+    'tags' =>  'Merker',
+    'tag_name' =>  'Merketittel',
+    'tag_value' => 'Merkeverdi (Valgfritt)',
+    'tags_explain' => "Legg til merker for å kategorisere innholdet ditt. \n Du kan legge til merkeverdier for å beskrive dem ytterligere.",
+    'tags_add' => 'Legg til flere merker',
+    'tags_remove' => 'Fjern merke',
+    'attachments' => 'Vedlegg',
+    'attachments_explain' => 'Last opp vedlegg eller legg til lenker for å berike innholdet. Disse vil vises i sidestolpen på siden.',
+    'attachments_explain_instant_save' => 'Endringer her blir lagret med en gang.',
+    'attachments_items' => 'Vedlegg',
+    'attachments_upload' => 'Last opp vedlegg',
+    'attachments_link' => 'Fest lenke',
+    'attachments_set_link' => 'Angi lenke',
+    'attachments_delete' => 'Er du sikker på at du vil fjerne vedlegget?',
+    'attachments_dropzone' => 'Dra og slipp eller trykk her for å feste vedlegg',
+    'attachments_no_files' => 'Ingen vedlegg er lastet opp',
+    'attachments_explain_link' => 'Du kan feste lenker til denne. Det kan være henvisning til andre sider, bøker etc. eller lenker fra nettet.',
+    'attachments_link_name' => 'Lenkenavn',
+    'attachment_link' => 'Vedleggslenke',
+    'attachments_link_url' => 'Lenke til vedlegg',
+    'attachments_link_url_hint' => 'Adresse til lenke eller vedlegg',
+    'attach' => 'Fest',
+    'attachments_insert_link' => 'Fest vedleggslenke',
+    'attachments_edit_file' => 'Endre vedlegg',
+    'attachments_edit_file_name' => 'Vedleggsnavn',
+    'attachments_edit_drop_upload' => 'Dra og slipp eller trykk her for å oppdatere eller overskrive',
+    'attachments_order_updated' => 'Vedleggssortering endret',
+    'attachments_updated_success' => 'Vedleggsdetaljer endret',
+    'attachments_deleted' => 'Vedlegg fjernet',
+    'attachments_file_uploaded' => 'Vedlegg ble lastet opp',
+    'attachments_file_updated' => 'Vedlegget ble oppdatert',
+    'attachments_link_attached' => 'Lenken ble festet til siden',
+    'templates' => 'Maler',
+    'templates_set_as_template' => 'Siden er en mal',
+    'templates_explain_set_as_template' => 'Du kan angi denne siden som en mal slik at innholdet kan brukes når du oppretter andre sider. Andre brukere vil kunne bruke denne malen hvis de har visningstillatelser for denne siden.',
+    'templates_replace_content' => 'Bytt sideinnhold',
+    'templates_append_content' => 'Legg til neders på siden',
+    'templates_prepend_content' => 'Legg til øverst på siden',
+
+    // Profile View
+    'profile_user_for_x' => 'Medlem i :time',
+    'profile_created_content' => 'Har skrevet',
+    'profile_not_created_pages' => ':userName har ikke forfattet noen sider',
+    'profile_not_created_chapters' => ':userName har ikke opprettet noen kapitler',
+    'profile_not_created_books' => ':userName har ikke laget noen bøker',
+    'profile_not_created_shelves' => ':userName har ikke hengt opp noen hyller',
+
+    // Comments
+    'comment' => 'Kommentar',
+    'comments' => 'Kommentarer',
+    'comment_add' => 'Skriv kommentar',
+    'comment_placeholder' => 'Skriv en kommentar her',
+    'comment_count' => '{0} Ingen kommentarer|{1} 1 kommentar|[2,*] :count kommentarer',
+    'comment_save' => 'Publiser kommentar',
+    'comment_saving' => 'Publiserer ...',
+    'comment_deleting' => 'Fjerner...',
+    'comment_new' => 'Ny kommentar',
+    'comment_created' => 'kommenterte :createDiff',
+    'comment_updated' => 'Oppdatert :updateDiff av :username',
+    'comment_deleted_success' => 'Kommentar fjernet',
+    'comment_created_success' => 'Kommentar skrevet',
+    'comment_updated_success' => 'Kommentar endret',
+    'comment_delete_confirm' => 'Er du sikker på at du vil fjerne kommentaren?',
+    'comment_in_reply_to' => 'Som svar til :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Vil du slette revisjonen?',
+    'revision_restore_confirm' => 'Vil du gjenopprette revisjonen? Innholdet på siden vil bli overskrevet med denne revisjonen.',
+    'revision_delete_success' => 'Revisjonen ble slettet',
+    'revision_cannot_delete_latest' => 'CKan ikke slette siste revisjon.'
+];
\ No newline at end of file
diff --git a/resources/lang/nb/errors.php b/resources/lang/nb/errors.php
new file mode 100644 (file)
index 0000000..4e5c07f
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'Du har ikke tilgang til å se denne siden.',
+    'permissionJson' => 'Du har ikke tilgang til å utføre denne handlingen.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'En konto med :email finnes allerede, men har andre detaljer.',
+    'email_already_confirmed' => 'E-posten er allerede bekreftet, du kan forsøke å logge inn.',
+    'email_confirmation_invalid' => 'Denne bekreftelseskoden er allerede benyttet eller utgått. Prøv å registrere på nytt.',
+    'email_confirmation_expired' => 'Bekreftelseskoden er allerede utgått, en ny e-post er sendt.',
+    'email_confirmation_awaiting' => 'Du må bekrefte e-posten for denne kontoen.',
+    'ldap_fail_anonymous' => 'LDAP kan ikke benyttes med anonym tilgang for denne tjeneren.',
+    'ldap_fail_authed' => 'LDAP tilgang feilet med angitt DN',
+    'ldap_extension_not_installed' => 'LDAP PHP modulen er ikke installert.',
+    'ldap_cannot_connect' => 'Klarer ikke koble til LDAP på denne adressen',
+    'saml_already_logged_in' => 'Allerede logget inn',
+    'saml_user_not_registered' => 'Kontoen med navn :name er ikke registert, registrering er også deaktivert.',
+    'saml_no_email_address' => 'Denne kontoinformasjonen finnes ikke i det eksterne autentiseringssystemet.',
+    'saml_invalid_response_id' => 'Forespørselen fra det eksterne autentiseringssystemet gjenkjennes ikke av en prosess som startes av dette programmet. Å navigere tilbake etter pålogging kan forårsake dette problemet.',
+    'saml_fail_authed' => 'Innlogging gjennom :system feilet. Fikk ikke kontakt med autentiseringstjeneren.',
+    'social_no_action_defined' => 'Ingen handlinger er definert',
+    'social_login_bad_response' => "Feilmelding mottat fra :socialAccount innloggingstjeneste: \n:error",
+    'social_account_in_use' => 'Denne :socialAccount kontoen er allerede registrert, Prøv å logge inn med :socialAccount alternativet.',
+    'social_account_email_in_use' => 'E-posten :email er allerede i bruk. Har du allerede en konto hos :socialAccount kan dette angis fra profilsiden din.',
+    'social_account_existing' => 'Denne :socialAccount er allerede koblet til din konto.',
+    'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
+    'social_account_not_used' => 'Denne :socialAccount konten er ikke koblet til noen konto, angi denne i profilinnstillingene dine. ',
+    'social_account_register_instructions' => 'Har du ikke en konto her ennå, kan du benytte :socialAccount alternativet for å registrere deg.',
+    'social_driver_not_found' => 'Autentiseringstjeneste fra sosiale medier er ikke installert',
+    'social_driver_not_configured' => 'Dine :socialAccount innstilliner er ikke angitt.',
+    'invite_token_expired' => 'Invitasjonslenken har utgått, du kan forsøke å be om nytt passord istede.',
+
+    // System
+    'path_not_writable' => 'Filstien :filePath aksepterer ikke filer, du må sjekke filstitilganger i systemet.',
+    'cannot_get_image_from_url' => 'Kan ikke hente bilde fra :url',
+    'cannot_create_thumbs' => 'Kan ikke opprette miniatyrbilder. GD PHP er ikke installert.',
+    'server_upload_limit' => 'Vedlegget er for stort, forsøk med et mindre vedlegg.',
+    'uploaded'  => 'Tjenesten aksepterer ikke vedlegg som er så stor.',
+    'image_upload_error' => 'Bildet kunne ikke lastes opp, forsøk igjen.',
+    'image_upload_type_error' => 'Bildeformatet støttes ikke, forsøk med et annet format.',
+    'file_upload_timeout' => 'Opplastingen gikk ut på tid.',
+
+    // Attachments
+    'attachment_not_found' => 'Vedlegget ble ikke funnet',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Kunne ikke lagre utkastet, forsikre deg om at du er tilkoblet tjeneren (Har du nettilgang?)',
+    'page_custom_home_deletion' => 'Kan ikke slette en side som er satt som forside.',
+
+    // Entities
+    'entity_not_found' => 'Entitet ble ikke funnet',
+    'bookshelf_not_found' => 'Bokhyllen ble ikke funnet',
+    'book_not_found' => 'Boken ble ikke funnet',
+    'page_not_found' => 'Siden ble ikke funnet',
+    'chapter_not_found' => 'Kapittel ble ikke funnet',
+    'selected_book_not_found' => 'Den valgte boken eksisterer ikke',
+    'selected_book_chapter_not_found' => 'Den valgte boken eller kapittelet eksisterer ikke',
+    'guests_cannot_save_drafts' => 'Gjester kan ikke lagre utkast',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'Du kan ikke kaste ut den eneste administratoren',
+    'users_cannot_delete_guest' => 'Du kan ikke slette gjestebrukeren (Du kan deaktivere offentlig visning istede)',
+
+    // Roles
+    'role_cannot_be_edited' => 'Denne rollen kan ikke endres',
+    'role_system_cannot_be_deleted' => 'Denne systemrollen kan ikke slettes',
+    'role_registration_default_cannot_delete' => 'Du kan ikke slette en rolle som er satt som registreringsrolle (rollen nye kontoer får når de registrerer seg)',
+    'role_cannot_remove_only_admin' => 'Denne brukeren er den eneste brukeren som er tildelt administratorrollen. Tilordne administratorrollen til en annen bruker før du prøver å fjerne den her.',
+
+    // Comments
+    'comment_list' => 'Det oppstod en feil under henting av kommentarene.',
+    'cannot_add_comment_to_draft' => 'Du kan ikke legge til kommentarer i et utkast.',
+    'comment_add' => 'Det oppsto en feil da kommentaren skulle legges til / oppdateres.',
+    'comment_delete' => 'Det oppstod en feil under sletting av kommentaren.',
+    'empty_comment' => 'Kan ikke legge til en tom kommentar.',
+
+    // Error pages
+    '404_page_not_found' => 'Siden finnes ikke',
+    'sorry_page_not_found' => 'Beklager, siden du leter etter ble ikke funnet.',
+    'sorry_page_not_found_permission_warning' => 'Hvis du forventet at denne siden skulle eksistere, har du kanskje ikke tillatelse til å se den.',
+    'return_home' => 'Gå til hovedside',
+    'error_occurred' => 'En feil oppsto',
+    'app_down' => ':appName er nede for øyeblikket',
+    'back_soon' => 'Den vil snart komme tilbake.',
+
+    // API errors
+    'api_no_authorization_found' => 'Ingen autorisasjonstoken ble funnet på forespørselen',
+    'api_bad_authorization_format' => 'Det ble funnet et autorisasjonstoken på forespørselen, men formatet virket feil',
+    'api_user_token_not_found' => 'Ingen samsvarende API-token ble funnet for det angitte autorisasjonstokenet',
+    'api_incorrect_token_secret' => 'Hemmeligheten som er gitt for det gitte brukte API-tokenet er feil',
+    'api_user_no_api_permission' => 'Eieren av det brukte API-tokenet har ikke tillatelse til å ringe API-samtaler',
+    'api_user_token_expired' => 'Autorisasjonstokenet som er brukt, har utløpt',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Feil kastet når du sendte en test-e-post:',
+
+];
diff --git a/resources/lang/nb/pagination.php b/resources/lang/nb/pagination.php
new file mode 100644 (file)
index 0000000..d910da1
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; Forrige',
+    'next'     => 'Neste &raquo;',
+
+];
diff --git a/resources/lang/nb/passwords.php b/resources/lang/nb/passwords.php
new file mode 100644 (file)
index 0000000..8c3215b
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+    'password' => 'Passord må inneholde minst åtte tegn og samsvarer med bekreftelsen.',
+    'user' => "Vi finner ikke en bruker med den e-postadressen.",
+    'token' => 'Passordet for tilbakestilling av passord er ugyldig for denne e-postadressen.',
+    'sent' => 'Vi har sendt e-postadressen til tilbakestilling av passordet ditt!',
+    'reset' => 'Passordet ditt har blitt tilbakestilt!',
+
+];
diff --git a/resources/lang/nb/settings.php b/resources/lang/nb/settings.php
new file mode 100644 (file)
index 0000000..e952225
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Innstillinger',
+    'settings_save' => 'Lagre innstillinger',
+    'settings_save_success' => 'Innstillinger lagret',
+
+    // App Settings
+    'app_customization' => 'Tilpassing',
+    'app_features_security' => 'Funksjoner og sikkerhet',
+    'app_name' => 'Applikasjonsnavn',
+    'app_name_desc' => 'Dette navnet vises i overskriften og i alle e-postmeldinger som sendes av systemet.',
+    'app_name_header' => 'Vis navn i topptekst',
+    'app_public_access' => 'Offentlig tilgang',
+    'app_public_access_desc' => 'Hvis du aktiverer dette alternativet, kan besøkende, som ikke er logget på, få tilgang til innhold i din BookStack-forekomst.',
+    'app_public_access_desc_guest' => 'Tilgang for offentlige besøkende kan kontrolleres gjennom "Gjest" -brukeren.',
+    'app_public_access_toggle' => 'Tillat offentlig tilgang',
+    'app_public_viewing' => 'Tillat offentlig visning?',
+    'app_secure_images' => 'Høyere sikkerhet på bildeopplastinger',
+    'app_secure_images_toggle' => 'Enable høyere sikkerhet på bildeopplastinger',
+    'app_secure_images_desc' => 'Av ytelsesgrunner er alle bilder offentlige. Dette alternativet legger til en tilfeldig streng som er vanskelig å gjette foran bildets nettadresser. Forsikre deg om at katalogindekser ikke er aktivert for å forhindre enkel tilgang.',
+    'app_editor' => 'Tekstbehandler',
+    'app_editor_desc' => 'Velg hvilken tekstbehandler som skal brukes av alle brukere til å redigere sider.',
+    'app_custom_html' => 'Tilpasset HTML-hodeinnhold',
+    'app_custom_html_desc' => 'Alt innhold som legges til her, blir satt inn i bunnen av <head> -delen på hver side. Dette er praktisk for å overstyre stiler eller legge til analysekode.',
+    'app_custom_html_disabled_notice' => 'Tilpasset HTML-hodeinnhold er deaktivert på denne innstillingssiden for å sikre at eventuelle endringer ødelegger noe, kan tilbakestilles.',
+    'app_logo' => 'Applikasjonslogo',
+    'app_logo_desc' => 'Dette bildet skal være 43 px høyt. <br> Store bilder blir nedskalert.',
+    'app_primary_color' => 'Applikasjonens primærfarge',
+    'app_primary_color_desc' => 'Angir primærfargen for applikasjonen inkludert banner, knapper og lenker.',
+    'app_homepage' => 'Applikasjonens hjemmeside',
+    'app_homepage_desc' => 'Velg en visning som skal vises på hjemmesiden i stedet for standardvisningen. Sidetillatelser ignoreres for utvalgte sider.',
+    'app_homepage_select' => 'Velg en side',
+    'app_disable_comments' => 'Deaktiver kommentarer',
+    'app_disable_comments_toggle' => 'Deaktiver kommentarer',
+    'app_disable_comments_desc' => 'Deaktiver kommentarer på tvers av alle sidene i applikasjonen. <br> Eksisterende kommentarer vises ikke.',
+
+    // Color settings
+    'content_colors' => 'Innholdsfarger',
+    'content_colors_desc' => 'Angir farger for alle elementene i sideorganisasjonshierarkiet. Det anbefales å lese farger med en lignende lysstyrke som standardfargene for lesbarhet.',
+    'bookshelf_color' => 'Hyllefarge',
+    'book_color' => 'Bokfarge',
+    'chapter_color' => 'Kapittelfarge',
+    'page_color' => 'Sidefarge',
+    'page_draft_color' => 'Sideutkastsfarge',
+
+    // Registration Settings
+    'reg_settings' => 'Registrering',
+    'reg_enable' => 'Tillat registrering',
+    'reg_enable_toggle' => 'Tillat registrering',
+    'reg_enable_desc' => 'Når registrering er aktivert vil brukeren kunne registrere seg som applikasjonsbruker. Ved registrering får de en standard brukerrolle.',
+    'reg_default_role' => 'Standard brukerrolle etter registrering',
+    'reg_enable_external_warning' => 'Alternativet ovenfor ignoreres mens ekstern LDAP- eller SAML-autentisering er aktiv. Brukerkontoer for ikke-eksisterende medlemmer blir automatisk opprettet hvis autentisering mot det eksterne systemet i bruk lykkes.',
+    'reg_email_confirmation' => 'E-postbekreftelse',
+    'reg_email_confirmation_toggle' => 'Krev e-postbekreftelse',
+    'reg_confirm_email_desc' => 'Hvis domenebegrensning brukes, vil e-postbekreftelse være nødvendig, og dette alternativet vil bli ignorert.',
+    'reg_confirm_restrict_domain' => 'Domenebegrensning',
+    'reg_confirm_restrict_domain_desc' => 'Skriv inn en kommaseparert liste over e-postdomener du vil begrense registreringen til. Brukerne vil bli sendt en e-post for å bekrefte adressen deres før de får lov til å kommunisere med applikasjonen. <br> Vær oppmerksom på at brukere vil kunne endre e-postadressene sine etter vellykket registrering.',
+    'reg_confirm_restrict_domain_placeholder' => 'Ingen begrensninger er satt',
+
+    // Maintenance settings
+    'maint' => 'Maintenance',
+    'maint_image_cleanup' => 'Bildeopprydding',
+    'maint_image_cleanup_desc' => "Skanner side og revisjonsinnhold for å sjekke hvilke bilder og tegninger som for øyeblikket er i bruk, og hvilke bilder som er overflødige. Forsikre deg om at du lager en full database og sikkerhetskopiering av bilder før du kjører denne.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_run' => 'Kjør opprydding',
+    'maint_image_cleanup_warning' => ':count potensielt ubrukte bilder ble funnet. Er du sikker på at du vil slette disse bildene?',
+    'maint_image_cleanup_success' => ':count potensielt ubrukte bilder funnet og slettet!',
+    'maint_image_cleanup_nothing_found' => 'Ingen ubrukte bilder funnet, ingenting slettet!',
+    'maint_send_test_email' => 'Send en test-e-post',
+    'maint_send_test_email_desc' => 'Dette sender en test-e-post til din e-postadresse som er angitt i profilen din.',
+    'maint_send_test_email_run' => 'Send en test-e-post',
+    'maint_send_test_email_success' => 'Send en test-e-post til :address',
+    'maint_send_test_email_mail_subject' => 'Test-e-post',
+    'maint_send_test_email_mail_greeting' => 'E-postsending ser ut til å fungere!',
+    'maint_send_test_email_mail_text' => 'Gratulerer! Da du mottok dette e-postvarselet, ser det ut til at e-postinnstillingene dine er konfigurert riktig.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Revisjonslogg',
+    'audit_desc' => 'Denne revisjonsloggen viser en liste over aktiviteter som spores i systemet. Denne listen er ufiltrert i motsetning til lignende aktivitetslister i systemet der tillatelsesfiltre brukes.',
+    'audit_event_filter' => 'Hendelsesfilter',
+    'audit_event_filter_no_filter' => 'Ingen filter',
+    'audit_deleted_item' => 'Slettet ting',
+    'audit_deleted_item_name' => 'Navn: :name',
+    'audit_table_user' => 'Kontoholder',
+    'audit_table_event' => 'Hendelse',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Aktivitetsdato',
+    'audit_date_from' => 'Datoperiode fra',
+    'audit_date_to' => 'Datoperiode til',
+
+    // Role Settings
+    'roles' => 'Roller',
+    'role_user_roles' => 'Kontoroller',
+    'role_create' => 'Opprett ny rolle',
+    'role_create_success' => 'Rolle opprettet',
+    'role_delete' => 'Rolle slettet',
+    'role_delete_confirm' => 'Dette vil slette rollen «:roleName».',
+    'role_delete_users_assigned' => 'Denne rollen har :userCount kontoer koblet opp mot seg. Velg hvilke rolle du vil flytte disse til.',
+    'role_delete_no_migration' => "Ikke flytt kontoer",
+    'role_delete_sure' => 'Er du sikker på at du vil slette rollen?',
+    'role_delete_success' => 'Rollen ble slettet',
+    'role_edit' => 'Endre rolle',
+    'role_details' => 'Rolledetaljer',
+    'role_name' => 'Rollenavn',
+    'role_desc' => 'Kort beskrivelse av rolle',
+    'role_external_auth_id' => 'Ekstern godkjennings-ID',
+    'role_system' => 'Systemtilganger',
+    'role_manage_users' => 'Behandle kontoer',
+    'role_manage_roles' => 'Behandle roller og rolletilganger',
+    'role_manage_entity_permissions' => 'Behandle bok-, kapittel- og sidetilganger',
+    'role_manage_own_entity_permissions' => 'Behandle tilganger på egne verk',
+    'role_manage_page_templates' => 'Behandle sidemaler',
+    'role_access_api' => 'Systemtilgang API',
+    'role_manage_settings' => 'Behandle applikasjonsinnstillinger',
+    'role_asset' => 'Eiendomstillatelser',
+    'roles_system_warning' => 'Vær oppmerksom på at tilgang til noen av de ovennevnte tre tillatelsene kan tillate en bruker å endre sine egne rettigheter eller rettighetene til andre i systemet. Bare tildel roller med disse tillatelsene til pålitelige brukere.',
+    'role_asset_desc' => 'Disse tillatelsene kontrollerer standard tilgang til eiendelene i systemet. Tillatelser til bøker, kapitler og sider overstyrer disse tillatelsene.',
+    'role_asset_admins' => 'Administratorer får automatisk tilgang til alt innhold, men disse alternativene kan vise eller skjule UI-alternativer.',
+    'role_all' => 'Alle',
+    'role_own' => 'Egne',
+    'role_controlled_by_asset' => 'Kontrollert av eiendelen de er lastet opp til',
+    'role_save' => 'Lagre rolle',
+    'role_update_success' => 'Rollen ble oppdatert',
+    'role_users' => 'Kontoholdere med denne rollen',
+    'role_users_none' => 'Ingen kontoholdere er gitt denne rollen',
+
+    // Users
+    'users' => 'Users',
+    'user_profile' => 'Profil',
+    'users_add_new' => 'Register ny konto',
+    'users_search' => 'Søk i kontoer',
+    'users_latest_activity' => 'Latest Activity',
+    'users_details' => 'Kontodetaljer',
+    'users_details_desc' => 'Angi et visningsnavn og en e-postadresse for denne kontoholderen. E-postadressen vil bli brukt til å logge på applikasjonen.',
+    'users_details_desc_no_email' => 'Angi et visningsnavn for denne kontoholderen slik at andre kan gjenkjenne dem.',
+    'users_role' => 'Roller',
+    'users_role_desc' => 'Velg hvilke roller denne kontoholderen vil bli tildelt. Hvis en kontoholderen er tildelt flere roller, vil tillatelsene fra disse rollene stable seg, og de vil motta alle evnene til de tildelte rollene.',
+    'users_password' => 'Passord',
+    'users_password_desc' => 'Angi et passord som brukes til å logge på applikasjonen. Dette må bestå av minst 6 tegn.',
+    'users_send_invite_text' => 'Du kan velge å sende denne kontoholderen en invitasjons-e-post som lar dem angi sitt eget passord, ellers kan du selv angi passordet.',
+    'users_send_invite_option' => 'Send invitasjonsmelding',
+    'users_external_auth_id' => 'Ekstern godkjennings-ID',
+    'users_external_auth_id_desc' => 'Dette er ID-en som brukes til å matche denne kontoholderen når de kommuniserer med det eksterne autentiseringssystemet.',
+    'users_password_warning' => 'Fyll bare ut nedenfor hvis du vil endre passordet ditt.',
+    'users_system_public' => 'Denne brukeren representerer alle gjester som besøker appliaksjonen din. Den kan ikke brukes til å logge på, men tildeles automatisk.',
+    'users_delete' => 'Slett konto',
+    'users_delete_named' => 'Slett kontoen :userName',
+    'users_delete_warning' => 'Dette vil fullstendig slette denne brukeren med navnet «:userName» fra systemet.',
+    'users_delete_confirm' => 'Er du sikker på at du vil slette denne kontoen?',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'Konto slettet',
+    'users_edit' => 'Rediger konto',
+    'users_edit_profile' => 'Rediger profil',
+    'users_edit_success' => 'Kontoen ble oppdatert',
+    'users_avatar' => 'Kontobilde',
+    'users_avatar_desc' => 'Velg et bilde for å representere denne kontoholderen. Dette skal være omtrent 256px kvadrat.',
+    'users_preferred_language' => 'Foretrukket språk',
+    'users_preferred_language_desc' => 'Dette alternativet vil endre språket som brukes til brukergrensesnittet til applikasjonen. Dette påvirker ikke noe brukeropprettet innhold.',
+    'users_social_accounts' => 'Sosiale kontoer',
+    'users_social_accounts_info' => 'Her kan du koble andre kontoer for raskere og enklere pålogging. Hvis du frakobler en konto her, tilbakekaller ikke dette tidligere autorisert tilgang. Tilbakekall tilgang fra profilinnstillingene dine på den tilkoblede sosiale kontoen.',
+    'users_social_connect' => 'Koble til konto',
+    'users_social_disconnect' => 'Koble fra konto',
+    'users_social_connected' => ':socialAccount ble lagt til din konto.',
+    'users_social_disconnected' => ':socialAccount ble koblet fra din konto.',
+    'users_api_tokens' => 'API-nøkler',
+    'users_api_tokens_none' => 'Ingen API-nøkler finnes for denne kontoen',
+    'users_api_tokens_create' => 'Opprett nøkkel',
+    'users_api_tokens_expires' => 'Utløper',
+    'users_api_tokens_docs' => 'API-dokumentasjon',
+
+    // API Tokens
+    'user_api_token_create' => 'Opprett API-nøkkel',
+    'user_api_token_name' => 'Navn',
+    'user_api_token_name_desc' => 'Gi nøkkelen et lesbart navn som en fremtidig påminnelse om det tiltenkte formålet.',
+    'user_api_token_expiry' => 'Utløpsdato',
+    'user_api_token_expiry_desc' => 'Angi en dato da denne nøkkelen utløper. Etter denne datoen vil forespørsler som er gjort med denne nøkkelen ikke lenger fungere. Å la dette feltet stå tomt vil sette utløpsdato 100 år inn i fremtiden.',
+    'user_api_token_create_secret_message' => 'Umiddelbart etter å ha opprettet denne nøkkelen vil en identifikator og hemmelighet bli generert og vist. Hemmeligheten vil bare vises en gang, så husk å kopiere verdien til et trygt sted før du fortsetter.',
+    'user_api_token_create_success' => 'API-nøkkel ble opprettet',
+    'user_api_token_update_success' => 'API-nøkkel ble oppdatert',
+    'user_api_token' => 'API-nøkkel',
+    'user_api_token_id' => 'Identifikator',
+    'user_api_token_id_desc' => 'Dette er en ikke-redigerbar systemgenerert identifikator for denne nøkkelen som må oppgis i API-forespørsler.',
+    'user_api_token_secret' => 'Hemmelighet',
+    'user_api_token_secret_desc' => 'Dette er en systemgenerert hemmelighet for denne nøkkelen som må leveres i API-forespørsler. Dette vises bare denne gangen, så kopier denne verdien til et trygt sted.',
+    'user_api_token_created' => 'Nøkkel opprettet :timeAgo',
+    'user_api_token_updated' => 'Nøkkel oppdatert :timeAgo',
+    'user_api_token_delete' => 'Slett nøkkel',
+    'user_api_token_delete_warning' => 'Dette vil slette API-nøkkelen \':tokenName\' fra systemet.',
+    'user_api_token_delete_confirm' => 'Sikker på at du vil slette nøkkelen?',
+    'user_api_token_delete_success' => 'API-nøkkelen ble slettet',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'Slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/nb/validation.php b/resources/lang/nb/validation.php
new file mode 100644 (file)
index 0000000..87e6c75
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => ':attribute må aksepteres.',
+    'active_url'           => ':attribute er ikke en godkjent URL.',
+    'after'                => ':attribute må være en dato etter :date.',
+    'alpha'                => ':attribute kan kun inneholde bokstaver.',
+    'alpha_dash'           => ':attribute kan kunne inneholde bokstaver, tall, bindestreker eller understreker.',
+    'alpha_num'            => ':attribute kan kun inneholde bokstaver og tall.',
+    'array'                => ':attribute må være en liste.',
+    'before'               => ':attribute må være en dato før :date.',
+    'between'              => [
+        'numeric' => ':attribute må være mellom :min og :max.',
+        'file'    => ':attribute må være mellom :min og :max kilobytes.',
+        'string'  => ':attribute må være mellom :min og :max tegn.',
+        'array'   => ':attribute må være mellom :min og :max ting.',
+    ],
+    'boolean'              => ':attribute feltet kan bare være sann eller falsk.',
+    'confirmed'            => ':attribute bekreftelsen samsvarer ikke.',
+    'date'                 => ':attribute er ikke en gyldig dato.',
+    'date_format'          => ':attribute samsvarer ikke med :format.',
+    'different'            => ':attribute og :other må være forskjellige.',
+    'digits'               => ':attribute må være :digits tall.',
+    'digits_between'       => ':attribute må være mellomg :min og :max tall.',
+    'email'                => ':attribute må være en gyldig e-post.',
+    'ends_with' => ':attribute må slutte med en av verdiene: :values',
+    'filled'               => ':attribute feltet er påkrevd.',
+    'gt'                   => [
+        'numeric' => ':attribute må være større enn :value.',
+        'file'    => ':attribute må være større enn :value kilobytes.',
+        'string'  => ':attribute må være større enn :value tegn.',
+        'array'   => ':attribute må ha mer en :value ting.',
+    ],
+    'gte'                  => [
+        'numeric' => ':attribute må være større enn eller lik :value.',
+        'file'    => ':attribute må være større enn eller lik :value kilobytes.',
+        'string'  => ':attribute må være større enn eller lik :value tegn.',
+        'array'   => ':attribute må ha :value eller flere ting.',
+    ],
+    'exists'               => 'Den valgte :attribute er ugyldig.',
+    'image'                => ':attribute må være et bilde.',
+    'image_extension'      => ':attribute må ha støttet formattype.',
+    'in'                   => 'Den valgte :attribute er ugyldig.',
+    'integer'              => ':attribute må være et heltall',
+    'ip'                   => ':attribute må være en gyldig IP adresse.',
+    'ipv4'                 => ':attribute må være en gyldig IPv4 adresse.',
+    'ipv6'                 => ':attribute må være en gyldig IPv6 adresse.',
+    'json'                 => ':attribute må være en gyldig JSON tekststreng.',
+    'lt'                   => [
+        'numeric' => ':attribute må være mindre enn :value.',
+        'file'    => ':attribute må være mindre enn :value kilobytes.',
+        'string'  => ':attribute må være mindre enn :value tegn.',
+        'array'   => ':attribute må ha mindre enn :value ting.',
+    ],
+    'lte'                  => [
+        'numeric' => ':attribute må være mindre enn eller lik :value.',
+        'file'    => ':attribute må være mindre enn eller lik :value kilobytes.',
+        'string'  => ':attribute må være mindre enn eller lik :value characters.',
+        'array'   => ':attribute må ha mindre enn eller lik :value ting.',
+    ],
+    'max'                  => [
+        'numeric' => ':attribute kan ikke være større enn :max.',
+        'file'    => ':attribute kan ikke være større enn :max kilobytes.',
+        'string'  => ':attribute kan ikke være større enn :max tegn.',
+        'array'   => ':attribute kan ikke inneholde mer enn :max ting.',
+    ],
+    'mimes'                => ':attribute må være en fil av typen: :values.',
+    'min'                  => [
+        'numeric' => ':attribute må være på minst :min.',
+        'file'    => ':attribute må være på minst :min kilobytes.',
+        'string'  => ':attribute må være på minst :min tegn.',
+        'array'   => ':attribute må minst ha :min ting.',
+    ],
+    'no_double_extension'  => ':attribute kan bare ha en formattype spesifisert.',
+    'not_in'               => 'Den valgte :attribute er ugyldig.',
+    'not_regex'            => ':attribute format er ugyldig.',
+    'numeric'              => ':attribute må være et nummer.',
+    'regex'                => ':attribute format er ugyldig.',
+    'required'             => ':attribute feltet er påkrevt.',
+    'required_if'          => ':attribute feltet er påkrevt når :other er :value.',
+    'required_with'        => ':attribute feltet er påkrevt når :values er tilgjengelig.',
+    'required_with_all'    => ':attribute feltet er påkrevt når :values er tilgjengelig',
+    'required_without'     => ':attribute feltet er påkrevt når :values ikke er tilgjengelig.',
+    'required_without_all' => ':attribute feltet er påkrevt når ingen av :values er tilgjengelig.',
+    'same'                 => ':attribute og :other må samsvare.',
+    'safe_url'             => 'The provided link may not be safe.',
+    'size'                 => [
+        'numeric' => ':attribute må være :size.',
+        'file'    => ':attribute må være :size kilobytes.',
+        'string'  => ':attribute må være :size tegn.',
+        'array'   => ':attribute må inneholde :size ting.',
+    ],
+    'string'               => ':attribute må være en tekststreng.',
+    'timezone'             => ':attribute må være en tidssone.',
+    'unique'               => ':attribute har allerede blitt tatt.',
+    'url'                  => ':attribute format er ugyldig.',
+    'uploaded'             => 'kunne ikke lastes opp, tjeneren støtter ikke filer av denne størrelsen.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'passordbekreftelse er påkrevd',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 76272888119a3127329f3cf986a49514bdb016be..634ea1202f8837eeb83474c888ad0cc53c9fd633 100644 (file)
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'reactie op',
+    'permissions_update'          => 'updated permissions',
 ];
index 059214bb477dc12f6db157578105f0fb1774bc04..ce0be87edfb2b201e640669591df021cfea536f7 100644 (file)
@@ -11,14 +11,14 @@ return [
 
     // Login & Register
     'sign_up' => 'Registreren',
-    'log_in' => 'Log in',
+    'log_in' => 'Inloggen',
     'log_in_with' => 'Login met :socialDriver',
     'sign_up_with' => 'Registreer met :socialDriver',
     'logout' => 'Uitloggen',
 
     'name' => 'Naam',
     'username' => 'Gebruikersnaam',
-    'email' => 'Email',
+    'email' => 'E-mail',
     'password' => 'Wachtwoord',
     'password_confirm' => 'Wachtwoord Bevestigen',
     'password_hint' => 'Minimaal 8 tekens',
@@ -26,9 +26,9 @@ return [
     'remember_me' => 'Mij onthouden',
     'ldap_email_hint' => 'Geef een email op waarmee je dit account wilt gebruiken.',
     'create_account' => 'Account Aanmaken',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
-    'social_login' => 'Social Login',
+    'already_have_account' => 'Heb je al een account?',
+    'dont_have_account' => 'Nog geen account?',
+    'social_login' => 'Aanmelden via een sociaal netwerk',
     'social_registration' => 'Social Registratie',
     'social_registration_text' => 'Registreer en log in met een andere dienst.',
 
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Wachtwoord Herstellen',
     'reset_password_send_instructions' => 'Geef je e-mail en we sturen je een link om je wachtwoord te herstellen',
     'reset_password_send_button' => 'Link Sturen',
-    'reset_password_sent_success' => 'Een link om je wachtwoord te herstellen is verstuurd naar :email.',
+    'reset_password_sent' => 'Een link om het wachtwoord te resetten zal verstuurd worden naar :email als dat e-mailadres in het systeem gevonden is.',
     'reset_password_success' => 'Je wachtwoord is succesvol hersteld.',
     'email_reset_subject' => 'Herstel je wachtwoord van :appName',
     'email_reset_text' => 'Je ontvangt deze e-mail zodat je je wachtwoord kunt herstellen.',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => 'Bevestigingsmail Opnieuw Verzenden',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Je bent uitgenodigd voor :appName!',
+    'user_invite_email_greeting' => 'Er is een account voor je aangemaakt op :appName.',
+    'user_invite_email_text' => 'Klik op de onderstaande knop om een account wachtwoord in te stellen en toegang te krijgen:',
+    'user_invite_email_action' => 'Account wachtwoord instellen',
+    'user_invite_page_welcome' => 'Welkom bij :appName!',
+    'user_invite_page_text' => 'Om je account af te ronden en toegang te krijgen moet je een wachtwoord instellen dat gebruikt wordt om in te loggen op :appName bij toekomstige bezoeken.',
+    'user_invite_page_confirm_button' => 'Bevestig wachtwoord',
+    'user_invite_success' => 'Wachtwoord ingesteld, je hebt nu toegang tot :appName!'
 ];
\ No newline at end of file
index 87d5935a67b64cd105a512a0aa19273847801dee..7a2d633c65e61b2de0204f35f13dbf384351eb15 100644 (file)
@@ -26,24 +26,26 @@ return [
     'view' => 'Bekijk',
     'view_all' => 'Bekijk Alle',
     'create' => 'Aanmaken',
-    'update' => 'Update',
+    'update' => 'Bijwerken',
     'edit' => 'Bewerk',
     'sort' => 'Sorteer',
     'move' => 'Verplaats',
     'copy' => 'Kopiëren',
     'reply' => 'Beantwoorden',
     'delete' => 'Verwijder',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Zoek',
     'search_clear' => 'Zoekopdracht wissen',
-    'reset' => 'Reset',
+    'reset' => 'Resetten',
     'remove' => 'Verwijderen',
     'add' => 'Toevoegen',
+    'fullscreen' => 'Volledig scherm',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
+    'sort_options' => 'Sorteeropties',
+    'sort_direction_toggle' => 'Sorteer richting',
+    'sort_ascending' => 'Sorteer oplopend',
+    'sort_descending' => 'Sorteer teruglopend',
     'sort_name' => 'Naam',
     'sort_created_at' => 'Aanmaakdatum',
     'sort_updated_at' => 'Gewijzigd op',
@@ -59,12 +61,14 @@ return [
     'grid_view' => 'Grid weergave',
     'list_view' => 'Lijst weergave',
     'default' => 'Standaard',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Kruimelpad',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Profiel menu',
     'view_profile' => 'Profiel Weergeven',
     'edit_profile' => 'Profiel Bewerken',
+    'dark_mode' => 'Donkere Modus',
+    'light_mode' => 'Lichte Modus',
 
     // Layout tabs
     'tab_info' => 'Info',
index ffff70b08fd2430834b0343abd3f7138c5331aaa..f40ac15b0f4c23342b6a245d7328b936ee4ce133 100644 (file)
@@ -11,11 +11,11 @@ return [
     'image_book_title' => 'Afbeeldingen van dit boek weergeven',
     'image_page_title' => 'Afbeeldingen van deze pagina weergeven',
     'image_search_hint' => 'Zoek op afbeeldingsnaam',
-    'image_uploaded' => 'Uploaded :uploadedDate',
+    'image_uploaded' => 'Geüpload :uploadedDate',
     'image_load_more' => 'Meer Laden',
     'image_image_name' => 'Afbeeldingsnaam',
     'image_delete_used' => 'Deze afbeeldingen is op onderstaande pagina\'s in gebruik.',
-    'image_delete_confirm' => 'Klik opnieuw op verwijderen om de afbeelding echt te verwijderen.',
+    'image_delete_confirm_text' => 'Weet u zeker dat u deze afbeelding wilt verwijderen?',
     'image_select_image' => 'Kies Afbeelding',
     'image_dropzone' => 'Sleep afbeeldingen hier of klik hier om te uploaden',
     'images_deleted' => 'Verwijderde Afbeeldingen',
@@ -23,11 +23,12 @@ return [
     'image_upload_success' => 'Afbeelding succesvol geüpload',
     'image_update_success' => 'Afbeeldingsdetails succesvol verwijderd',
     'image_delete_success' => 'Afbeelding succesvol verwijderd',
-    'image_upload_remove' => 'Remove',
+    'image_upload_remove' => 'Verwijderen',
 
     // Code Editor
     'code_editor' => 'Code invoegen',
     'code_language' => 'Code taal',
     'code_content' => 'Code',
+    'code_session_history' => 'Zittingsgeschiedenis',
     'code_save' => 'Sla code op',
 ];
index bcd0690538174fe48b975289659761ad5d48897a..0caab99f09afba113fd1804051a9d60bd399f162 100644 (file)
@@ -11,7 +11,7 @@ return [
     'recently_updated_pages' => 'Recent Bijgewerkte Pagina\'s',
     'recently_created_chapters' => 'Recent Aangemaakte Hoofdstukken',
     'recently_created_books' => 'Recent Aangemaakte Boeken',
-    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_created_shelves' => 'Recent Aangemaakte Boekenplanken',
     'recently_update' => 'Recent Bijgewerkt',
     'recently_viewed' => 'Recent Bekeken',
     'recent_activity' => 'Recente Activiteit',
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => 'Aangemaakt: :timeLength door :user',
     'meta_updated' => ':timeLength Aangepast',
     'meta_updated_name' => 'Aangepast: :timeLength door :user',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Entiteit Selecteren',
     'images' => 'Afbeeldingen',
     'my_recent_drafts' => 'Mijn Concepten',
@@ -30,15 +31,16 @@ return [
     'no_pages_recently_created' => 'Er zijn geen recent aangemaakte pagina\'s',
     'no_pages_recently_updated' => 'Er zijn geen recente wijzigingen',
     'export' => 'Exporteren',
-    'export_html' => 'Contained Web File',
-    'export_pdf' => 'PDF File',
-    'export_text' => 'Plain Text File',
+    'export_html' => 'Ingesloten Webbestand',
+    'export_pdf' => 'PDF Bestand',
+    'export_text' => 'Normaal Tekstbestand',
 
     // Permissions and restrictions
     'permissions' => 'Permissies',
     'permissions_intro' => 'Als je dit aanzet, dan gelden rol-permissies niet meer voor deze pagina.',
     'permissions_enable' => 'Custom Permissies Aanzetten',
     'permissions_save' => 'Permissies Opslaan',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Zoekresultaten',
@@ -47,22 +49,23 @@ return [
     'search_no_pages' => 'Er zijn geen pagina\'s gevonden',
     'search_for_term' => 'Zoeken op :term',
     'search_more' => 'Meer resultaten',
-    'search_filters' => 'Zoek filters',
-    'search_content_type' => 'Content Type',
+    'search_advanced' => 'Uitgebreid zoeken',
+    'search_terms' => 'Zoektermen',
+    'search_content_type' => 'Inhoudstype',
     'search_exact_matches' => 'Exacte Matches',
     'search_tags' => 'Zoek tags',
-    'search_options' => 'Options',
+    'search_options' => 'Opties',
     'search_viewed_by_me' => 'Bekeken door mij',
     'search_not_viewed_by_me' => 'Niet bekeken door mij',
     'search_permissions_set' => 'Permissies gezet',
     'search_created_by_me' => 'Door mij gemaakt',
     'search_updated_by_me' => 'Door mij geupdate',
-    'search_date_options' => 'Date Options',
+    'search_date_options' => 'Datum Opties',
     'search_updated_before' => 'Geupdate voor',
     'search_updated_after' => 'Geupdate na',
-    'search_created_before' => 'Gecreeerd voor',
-    'search_created_after' => 'Gecreeerd na',
-    'search_set_date' => 'Zet datum',
+    'search_created_before' => 'Gecreëerd voor',
+    'search_created_after' => 'Gecreëerd na',
+    'search_set_date' => 'Stel datum in',
     'search_update' => 'Update zoekresultaten',
 
     // Shelves
@@ -74,7 +77,7 @@ return [
     'shelves_create' => 'Nieuwe Boekenplank Aanmaken',
     'shelves_popular' => 'Populaire Boekenplanken',
     'shelves_new' => 'Nieuwe Boekenplanken',
-    'shelves_new_action' => 'New Shelf',
+    'shelves_new_action' => 'Nieuwe Boekplank',
     'shelves_popular_empty' => 'De meest populaire boekenplanken worden hier weergegeven.',
     'shelves_new_empty' => 'De meest recent aangemaakt boekenplanken worden hier weergeven.',
     'shelves_save' => 'Boekenplanken Opslaan',
@@ -105,9 +108,9 @@ return [
     'books_popular' => 'Populaire Boeken',
     'books_recent' => 'Recente Boeken',
     'books_new' => 'Nieuwe Boeken',
-    'books_new_action' => 'New Book',
+    'books_new_action' => 'Nieuw Boek',
     'books_popular_empty' => 'De meest populaire boeken worden hier weergegeven.',
-    'books_new_empty' => 'The most recently created books will appear here.',
+    'books_new_empty' => 'De meest recent aangemaakte boeken verschijnen hier.',
     'books_create' => 'Nieuw Boek Aanmaken',
     'books_delete' => 'Boek Verwijderen',
     'books_delete_named' => 'Verwijder Boek :bookName',
@@ -128,11 +131,11 @@ return [
     'books_navigation' => 'Boek Navigatie',
     'books_sort' => 'Inhoud van het boek sorteren',
     'books_sort_named' => 'Sorteer Boek :bookName',
-    'books_sort_name' => 'Sort by Name',
-    'books_sort_created' => 'Sort by Created Date',
-    'books_sort_updated' => 'Sort by Updated Date',
-    'books_sort_chapters_first' => 'Chapters First',
-    'books_sort_chapters_last' => 'Chapters Last',
+    'books_sort_name' => 'Sorteren op Naam',
+    'books_sort_created' => 'Sorteren op datum van aanmaken',
+    'books_sort_updated' => 'Sorteren op datum van bijgewerkt',
+    'books_sort_chapters_first' => 'Hoofdstukken eerst',
+    'books_sort_chapters_last' => 'Hoofdstukken Laatst',
     'books_sort_show_other' => 'Bekijk Andere Boeken',
     'books_sort_save' => 'Nieuwe Order Opslaan',
 
@@ -145,8 +148,7 @@ return [
     'chapters_create' => 'Hoofdstuk Toevoegen',
     'chapters_delete' => 'Hoofdstuk Verwijderen',
     'chapters_delete_named' => 'Verwijder Hoofdstuk :chapterName',
-    'chapters_delete_explain' => 'Dit verwijdert het hoofdstuk \':chapterName\', Alle pagina\'s zullen verwijdert worden.
-        en toegevoegd worden aan het bijbehorende boek.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'Weet je zeker dat je dit boek wilt verwijderen?',
     'chapters_edit' => 'Hoofdstuk Aanpassen',
     'chapters_edit_named' => 'Hoofdstuk :chapterName Aanpassen',
@@ -177,7 +179,7 @@ return [
     'pages_delete_confirm' => 'Weet je zeker dat je deze pagina wilt verwijderen?',
     'pages_delete_draft_confirm' => 'Weet je zeker dat je dit concept wilt verwijderen?',
     'pages_editing_named' => 'Pagina :pageName Bewerken',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Concept Opties',
     'pages_edit_save_draft' => 'Concept opslaan',
     'pages_edit_draft' => 'Paginaconcept Bewerken',
     'pages_editing_draft' => 'Concept Bewerken',
@@ -195,28 +197,29 @@ return [
     'pages_md_preview' => 'Voorbeeld',
     'pages_md_insert_image' => 'Afbeelding Invoegen',
     'pages_md_insert_link' => 'Entity Link Invoegen',
-    'pages_md_insert_drawing' => 'Insert Drawing',
+    'pages_md_insert_drawing' => 'Tekening Toevoegen',
     'pages_not_in_chapter' => 'Deze pagina staat niet in een hoofdstuk',
     'pages_move' => 'Pagina Verplaatsten',
     'pages_move_success' => 'Pagina verplaatst naar ":parentName"',
-    'pages_copy' => 'Copy Page',
-    'pages_copy_desination' => 'Copy Destination',
-    'pages_copy_success' => 'Page successfully copied',
+    'pages_copy' => 'Pagina Kopiëren',
+    'pages_copy_desination' => 'Kopieerbestemming',
+    'pages_copy_success' => 'Pagina succesvol gekopieerd',
     'pages_permissions' => 'Pagina Permissies',
     'pages_permissions_success' => 'Pagina Permissies bijgwerkt',
     'pages_revision' => 'Revisie',
     'pages_revisions' => 'Pagina Revisies',
     'pages_revisions_named' => 'Pagina Revisies voor :pageName',
     'pages_revision_named' => 'Pagina Revisie voor :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Aangemaakt door',
     'pages_revisions_date' => 'Revisiedatum',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
-    'pages_revisions_changelog' => 'Changelog',
+    'pages_revisions_numbered' => 'Revisie #:id',
+    'pages_revisions_numbered_changes' => 'Revisie #:id Wijzigingen',
+    'pages_revisions_changelog' => 'Wijzigingslogboek',
     'pages_revisions_changes' => 'Wijzigingen',
     'pages_revisions_current' => 'Huidige Versie',
-    'pages_revisions_preview' => 'Preview',
+    'pages_revisions_preview' => 'Voorbeeld',
     'pages_revisions_restore' => 'Herstellen',
     'pages_revisions_none' => 'Deze pagina heeft geen revisies',
     'pages_copy_link' => 'Link Kopiëren',
@@ -224,31 +227,31 @@ return [
     'pages_permissions_active' => 'Pagina Permissies Actief',
     'pages_initial_revision' => 'Eerste publicatie',
     'pages_initial_name' => 'Nieuwe Pagina',
-    'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.',
-    'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.',
+    'pages_editing_draft_notification' => 'U bewerkt momenteel een concept dat voor het laatst is opgeslagen op :timeDiff.',
+    'pages_draft_edited_notification' => 'Deze pagina is sindsdien bijgewerkt. Het wordt aanbevolen dat u dit concept verwijderd.',
     'pages_draft_edit_active' => [
-        'start_a' => ':count users have started editing this page',
-        'start_b' => ':userName has started editing this page',
+        'start_a' => ':count gebruikers zijn begonnen deze pagina te bewerken',
+        'start_b' => ':userName is begonnen met het bewerken van deze pagina',
         'time_a' => 'since the pages was last updated',
-        'time_b' => 'in the last :minCount minutes',
-        'message' => ':start :time. Take care not to overwrite each other\'s updates!',
+        'time_b' => 'in de laatste :minCount minuten',
+        'message' => ':start :time. Let op om elkaars updates niet te overschrijven!',
     ],
-    'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content',
-    'pages_specific' => 'Specific Page',
-    'pages_is_template' => 'Page Template',
+    'pages_draft_discarded' => 'Concept verwijderd, de editor is bijgewerkt met de huidige pagina-inhoud',
+    'pages_specific' => 'Specifieke Pagina',
+    'pages_is_template' => 'Paginasjabloon',
 
     // Editor Sidebar
     'page_tags' => 'Pagina Labels',
-    'chapter_tags' => 'Chapter Tags',
-    'book_tags' => 'Book Tags',
-    'shelf_tags' => 'Shelf Tags',
+    'chapter_tags' => 'Tags van Hoofdstuk',
+    'book_tags' => 'Tags van Boeken',
+    'shelf_tags' => 'Tags van Boekplanken',
     'tag' => 'Label',
     'tags' =>  'Tags',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'Naam Tag',
     'tag_value' => 'Label Waarde (Optioneel)',
     'tags_explain' => "Voeg labels toe om de inhoud te categoriseren. \n Je kunt meerdere labels toevoegen.",
     'tags_add' => 'Voeg een extra label toe',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Deze tag verwijderen',
     'attachments' => 'Bijlages',
     'attachments_explain' => 'Upload bijlages of voeg een link toe. Deze worden zichtbaar in het navigatiepaneel.',
     'attachments_explain_instant_save' => 'Wijzigingen worden meteen opgeslagen.',
@@ -256,7 +259,7 @@ return [
     'attachments_upload' => 'Bestand Uploaden',
     'attachments_link' => 'Link Toevoegen',
     'attachments_set_link' => 'Zet Link',
-    'attachments_delete_confirm' => 'Klik opnieuw op \'verwijderen\' om de bijlage definitief te verwijderen.',
+    'attachments_delete' => 'Weet u zeker dat u deze bijlage wilt verwijderen?',
     'attachments_dropzone' => 'Sleep hier een bestand of klik hier om een bestand toe te voegen',
     'attachments_no_files' => 'Er zijn geen bestanden geüpload',
     'attachments_explain_link' => 'Je kunt een link toevoegen als je geen bestanden wilt uploaden. Dit kan een link naar een andere pagina op deze website zijn, maar ook een link naar een andere website.',
@@ -265,6 +268,7 @@ return [
     'attachments_link_url' => 'Link naar bestand',
     'attachments_link_url_hint' => 'Url, site of bestand',
     'attach' => 'Koppelen',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Bestand Bewerken',
     'attachments_edit_file_name' => 'Bestandsnaam',
     'attachments_edit_drop_upload' => 'Sleep een bestand of klik hier om te uploaden en te overschrijven',
@@ -274,12 +278,12 @@ return [
     'attachments_file_uploaded' => 'Bestand succesvol geüpload',
     'attachments_file_updated' => 'Bestand succesvol bijgewerkt',
     'attachments_link_attached' => 'Link successfully gekoppeld aan de pagina',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Sjablonen',
+    'templates_set_as_template' => 'Pagina is een sjabloon',
+    'templates_explain_set_as_template' => 'Je kunt deze pagina als template instellen zodat de inhoud wordt gebruikt bij het maken van andere pagina\'s. Andere gebruikers kunnen deze template gebruiken als ze rechten hebben om deze pagina te bekijken.',
+    'templates_replace_content' => 'Pagina-inhoud vervangen',
+    'templates_append_content' => 'Toevoegen aan pagina-inhoud',
+    'templates_prepend_content' => 'Voeg vooraan toe aan pagina-inhoud',
 
     // Profile View
     'profile_user_for_x' => 'Lid sinds :time',
@@ -287,12 +291,12 @@ return [
     'profile_not_created_pages' => ':userName heeft geen pagina\'s gemaakt',
     'profile_not_created_chapters' => ':userName heeft geen hoofdstukken gemaakt',
     'profile_not_created_books' => ':userName heeft geen boeken gemaakt',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_not_created_shelves' => ':userName heeft nog geen boekenplanken gemaakt',
 
     // Comments
     'comment' => 'Reactie',
     'comments' => 'Reacties',
-    'comment_add' => 'Add Comment',
+    'comment_add' => 'Reactie Toevoegen',
     'comment_placeholder' => 'Laat hier een reactie achter',
     'comment_count' => '{0} Geen reacties|{1} 1 Reactie|[2,*] :count Reacties',
     'comment_save' => 'Sla reactie op',
@@ -309,7 +313,7 @@ return [
 
     // Revision
     'revision_delete_confirm' => 'Weet u zeker dat u deze revisie wilt verwijderen?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_restore_confirm' => 'Weet u zeker dat u deze revisie wilt herstellen? De huidige pagina-inhoud wordt vervangen.',
     'revision_delete_success' => 'Revisie verwijderd',
     'revision_cannot_delete_latest' => 'Kan de laatste revisie niet verwijderen.'
 ];
\ No newline at end of file
index f83f56a46c2ea3c726029075d82aff6990c756ad..585346748a8825958efd06396f6f9af8048c257c 100644 (file)
@@ -13,12 +13,18 @@ return [
     'email_already_confirmed' => 'Het e-mailadres is al bevestigd. Probeer in te loggen.',
     'email_confirmation_invalid' => 'Deze bevestigingstoken is ongeldig, Probeer opnieuw te registreren.',
     'email_confirmation_expired' => 'De bevestigingstoken is verlopen, Een nieuwe bevestigingsmail is verzonden.',
+    'email_confirmation_awaiting' => 'Het e-mail adres van dit account moet worden bevestigd',
     'ldap_fail_anonymous' => 'LDAP toegang kon geen \'anonymous bind\' uitvoeren',
     'ldap_fail_authed' => 'LDAP toegang was niet mogelijk met de opgegeven dn & wachtwoord',
-    'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
+    'ldap_extension_not_installed' => 'LDAP PHP-extensie is niet geïnstalleerd',
     'ldap_cannot_connect' => 'Kon niet met de LDAP server verbinden',
+    'saml_already_logged_in' => 'Al ingelogd',
+    'saml_user_not_registered' => 'De gebruiker: naam is niet geregistreerd en automatische registratie is uitgeschakeld',
+    'saml_no_email_address' => 'Kan geen e-mailadres voor deze gebruiker vinden in de gegevens die door het externe verificatiesysteem worden verstrekt',
+    'saml_invalid_response_id' => 'Het verzoek van het externe verificatiesysteem is niet herkend door een door deze applicatie gestart proces. Het terug navigeren na een login kan dit probleem veroorzaken.',
+    'saml_fail_authed' => 'Inloggen met :system mislukt, het systeem gaf geen succesvolle autorisatie',
     'social_no_action_defined' => 'Geen actie gedefineerd',
-    'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
+    'social_login_bad_response' => "Fout ontvangen tijdens :socialAccount login: \n:error",
     'social_account_in_use' => 'Dit :socialAccount account is al in gebruik, Probeer in te loggen met de :socialAccount optie.',
     'social_account_email_in_use' => 'Het e-mailadres :email is al in gebruik. Als je al een account hebt kun je een :socialAccount account verbinden met je profielinstellingen.',
     'social_account_existing' => 'Dit :socialAccount is al gekoppeld aan een profiel.',
@@ -27,29 +33,28 @@ return [
     'social_account_register_instructions' => 'Als je nog geen account hebt kun je je registreren met de :socialAccount optie.',
     'social_driver_not_found' => 'Social driver niet gevonden',
     'social_driver_not_configured' => 'Je :socialAccount instellingen zijn correct geconfigureerd.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Deze uitnodigingslink is verlopen. U kunt in plaats daarvan proberen uw wachtwoord opnieuw in te stellen.',
 
     // System
     'path_not_writable' => 'Bestand :filePath kon niet geupload worden. Zorg dat je schrijfrechten op de server hebt.',
     'cannot_get_image_from_url' => 'Kon geen afbeelding genereren van :url',
     'cannot_create_thumbs' => 'De server kon geen thumbnails maken. Controleer of je de GD PHP extensie geïnstalleerd hebt.',
     'server_upload_limit' => 'Het afbeeldingsformaat is te groot. Probeer een kleinere bestandsgrootte.',
-    'uploaded'  => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'uploaded'  => 'Server staat geen uploads van deze grootte toe. Probeer een kleinere grootte van het bestand.',
     'image_upload_error' => 'Er ging iets fout bij het uploaden van de afbeelding',
-    'image_upload_type_error' => 'The image type being uploaded is invalid',
+    'image_upload_type_error' => 'Het afbeeldingstype dat wordt geüpload is ongeldig',
     'file_upload_timeout' => 'Het uploaden van het bestand is verlopen.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Bij het bijwerken van de bijlage bleek de pagina onjuist',
-    'attachment_not_found' => 'Attachment not found',
+    'attachment_not_found' => 'Bijlage niet gevonden',
 
     // Pages
     'page_draft_autosave_fail' => 'Kon het concept niet opslaan. Zorg ervoor dat je een werkende internetverbinding hebt.',
-    'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage',
+    'page_custom_home_deletion' => 'Kan geen pagina verwijderen terwijl deze is ingesteld als een homepage',
 
     // Entities
     'entity_not_found' => 'Entiteit niet gevonden',
-    'bookshelf_not_found' => 'Bookshelf not found',
+    'bookshelf_not_found' => 'Boekenplank niet gevonden',
     'book_not_found' => 'Boek niet gevonden',
     'page_not_found' => 'Pagina niet gevonden',
     'chapter_not_found' => 'Hoofdstuk niet gevonden',
@@ -65,7 +70,7 @@ return [
     'role_cannot_be_edited' => 'Deze rol kan niet bewerkt worden',
     'role_system_cannot_be_deleted' => 'Dit is een systeemrol en kan niet verwijderd worden',
     'role_registration_default_cannot_delete' => 'Deze rol kan niet verwijerd worden zolang dit de standaardrol na registratie is.',
-    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+    'role_cannot_remove_only_admin' => 'Deze gebruiker is de enige gebruiker die is toegewezen aan de beheerdersrol. Wijs de beheerdersrol toe aan een andere gebruiker voordat u probeert deze hier te verwijderen.',
 
     // Comments
     'comment_list' => 'Er is een fout opgetreden tijdens het ophalen van de reacties.',
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Pagina Niet Gevonden',
     'sorry_page_not_found' => 'Sorry, de pagina die je zocht is niet beschikbaar.',
+    'sorry_page_not_found_permission_warning' => 'Als u verwacht dat deze pagina bestaat heeft u misschien geen rechten om het te bekijken.',
     'return_home' => 'Terug naar home',
     'error_occurred' => 'Er Ging Iets Fout',
     'app_down' => ':appName is nu niet beschikbaar',
     'back_soon' => 'Komt snel weer online.',
 
+    // API errors
+    'api_no_authorization_found' => 'Geen autorisatie token gevonden',
+    'api_bad_authorization_format' => 'Een autorisatie token is gevonden, maar het formaat schijnt onjuist te zijn',
+    'api_user_token_not_found' => 'Er is geen overeenkomende API token gevonden voor de opgegeven autorisatie token',
+    'api_incorrect_token_secret' => 'Het opgegeven geheim voor de API token is onjuist',
+    'api_user_no_api_permission' => 'De eigenaar van de gebruikte API token heeft geen toestemming om API calls te maken',
+    'api_user_token_expired' => 'De gebruikte autorisatie token is verlopen',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Fout opgetreden bij het verzenden van een test email:',
+
 ];
index a1efd480e488f197d8234e23a43fc600ff49466e..4b27f03c20ac4372a8c379db1b15df72035c0ed1 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Wachtwoorden moeten overeenkomen en minimaal zes tekens lang zijn.',
     'user' => "We kunnen niemand vinden met dat e-mailadres.",
-    'token' => 'De token om het wachtwoord te herstellen is ongeldig.',
+    'token' => 'Het wachtwoord reset token is ongeldig voor dit e-mailadres.',
     'sent' => 'We hebben je een link gestuurd om je wachtwoord te herstellen!',
     'reset' => 'Je wachtwoord is hersteld!',
 
index dc5521797a90f997498bea516471457fbc12757d..12f3150a0d2b0e2f06f84896f802ab29ace08f9e 100644 (file)
@@ -8,61 +8,113 @@ return [
 
     // Common Messages
     'settings' => 'Instellingen',
-    'settings_save' => 'Instellingen Opslaan',
+    'settings_save' => 'Instellingen opslaan',
     'settings_save_success' => 'Instellingen Opgeslagen',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
+    'app_customization' => 'Aanpassingen',
+    'app_features_security' => 'Functies en beveiliging',
     'app_name' => 'Applicatienaam',
     'app_name_desc' => 'De applicatienaam wordt in e-mails in in de header weergegeven.',
     'app_name_header' => 'Applicatienaam in de header weergeven?',
-    'app_public_access' => 'Public Access',
-    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
-    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
-    'app_public_access_toggle' => 'Allow public access',
+    'app_public_access' => 'Openbare toegang',
+    'app_public_access_desc' => 'Door deze optie in te schakelen, krijgen bezoekers die niet zijn ingelogd, toegang tot content in je BookStack.',
+    'app_public_access_desc_guest' => 'Toegang voor openbare bezoekers kan worden gecontroleerd via de "Guest" gebruiker.',
+    'app_public_access_toggle' => 'Openbare toegang toestaan',
     'app_public_viewing' => 'Publieke bewerkingen toestaan?',
     'app_secure_images' => 'Beter beveiligide afbeeldingen gebruiken?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_toggle' => 'Hogere beveiliging geuploade afbeeldingen inschakelen',
     'app_secure_images_desc' => 'Omwille van de performance zijn alle afbeeldingen publiek toegankelijk. Zorg ervoor dat je de \'directory index\' niet hebt ingeschakeld.',
     'app_editor' => 'Pagina Bewerken',
     'app_editor_desc' => 'Selecteer welke tekstverwerker je wilt gebruiken.',
     'app_custom_html' => 'Speciale HTML toevoegen',
     'app_custom_html_desc' => 'Alles wat je hier toevoegd wordt in de <head> sectie van elke pagina meengenomen. Dit kun je bijvoorbeeld voor analytics gebruiken.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'Aangepaste HTML-hoofd-inhoud is uitgeschakeld op deze instellingenpagina om ervoor te zorgen dat breekbare wijzigingen ongedaan gemaakt kunnen worden.',
     'app_logo' => 'Applicatielogo',
     'app_logo_desc' => 'De afbeelding moet 43px hoog zijn. <br>Grotere afbeeldingen worden geschaald.',
     'app_primary_color' => 'Applicatie hoofdkleur',
     'app_primary_color_desc' => 'Geef een hexadecimale waarde. <br>Als je niks invult wordt de standaardkleur gebruikt.',
-    'app_homepage' => 'Application Homepage',
-    'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
-    'app_homepage_select' => 'Select a page',
+    'app_homepage' => 'Applicatie Homepagina',
+    'app_homepage_desc' => 'Selecteer een weergave om weer te geven op de homepage in plaats van de standaard weergave. Paginarechten worden genegeerd voor geselecteerde pagina\'s.',
+    'app_homepage_select' => 'Selecteer een pagina',
     'app_disable_comments' => 'Reacties uitschakelen',
-    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_toggle' => 'Opmerkingen uitschakelen',
     'app_disable_comments_desc' => 'Schakel opmerkingen uit op alle pagina\'s in de applicatie. Bestaande opmerkingen worden niet getoond.',
 
+    // Color settings
+    'content_colors' => 'Kleuren inhoud',
+    'content_colors_desc' => 'Stelt de kleuren in voor alle elementen in de pagina-organisatieleiding. Het kiezen van kleuren met dezelfde helderheid als de standaard kleuren wordt aanbevolen voor de leesbaarheid.',
+    'bookshelf_color' => 'Kleur van de Boekenplank',
+    'book_color' => 'Kleur van het Boek',
+    'chapter_color' => 'Kleur van het Hoofdstuk',
+    'page_color' => 'Pagina kleur',
+    'page_draft_color' => 'Klad pagina kleur',
+
     // Registration Settings
     'reg_settings' => 'Registratieinstellingen',
-    'reg_enable' => 'Enable Registration',
-    'reg_enable_toggle' => 'Enable registration',
-    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_enable' => 'Registratie inschakelen',
+    'reg_enable_toggle' => 'Registratie inschakelen',
+    'reg_enable_desc' => 'Wanneer registratie is ingeschakeld, kan de gebruiker zich aanmelden als een gebruiker. Na registratie krijgen ze een enkele, standaard gebruikersrol.',
     'reg_default_role' => 'Standaard rol na registratie',
-    'reg_email_confirmation' => 'Email Confirmation',
-    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_enable_external_warning' => 'De optie hierboven wordt niet gebruikt terwijl LDAP authenticatie actief is. Gebruikersaccounts voor niet-bestaande leden zullen automatisch worden gecreëerd als authenticatie tegen het gebruikte LDAP-systeem succesvol is.',
+    'reg_email_confirmation' => 'E-mail bevestiging',
+    'reg_email_confirmation_toggle' => 'E-mailbevestiging verplichten',
     'reg_confirm_email_desc' => 'Als domeinrestricties aan staan dan is altijd e-maibevestiging nodig. Onderstaande instelling wordt dan genegeerd.',
     'reg_confirm_restrict_domain' => 'Beperk registratie tot een maildomein',
     'reg_confirm_restrict_domain_desc' => 'Geen een komma-gescheiden lijst van domeinnamen die gebruikt mogen worden bij registratie. <br> Let op: na registratie kunnen gebruikers hun e-mailadres nog steeds wijzigen.',
     'reg_confirm_restrict_domain_placeholder' => 'Geen beperkingen ingesteld',
 
     // Maintenance settings
-    'maint' => 'Maintenance',
-    'maint_image_cleanup' => 'Cleanup Images',
-    'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
-    'maint_image_cleanup_run' => 'Run Cleanup',
-    'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
-    'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
-    'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
+    'maint' => 'Onderhoud',
+    'maint_image_cleanup' => 'Afbeeldingen opschonen',
+    'maint_image_cleanup_desc' => "Scant pagina- en revisie inhoud om te controleren welke afbeeldingen en tekeningen momenteel worden gebruikt en welke afbeeldingen overbodig zijn. Zorg ervoor dat je een volledige database en afbeelding backup maakt voordat je dit uitvoert.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_run' => 'Opschonen uitvoeren',
+    'maint_image_cleanup_warning' => ':count potentieel ongebruikte afbeeldingen gevonden. Weet u zeker dat u deze afbeeldingen wilt verwijderen?',
+    'maint_image_cleanup_success' => ':count potentieel ongebruikte afbeeldingen gevonden en verwijderd!',
+    'maint_image_cleanup_nothing_found' => 'Geen ongebruikte afbeeldingen gevonden, niets verwijderd!',
+    'maint_send_test_email' => 'Stuur een test e-mail',
+    'maint_send_test_email_desc' => 'Dit verstuurt een test e-mail naar het e-mailadres dat je in je profiel hebt opgegeven.',
+    'maint_send_test_email_run' => 'Test e-mail verzenden',
+    'maint_send_test_email_success' => 'E-mail verzonden naar :address',
+    'maint_send_test_email_mail_subject' => 'Test E-mail',
+    'maint_send_test_email_mail_greeting' => 'E-mailbezorging lijkt te werken!',
+    'maint_send_test_email_mail_text' => 'Gefeliciteerd! Nu je deze e-mailmelding hebt ontvangen, lijken je e-mailinstellingen correct te zijn geconfigureerd.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
 
     // Role Settings
     'roles' => 'Rollen',
@@ -79,17 +131,19 @@ return [
     'role_details' => 'Rol Details',
     'role_name' => 'Rolnaam',
     'role_desc' => 'Korte beschrijving van de rol',
-    'role_external_auth_id' => 'External Authentication IDs',
+    'role_external_auth_id' => 'Externe authenticatie ID\'s',
     'role_system' => 'Systeem Permissies',
     'role_manage_users' => 'Gebruikers beheren',
     'role_manage_roles' => 'Rollen en rechten beheren',
     'role_manage_entity_permissions' => 'Beheer alle boeken-, hoofdstukken- en paginaresitrcties',
     'role_manage_own_entity_permissions' => 'Beheer restricties van je eigen boeken, hoofdstukken en pagina\'s',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => 'Paginasjablonen beheren',
+    'role_access_api' => 'Ga naar systeem API',
     'role_manage_settings' => 'Beheer app instellingen',
     'role_asset' => 'Asset Permissies',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Deze permissies bepalen de standaardtoegangsrechten. Permissies op boeken, hoofdstukken en pagina\'s overschrijven deze instelling.',
-    'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
+    'role_asset_admins' => 'Beheerders krijgen automatisch toegang tot alle inhoud, maar deze opties kunnen interface opties tonen of verbergen.',
     'role_all' => 'Alles',
     'role_own' => 'Eigen',
     'role_controlled_by_asset' => 'Gecontroleerd door de asset waar deze is geüpload',
@@ -103,37 +157,67 @@ return [
     'user_profile' => 'Gebruikersprofiel',
     'users_add_new' => 'Gebruiker toevoegen',
     'users_search' => 'Gebruiker zoeken',
-    'users_details' => 'User Details',
-    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
-    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_latest_activity' => 'Latest Activity',
+    'users_details' => 'Gebruiker details',
+    'users_details_desc' => 'Stel een weergavenaam en e-mailadres in voor deze gebruiker. Het e-mailadres zal worden gebruikt om in te loggen.',
+    'users_details_desc_no_email' => 'Stel een weergavenaam in voor deze gebruiker zodat anderen deze kunnen herkennen.',
     'users_role' => 'Gebruikersrollen',
-    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
-    'users_password' => 'User Password',
-    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
-    'users_external_auth_id' => 'External Authentication ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+    'users_role_desc' => 'Selecteer aan welke rollen deze gebruiker zal worden toegewezen. Als een gebruiker aan meerdere rollen wordt toegewezen worden de machtigingen van deze rollen samengevoegd en krijgen ze alle machtigingen van de toegewezen rollen.',
+    'users_password' => 'Wachtwoord gebruiker',
+    'users_password_desc' => 'Stel een wachtwoord in dat gebruikt wordt om in te loggen op de applicatie. Dit moet minstens 6 tekens lang zijn.',
+    'users_send_invite_text' => 'U kunt ervoor kiezen om deze gebruiker een uitnodigingsmail te sturen waarmee hij zijn eigen wachtwoord kan instellen, anders kunt u zelf zijn wachtwoord instellen.',
+    'users_send_invite_option' => 'Stuur gebruiker uitnodigings e-mail',
+    'users_external_auth_id' => 'Externe authenticatie ID',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
     'users_password_warning' => 'Vul onderstaande formulier alleen in als je het wachtwoord wilt aanpassen:',
     'users_system_public' => 'De eigenschappen van deze gebruiker worden voor elke gastbezoeker gebruikt. Er kan niet mee ingelogd worden en wordt automatisch toegewezen.',
     'users_delete' => 'Verwijder gebruiker',
     'users_delete_named' => 'Verwijder gebruiker :userName',
     'users_delete_warning' => 'Dit zal de gebruiker \':userName\' volledig uit het systeem verwijderen.',
     'users_delete_confirm' => 'Weet je zeker dat je deze gebruiker wilt verwijderen?',
-    'users_delete_success' => 'Gebruiker succesvol verwijderd',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Bewerk Gebruiker',
     'users_edit_profile' => 'Bewerk Profiel',
     'users_edit_success' => 'Gebruiker succesvol bijgewerkt',
     'users_avatar' => 'Avatar',
     'users_avatar_desc' => 'De afbeelding moet vierkant zijn en ongeveer 256px breed.',
     'users_preferred_language' => 'Voorkeurstaal',
-    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
-    'users_social_accounts' => 'Social Accounts',
+    'users_preferred_language_desc' => 'Deze optie wijzigt de taal die gebruikt wordt voor de gebruikersinterface. Dit heeft geen invloed op de door de gebruiker gemaakte inhoud.',
+    'users_social_accounts' => 'Sociale accounts',
     'users_social_accounts_info' => 'Hier kun je accounts verbinden om makkelijker in te loggen. Via je profiel kun je ook weer rechten intrekken die bij deze social accountsh horen.',
     'users_social_connect' => 'Account Verbinden',
     'users_social_disconnect' => 'Account Ontkoppelen',
     'users_social_connected' => ':socialAccount account is succesvol aan je profiel gekoppeld.',
     'users_social_disconnected' => ':socialAccount account is succesvol ontkoppeld van je profiel.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Naam',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Vervaldatum',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
+    'user_api_token_delete' => 'Token Verwijderen',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 05e09d8a19d7b2b61899bee8b4f1d89a007960d9..2f8ffc9f0531959e4ff2098ad06f7a297890d9dd 100644 (file)
 return [
 
     // Standard laravel validation lines
-    'accepted'             => 'The :attribute must be accepted.',
-    'active_url'           => 'The :attribute is not a valid URL.',
-    'after'                => 'The :attribute must be a date after :date.',
-    'alpha'                => 'The :attribute may only contain letters.',
-    'alpha_dash'           => 'The :attribute may only contain letters, numbers, and dashes.',
-    'alpha_num'            => 'The :attribute may only contain letters and numbers.',
-    'array'                => 'The :attribute must be an array.',
-    'before'               => 'The :attribute must be a date before :date.',
+    'accepted'             => ':attribute moet geaccepteerd worden.',
+    'active_url'           => ':attribute is geen geldige URL.',
+    'after'                => ':attribute moet een datum zijn later dan :date.',
+    'alpha'                => ':attribute mag alleen letters bevatten.',
+    'alpha_dash'           => ':attribute mag alleen letters, cijfers, streepjes en liggende streepjes bevatten.',
+    'alpha_num'            => ':attribute mag alleen letters en nummers bevatten.',
+    'array'                => ':attribute moet een reeks zijn.',
+    'before'               => ':attribute moet een datum zijn voor :date.',
     'between'              => [
-        'numeric' => 'The :attribute must be between :min and :max.',
-        'file'    => 'The :attribute must be between :min and :max kilobytes.',
-        'string'  => 'The :attribute must be between :min and :max characters.',
-        'array'   => 'The :attribute must have between :min and :max items.',
+        'numeric' => ':attribute moet tussen de :min en :max zijn.',
+        'file'    => ':attribute moet tussen de :min en :max kilobytes zijn.',
+        'string'  => ':attribute moet tussen de :min en :max tekens zijn.',
+        'array'   => ':attribute moet tussen de :min en :max items bevatten.',
     ],
-    'boolean'              => 'The :attribute field must be true or false.',
-    'confirmed'            => 'The :attribute confirmation does not match.',
-    'date'                 => 'The :attribute is not a valid date.',
-    'date_format'          => 'The :attribute does not match the format :format.',
-    'different'            => 'The :attribute and :other must be different.',
-    'digits'               => 'The :attribute must be :digits digits.',
-    'digits_between'       => 'The :attribute must be between :min and :max digits.',
-    'email'                => 'The :attribute must be a valid email address.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
-    'filled'               => 'The :attribute field is required.',
+    'boolean'              => ':attribute moet ja of nee zijn.',
+    'confirmed'            => ':attribute bevestiging komt niet overeen.',
+    'date'                 => ':attribute is geen geldige datum.',
+    'date_format'          => ':attribute komt niet overeen met het formaat :format.',
+    'different'            => ':attribute en :other moeten verschillend zijn.',
+    'digits'               => ':attribute moet bestaan uit :digits cijfers.',
+    'digits_between'       => ':attribute moet tussen de :min en :max cijfers zijn.',
+    'email'                => ':attribute is geen geldig e-mailadres.',
+    'ends_with' => ':attribute moet eindigen met een van de volgende: :values',
+    'filled'               => ':attribute is verplicht.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute moet groter zijn dan :value.',
+        'file'    => ':attribute moet groter zijn dan :value kilobytes.',
+        'string'  => ':attribute moet meer dan :value tekens bevatten.',
+        'array'   => ':attribute moet meer dan :value items bevatten.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => ':attribute moet groter of gelijk zijn aan :value.',
+        'file'    => ':attribute moet groter of gelijk zijn aan :value kilobytes.',
+        'string'  => ':attribute moet :value of meer tekens bevatten.',
+        'array'   => ':attribute moet :value items of meer bevatten.',
     ],
-    'exists'               => 'The selected :attribute is invalid.',
-    'image'                => 'The :attribute must be an image.',
-    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
-    'in'                   => 'The selected :attribute is invalid.',
-    'integer'              => 'The :attribute must be an integer.',
-    'ip'                   => 'The :attribute must be a valid IP address.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'exists'               => ':attribute is ongeldig.',
+    'image'                => ':attribute moet een afbeelding zijn.',
+    'image_extension'      => ':attribute moet een geldige en ondersteunde afbeeldings-extensie hebben.',
+    'in'                   => ':attribute is ongeldig.',
+    'integer'              => ':attribute moet een getal zijn.',
+    'ip'                   => ':attribute moet een geldig IP-adres zijn.',
+    'ipv4'                 => ':attribute moet een geldig IPv4-adres zijn.',
+    'ipv6'                 => ':attribute moet een geldig IPv6-adres zijn.',
+    'json'                 => ':attribute moet een geldige JSON-string zijn.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute moet kleiner zijn dan :value.',
+        'file'    => ':attribute moet kleiner zijn dan :value kilobytes.',
+        'string'  => ':attribute moet minder dan :value tekens bevatten.',
+        'array'   => ':attribute moet minder dan :value items bevatten.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute moet kleiner of gelijk zijn aan :value.',
+        'file'    => ':attribute moet kleiner of gelijk zijn aan :value kilobytes.',
+        'string'  => ':attribute moet :value tekens of minder bevatten.',
+        'array'   => ':attribute mag niet meer dan :value items bevatten.',
     ],
     'max'                  => [
-        'numeric' => 'The :attribute may not be greater than :max.',
-        'file'    => 'The :attribute may not be greater than :max kilobytes.',
-        'string'  => 'The :attribute may not be greater than :max characters.',
-        'array'   => 'The :attribute may not have more than :max items.',
+        'numeric' => ':attribute mag niet groter zijn dan :max.',
+        'file'    => ':attribute mag niet groter zijn dan :max kilobytes.',
+        'string'  => ':attribute mag niet groter zijn dan :max tekens.',
+        'array'   => ':attribute mag niet meer dan :max items bevatten.',
     ],
-    'mimes'                => 'The :attribute must be a file of type: :values.',
+    'mimes'                => ':attribute moet een bestand zijn van het type: :values.',
     'min'                  => [
-        'numeric' => 'The :attribute must be at least :min.',
-        'file'    => 'The :attribute must be at least :min kilobytes.',
-        'string'  => 'The :attribute must be at least :min characters.',
-        'array'   => 'The :attribute must have at least :min items.',
+        'numeric' => ':attribute moet minstens :min zijn.',
+        'file'    => ':attribute moet minstens :min kilobytes zijn.',
+        'string'  => ':attribute moet minstens :min karakters bevatten.',
+        'array'   => ':attribute moet minstens :min items bevatten.',
     ],
-    'no_double_extension'  => 'The :attribute must only have a single file extension.',
-    'not_in'               => 'The selected :attribute is invalid.',
-    'not_regex'            => 'The :attribute format is invalid.',
-    'numeric'              => 'The :attribute must be a number.',
-    'regex'                => 'The :attribute format is invalid.',
-    'required'             => 'The :attribute field is required.',
-    'required_if'          => 'The :attribute field is required when :other is :value.',
-    'required_with'        => 'The :attribute field is required when :values is present.',
-    'required_with_all'    => 'The :attribute field is required when :values is present.',
-    'required_without'     => 'The :attribute field is required when :values is not present.',
-    'required_without_all' => 'The :attribute field is required when none of :values are present.',
-    'same'                 => 'The :attribute and :other must match.',
+    'no_double_extension'  => ':attribute mag maar een enkele bestandsextensie hebben.',
+    'not_in'               => ':attribute is ongeldig.',
+    'not_regex'            => ':attribute formaat is ongeldig.',
+    'numeric'              => ':attribute moet een getal zijn.',
+    'regex'                => ':attribute formaat is ongeldig.',
+    'required'             => ':attribute veld is verplicht.',
+    'required_if'          => ':attribute veld is verplicht als :other gelijk is aan :value.',
+    'required_with'        => ':attribute veld is verplicht wanneer :values ingesteld is.',
+    'required_with_all'    => ':attribute veld is verplicht wanneer :values ingesteld is.',
+    'required_without'     => ':attribute veld is verplicht wanneer :values niet ingesteld is.',
+    'required_without_all' => ':attribute veld is verplicht wanneer geen van :values ingesteld zijn.',
+    'same'                 => ':attribute en :other moeten overeenkomen.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
-        'numeric' => 'The :attribute must be :size.',
-        'file'    => 'The :attribute must be :size kilobytes.',
-        'string'  => 'The :attribute must be :size characters.',
-        'array'   => 'The :attribute must contain :size items.',
+        'numeric' => ':attribute moet :size zijn.',
+        'file'    => ':attribute moet :size kilobytes zijn.',
+        'string'  => ':attribute moet :size tekens bevatten.',
+        'array'   => ':attribute moet :size items bevatten.',
     ],
-    'string'               => 'The :attribute must be a string.',
-    'timezone'             => 'The :attribute must be a valid zone.',
-    'unique'               => 'The :attribute has already been taken.',
-    'url'                  => 'The :attribute format is invalid.',
-    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+    'string'               => ':attribute moet tekst zijn.',
+    'timezone'             => ':attribute moet een geldige zone zijn.',
+    'unique'               => ':attribute is al in gebruik.',
+    'url'                  => ':attribute formaat is ongeldig.',
+    'uploaded'             => 'Het bestand kon niet worden geüpload. De server accepteert mogelijk geen bestanden van deze grootte.',
 
     // Custom validation lines
     'custom' => [
         'password-confirm' => [
-            'required_with' => 'Password confirmation required',
+            'required_with' => 'Wachtwoord bevestiging verplicht',
         ],
     ],
 
index 08a35c30def0cd13ede2b095f5eddc07062efa71..c4e8ef0de831b0954d646c4007138a13bf40b450 100644 (file)
@@ -26,14 +26,14 @@ return [
     'chapter_move'                => 'przeniesiono rozdział',
 
     // Books
-    'book_create'                 => 'utworzono podręcznik',
-    'book_create_notification'    => 'Podręcznik utworzony pomyślnie',
-    'book_update'                 => 'zaktualizowano podręcznik',
-    'book_update_notification'    => 'Podręcznik zaktualizowany pomyślnie',
-    'book_delete'                 => 'usunięto podręcznik',
-    'book_delete_notification'    => 'Podręcznik usunięty pomyślnie',
-    'book_sort'                   => 'posortowano podręcznik',
-    'book_sort_notification'      => 'Podręcznik posortowany pomyślnie',
+    'book_create'                 => 'utworzono książkę',
+    'book_create_notification'    => 'Książkę utworzony pomyślnie',
+    'book_update'                 => 'zaktualizowano książkę',
+    'book_update_notification'    => 'Książkę zaktualizowany pomyślnie',
+    'book_delete'                 => 'usunięto książkę',
+    'book_delete_notification'    => 'Książkę usunięty pomyślnie',
+    'book_sort'                   => 'posortowano książkę',
+    'book_sort_notification'      => 'Książkę posortowany pomyślnie',
 
     // Bookshelves
     'bookshelf_create'            => 'utworzono półkę',
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'skomentował',
+    'permissions_update'          => 'updated permissions',
 ];
index 1e135880e475c24ce1ac79a80bfb5c3c49500ab8..01d74b99cddfbb90fc9bcaf5b278e2c7a14605f5 100644 (file)
@@ -26,8 +26,8 @@ return [
     'remember_me' => 'Zapamiętaj mnie',
     'ldap_email_hint' => 'Wprowadź adres e-mail dla tego konta.',
     'create_account' => 'Utwórz konto',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
+    'already_have_account' => 'Masz już konto?',
+    'dont_have_account' => 'Nie masz konta?',
     'social_login' => 'Logowanie za pomocą konta społecznościowego',
     'social_registration' => 'Rejestracja za pomocą konta społecznościowego',
     'social_registration_text' => 'Zarejestruj się za pomocą innej usługi.',
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Resetowanie hasła',
     'reset_password_send_instructions' => 'Wprowadź adres e-mail powiązany z Twoim kontem, by otrzymać link do resetowania hasła.',
     'reset_password_send_button' => 'Wyślij link do resetowania hasła',
-    'reset_password_sent_success' => 'Wysłano link do resetowania hasła na adres :email.',
+    'reset_password_sent' => 'Link z resetem hasła zostanie wysłany na :email jeśli mamy ten adres w systemie.',
     'reset_password_success' => 'Hasło zostało zresetowane pomyślnie.',
     'email_reset_subject' => 'Resetowanie hasła do :appName',
     'email_reset_text' => 'Otrzymujesz tę wiadomość ponieważ ktoś zażądał zresetowania hasła do Twojego konta.',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => 'Wyślij ponownie wiadomość z potwierdzeniem',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Zostałeś zaproszony do :appName!',
+    'user_invite_email_greeting' => 'Zostało dla Ciebie utworzone konto w :appName.',
+    'user_invite_email_text' => 'Kliknij przycisk poniżej, aby ustawić hasło do konta i uzyskać do niego dostęp:',
+    'user_invite_email_action' => 'Ustaw hasło do konta',
+    'user_invite_page_welcome' => 'Witaj w :appName!',
+    'user_invite_page_text' => 'Aby zakończyć tworzenie konta musisz ustawić hasło, które będzie używane do logowania do :appName w przyszłości.',
+    'user_invite_page_confirm_button' => 'Potwierdź hasło',
+    'user_invite_success' => 'Hasło zostało ustawione, teraz masz dostęp do :appName!'
 ];
\ No newline at end of file
index d5bf0f199e9f876e08c7d13b91a86491bb6333df..c93d0b9e108f97638ffa8546cf70b66d90d9c8d0 100644 (file)
@@ -11,7 +11,7 @@ return [
     'save' => 'Zapisz',
     'continue' => 'Kontynuuj',
     'select' => 'Wybierz',
-    'toggle_all' => 'Toggle All',
+    'toggle_all' => 'Przełącz wszystko',
     'more' => 'Więcej',
 
     // Form Labels
@@ -24,7 +24,7 @@ return [
     // Actions
     'actions' => 'Akcje',
     'view' => 'Widok',
-    'view_all' => 'View All',
+    'view_all' => 'Zobacz wszystkie',
     'create' => 'Utwórz',
     'update' => 'Zaktualizuj',
     'edit' => 'Edytuj',
@@ -33,20 +33,22 @@ return [
     'copy' => 'Skopiuj',
     'reply' => 'Odpowiedz',
     'delete' => 'Usuń',
+    'delete_confirm' => 'Potwierdź usunięcie',
     'search' => 'Szukaj',
     'search_clear' => 'Wyczyść wyszukiwanie',
     'reset' => 'Resetuj',
     'remove' => 'Usuń',
     'add' => 'Dodaj',
+    'fullscreen' => 'Pełny ekran',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
-    'sort_name' => 'Name',
-    'sort_created_at' => 'Created Date',
-    'sort_updated_at' => 'Updated Date',
+    'sort_options' => 'Opcje sortowania',
+    'sort_direction_toggle' => 'Przełącz kierunek sortowania',
+    'sort_ascending' => 'Sortuj rosnąco',
+    'sort_descending' => 'Sortuj malejąco',
+    'sort_name' => 'Nazwa',
+    'sort_created_at' => 'Data utworzenia',
+    'sort_updated_at' => 'Data aktualizacji',
 
     // Misc
     'deleted_user' => 'Użytkownik usunięty',
@@ -59,16 +61,18 @@ return [
     'grid_view' => 'Widok kafelkowy',
     'list_view' => 'Widok listy',
     'default' => 'Domyślny',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Ścieżka nawigacji',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Menu profilu',
     'view_profile' => 'Zobacz profil',
     'edit_profile' => 'Edytuj profil',
+    'dark_mode' => 'Tryb ciemny',
+    'light_mode' => 'Tryb jasny',
 
     // Layout tabs
-    'tab_info' => 'Info',
-    'tab_content' => 'Content',
+    'tab_info' => 'Informacje',
+    'tab_content' => 'Treść',
 
     // Email Content
     'email_action_help' => 'Jeśli masz problem z kliknięciem przycisku ":actionText", skopiuj i wklej poniższy adres URL w nowej karcie swojej przeglądarki:',
index 9c044d23773a646b10627bd4e9d2918bed5c5629..b03ca35505ad6fb6008857f2989f95de86454773 100644 (file)
@@ -8,14 +8,14 @@ return [
     'image_select' => 'Wybór obrazka',
     'image_all' => 'Wszystkie',
     'image_all_title' => 'Zobacz wszystkie obrazki',
-    'image_book_title' => 'Zobacz obrazki zapisane w tym podręczniku',
+    'image_book_title' => 'Zobacz obrazki zapisane w tej książce',
     'image_page_title' => 'Zobacz obrazki zapisane na tej stronie',
     'image_search_hint' => 'Szukaj po nazwie obrazka',
     'image_uploaded' => 'Przesłano :uploadedDate',
     'image_load_more' => 'Wczytaj więcej',
     'image_image_name' => 'Nazwa obrazka',
     'image_delete_used' => 'Ten obrazek jest używany na stronach wyświetlonych poniżej.',
-    'image_delete_confirm' => 'Kliknij ponownie Usuń by potwierdzić usunięcie obrazka.',
+    'image_delete_confirm_text' => 'Czy na pewno chcesz usunąć ten obraz?',
     'image_select_image' => 'Wybierz obrazek',
     'image_dropzone' => 'Upuść obrazki tutaj lub kliknij by wybrać obrazki do przesłania',
     'images_deleted' => 'Usunięte obrazki',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Edytuj kod',
     'code_language' => 'Język kodu',
     'code_content' => 'Zawartość kodu',
+    'code_session_history' => 'Historia sesji',
     'code_save' => 'Zapisz kod',
 ];
index a87959598da6cca946e11feb7426a2201c4bfad9..59960bd301e3b9b7564b607a23d15265175f44ac 100644 (file)
@@ -10,8 +10,8 @@ return [
     'recently_created_pages' => 'Ostatnio utworzone strony',
     'recently_updated_pages' => 'Ostatnio zaktualizowane strony',
     'recently_created_chapters' => 'Ostatnio utworzone rozdziały',
-    'recently_created_books' => 'Ostatnio utworzone podręczniki',
-    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_created_books' => 'Ostatnio utworzone książki',
+    'recently_created_shelves' => 'Ostatnio utworzone półki',
     'recently_update' => 'Ostatnio zaktualizowane',
     'recently_viewed' => 'Ostatnio wyświetlane',
     'recent_activity' => 'Ostatnia aktywność',
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => 'Utworzono :timeLength przez :user',
     'meta_updated' => 'Zaktualizowano :timeLength',
     'meta_updated_name' => 'Zaktualizowano :timeLength przez :user',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Wybór obiektu',
     'images' => 'Obrazki',
     'my_recent_drafts' => 'Moje ostatnie wersje robocze',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Jeśli włączone są indywidualne uprawnienia, to te uprawnienia będą miały priorytet względem pozostałych ustawionych uprawnień ról.',
     'permissions_enable' => 'Włącz własne uprawnienia',
     'permissions_save' => 'Zapisz uprawnienia',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Wyniki wyszukiwania',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Brak stron spełniających zadane kryterium',
     'search_for_term' => 'Szukaj :term',
     'search_more' => 'Więcej wyników',
-    'search_filters' => 'Filtry wyszukiwania',
+    'search_advanced' => 'Wyszukiwanie zaawansowane',
+    'search_terms' => 'Szukane frazy',
     'search_content_type' => 'Rodzaj treści',
     'search_exact_matches' => 'Dokładne frazy',
     'search_tags' => 'Tagi wyszukiwania',
@@ -68,72 +71,72 @@ return [
     // Shelves
     'shelf' => 'Półka',
     'shelves' => 'Półki',
-    'x_shelves' => ':count Shelf|:count Shelves',
+    'x_shelves' => ':count Półek|:count Półek',
     'shelves_long' => 'Półki',
     'shelves_empty' => 'Brak utworzonych półek',
     'shelves_create' => 'Utwórz półkę',
     'shelves_popular' => 'Popularne półki',
     'shelves_new' => 'Nowe półki',
-    'shelves_new_action' => 'New Shelf',
+    'shelves_new_action' => 'Nowa półka',
     'shelves_popular_empty' => 'Najpopularniejsze półki pojawią się w tym miejscu.',
     'shelves_new_empty' => 'Tutaj pojawią się ostatnio utworzone półki.',
     'shelves_save' => 'Zapisz półkę',
-    'shelves_books' => 'Podręczniki na tej półce',
-    'shelves_add_books' => 'Dodaj podręczniki do tej półki',
-    'shelves_drag_books' => 'Przeciągnij podręczniki tutaj aby dodać je do półki',
-    'shelves_empty_contents' => 'Ta półka nie ma przypisanych żadnych podręczników',
-    'shelves_edit_and_assign' => 'Edytuj półkę aby przypisać podręczniki',
+    'shelves_books' => 'Książki na tej półce',
+    'shelves_add_books' => 'Dodaj książkę do tej półki',
+    'shelves_drag_books' => 'Przeciągnij książki tutaj aby dodać je do półki',
+    'shelves_empty_contents' => 'Ta półka nie ma przypisanych żadnych książek',
+    'shelves_edit_and_assign' => 'Edytuj półkę aby przypisać książki',
     'shelves_edit_named' => 'Edytuj półkę :name',
     'shelves_edit' => 'Edytuj półkę',
     'shelves_delete' => 'Usuń półkę',
     'shelves_delete_named' => 'Usuń półkę :name',
-    'shelves_delete_explain' => "Ta operacja usunie półkę o nazwie ':name'. Podręczniki z tej półki nie zostaną usunięte.",
+    'shelves_delete_explain' => "Ta operacja usunie półkę o nazwie ':name'. Książki z tej półki nie zostaną usunięte.",
     'shelves_delete_confirmation' => 'Czy jesteś pewien, że chcesz usunąć tę półkę?',
     'shelves_permissions' => 'Uprawnienia półki',
     'shelves_permissions_updated' => 'Uprawnienia półki zostały zaktualizowane',
     'shelves_permissions_active' => 'Uprawnienia półki są aktywne',
-    'shelves_copy_permissions_to_books' => 'Skopiuj uprawnienia do podręczników',
+    'shelves_copy_permissions_to_books' => 'Skopiuj uprawnienia do książek',
     'shelves_copy_permissions' => 'Skopiuj uprawnienia',
-    'shelves_copy_permissions_explain' => 'To spowoduje zastosowanie obecnych ustawień uprawnień dla tej półki do wszystkich podręczników w niej zawartych. Przed aktywacją upewnij się, że wszelkie zmiany w uprawnieniach do tej półki zostały zapisane.',
-    'shelves_copy_permission_success' => 'Uprawnienia półki zostały skopiowane do :count podręczników',
+    'shelves_copy_permissions_explain' => 'To spowoduje zastosowanie obecnych ustawień uprawnień dla tej półki do wszystkich książek w niej zawartych. Przed aktywacją upewnij się, że wszelkie zmiany w uprawnieniach do tej półki zostały zapisane.',
+    'shelves_copy_permission_success' => 'Uprawnienia półki zostały skopiowane do :count książek',
 
     // Books
-    'book' => 'Podręcznik',
-    'books' => 'Podręczniki',
-    'x_books' => ':count Podręcznik|:count Podręczniki',
-    'books_empty' => 'Brak utworzonych podręczników',
-    'books_popular' => 'Popularne podręczniki',
-    'books_recent' => 'Ostatnie podręczniki',
-    'books_new' => 'Nowe podręczniki',
-    'books_new_action' => 'New Book',
-    'books_popular_empty' => 'Najpopularniejsze podręczniki pojawią się w tym miejscu.',
-    'books_new_empty' => 'Tutaj pojawią się ostatnio utworzone podręczniki.',
-    'books_create' => 'Utwórz podręcznik',
-    'books_delete' => 'Usuń podręcznik',
-    'books_delete_named' => 'Usuń podręcznik :bookName',
-    'books_delete_explain' => 'To spowoduje usunięcie podręcznika \':bookName\', Wszystkie strony i rozdziały zostaną usunięte.',
-    'books_delete_confirmation' => 'Czy na pewno chcesz usunąc ten podręcznik?',
-    'books_edit' => 'Edytuj podręcznik',
-    'books_edit_named' => 'Edytuj podręcznik :bookName',
-    'books_form_book_name' => 'Nazwa podręcznika',
-    'books_save' => 'Zapisz podręcznik',
-    'books_permissions' => 'Uprawnienia podręcznika',
-    'books_permissions_updated' => 'Zaktualizowano uprawnienia podręcznika',
-    'books_empty_contents' => 'Brak stron lub rozdziałów w tym podręczniku.',
+    'book' => 'Książka',
+    'books' => 'Książki',
+    'x_books' => ':count Książka|:count Książki',
+    'books_empty' => 'Brak utworzonych książek',
+    'books_popular' => 'Popularne książki',
+    'books_recent' => 'Ostatnie książki',
+    'books_new' => 'Nowe książki',
+    'books_new_action' => 'Nowa księga',
+    'books_popular_empty' => 'Najpopularniejsze książki pojawią się w tym miejscu.',
+    'books_new_empty' => 'Tutaj pojawią się ostatnio utworzone książki.',
+    'books_create' => 'Utwórz książkę',
+    'books_delete' => 'Usuń książkę',
+    'books_delete_named' => 'Usuń książkę :bookName',
+    'books_delete_explain' => 'To spowoduje usunięcie książki \':bookName\', Wszystkie strony i rozdziały zostaną usunięte.',
+    'books_delete_confirmation' => 'Czy na pewno chcesz usunąc tę książkę?',
+    'books_edit' => 'Edytuj książkę',
+    'books_edit_named' => 'Edytuj książkę :bookName',
+    'books_form_book_name' => 'Nazwa książki',
+    'books_save' => 'Zapisz książkę',
+    'books_permissions' => 'Uprawnienia książki',
+    'books_permissions_updated' => 'Zaktualizowano uprawnienia książki',
+    'books_empty_contents' => 'Brak stron lub rozdziałów w tej książce.',
     'books_empty_create_page' => 'Utwórz nową stronę',
-    'books_empty_sort_current_book' => 'posortuj bieżący podręcznik',
+    'books_empty_sort_current_book' => 'posortuj bieżącą książkę',
     'books_empty_add_chapter' => 'Dodaj rozdział',
-    'books_permissions_active' => 'Uprawnienia podręcznika są aktywne',
-    'books_search_this' => 'Wyszukaj w tym podręczniku',
-    'books_navigation' => 'Nawigacja po podręczniku',
-    'books_sort' => 'Sortuj zawartość podręcznika',
-    'books_sort_named' => 'Sortuj podręcznik :bookName',
-    'books_sort_name' => 'Sort by Name',
-    'books_sort_created' => 'Sort by Created Date',
-    'books_sort_updated' => 'Sort by Updated Date',
-    'books_sort_chapters_first' => 'Chapters First',
-    'books_sort_chapters_last' => 'Chapters Last',
-    'books_sort_show_other' => 'Pokaż inne podręczniki',
+    'books_permissions_active' => 'Uprawnienia książki są aktywne',
+    'books_search_this' => 'Wyszukaj w tej książce',
+    'books_navigation' => 'Nawigacja po książce',
+    'books_sort' => 'Sortuj zawartość książki',
+    'books_sort_named' => 'Sortuj książkę :bookName',
+    'books_sort_name' => 'Sortuj według nazwy',
+    'books_sort_created' => 'Sortuj według daty utworzenia',
+    'books_sort_updated' => 'Sortuj według daty modyfikacji',
+    'books_sort_chapters_first' => 'Rozdziały na początku',
+    'books_sort_chapters_last' => 'Rozdziały na końcu',
+    'books_sort_show_other' => 'Pokaż inne książki',
     'books_sort_save' => 'Zapisz nową kolejność',
 
     // Chapters
@@ -145,8 +148,7 @@ return [
     'chapters_create' => 'Utwórz nowy rozdział',
     'chapters_delete' => 'Usuń rozdział',
     'chapters_delete_named' => 'Usuń rozdział :chapterName',
-    'chapters_delete_explain' => 'To spowoduje usunięcie rozdziału \':chapterName\', Wszystkie strony zostaną usunięte
-        i dodane bezpośrednio do podręcznika nadrzędnego.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'Czy na pewno chcesz usunąć ten rozdział?',
     'chapters_edit' => 'Edytuj rozdział',
     'chapters_edit_named' => 'Edytuj rozdział :chapterName',
@@ -177,7 +179,7 @@ return [
     'pages_delete_confirm' => 'Czy na pewno chcesz usunąć tę stronę?',
     'pages_delete_draft_confirm' => 'Czy na pewno chcesz usunąć wersje roboczą strony?',
     'pages_editing_named' => 'Edytowanie strony :pageName',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Ustawienia wersji roboczej',
     'pages_edit_save_draft' => 'Zapisano wersje roboczą o ',
     'pages_edit_draft' => 'Edytuj wersje roboczą',
     'pages_editing_draft' => 'Edytowanie wersji roboczej',
@@ -208,11 +210,12 @@ return [
     'pages_revisions' => 'Wersje strony',
     'pages_revisions_named' => 'Wersje strony :pageName',
     'pages_revision_named' => 'Wersja strony :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Utworzona przez',
     'pages_revisions_date' => 'Data wersji',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_numbered' => 'Wersja #:id',
+    'pages_revisions_numbered_changes' => 'Zmiany w wersji #:id',
     'pages_revisions_changelog' => 'Dziennik zmian',
     'pages_revisions_changes' => 'Zmiany',
     'pages_revisions_current' => 'Obecna wersja',
@@ -235,20 +238,20 @@ return [
     ],
     'pages_draft_discarded' => 'Wersja robocza odrzucona, edytor został uzupełniony najnowszą wersją strony',
     'pages_specific' => 'Określona strona',
-    'pages_is_template' => 'Page Template',
+    'pages_is_template' => 'Szablon strony',
 
     // Editor Sidebar
     'page_tags' => 'Tagi strony',
     'chapter_tags' => 'Tagi rozdziału',
-    'book_tags' => 'Tagi podręcznika',
+    'book_tags' => 'Tagi książki',
     'shelf_tags' => 'Tagi półki',
     'tag' => 'Tag',
     'tags' =>  'Tagi',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'Nazwa tagu',
     'tag_value' => 'Wartość tagu (opcjonalnie)',
     'tags_explain' => "Dodaj tagi by skategoryzować zawartość. \n W celu dokładniejszej organizacji zawartości możesz dodać wartości do tagów.",
     'tags_add' => 'Dodaj kolejny tag',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Usuń ten tag',
     'attachments' => 'Załączniki',
     'attachments_explain' => 'Prześlij kilka plików lub załącz linki. Będą one widoczne na pasku bocznym strony.',
     'attachments_explain_instant_save' => 'Zmiany są zapisywane natychmiastowo.',
@@ -256,7 +259,7 @@ return [
     'attachments_upload' => 'Dodaj plik',
     'attachments_link' => 'Dodaj link',
     'attachments_set_link' => 'Ustaw link',
-    'attachments_delete_confirm' => 'Kliknij ponownie Usuń by potwierdzić usunięcie załącznika.',
+    'attachments_delete' => 'Jesteś pewien, że chcesz usunąć ten załącznik?',
     'attachments_dropzone' => 'Upuść pliki lub kliknij tutaj by przesłać pliki',
     'attachments_no_files' => 'Nie przesłano żadnych plików',
     'attachments_explain_link' => 'Możesz załączyć link jeśli nie chcesz przesyłać pliku. Może być to link do innej strony lub link do pliku w chmurze.',
@@ -265,6 +268,7 @@ return [
     'attachments_link_url' => 'Link do pliku',
     'attachments_link_url_hint' => 'Strona lub plik',
     'attach' => 'Załącz',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Edytuj plik',
     'attachments_edit_file_name' => 'Nazwa pliku',
     'attachments_edit_drop_upload' => 'Upuść pliki lub kliknij tutaj by przesłać pliki i nadpisać istniejące',
@@ -274,20 +278,20 @@ return [
     'attachments_file_uploaded' => 'Plik załączony pomyślnie',
     'attachments_file_updated' => 'Plik zaktualizowany pomyślnie',
     'attachments_link_attached' => 'Link pomyślnie dodany do strony',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Szablony',
+    'templates_set_as_template' => 'Strona jest szablonem',
+    'templates_explain_set_as_template' => 'Możesz ustawić tę stronę jako szablon, tak aby jej zawartość była wykorzystywana przy tworzeniu innych stron. Inni użytkownicy będą mogli korzystać z tego szablonu, jeśli mają uprawnienia do przeglądania tej strony.',
+    'templates_replace_content' => 'Zmień zawartość strony',
+    'templates_append_content' => 'Dodaj do zawartośći strony na końcu',
+    'templates_prepend_content' => 'Dodaj do zawartośći strony na początku',
 
     // Profile View
     'profile_user_for_x' => 'Użytkownik od :time',
     'profile_created_content' => 'Utworzona zawartość',
     'profile_not_created_pages' => ':userName nie utworzył żadnych stron',
     'profile_not_created_chapters' => ':userName nie utworzył żadnych rozdziałów',
-    'profile_not_created_books' => ':userName nie utworzył żadnych podręczników',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_not_created_books' => ':userName nie utworzył żadnych książek',
+    'profile_not_created_shelves' => ':userName nie utworzył żadnych półek',
 
     // Comments
     'comment' => 'Komentarz',
@@ -309,7 +313,7 @@ return [
 
     // Revision
     'revision_delete_confirm' => 'Czy na pewno chcesz usunąć tę wersję?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_restore_confirm' => 'Czu ma pewno chcesz przywrócić tą wersję? Aktualna zawartość strony zostanie nadpisana.',
     'revision_delete_success' => 'Usunięto wersję',
     'revision_cannot_delete_latest' => 'Nie można usunąć najnowszej wersji.'
 ];
\ No newline at end of file
index 5a637524303add536e27be7fba5348acec6efc6a..5f5ec9db25150366d9c90c0f164814cb67a3386f 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'E-mail został potwierdzony, spróbuj się zalogować.',
     'email_confirmation_invalid' => 'Ten token jest nieprawidłowy lub został już wykorzystany. Spróbuj zarejestrować się ponownie.',
     'email_confirmation_expired' => 'Ten token potwierdzający wygasł. Wysłaliśmy Ci kolejny.',
+    'email_confirmation_awaiting' => 'Adres e-mail dla używanego konta musi zostać potwierdzony',
     'ldap_fail_anonymous' => 'Dostęp LDAP przy użyciu anonimowego powiązania nie powiódł się',
     'ldap_fail_authed' => 'Dostęp LDAP przy użyciu tego DN i hasła nie powiódł się',
     'ldap_extension_not_installed' => 'Rozszerzenie LDAP PHP nie zostało zainstalowane',
     'ldap_cannot_connect' => 'Nie można połączyć z serwerem LDAP, połączenie nie zostało ustanowione',
+    'saml_already_logged_in' => 'Już zalogowany',
+    'saml_user_not_registered' => 'Użytkownik :name nie jest zarejestrowany i automatyczna rejestracja jest wyłączona',
+    'saml_no_email_address' => 'Nie można odnaleźć adresu email dla tego użytkownika w danych dostarczonych przez zewnętrzny system uwierzytelniania',
+    'saml_invalid_response_id' => 'Żądanie z zewnętrznego systemu uwierzytelniania nie zostało rozpoznane przez proces rozpoczęty przez tę aplikację. Cofnięcie po zalogowaniu mogło spowodować ten problem.',
+    'saml_fail_authed' => 'Logowanie przy użyciu :system nie powiodło się, system nie mógł pomyślnie ukończyć uwierzytelniania',
     'social_no_action_defined' => 'Brak zdefiniowanej akcji',
     'social_login_bad_response' => "Podczas próby logowania :socialAccount wystąpił błąd: \n:error",
     'social_account_in_use' => 'To konto :socialAccount jest już w użyciu. Spróbuj zalogować się za pomocą opcji :socialAccount.',
@@ -27,7 +33,7 @@ return [
     'social_account_register_instructions' => 'Jeśli nie masz jeszcze konta, możesz zarejestrować je używając opcji :socialAccount.',
     'social_driver_not_found' => 'Funkcja społecznościowa nie została odnaleziona',
     'social_driver_not_configured' => 'Ustawienia konta :socialAccount nie są poprawne.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Zaproszenie wygasło. Możesz spróować zresetować swoje hasło.',
 
     // System
     'path_not_writable' => 'Zapis do ścieżki :filePath jest niemożliwy. Upewnij się że aplikacja ma prawa do zapisu plików na serwerze.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'Przesyłanie pliku przekroczyło limit czasu.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Niezgodność strony podczas aktualizacji załącznika',
     'attachment_not_found' => 'Nie znaleziono załącznika',
 
     // Pages
@@ -50,11 +55,11 @@ return [
     // Entities
     'entity_not_found' => 'Nie znaleziono obiektu',
     'bookshelf_not_found' => 'Nie znaleziono półki',
-    'book_not_found' => 'Nie znaleziono podręcznika',
+    'book_not_found' => 'Nie znaleziono książki',
     'page_not_found' => 'Nie znaleziono strony',
     'chapter_not_found' => 'Nie znaleziono rozdziału',
-    'selected_book_not_found' => 'Wybrany podręcznik nie został znaleziony',
-    'selected_book_chapter_not_found' => 'Wybrany podręcznik lub rozdział nie został znaleziony',
+    'selected_book_not_found' => 'Wybrana książka nie została znaleziona',
+    'selected_book_chapter_not_found' => 'Wybrana książka lub rozdział nie został znaleziony',
     'guests_cannot_save_drafts' => 'Goście nie mogą zapisywać wersji roboczych',
 
     // Users
@@ -65,7 +70,7 @@ return [
     'role_cannot_be_edited' => 'Ta rola nie może być edytowana',
     'role_system_cannot_be_deleted' => 'Ta rola jest rolą systemową i nie może zostać usunięta',
     'role_registration_default_cannot_delete' => 'Ta rola nie może zostać usunięta, dopóki jest ustawiona jako domyślna rola użytkownika',
-    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+    'role_cannot_remove_only_admin' => 'Ten użytkownik jest jedynym użytkownikiem przypisanym do roli administratora. Przypisz rolę administratora innemu użytkownikowi przed próbą usunięcia.',
 
     // Comments
     'comment_list' => 'Wystąpił błąd podczas pobierania komentarzy.',
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Strona nie została znaleziona',
     'sorry_page_not_found' => 'Przepraszamy, ale strona której szukasz nie została znaleziona.',
+    'sorry_page_not_found_permission_warning' => 'Jeśli spodziewałeś się, że ta strona istnieje, prawdopodobnie nie masz uprawnień do jej wyświetlenia.',
     'return_home' => 'Powrót do strony głównej',
     'error_occurred' => 'Wystąpił błąd',
     'app_down' => ':appName jest aktualnie wyłączona',
     'back_soon' => 'Niedługo zostanie uruchomiona ponownie.',
 
+    // API errors
+    'api_no_authorization_found' => 'Nie znaleziono tokenu autoryzacji dla żądania',
+    'api_bad_authorization_format' => 'Token autoryzacji został znaleziony w żądaniu, ale format okazał się nieprawidłowy',
+    'api_user_token_not_found' => 'Nie znaleziono pasującego tokenu API dla podanego tokenu autoryzacji',
+    'api_incorrect_token_secret' => 'Podany sekret dla tego API jest nieprawidłowy',
+    'api_user_no_api_permission' => 'Właściciel używanego tokenu API nie ma uprawnień do wykonywania zapytań do API',
+    'api_user_token_expired' => 'Token uwierzytelniania wygasł',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Błąd podczas wysyłania testowej wiadomości e-mail:',
+
 ];
index 8c9eae227c7656d8e2bd9f645a56ab0db83de87f..7b67bf38de6a9dc724b7279c733d80357604761b 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Hasło musi zawierać co najmniej 6 znaków i być zgodne z powtórzeniem.',
     'user' => "Nie znaleziono użytkownika o takim adresie e-mail.",
-    'token' => 'Ten token resetowania hasła jest nieprawidłowy.',
+    'token' => 'Token resetowania hasła jest nieprawidłowy dla tego adresu e-mail.',
     'sent' => 'Wysłaliśmy Ci link do resetowania hasła!',
     'reset' => 'Twoje hasło zostało zresetowane!',
 
index 2c17657e4f057a4c88c878d0084ddca205e680bf..abba0ce60d89fc62962f943cecfe14757618321d 100644 (file)
@@ -12,24 +12,24 @@ return [
     'settings_save_success' => 'Ustawienia zapisane',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
+    'app_customization' => 'Dostosowywanie',
+    'app_features_security' => 'Funkcje i bezpieczeństwo',
     'app_name' => 'Nazwa aplikacji',
     'app_name_desc' => 'Ta nazwa jest wyświetlana w nagłówku i e-mailach.',
     'app_name_header' => 'Pokazać nazwę aplikacji w nagłówku?',
-    'app_public_access' => 'Public Access',
-    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
-    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
-    'app_public_access_toggle' => 'Allow public access',
+    'app_public_access' => 'Dostęp publiczny',
+    'app_public_access_desc' => 'Włączenie tej opcji umożliwi niezalogowanym odwiedzającym dostęp do treści w Twojej instancji BookStack.',
+    'app_public_access_desc_guest' => 'Dostęp dla niezalogowanych odwiedzających jest dostępny poprzez użytkownika "Guest".',
+    'app_public_access_toggle' => 'Zezwalaj na dostęp publiczny',
     'app_public_viewing' => 'Zezwolić na publiczne przeglądanie?',
     'app_secure_images' => 'Włączyć przesyłanie obrazów o wyższym poziomie bezpieczeństwa?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_toggle' => 'Włącz wyższy poziom bezpieczeństwa dla obrazów',
     'app_secure_images_desc' => 'Ze względów wydajnościowych wszystkie obrazki są publiczne. Ta opcja dodaje dodatkowy, trudny do odgadnięcia losowy ciąg na początku nazwy obrazka. Upewnij się że indeksowanie katalogów jest zablokowane, aby uniemożliwić łatwy dostęp do obrazków.',
     'app_editor' => 'Edytor strony',
     'app_editor_desc' => 'Wybierz edytor używany przez użytkowników do edycji zawartości.',
     'app_custom_html' => 'Własna zawartość w tagu <head>',
     'app_custom_html_desc' => 'Zawartość dodana tutaj zostanie dołączona na dole sekcji <head> każdej strony. Przydatne przy nadpisywaniu styli lub dodawaniu analityki.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'Niestandardowa zawartość nagłówka HTML jest wyłączona na tej stronie ustawień aby zapewnić, że wszystkie błedne zmiany (braking change) mogą zostać cofnięte.',
     'app_logo' => 'Logo aplikacji',
     'app_logo_desc' => 'Ten obrazek powinien mieć nie więcej niż 43px wysokosci. <br>Większe obrazki zostaną zmniejszone.',
     'app_primary_color' => 'Podstawowy kolor aplikacji',
@@ -38,17 +38,27 @@ return [
     'app_homepage_desc' => 'Wybierz widok, który będzie wyświetlany na stronie głównej zamiast w widoku domyślnego. Uprawnienia dostępowe są ignorowane dla wybranych stron.',
     'app_homepage_select' => 'Wybierz stronę',
     'app_disable_comments' => 'Wyłącz komentarze',
-    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_toggle' => 'Wyłącz komentowanie',
     'app_disable_comments_desc' => 'Wyłącz komentarze na wszystkich stronach w aplikacji. Istniejące komentarze nie będą pokazywane.',
 
+    // Color settings
+    'content_colors' => 'Kolory zawartości',
+    'content_colors_desc' => 'Ustawia kolory dla wszystkich elementów w hierarchii organizacji stron. Wybór kolorów o podobnej jasności do domyślnych kolorów jest zalecany dla czytelności.',
+    'bookshelf_color' => 'Kolor półki',
+    'book_color' => 'Kolor książki',
+    'chapter_color' => 'Kolor rozdziału',
+    'page_color' => 'Kolor strony',
+    'page_draft_color' => 'Kolor szkicu strony',
+
     // Registration Settings
     'reg_settings' => 'Ustawienia rejestracji',
-    'reg_enable' => 'Enable Registration',
-    'reg_enable_toggle' => 'Enable registration',
-    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_enable' => 'Włącz rejestrację',
+    'reg_enable_toggle' => 'Włącz rejestrację',
+    'reg_enable_desc' => 'Kiedy rejestracja jest włączona użytkownicy mogą się rejestrować. Po rejestracji otrzymują jedną domyślną rolę użytkownika.',
     'reg_default_role' => 'Domyślna rola użytkownika po rejestracji',
-    'reg_email_confirmation' => 'Email Confirmation',
-    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_enable_external_warning' => 'Powyższa opcja jest ignorowana, gdy zewnętrzne uwierzytelnianie LDAP lub SAML jest aktywne. Konta użytkowników dla nieistniejących użytkowników zostaną automatycznie utworzone, jeśli uwierzytelnianie za pomocą systemu zewnętrznego zakończy się sukcesem.',
+    'reg_email_confirmation' => 'Potwierdzenie adresu email',
+    'reg_email_confirmation_toggle' => 'Wymagaj potwierdzenia adresu email',
     'reg_confirm_email_desc' => 'Jeśli restrykcje domenowe zostały ustawione, potwierdzenie adresu stanie się konieczne, a poniższa wartośc zostanie zignorowana.',
     'reg_confirm_restrict_domain' => 'Restrykcje domenowe dot. adresu e-mail',
     'reg_confirm_restrict_domain_desc' => 'Wprowadź listę domen adresów e-mail, rozdzieloną przecinkami, którym chciałbyś zezwolić na rejestrację. Wymusi to konieczność potwierdzenia adresu e-mail przez użytkownika przed uzyskaniem dostępu do aplikacji. <br> Pamiętaj, że użytkownicy będą mogli zmienić adres e-mail po rejestracji.',
@@ -58,11 +68,53 @@ return [
     'maint' => 'Konserwacja',
     'maint_image_cleanup' => 'Czyszczenie obrazków',
     'maint_image_cleanup_desc' => "Skanuje zawartość strony i poprzednie wersje, aby sprawdzić, które obrazy i rysunki są aktualnie używane, a które obrazy są zbędne. Przed uruchomieniem tej opcji należy utworzyć pełną kopię zapasową bazy danych i obrazków.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignoruje obrazki w poprzednich wersjach',
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Uruchom czyszczenie',
     'maint_image_cleanup_warning' => 'Znaleziono :count potencjalnie niepotrzebnych obrazków. Czy na pewno chcesz je usunąć?',
     'maint_image_cleanup_success' => ':count potencjalnie nieużywane obrazki zostały znalezione i usunięte!',
     'maint_image_cleanup_nothing_found' => 'Nie znaleziono żadnych nieużywanych obrazków. Nic nie zostało usunięte!',
+    'maint_send_test_email' => 'Wyślij testową wiadomość e-mail',
+    'maint_send_test_email_desc' => 'Ta opcje wyśle wiadomość testową na adres e-mail podany w Twoim profilu',
+    'maint_send_test_email_run' => 'Wyślij testową wiadomość e-mail',
+    'maint_send_test_email_success' => 'E-mail wysłany na adres :address',
+    'maint_send_test_email_mail_subject' => 'E-mail testowy',
+    'maint_send_test_email_mail_greeting' => 'Wygląda na to, że wysyłka wiadomości e-mail działa!',
+    'maint_send_test_email_mail_text' => 'Gratulacje! Otrzymałeś tego e-maila więc Twoje ustawienia poczty elektronicznej wydają się być prawidłowo skonfigurowane.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
 
     // Role Settings
     'roles' => 'Role',
@@ -83,12 +135,14 @@ return [
     'role_system' => 'Uprawnienia systemowe',
     'role_manage_users' => 'Zarządzanie użytkownikami',
     'role_manage_roles' => 'Zarządzanie rolami i uprawnieniami ról',
-    'role_manage_entity_permissions' => 'Zarządzanie uprawnieniami podręczników, rozdziałów i stron',
-    'role_manage_own_entity_permissions' => 'Zarządzanie uprawnieniami własnych podręczników, rozdziałów i stron',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_entity_permissions' => 'Zarządzanie uprawnieniami książek, rozdziałów i stron',
+    'role_manage_own_entity_permissions' => 'Zarządzanie uprawnieniami własnych książek, rozdziałów i stron',
+    'role_manage_page_templates' => 'Zarządzaj szablonami stron',
+    'role_access_api' => 'Dostęp do systemowego API',
     'role_manage_settings' => 'Zarządzanie ustawieniami aplikacji',
     'role_asset' => 'Zarządzanie zasobami',
-    'role_asset_desc' => 'Te ustawienia kontrolują zarządzanie zasobami systemu. Uprawnienia podręczników, rozdziałów i stron nadpisują te ustawienia.',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => 'Te ustawienia kontrolują zarządzanie zasobami systemu. Uprawnienia książek, rozdziałów i stron nadpisują te ustawienia.',
     'role_asset_admins' => 'Administratorzy mają automatycznie dostęp do wszystkich treści, ale te opcję mogą być pokazywać lub ukrywać opcje interfejsu użytkownika.',
     'role_all' => 'Wszyscy',
     'role_own' => 'Własne',
@@ -103,37 +157,67 @@ return [
     'user_profile' => 'Profil użytkownika',
     'users_add_new' => 'Dodaj użytkownika',
     'users_search' => 'Wyszukaj użytkownika',
-    'users_details' => 'User Details',
-    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
-    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_latest_activity' => 'Latest Activity',
+    'users_details' => 'Szczegóły użytkownika',
+    'users_details_desc' => 'Ustaw wyświetlaną nazwę i adres e-mail dla tego użytkownika. Adres e-mail zostanie wykorzystany do zalogowania się do aplikacji.',
+    'users_details_desc_no_email' => 'Ustaw wyświetlaną nazwę dla tego użytkownika, aby inni mogli go rozpoznać.',
     'users_role' => 'Role użytkownika',
-    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
-    'users_password' => 'User Password',
-    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
+    'users_role_desc' => 'Wybierz role, do których ten użytkownik zostanie przypisany. Jeśli użytkownik jest przypisany do wielu ról, uprawnienia z tych ról zostaną nałożone i otrzyma wszystkie uprawnienia przypisanych ról.',
+    'users_password' => 'Hasło użytkownika',
+    'users_password_desc' => 'Ustaw hasło logowania do aplikacji. Hasło musi mieć przynajmniej 6 znaków.',
+    'users_send_invite_text' => 'Możesz wybrać wysłanie do tego użytkownika wiadomości e-mail z zaproszeniem, która pozwala mu ustawić własne hasło, w przeciwnym razie możesz ustawić je samemu.',
+    'users_send_invite_option' => 'Wyślij e-mail z zaproszeniem',
     'users_external_auth_id' => 'Zewnętrzne identyfikatory autentykacji',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+    'users_external_auth_id_desc' => 'Jest to identyfikator używany do dopasowania tego użytkownika podczas komunikacji z zewnętrznym systemem uwierzytelniania.',
     'users_password_warning' => 'Wypełnij poniżej tylko jeśli chcesz zmienić swoje hasło:',
     'users_system_public' => 'Ten użytkownik reprezentuje każdego gościa odwiedzającego tę aplikację. Nie można się na niego zalogować, lecz jest przyznawany automatycznie.',
     'users_delete' => 'Usuń użytkownika',
     'users_delete_named' => 'Usuń :userName',
     'users_delete_warning' => 'To usunie użytkownika \':userName\' z systemu.',
     'users_delete_confirm' => 'Czy na pewno chcesz usunąć tego użytkownika?',
-    'users_delete_success' => 'Użytkownik usunięty pomyślnie',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Edytuj użytkownika',
     'users_edit_profile' => 'Edytuj profil',
-    'users_edit_success' => 'Użytkownik zaktualizowany pomyśłnie',
+    'users_edit_success' => 'Użytkownik zaktualizowany pomyślnie',
     'users_avatar' => 'Avatar użytkownika',
     'users_avatar_desc' => 'Ten obrazek powinien posiadać wymiary 256x256px.',
     'users_preferred_language' => 'Preferowany język',
-    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_preferred_language_desc' => 'Opcja ta zmieni język używany w interfejsie użytkownika aplikacji. Nie wpłynie to na zawartość stworzoną przez użytkownika.',
     'users_social_accounts' => 'Konta społecznościowe',
     'users_social_accounts_info' => 'Tutaj możesz połączyć kilka kont społecznościowych w celu łatwiejszego i szybszego logowania. Odłączenie konta tutaj nie autoryzowało dostępu. Odwołaj dostęp z ustawień profilu na podłączonym koncie społecznościowym.',
     'users_social_connect' => 'Podłącz konto',
     'users_social_disconnect' => 'Odłącz konto',
     'users_social_connected' => ':socialAccount zostało dodane do Twojego profilu.',
     'users_social_disconnected' => ':socialAccount zostało odłączone od Twojego profilu.',
+    'users_api_tokens' => 'Tokeny API',
+    'users_api_tokens_none' => 'Nie utworzono tokenów API dla tego użytkownika',
+    'users_api_tokens_create' => 'Utwórz token',
+    'users_api_tokens_expires' => 'Wygasa',
+    'users_api_tokens_docs' => 'Dokumentacja API',
+
+    // API Tokens
+    'user_api_token_create' => 'Utwórz klucz API',
+    'user_api_token_name' => 'Nazwa',
+    'user_api_token_name_desc' => 'Nadaj swojemu tokenowi czytelną nazwę jako opisującego jego cel.',
+    'user_api_token_expiry' => 'Data ważności',
+    'user_api_token_expiry_desc' => 'Ustaw datę, kiedy ten token wygasa. Po tej dacie żądania wykonane przy użyciu tego tokenu nie będą już działać. Pozostawienie tego pola pustego, ustawi ważność na 100 lat.',
+    'user_api_token_create_secret_message' => 'Natychmiast po utworzeniu tego tokenu zostanie wygenerowany i wyświetlony "Identyfikator tokenu"" i "Token Secret". Sekret zostanie wyświetlony tylko raz, więc przed kontynuacją upewnij się, że zostanie on skopiowany w bezpiecznie miejsce.',
+    'user_api_token_create_success' => 'Klucz API został poprawnie wygenerowany',
+    'user_api_token_update_success' => 'Klucz API został poprawnie zaktualizowany',
+    'user_api_token' => 'Token API',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'Jest to nieedytowalny identyfikator wygenerowany przez system dla tego tokenu, który musi być dostarczony w żądaniach API.',
+    'user_api_token_secret' => 'Token Api',
+    'user_api_token_secret_desc' => 'To jest wygenerowany przez system sekretny token, który musi być dostarczony w żądaniach API. Token zostanie wyświetlany tylko raz, więc skopiuj go w bezpiecznie miejsce.',
+    'user_api_token_created' => 'Token utworzony :timeAgo',
+    'user_api_token_updated' => 'Token zaktualizowany :timeAgo',
+    'user_api_token_delete' => 'Usuń token',
+    'user_api_token_delete_warning' => 'Spowoduje to całkowite usunięcie tokenu API o nazwie \':tokenName\' z systemu.',
+    'user_api_token_delete_confirm' => 'Czy jesteś pewien, że chcesz usunąć ten token?',
+    'user_api_token_delete_success' => 'Token API został poprawnie usunięty',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index d5e5ab343d5b399778093de9957b70b51e95db1a..26ac348c1aa955d97af065e3bd538f7eb229df10 100644 (file)
@@ -30,40 +30,40 @@ return [
     'digits'               => ':attribute musi mieć :digits cyfr.',
     'digits_between'       => ':attribute musi mieć od :min do :max cyfr.',
     'email'                => ':attribute musi być prawidłowym adresem e-mail.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ':attribute musi kończyć się jedną z poniższych wartości: :values',
     'filled'               => ':attribute jest wymagany.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute musi być większy niż :value.',
+        'file'    => ':attribute musi mieć rozmiar większy niż :value kilobajtów.',
+        'string'  => ':attribute musi mieć więcej niż :value znaków.',
+        'array'   => ':attribute musi mieć więcej niż :value elementów.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => ':attribute musi być większy lub równy :value.',
+        'file'    => ':attribute musi mieć rozmiar większy niż lub równy :value kilobajtów.',
+        'string'  => ':attribute musi mieć :value lub więcej znaków.',
+        'array'   => ':attribute musi mieć :value lub więcej elementów.',
     ],
     'exists'               => 'Wybrana wartość :attribute jest nieprawidłowa.',
     'image'                => ':attribute musi być obrazkiem.',
-    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'image_extension'      => ':attribute musi mieć prawidłowe i wspierane rozszerzenie',
     'in'                   => 'Wybrana wartość :attribute jest nieprawidłowa.',
     'integer'              => ':attribute musi być liczbą całkowitą.',
     'ip'                   => ':attribute musi być prawidłowym adresem IP.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ipv4'                 => ':attribute musi być prawidłowym adresem IPv4.',
+    'ipv6'                 => ':attribute musi być prawidłowym adresem  IPv6.',
+    'json'                 => ':attribute musi być prawidłowym ciągiem JSON.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute musi być mniejszy niż :value.',
+        'file'    => ':attribute musi mieć rozmiar mniejszy niż :value kilobajtów.',
+        'string'  => ':attribute musi mieć mniej niż :value znaków.',
+        'array'   => ':attribute musi mieć mniej niż :value elementów.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute musi być mniejszy lub równy :value.',
+        'file'    => ':attribute musi mieć rozmiar mniejszy lub równy:value kilobajtów.',
+        'string'  => ':attribute nie może mieć więcej niż :value znaków.',
+        'array'   => ':attribute nie może mieć więcej niż  :value elementów.',
     ],
     'max'                  => [
         'numeric' => 'Wartość :attribute nie może być większa niż :max.',
@@ -78,9 +78,9 @@ return [
         'string'  => 'Długość :attribute nie może być mniejsza niż :min znaków.',
         'array'   => 'Rozmiar :attribute musi posiadać co najmniej :min elementy.',
     ],
-    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'no_double_extension'  => ':attribute może mieć tylko jedno rozszerzenie.',
     'not_in'               => 'Wartość :attribute jest nieprawidłowa.',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => 'Format :attribute jest nieprawidłowy.',
     'numeric'              => ':attribute musi być liczbą.',
     'regex'                => 'Format :attribute jest nieprawidłowy.',
     'required'             => 'Pole :attribute jest wymagane.',
@@ -90,6 +90,7 @@ return [
     'required_without'     => 'Pole :attribute jest wymagane jeśli :values nie zostało wprowadzone.',
     'required_without_all' => 'Pole :attribute jest wymagane jeśli żadna z wartości :values nie została podana.',
     'same'                 => 'Pole :attribute i :other muszą być takie same.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
         'numeric' => ':attribute musi mieć długość :size.',
         'file'    => ':attribute musi mieć :size kilobajtów.',
@@ -100,7 +101,7 @@ return [
     'timezone'             => ':attribute musi być prawidłową strefą czasową.',
     'unique'               => ':attribute zostało już zajęte.',
     'url'                  => 'Format :attribute jest nieprawidłowy.',
-    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+    'uploaded'             => 'Plik nie może zostać wysłany. Serwer nie akceptuje plików o takim rozmiarze.',
 
     // Custom validation lines
     'custom' => [
diff --git a/resources/lang/pt/activities.php b/resources/lang/pt/activities.php
new file mode 100644 (file)
index 0000000..4cac54b
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'created page',
+    'page_create_notification'    => 'Page Successfully Created',
+    'page_update'                 => 'updated page',
+    'page_update_notification'    => 'Page Successfully Updated',
+    'page_delete'                 => 'deleted page',
+    'page_delete_notification'    => 'Page Successfully Deleted',
+    'page_restore'                => 'restored page',
+    'page_restore_notification'   => 'Page Successfully Restored',
+    'page_move'                   => 'moved page',
+
+    // Chapters
+    'chapter_create'              => 'created chapter',
+    'chapter_create_notification' => 'Chapter Successfully Created',
+    'chapter_update'              => 'updated chapter',
+    'chapter_update_notification' => 'Chapter Successfully Updated',
+    'chapter_delete'              => 'deleted chapter',
+    'chapter_delete_notification' => 'Chapter Successfully Deleted',
+    'chapter_move'                => 'moved chapter',
+
+    // Books
+    'book_create'                 => 'created book',
+    'book_create_notification'    => 'Book Successfully Created',
+    'book_update'                 => 'updated book',
+    'book_update_notification'    => 'Book Successfully Updated',
+    'book_delete'                 => 'deleted book',
+    'book_delete_notification'    => 'Book Successfully Deleted',
+    'book_sort'                   => 'sorted book',
+    'book_sort_notification'      => 'Book Successfully Re-sorted',
+
+    // Bookshelves
+    'bookshelf_create'            => 'created Bookshelf',
+    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
+    'bookshelf_update'                 => 'updated bookshelf',
+    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
+    'bookshelf_delete'                 => 'deleted bookshelf',
+    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+
+    // Other
+    'commented_on'                => 'commented on',
+];
diff --git a/resources/lang/pt/auth.php b/resources/lang/pt/auth.php
new file mode 100644 (file)
index 0000000..d64fce9
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'These credentials do not match our records.',
+    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
+
+    // Login & Register
+    'sign_up' => 'Sign up',
+    'log_in' => 'Log in',
+    'log_in_with' => 'Login with :socialDriver',
+    'sign_up_with' => 'Sign up with :socialDriver',
+    'logout' => 'Logout',
+
+    'name' => 'Name',
+    'username' => 'Username',
+    'email' => 'Email',
+    'password' => 'Password',
+    'password_confirm' => 'Confirm Password',
+    'password_hint' => 'Must be over 7 characters',
+    'forgot_password' => 'Forgot Password?',
+    'remember_me' => 'Remember Me',
+    'ldap_email_hint' => 'Please enter an email to use for this account.',
+    'create_account' => 'Create Account',
+    'already_have_account' => 'Already have an account?',
+    'dont_have_account' => 'Don\'t have an account?',
+    'social_login' => 'Social Login',
+    'social_registration' => 'Social Registration',
+    'social_registration_text' => 'Register and sign in using another service.',
+
+    'register_thanks' => 'Thanks for registering!',
+    'register_confirm' => 'Please check your email and click the confirmation button to access :appName.',
+    'registrations_disabled' => 'Registrations are currently disabled',
+    'registration_email_domain_invalid' => 'That email domain does not have access to this application',
+    'register_success' => 'Thanks for signing up! You are now registered and signed in.',
+
+
+    // Password Reset
+    'reset_password' => 'Reset Password',
+    'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.',
+    'reset_password_send_button' => 'Send Reset Link',
+    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_success' => 'Your password has been successfully reset.',
+    'email_reset_subject' => 'Reset your :appName password',
+    'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.',
+    'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Confirm your email on :appName',
+    'email_confirm_greeting' => 'Thanks for joining :appName!',
+    'email_confirm_text' => 'Please confirm your email address by clicking the button below:',
+    'email_confirm_action' => 'Confirm Email',
+    'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.',
+    'email_confirm_success' => 'Your email has been confirmed!',
+    'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.',
+
+    'email_not_confirmed' => 'Email Address Not Confirmed',
+    'email_not_confirmed_text' => 'Your email address has not yet been confirmed.',
+    'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.',
+    'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.',
+    'email_not_confirmed_resend_button' => 'Resend Confirmation Email',
+
+    // User Invite
+    'user_invite_email_subject' => 'You have been invited to join :appName!',
+    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
+    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
+    'user_invite_email_action' => 'Set Account Password',
+    'user_invite_page_welcome' => 'Welcome to :appName!',
+    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
+    'user_invite_page_confirm_button' => 'Confirm Password',
+    'user_invite_success' => 'Password set, you now have access to :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/pt/common.php b/resources/lang/pt/common.php
new file mode 100644 (file)
index 0000000..e87bd11
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Cancel',
+    'confirm' => 'Confirm',
+    'back' => 'Back',
+    'save' => 'Save',
+    'continue' => 'Continue',
+    'select' => 'Select',
+    'toggle_all' => 'Toggle All',
+    'more' => 'More',
+
+    // Form Labels
+    'name' => 'Name',
+    'description' => 'Description',
+    'role' => 'Role',
+    'cover_image' => 'Cover image',
+    'cover_image_description' => 'This image should be approx 440x250px.',
+    
+    // Actions
+    'actions' => 'Actions',
+    'view' => 'View',
+    'view_all' => 'View All',
+    'create' => 'Create',
+    'update' => 'Update',
+    'edit' => 'Edit',
+    'sort' => 'Sort',
+    'move' => 'Move',
+    'copy' => 'Copy',
+    'reply' => 'Reply',
+    'delete' => 'Delete',
+    'delete_confirm' => 'Confirm Deletion',
+    'search' => 'Search',
+    'search_clear' => 'Clear Search',
+    'reset' => 'Reset',
+    'remove' => 'Remove',
+    'add' => 'Add',
+    'fullscreen' => 'Fullscreen',
+
+    // Sort Options
+    'sort_options' => 'Sort Options',
+    'sort_direction_toggle' => 'Sort Direction Toggle',
+    'sort_ascending' => 'Sort Ascending',
+    'sort_descending' => 'Sort Descending',
+    'sort_name' => 'Name',
+    'sort_created_at' => 'Created Date',
+    'sort_updated_at' => 'Updated Date',
+
+    // Misc
+    'deleted_user' => 'Deleted User',
+    'no_activity' => 'No activity to show',
+    'no_items' => 'No items available',
+    'back_to_top' => 'Back to top',
+    'toggle_details' => 'Toggle Details',
+    'toggle_thumbnails' => 'Toggle Thumbnails',
+    'details' => 'Details',
+    'grid_view' => 'Grid View',
+    'list_view' => 'List View',
+    'default' => 'Default',
+    'breadcrumb' => 'Breadcrumb',
+
+    // Header
+    'profile_menu' => 'Profile Menu',
+    'view_profile' => 'View Profile',
+    'edit_profile' => 'Edit Profile',
+    'dark_mode' => 'Dark Mode',
+    'light_mode' => 'Light Mode',
+
+    // Layout tabs
+    'tab_info' => 'Info',
+    'tab_content' => 'Content',
+
+    // Email Content
+    'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
+    'email_rights' => 'All rights reserved',
+];
diff --git a/resources/lang/pt/components.php b/resources/lang/pt/components.php
new file mode 100644 (file)
index 0000000..48a0a32
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Image Select',
+    'image_all' => 'All',
+    'image_all_title' => 'View all images',
+    'image_book_title' => 'View images uploaded to this book',
+    'image_page_title' => 'View images uploaded to this page',
+    'image_search_hint' => 'Search by image name',
+    'image_uploaded' => 'Uploaded :uploadedDate',
+    'image_load_more' => 'Load More',
+    'image_image_name' => 'Image Name',
+    'image_delete_used' => 'This image is used in the pages below.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_select_image' => 'Select Image',
+    'image_dropzone' => 'Drop images or click here to upload',
+    'images_deleted' => 'Images Deleted',
+    'image_preview' => 'Image Preview',
+    'image_upload_success' => 'Image uploaded successfully',
+    'image_update_success' => 'Image details successfully updated',
+    'image_delete_success' => 'Image successfully deleted',
+    'image_upload_remove' => 'Remove',
+
+    // Code Editor
+    'code_editor' => 'Edit Code',
+    'code_language' => 'Code Language',
+    'code_content' => 'Code Content',
+    'code_session_history' => 'Session History',
+    'code_save' => 'Save Code',
+];
diff --git a/resources/lang/pt/entities.php b/resources/lang/pt/entities.php
new file mode 100644 (file)
index 0000000..f64867a
--- /dev/null
@@ -0,0 +1,316 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Recently Created',
+    'recently_created_pages' => 'Recently Created Pages',
+    'recently_updated_pages' => 'Recently Updated Pages',
+    'recently_created_chapters' => 'Recently Created Chapters',
+    'recently_created_books' => 'Recently Created Books',
+    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_update' => 'Recently Updated',
+    'recently_viewed' => 'Recently Viewed',
+    'recent_activity' => 'Recent Activity',
+    'create_now' => 'Create one now',
+    'revisions' => 'Revisions',
+    'meta_revision' => 'Revision #:revisionCount',
+    'meta_created' => 'Created :timeLength',
+    'meta_created_name' => 'Created :timeLength by :user',
+    'meta_updated' => 'Updated :timeLength',
+    'meta_updated_name' => 'Updated :timeLength by :user',
+    'entity_select' => 'Entity Select',
+    'images' => 'Images',
+    'my_recent_drafts' => 'My Recent Drafts',
+    'my_recently_viewed' => 'My Recently Viewed',
+    'no_pages_viewed' => 'You have not viewed any pages',
+    'no_pages_recently_created' => 'No pages have been recently created',
+    'no_pages_recently_updated' => 'No pages have been recently updated',
+    'export' => 'Export',
+    'export_html' => 'Contained Web File',
+    'export_pdf' => 'PDF File',
+    'export_text' => 'Plain Text File',
+
+    // Permissions and restrictions
+    'permissions' => 'Permissions',
+    'permissions_intro' => 'Once enabled, These permissions will take priority over any set role permissions.',
+    'permissions_enable' => 'Enable Custom Permissions',
+    'permissions_save' => 'Save Permissions',
+
+    // Search
+    'search_results' => 'Search Results',
+    'search_total_results_found' => ':count result found|:count total results found',
+    'search_clear' => 'Clear Search',
+    'search_no_pages' => 'No pages matched this search',
+    'search_for_term' => 'Search for :term',
+    'search_more' => 'More Results',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
+    'search_content_type' => 'Content Type',
+    'search_exact_matches' => 'Exact Matches',
+    'search_tags' => 'Tag Searches',
+    'search_options' => 'Options',
+    'search_viewed_by_me' => 'Viewed by me',
+    'search_not_viewed_by_me' => 'Not viewed by me',
+    'search_permissions_set' => 'Permissions set',
+    'search_created_by_me' => 'Created by me',
+    'search_updated_by_me' => 'Updated by me',
+    'search_date_options' => 'Date Options',
+    'search_updated_before' => 'Updated before',
+    'search_updated_after' => 'Updated after',
+    'search_created_before' => 'Created before',
+    'search_created_after' => 'Created after',
+    'search_set_date' => 'Set Date',
+    'search_update' => 'Update Search',
+
+    // Shelves
+    'shelf' => 'Shelf',
+    'shelves' => 'Shelves',
+    'x_shelves' => ':count Shelf|:count Shelves',
+    'shelves_long' => 'Bookshelves',
+    'shelves_empty' => 'No shelves have been created',
+    'shelves_create' => 'Create New Shelf',
+    'shelves_popular' => 'Popular Shelves',
+    'shelves_new' => 'New Shelves',
+    'shelves_new_action' => 'New Shelf',
+    'shelves_popular_empty' => 'The most popular shelves will appear here.',
+    'shelves_new_empty' => 'The most recently created shelves will appear here.',
+    'shelves_save' => 'Save Shelf',
+    'shelves_books' => 'Books on this shelf',
+    'shelves_add_books' => 'Add books to this shelf',
+    'shelves_drag_books' => 'Drag books here to add them to this shelf',
+    'shelves_empty_contents' => 'This shelf has no books assigned to it',
+    'shelves_edit_and_assign' => 'Edit shelf to assign books',
+    'shelves_edit_named' => 'Edit Bookshelf :name',
+    'shelves_edit' => 'Edit Bookshelf',
+    'shelves_delete' => 'Delete Bookshelf',
+    'shelves_delete_named' => 'Delete Bookshelf :name',
+    'shelves_delete_explain' => "This will delete the bookshelf with the name ':name'. Contained books will not be deleted.",
+    'shelves_delete_confirmation' => 'Are you sure you want to delete this bookshelf?',
+    'shelves_permissions' => 'Bookshelf Permissions',
+    'shelves_permissions_updated' => 'Bookshelf Permissions Updated',
+    'shelves_permissions_active' => 'Bookshelf Permissions Active',
+    'shelves_copy_permissions_to_books' => 'Copy Permissions to Books',
+    'shelves_copy_permissions' => 'Copy Permissions',
+    'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this bookshelf to all books contained within. Before activating, ensure any changes to the permissions of this bookshelf have been saved.',
+    'shelves_copy_permission_success' => 'Bookshelf permissions copied to :count books',
+
+    // Books
+    'book' => 'Book',
+    'books' => 'Books',
+    'x_books' => ':count Book|:count Books',
+    'books_empty' => 'No books have been created',
+    'books_popular' => 'Popular Books',
+    'books_recent' => 'Recent Books',
+    'books_new' => 'New Books',
+    'books_new_action' => 'New Book',
+    'books_popular_empty' => 'The most popular books will appear here.',
+    'books_new_empty' => 'The most recently created books will appear here.',
+    'books_create' => 'Create New Book',
+    'books_delete' => 'Delete Book',
+    'books_delete_named' => 'Delete Book :bookName',
+    'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.',
+    'books_delete_confirmation' => 'Are you sure you want to delete this book?',
+    'books_edit' => 'Edit Book',
+    'books_edit_named' => 'Edit Book :bookName',
+    'books_form_book_name' => 'Book Name',
+    'books_save' => 'Save Book',
+    'books_permissions' => 'Book Permissions',
+    'books_permissions_updated' => 'Book Permissions Updated',
+    'books_empty_contents' => 'No pages or chapters have been created for this book.',
+    'books_empty_create_page' => 'Create a new page',
+    'books_empty_sort_current_book' => 'Sort the current book',
+    'books_empty_add_chapter' => 'Add a chapter',
+    'books_permissions_active' => 'Book Permissions Active',
+    'books_search_this' => 'Search this book',
+    'books_navigation' => 'Book Navigation',
+    'books_sort' => 'Sort Book Contents',
+    'books_sort_named' => 'Sort Book :bookName',
+    'books_sort_name' => 'Sort by Name',
+    'books_sort_created' => 'Sort by Created Date',
+    'books_sort_updated' => 'Sort by Updated Date',
+    'books_sort_chapters_first' => 'Chapters First',
+    'books_sort_chapters_last' => 'Chapters Last',
+    'books_sort_show_other' => 'Show Other Books',
+    'books_sort_save' => 'Save New Order',
+
+    // Chapters
+    'chapter' => 'Chapter',
+    'chapters' => 'Chapters',
+    'x_chapters' => ':count Chapter|:count Chapters',
+    'chapters_popular' => 'Popular Chapters',
+    'chapters_new' => 'New Chapter',
+    'chapters_create' => 'Create New Chapter',
+    'chapters_delete' => 'Delete Chapter',
+    'chapters_delete_named' => 'Delete Chapter :chapterName',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages will be removed and added directly to the parent book.',
+    'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?',
+    'chapters_edit' => 'Edit Chapter',
+    'chapters_edit_named' => 'Edit Chapter :chapterName',
+    'chapters_save' => 'Save Chapter',
+    'chapters_move' => 'Move Chapter',
+    'chapters_move_named' => 'Move Chapter :chapterName',
+    'chapter_move_success' => 'Chapter moved to :bookName',
+    'chapters_permissions' => 'Chapter Permissions',
+    'chapters_empty' => 'No pages are currently in this chapter.',
+    'chapters_permissions_active' => 'Chapter Permissions Active',
+    'chapters_permissions_success' => 'Chapter Permissions Updated',
+    'chapters_search_this' => 'Search this chapter',
+
+    // Pages
+    'page' => 'Page',
+    'pages' => 'Pages',
+    'x_pages' => ':count Page|:count Pages',
+    'pages_popular' => 'Popular Pages',
+    'pages_new' => 'New Page',
+    'pages_attachments' => 'Attachments',
+    'pages_navigation' => 'Page Navigation',
+    'pages_delete' => 'Delete Page',
+    'pages_delete_named' => 'Delete Page :pageName',
+    'pages_delete_draft_named' => 'Delete Draft Page :pageName',
+    'pages_delete_draft' => 'Delete Draft Page',
+    'pages_delete_success' => 'Page deleted',
+    'pages_delete_draft_success' => 'Draft page deleted',
+    'pages_delete_confirm' => 'Are you sure you want to delete this page?',
+    'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?',
+    'pages_editing_named' => 'Editing Page :pageName',
+    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_save_draft' => 'Save Draft',
+    'pages_edit_draft' => 'Edit Page Draft',
+    'pages_editing_draft' => 'Editing Draft',
+    'pages_editing_page' => 'Editing Page',
+    'pages_edit_draft_save_at' => 'Draft saved at ',
+    'pages_edit_delete_draft' => 'Delete Draft',
+    'pages_edit_discard_draft' => 'Discard Draft',
+    'pages_edit_set_changelog' => 'Set Changelog',
+    'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made',
+    'pages_edit_enter_changelog' => 'Enter Changelog',
+    'pages_save' => 'Save Page',
+    'pages_title' => 'Page Title',
+    'pages_name' => 'Page Name',
+    'pages_md_editor' => 'Editor',
+    'pages_md_preview' => 'Preview',
+    'pages_md_insert_image' => 'Insert Image',
+    'pages_md_insert_link' => 'Insert Entity Link',
+    'pages_md_insert_drawing' => 'Insert Drawing',
+    'pages_not_in_chapter' => 'Page is not in a chapter',
+    'pages_move' => 'Move Page',
+    'pages_move_success' => 'Page moved to ":parentName"',
+    'pages_copy' => 'Copy Page',
+    'pages_copy_desination' => 'Copy Destination',
+    'pages_copy_success' => 'Page successfully copied',
+    'pages_permissions' => 'Page Permissions',
+    'pages_permissions_success' => 'Page permissions updated',
+    'pages_revision' => 'Revision',
+    'pages_revisions' => 'Page Revisions',
+    'pages_revisions_named' => 'Page Revisions for :pageName',
+    'pages_revision_named' => 'Page Revision for :pageName',
+    'pages_revisions_created_by' => 'Created By',
+    'pages_revisions_date' => 'Revision Date',
+    'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'Revision #:id',
+    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_changelog' => 'Changelog',
+    'pages_revisions_changes' => 'Changes',
+    'pages_revisions_current' => 'Current Version',
+    'pages_revisions_preview' => 'Preview',
+    'pages_revisions_restore' => 'Restore',
+    'pages_revisions_none' => 'This page has no revisions',
+    'pages_copy_link' => 'Copy Link',
+    'pages_edit_content_link' => 'Edit Content',
+    'pages_permissions_active' => 'Page Permissions Active',
+    'pages_initial_revision' => 'Initial publish',
+    'pages_initial_name' => 'New Page',
+    'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.',
+    'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count users have started editing this page',
+        'start_b' => ':userName has started editing this page',
+        'time_a' => 'since the page was last updated',
+        'time_b' => 'in the last :minCount minutes',
+        'message' => ':start :time. Take care not to overwrite each other\'s updates!',
+    ],
+    'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content',
+    'pages_specific' => 'Specific Page',
+    'pages_is_template' => 'Page Template',
+
+    // Editor Sidebar
+    'page_tags' => 'Page Tags',
+    'chapter_tags' => 'Chapter Tags',
+    'book_tags' => 'Book Tags',
+    'shelf_tags' => 'Shelf Tags',
+    'tag' => 'Tag',
+    'tags' =>  'Tags',
+    'tag_name' =>  'Tag Name',
+    'tag_value' => 'Tag Value (Optional)',
+    'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.",
+    'tags_add' => 'Add another tag',
+    'tags_remove' => 'Remove this tag',
+    'attachments' => 'Attachments',
+    'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.',
+    'attachments_explain_instant_save' => 'Changes here are saved instantly.',
+    'attachments_items' => 'Attached Items',
+    'attachments_upload' => 'Upload File',
+    'attachments_link' => 'Attach Link',
+    'attachments_set_link' => 'Set Link',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
+    'attachments_dropzone' => 'Drop files or click here to attach a file',
+    'attachments_no_files' => 'No files have been uploaded',
+    'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
+    'attachments_link_name' => 'Link Name',
+    'attachment_link' => 'Attachment link',
+    'attachments_link_url' => 'Link to file',
+    'attachments_link_url_hint' => 'Url of site or file',
+    'attach' => 'Attach',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
+    'attachments_edit_file' => 'Edit File',
+    'attachments_edit_file_name' => 'File Name',
+    'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
+    'attachments_order_updated' => 'Attachment order updated',
+    'attachments_updated_success' => 'Attachment details updated',
+    'attachments_deleted' => 'Attachment deleted',
+    'attachments_file_uploaded' => 'File successfully uploaded',
+    'attachments_file_updated' => 'File successfully updated',
+    'attachments_link_attached' => 'Link successfully attached to page',
+    'templates' => 'Templates',
+    'templates_set_as_template' => 'Page is a template',
+    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
+    'templates_replace_content' => 'Replace page content',
+    'templates_append_content' => 'Append to page content',
+    'templates_prepend_content' => 'Prepend to page content',
+
+    // Profile View
+    'profile_user_for_x' => 'User for :time',
+    'profile_created_content' => 'Created Content',
+    'profile_not_created_pages' => ':userName has not created any pages',
+    'profile_not_created_chapters' => ':userName has not created any chapters',
+    'profile_not_created_books' => ':userName has not created any books',
+    'profile_not_created_shelves' => ':userName has not created any shelves',
+
+    // Comments
+    'comment' => 'Comment',
+    'comments' => 'Comments',
+    'comment_add' => 'Add Comment',
+    'comment_placeholder' => 'Leave a comment here',
+    'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments',
+    'comment_save' => 'Save Comment',
+    'comment_saving' => 'Saving comment...',
+    'comment_deleting' => 'Deleting comment...',
+    'comment_new' => 'New Comment',
+    'comment_created' => 'commented :createDiff',
+    'comment_updated' => 'Updated :updateDiff by :username',
+    'comment_deleted_success' => 'Comment deleted',
+    'comment_created_success' => 'Comment added',
+    'comment_updated_success' => 'Comment updated',
+    'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
+    'comment_in_reply_to' => 'In reply to :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Are you sure you want to delete this revision?',
+    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_delete_success' => 'Revision deleted',
+    'revision_cannot_delete_latest' => 'Cannot delete the latest revision.'
+];
\ No newline at end of file
diff --git a/resources/lang/pt/errors.php b/resources/lang/pt/errors.php
new file mode 100644 (file)
index 0000000..79024e4
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'You do not have permission to access the requested page.',
+    'permissionJson' => 'You do not have permission to perform the requested action.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.',
+    'email_already_confirmed' => 'Email has already been confirmed, Try logging in.',
+    'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.',
+    'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.',
+    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+    'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
+    'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
+    'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
+    'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
+    'saml_already_logged_in' => 'Already logged in',
+    'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
+    'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
+    'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
+    'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
+    'social_no_action_defined' => 'No action defined',
+    'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
+    'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
+    'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.',
+    'social_account_existing' => 'This :socialAccount is already attached to your profile.',
+    'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
+    'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ',
+    'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.',
+    'social_driver_not_found' => 'Social driver not found',
+    'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
+    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+
+    // System
+    'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
+    'cannot_get_image_from_url' => 'Cannot get image from :url',
+    'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
+    'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'uploaded'  => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'image_upload_error' => 'An error occurred uploading the image',
+    'image_upload_type_error' => 'The image type being uploaded is invalid',
+    'file_upload_timeout' => 'The file upload has timed out.',
+
+    // Attachments
+    'attachment_not_found' => 'Attachment not found',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
+    'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage',
+
+    // Entities
+    'entity_not_found' => 'Entity not found',
+    'bookshelf_not_found' => 'Bookshelf not found',
+    'book_not_found' => 'Book not found',
+    'page_not_found' => 'Page not found',
+    'chapter_not_found' => 'Chapter not found',
+    'selected_book_not_found' => 'The selected book was not found',
+    'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
+    'guests_cannot_save_drafts' => 'Guests cannot save drafts',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
+    'users_cannot_delete_guest' => 'You cannot delete the guest user',
+
+    // Roles
+    'role_cannot_be_edited' => 'This role cannot be edited',
+    'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
+    'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
+    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+
+    // Comments
+    'comment_list' => 'An error occurred while fetching the comments.',
+    'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.',
+    'comment_add' => 'An error occurred while adding / updating the comment.',
+    'comment_delete' => 'An error occurred while deleting the comment.',
+    'empty_comment' => 'Cannot add an empty comment.',
+
+    // Error pages
+    '404_page_not_found' => 'Page Not Found',
+    'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
+    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'return_home' => 'Return to home',
+    'error_occurred' => 'An Error Occurred',
+    'app_down' => ':appName is down right now',
+    'back_soon' => 'It will be back up soon.',
+
+    // API errors
+    'api_no_authorization_found' => 'No authorization token found on the request',
+    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
+    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
+    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
+    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
+    'api_user_token_expired' => 'The authorization token used has expired',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+
+];
diff --git a/resources/lang/pt/pagination.php b/resources/lang/pt/pagination.php
new file mode 100644 (file)
index 0000000..85bd12f
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; Previous',
+    'next'     => 'Next &raquo;',
+
+];
diff --git a/resources/lang/pt/passwords.php b/resources/lang/pt/passwords.php
new file mode 100644 (file)
index 0000000..b408f3c
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+    'password' => 'Passwords must be at least eight characters and match the confirmation.',
+    'user' => "We can't find a user with that e-mail address.",
+    'token' => 'The password reset token is invalid for this email address.',
+    'sent' => 'We have e-mailed your password reset link!',
+    'reset' => 'Your password has been reset!',
+
+];
diff --git a/resources/lang/pt/settings.php b/resources/lang/pt/settings.php
new file mode 100644 (file)
index 0000000..2bd314c
--- /dev/null
@@ -0,0 +1,229 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Settings',
+    'settings_save' => 'Save Settings',
+    'settings_save_success' => 'Settings saved',
+
+    // App Settings
+    'app_customization' => 'Customization',
+    'app_features_security' => 'Features & Security',
+    'app_name' => 'Application Name',
+    'app_name_desc' => 'This name is shown in the header and in any system-sent emails.',
+    'app_name_header' => 'Show name in header',
+    'app_public_access' => 'Public Access',
+    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
+    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
+    'app_public_access_toggle' => 'Allow public access',
+    'app_public_viewing' => 'Allow public viewing?',
+    'app_secure_images' => 'Higher Security Image Uploads',
+    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.',
+    'app_editor' => 'Page Editor',
+    'app_editor_desc' => 'Select which editor will be used by all users to edit pages.',
+    'app_custom_html' => 'Custom HTML Head Content',
+    'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the <head> section of every page. This is handy for overriding styles or adding analytics code.',
+    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_logo' => 'Application Logo',
+    'app_logo_desc' => 'This image should be 43px in height. <br>Large images will be scaled down.',
+    'app_primary_color' => 'Application Primary Color',
+    'app_primary_color_desc' => 'Sets the primary color for the application including the banner, buttons, and links.',
+    'app_homepage' => 'Application Homepage',
+    'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
+    'app_homepage_select' => 'Select a page',
+    'app_disable_comments' => 'Disable Comments',
+    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
+
+    // Color settings
+    'content_colors' => 'Content Colors',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
+    // Registration Settings
+    'reg_settings' => 'Registration',
+    'reg_enable' => 'Enable Registration',
+    'reg_enable_toggle' => 'Enable registration',
+    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_default_role' => 'Default user role after registration',
+    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    'reg_email_confirmation' => 'Email Confirmation',
+    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.',
+    'reg_confirm_restrict_domain' => 'Domain Restriction',
+    'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
+    'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
+
+    // Maintenance settings
+    'maint' => 'Maintenance',
+    'maint_image_cleanup' => 'Cleanup Images',
+    'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
+    'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+    'maint_image_cleanup_run' => 'Run Cleanup',
+    'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
+    'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
+    'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
+    'maint_send_test_email' => 'Send a Test Email',
+    'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
+    'maint_send_test_email_run' => 'Send test email',
+    'maint_send_test_email_success' => 'Email sent to :address',
+    'maint_send_test_email_mail_subject' => 'Test Email',
+    'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
+    'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
+    // Role Settings
+    'roles' => 'Roles',
+    'role_user_roles' => 'User Roles',
+    'role_create' => 'Create New Role',
+    'role_create_success' => 'Role successfully created',
+    'role_delete' => 'Delete Role',
+    'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
+    'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
+    'role_delete_no_migration' => "Don't migrate users",
+    'role_delete_sure' => 'Are you sure you want to delete this role?',
+    'role_delete_success' => 'Role successfully deleted',
+    'role_edit' => 'Edit Role',
+    'role_details' => 'Role Details',
+    'role_name' => 'Role Name',
+    'role_desc' => 'Short Description of Role',
+    'role_external_auth_id' => 'External Authentication IDs',
+    'role_system' => 'System Permissions',
+    'role_manage_users' => 'Manage users',
+    'role_manage_roles' => 'Manage roles & role permissions',
+    'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions',
+    'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
+    'role_manage_page_templates' => 'Manage page templates',
+    'role_access_api' => 'Access system API',
+    'role_manage_settings' => 'Manage app settings',
+    'role_asset' => 'Asset Permissions',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
+    'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
+    'role_all' => 'All',
+    'role_own' => 'Own',
+    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_save' => 'Save Role',
+    'role_update_success' => 'Role successfully updated',
+    'role_users' => 'Users in this role',
+    'role_users_none' => 'No users are currently assigned to this role',
+
+    // Users
+    'users' => 'Users',
+    'user_profile' => 'User Profile',
+    'users_add_new' => 'Add New User',
+    'users_search' => 'Search Users',
+    'users_details' => 'User Details',
+    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
+    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_role' => 'User Roles',
+    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
+    'users_password' => 'User Password',
+    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
+    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
+    'users_send_invite_option' => 'Send user invite email',
+    'users_external_auth_id' => 'External Authentication ID',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+    'users_password_warning' => 'Only fill the below if you would like to change your password.',
+    'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
+    'users_delete' => 'Delete User',
+    'users_delete_named' => 'Delete user :userName',
+    'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
+    'users_delete_confirm' => 'Are you sure you want to delete this user?',
+    'users_delete_success' => 'Users successfully removed',
+    'users_edit' => 'Edit User',
+    'users_edit_profile' => 'Edit Profile',
+    'users_edit_success' => 'User successfully updated',
+    'users_avatar' => 'User Avatar',
+    'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.',
+    'users_preferred_language' => 'Preferred Language',
+    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_social_accounts' => 'Social Accounts',
+    'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.',
+    'users_social_connect' => 'Connect Account',
+    'users_social_disconnect' => 'Disconnect Account',
+    'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
+    'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token Created :timeAgo',
+    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'Slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/pt/validation.php b/resources/lang/pt/validation.php
new file mode 100644 (file)
index 0000000..76b57a2
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => 'The :attribute must be accepted.',
+    'active_url'           => 'The :attribute is not a valid URL.',
+    'after'                => 'The :attribute must be a date after :date.',
+    'alpha'                => 'The :attribute may only contain letters.',
+    'alpha_dash'           => 'The :attribute may only contain letters, numbers, dashes and underscores.',
+    'alpha_num'            => 'The :attribute may only contain letters and numbers.',
+    'array'                => 'The :attribute must be an array.',
+    'before'               => 'The :attribute must be a date before :date.',
+    'between'              => [
+        'numeric' => 'The :attribute must be between :min and :max.',
+        'file'    => 'The :attribute must be between :min and :max kilobytes.',
+        'string'  => 'The :attribute must be between :min and :max characters.',
+        'array'   => 'The :attribute must have between :min and :max items.',
+    ],
+    'boolean'              => 'The :attribute field must be true or false.',
+    'confirmed'            => 'The :attribute confirmation does not match.',
+    'date'                 => 'The :attribute is not a valid date.',
+    'date_format'          => 'The :attribute does not match the format :format.',
+    'different'            => 'The :attribute and :other must be different.',
+    'digits'               => 'The :attribute must be :digits digits.',
+    'digits_between'       => 'The :attribute must be between :min and :max digits.',
+    'email'                => 'The :attribute must be a valid email address.',
+    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'filled'               => 'The :attribute field is required.',
+    'gt'                   => [
+        'numeric' => 'The :attribute must be greater than :value.',
+        'file'    => 'The :attribute must be greater than :value kilobytes.',
+        'string'  => 'The :attribute must be greater than :value characters.',
+        'array'   => 'The :attribute must have more than :value items.',
+    ],
+    'gte'                  => [
+        'numeric' => 'The :attribute must be greater than or equal :value.',
+        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be greater than or equal :value characters.',
+        'array'   => 'The :attribute must have :value items or more.',
+    ],
+    'exists'               => 'The selected :attribute is invalid.',
+    'image'                => 'The :attribute must be an image.',
+    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'in'                   => 'The selected :attribute is invalid.',
+    'integer'              => 'The :attribute must be an integer.',
+    'ip'                   => 'The :attribute must be a valid IP address.',
+    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
+    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
+    'json'                 => 'The :attribute must be a valid JSON string.',
+    'lt'                   => [
+        'numeric' => 'The :attribute must be less than :value.',
+        'file'    => 'The :attribute must be less than :value kilobytes.',
+        'string'  => 'The :attribute must be less than :value characters.',
+        'array'   => 'The :attribute must have less than :value items.',
+    ],
+    'lte'                  => [
+        'numeric' => 'The :attribute must be less than or equal :value.',
+        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be less than or equal :value characters.',
+        'array'   => 'The :attribute must not have more than :value items.',
+    ],
+    'max'                  => [
+        'numeric' => 'The :attribute may not be greater than :max.',
+        'file'    => 'The :attribute may not be greater than :max kilobytes.',
+        'string'  => 'The :attribute may not be greater than :max characters.',
+        'array'   => 'The :attribute may not have more than :max items.',
+    ],
+    'mimes'                => 'The :attribute must be a file of type: :values.',
+    'min'                  => [
+        'numeric' => 'The :attribute must be at least :min.',
+        'file'    => 'The :attribute must be at least :min kilobytes.',
+        'string'  => 'The :attribute must be at least :min characters.',
+        'array'   => 'The :attribute must have at least :min items.',
+    ],
+    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'not_in'               => 'The selected :attribute is invalid.',
+    'not_regex'            => 'The :attribute format is invalid.',
+    'numeric'              => 'The :attribute must be a number.',
+    'regex'                => 'The :attribute format is invalid.',
+    'required'             => 'The :attribute field is required.',
+    'required_if'          => 'The :attribute field is required when :other is :value.',
+    'required_with'        => 'The :attribute field is required when :values is present.',
+    'required_with_all'    => 'The :attribute field is required when :values is present.',
+    'required_without'     => 'The :attribute field is required when :values is not present.',
+    'required_without_all' => 'The :attribute field is required when none of :values are present.',
+    'same'                 => 'The :attribute and :other must match.',
+    'size'                 => [
+        'numeric' => 'The :attribute must be :size.',
+        'file'    => 'The :attribute must be :size kilobytes.',
+        'string'  => 'The :attribute must be :size characters.',
+        'array'   => 'The :attribute must contain :size items.',
+    ],
+    'string'               => 'The :attribute must be a string.',
+    'timezone'             => 'The :attribute must be a valid zone.',
+    'unique'               => 'The :attribute has already been taken.',
+    'url'                  => 'The :attribute format is invalid.',
+    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Password confirmation required',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 5a40886004bf9593f08a10e59c5711f89be5a765..2fa3d65717f82856b0ddfe62f33b6cc487c54649 100644 (file)
@@ -6,43 +6,44 @@
 return [
 
     // Pages
-    'page_create'                 => 'página criada',
+    'page_create'                 => 'criou a página',
     'page_create_notification'    => 'Página criada com sucesso',
-    'page_update'                 => 'página atualizada',
+    'page_update'                 => 'atualizou a página',
     'page_update_notification'    => 'Página atualizada com sucesso',
-    'page_delete'                 => 'página excluída',
+    'page_delete'                 => 'excluiu a página',
     'page_delete_notification'    => 'Página excluída com sucesso',
-    'page_restore'                => 'página restaurada',
+    'page_restore'                => 'restaurou a página',
     'page_restore_notification'   => 'Página restaurada com sucesso',
-    'page_move'                   => 'página movida',
+    'page_move'                   => 'moveu a página',
 
     // Chapters
-    'chapter_create'              => 'capítulo criado',
+    'chapter_create'              => 'criou o capítulo',
     'chapter_create_notification' => 'Capítulo criado com sucesso',
-    'chapter_update'              => 'capítulo atualizado',
-    'chapter_update_notification' => 'capítulo atualizado com sucesso',
-    'chapter_delete'              => 'capítulo excluído',
+    'chapter_update'              => 'atualizou o capítulo',
+    'chapter_update_notification' => 'Capítulo atualizado com sucesso',
+    'chapter_delete'              => 'excluiu o capítulo',
     'chapter_delete_notification' => 'Capítulo excluído com sucesso',
-    'chapter_move'                => 'capitulo movido',
+    'chapter_move'                => 'moveu o capítulo',
 
     // Books
-    'book_create'                 => 'livro criado',
+    'book_create'                 => 'criou o livro',
     'book_create_notification'    => 'Livro criado com sucesso',
-    'book_update'                 => 'livro atualizado',
+    'book_update'                 => 'atualizou o livro',
     'book_update_notification'    => 'Livro atualizado com sucesso',
-    'book_delete'                 => 'livro excluído',
+    'book_delete'                 => 'excluiu o livro',
     'book_delete_notification'    => 'Livro excluído com sucesso',
-    'book_sort'                   => 'livro classificado',
-    'book_sort_notification'      => 'Livro reclassificado com sucesso',
+    'book_sort'                   => 'ordenou o livro',
+    'book_sort_notification'      => 'Livro reordenado com sucesso',
 
     // Bookshelves
-    'bookshelf_create'            => 'prateleira de livros criada',
-    'bookshelf_create_notification'    => 'Prateleira de Livros criada com sucesso',
-    'bookshelf_update'                 => 'prateleira de livros atualizada',
-    'bookshelf_update_notification'    => 'Prateleira de Livros atualizada com sucesso',
-    'bookshelf_delete'                 => 'prateleira de livros excluída',
-    'bookshelf_delete_notification'    => 'Prateleira de Livros excluída com sucesso',
+    'bookshelf_create'            => 'criou a prateleira',
+    'bookshelf_create_notification'    => 'Prateleira criada com sucesso',
+    'bookshelf_update'                 => 'atualizou a prateleira',
+    'bookshelf_update_notification'    => 'Prateleira atualizada com sucesso',
+    'bookshelf_delete'                 => 'excluiu a prateleira',
+    'bookshelf_delete_notification'    => 'Prateleira excluída com sucesso',
 
     // Other
     'commented_on'                => 'comentou em',
+    'permissions_update'          => 'updated permissions',
 ];
index 21c16699c07b6fa78a8a808c8f1bee63a8597812..b2c3072c41a8fe6af821064239c6ddc1511ae895 100644 (file)
@@ -6,14 +6,14 @@
  */
 return [
 
-    'failed' => 'As credenciais fornecidas não puderam ser validadas em nossos registros..',
+    'failed' => 'As credenciais fornecidas não puderam ser validadas em nossos registros.',
     'throttle' => 'Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.',
 
     // Login & Register
-    'sign_up' => 'Registrar-se',
+    'sign_up' => 'Criar Conta',
     'log_in' => 'Entrar',
     'log_in_with' => 'Entrar com :socialDriver',
-    'sign_up_with' => 'Registrar com :socialDriver',
+    'sign_up_with' => 'Cadastre-se com :socialDriver',
     'logout' => 'Sair',
 
     'name' => 'Nome',
@@ -21,57 +21,57 @@ return [
     'email' => 'E-mail',
     'password' => 'Senha',
     'password_confirm' => 'Confirmar Senha',
-    'password_hint' => 'Senha deverá ser maior que 7 caracteres',
+    'password_hint' => 'Deve ser maior que 7 caracteres',
     'forgot_password' => 'Esqueceu a senha?',
     'remember_me' => 'Lembrar de mim',
     'ldap_email_hint' => 'Por favor, digite um e-mail para essa conta.',
-    'create_account' => 'Criar conta',
-    'already_have_account' => 'Você já possui uma conta?',
+    'create_account' => 'Criar Conta',
+    'already_have_account' => 'Já possui uma conta?',
     'dont_have_account' => 'Não possui uma conta?',
-    'social_login' => 'Login social',
-    'social_registration' => 'Registro social',
-    'social_registration_text' => 'Registre e entre usando outro serviço.',
+    'social_login' => 'Login Social',
+    'social_registration' => 'Cadastro Social',
+    'social_registration_text' => 'Cadastre-se e entre utilizando outro serviço.',
 
-    'register_thanks' => 'Obrigado por efetuar o registro!',
+    'register_thanks' => 'Obrigado por se cadastrar!',
     'register_confirm' => 'Por favor, verifique seu e-mail e clique no botão de confirmação para acessar :appName.',
-    'registrations_disabled' => 'Registros estão temporariamente desabilitados',
+    'registrations_disabled' => 'Cadastros estão temporariamente desabilitados',
     'registration_email_domain_invalid' => 'O domínio de e-mail usado não tem acesso permitido a essa aplicação',
-    'register_success' => 'Obrigado por se registrar! Você agora encontra-se registrado e logado..',
+    'register_success' => 'Obrigado por se cadastrar! Você agora encontra-se cadastrado(a) e logado(a).',
 
 
     // Password Reset
-    'reset_password' => 'Resetar senha',
-    'reset_password_send_instructions' => 'Digite seu e-mail abaixo e o sistema enviará uma mensagem com o link de reset de senha.',
-    'reset_password_send_button' => 'Enviar o link de reset de senha',
-    'reset_password_sent_success' => 'Um link de reset de senha foi enviado para :email.',
-    'reset_password_success' => 'Sua senha foi resetada com sucesso.',
-    'email_reset_subject' => 'Resetar a senha de :appName',
-    'email_reset_text' => 'Você recebeu esse e-mail pois recebemos uma solicitação de reset de senha para sua conta.',
-    'email_reset_not_requested' => 'Caso não tenha sido você a solicitar o reset de senha, ignore esse e-mail.',
+    'reset_password' => 'Redefinir Senha',
+    'reset_password_send_instructions' => 'Insira seu e-mail abaixo e uma mensagem com o link de redefinição de senha lhe será enviada.',
+    'reset_password_send_button' => 'Enviar o Link de Redefinição',
+    'reset_password_sent' => 'Um link de redefinição de senha será enviado para :email se o endereço de e-mail for encontrado no sistema.',
+    'reset_password_success' => 'Sua senha foi redefinida com sucesso.',
+    'email_reset_subject' => 'Redefina a senha de :appName',
+    'email_reset_text' => 'Você recebeu esse e-mail pois recebemos uma solicitação de redefinição de senha para a sua conta.',
+    'email_reset_not_requested' => 'Caso não tenha sido você a solicitar a redefinição de senha, ignore esse e-mail.',
 
 
     // Email Confirmation
     'email_confirm_subject' => 'Confirme seu e-mail para :appName',
-    'email_confirm_greeting' => 'Obrigado por se registrar em :appName!',
+    'email_confirm_greeting' => 'Obrigado por se cadastrar em :appName!',
     'email_confirm_text' => 'Por favor, confirme seu endereço de e-mail clicando no botão abaixo:',
     'email_confirm_action' => 'Confirmar E-mail',
-    'email_confirm_send_error' => 'E-mail de confirmação é requerido, mas o sistema não pôde enviar a mensagem. Por favor, entre em contato com o admin para se certificar que o serviço de envio de e-mails está corretamente configurado.',
+    'email_confirm_send_error' => 'A confirmação de e-mail é requerida, mas o sistema não pôde enviar a mensagem. Por favor, entre em contato com o administrador para se certificar que o serviço de envio de e-mails está corretamente configurado.',
     'email_confirm_success' => 'Seu e-mail foi confirmado!',
-    'email_confirm_resent' => 'E-mail de confirmação reenviado. Por favor, cheque sua caixa postal.',
+    'email_confirm_resent' => 'E-mail de confirmação reenviado. Por favor, verifique sua caixa de entrada.',
 
-    'email_not_confirmed' => 'Endereço de e-mail não foi confirmado',
+    'email_not_confirmed' => 'Endereço de E-mail Não Confirmado',
     'email_not_confirmed_text' => 'Seu endereço de e-mail ainda não foi confirmado.',
-    'email_not_confirmed_click_link' => 'Por favor, clique no link no e-mail que foi enviado após o registro.',
+    'email_not_confirmed_click_link' => 'Por favor, clique no link no e-mail que foi enviado após o cadastro.',
     'email_not_confirmed_resend' => 'Caso não encontre o e-mail você poderá reenviar a confirmação usando o formulário abaixo.',
-    'email_not_confirmed_resend_button' => 'Reenviar o e-mail de confirmação',
+    'email_not_confirmed_resend_button' => 'Reenviar o E-mail de Confirmação',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Você recebeu um convite para :appName!',
+    'user_invite_email_greeting' => 'Uma conta foi criada para você em :appName.',
+    'user_invite_email_text' => 'Clique no botão abaixo para definir uma senha de conta e obter acesso:',
+    'user_invite_email_action' => 'Defina a Senha da Conta',
+    'user_invite_page_welcome' => 'Bem-vindo(a) a :appName!',
+    'user_invite_page_text' => 'Para finalizar sua conta e obter acesso, você precisa definir uma senha que será usada para efetuar login em :appName em futuras visitas.',
+    'user_invite_page_confirm_button' => 'Confirmar Senha',
+    'user_invite_success' => 'Senha definida, você agora tem acesso a :appName!'
 ];
\ No newline at end of file
index 03d9b1d8e4b216153b20ac3ec1237252309158cb..8b0dffe6e844cfbe55480cd7b7aaf57540df47d9 100644 (file)
@@ -11,20 +11,20 @@ return [
     'save' => 'Salvar',
     'continue' => 'Continuar',
     'select' => 'Selecionar',
-    'toggle_all' => 'Alternar Tudo',
+    'toggle_all' => 'Alternar Todos',
     'more' => 'Mais',
 
     // Form Labels
     'name' => 'Nome',
     'description' => 'Descrição',
-    'role' => 'Regra',
+    'role' => 'Cargo',
     'cover_image' => 'Imagem de capa',
-    'cover_image_description' => 'Esta imagem deve ser aproximadamente 300x170px.',
+    'cover_image_description' => 'Esta imagem deve ser aproximadamente 440x250px.',
     
     // Actions
     'actions' => 'Ações',
     'view' => 'Visualizar',
-    'view_all' => 'Ver Tudo',
+    'view_all' => 'Visualizar Tudo',
     'create' => 'Criar',
     'update' => 'Atualizar',
     'edit' => 'Editar',
@@ -33,17 +33,19 @@ return [
     'copy' => 'Copiar',
     'reply' => 'Responder',
     'delete' => 'Excluir',
+    'delete_confirm' => 'Confirmar Exclusão',
     'search' => 'Pesquisar',
     'search_clear' => 'Limpar Pesquisa',
-    'reset' => 'Resetar',
+    'reset' => 'Redefinir',
     'remove' => 'Remover',
     'add' => 'Adicionar',
+    'fullscreen' => 'Tela cheia',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
+    'sort_options' => 'Opções de Ordenação',
+    'sort_direction_toggle' => 'Alternar Direção de Ordenação',
+    'sort_ascending' => 'Ordenação Crescente',
+    'sort_descending' => 'Ordenação Decrescente',
     'sort_name' => 'Nome',
     'sort_created_at' => 'Data de Criação',
     'sort_updated_at' => 'Data de Atualização',
@@ -59,15 +61,17 @@ return [
     'grid_view' => 'Visualização em Grade',
     'list_view' => 'Visualização em Lista',
     'default' => 'Padrão',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Caminho',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Menu de Perfil',
     'view_profile' => 'Visualizar Perfil',
     'edit_profile' => 'Editar Perfil',
+    'dark_mode' => 'Modo Escuro',
+    'light_mode' => 'Modo Claro',
 
     // Layout tabs
-    'tab_info' => 'Info',
+    'tab_info' => 'Informações',
     'tab_content' => 'Conteúdo',
 
     // Email Content
index e983e9f8df194aa03cccf10b63813d205e25d934..a1210d52ce99c92a784e66a063fdb48a53e76a77 100644 (file)
@@ -5,23 +5,23 @@
 return [
 
     // Image Manager
-    'image_select' => 'Selecionar imagem',
-    'image_all' => 'Todos',
+    'image_select' => 'Selecionar Imagem',
+    'image_all' => 'Todas',
     'image_all_title' => 'Visualizar todas as imagens',
     'image_book_title' => 'Visualizar imagens relacionadas a esse livro',
     'image_page_title' => 'visualizar imagens relacionadas a essa página',
     'image_search_hint' => 'Pesquisar imagem por nome',
-    'image_uploaded' => 'Carregado :uploadedDate',
+    'image_uploaded' => 'Adicionada em :uploadedDate',
     'image_load_more' => 'Carregar Mais',
     'image_image_name' => 'Nome da Imagem',
     'image_delete_used' => 'Essa imagem é usada nas páginas abaixo.',
-    'image_delete_confirm' => 'Clique em Excluir novamente para confirmar que você deseja mesmo eliminar a imagem.',
+    'image_delete_confirm_text' => 'Tem certeza de que deseja excluir essa imagem?',
     'image_select_image' => 'Selecionar Imagem',
     'image_dropzone' => 'Arraste imagens ou clique aqui para fazer upload',
-    'images_deleted' => 'Imagens excluídas',
-    'image_preview' => 'Virtualização de Imagem',
+    'images_deleted' => 'Imagens Excluídas',
+    'image_preview' => 'Pré-Visualização de Imagem',
     'image_upload_success' => 'Upload de imagem efetuado com sucesso',
-    'image_update_success' => 'Upload de detalhes da imagem efetuado com sucesso',
+    'image_update_success' => 'Detalhes da imagem atualizados com sucesso',
     'image_delete_success' => 'Imagem excluída com sucesso',
     'image_upload_remove' => 'Remover',
 
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Editar Código',
     'code_language' => 'Linguagem do Código',
     'code_content' => 'Código',
+    'code_session_history' => 'Histórico de Sessão',
     'code_save' => 'Salvar Código',
 ];
index c0e2c5ee9fd6e438af0052d1a3e1c6ce708454fc..129e1f260876dabb5a63088bc9f2e26ec523ad59 100644 (file)
@@ -6,29 +6,30 @@
 return [
 
     // Shared
-    'recently_created' => 'Recentemente Criado',
-    'recently_created_pages' => 'Páginas Recentemente Criadas',
-    'recently_updated_pages' => 'Páginas Recentemente Atualizadas',
-    'recently_created_chapters' => 'Capítulos Recentemente Criados',
-    'recently_created_books' => 'Livros Recentemente Criados',
-    'recently_created_shelves' => 'Prateleiras Recentemente Criadas',
-    'recently_update' => 'Recentemente Atualizado',
-    'recently_viewed' => 'Recentemente Visualizado',
+    'recently_created' => 'Criados Recentemente',
+    'recently_created_pages' => 'Páginas Criadas Recentemente',
+    'recently_updated_pages' => 'Páginas Atualizadas Recentemente',
+    'recently_created_chapters' => 'Capítulos Criados Recentemente',
+    'recently_created_books' => 'Livros Criados Recentemente',
+    'recently_created_shelves' => 'Prateleiras Criadas Recentemente',
+    'recently_update' => 'Atualizados Recentemente',
+    'recently_viewed' => 'Visualizados Recentemente',
     'recent_activity' => 'Atividade Recente',
     'create_now' => 'Criar um agora',
     'revisions' => 'Revisões',
     'meta_revision' => 'Revisão #:revisionCount',
-    'meta_created' => 'Criado em :timeLength',
-    'meta_created_name' => 'Criado em :timeLength por :user',
-    'meta_updated' => 'Atualizado em :timeLength',
-    'meta_updated_name' => 'Atualizado em :timeLength por :user',
+    'meta_created' => 'Criado :timeLength',
+    'meta_created_name' => 'Criado :timeLength por :user',
+    'meta_updated' => 'Atualizado :timeLength',
+    'meta_updated_name' => 'Atualizado :timeLength por :user',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Seleção de Entidade',
     'images' => 'Imagens',
-    'my_recent_drafts' => 'Meus rascunhos recentes',
-    'my_recently_viewed' => 'Meus itens recentemente visto',
+    'my_recent_drafts' => 'Meus Rascunhos Recentes',
+    'my_recently_viewed' => 'Visualizados por mim Recentemente',
     'no_pages_viewed' => 'Você não visualizou nenhuma página',
-    'no_pages_recently_created' => 'Nenhuma página recentemente criada',
-    'no_pages_recently_updated' => 'Nenhuma página recentemente atualizada',
+    'no_pages_recently_created' => 'Nenhuma página criada recentemente',
+    'no_pages_recently_updated' => 'Nenhuma página atualizada recentemente',
     'export' => 'Exportar',
     'export_html' => 'Arquivo Web Contained',
     'export_pdf' => 'Arquivo PDF',
@@ -36,9 +37,10 @@ return [
 
     // Permissions and restrictions
     'permissions' => 'Permissões',
-    'permissions_intro' => 'Uma vez habilitado, as permissões terão prioridade sobre outro conjunto de permissões.',
+    'permissions_intro' => 'Uma vez habilitadas, estas permissões terão prioridade sobre outro conjunto de permissões.',
     'permissions_enable' => 'Habilitar Permissões Customizadas',
     'permissions_save' => 'Salvar Permissões',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Resultado(s) da Pesquisa',
@@ -47,13 +49,14 @@ return [
     'search_no_pages' => 'Nenhuma página corresponde à pesquisa',
     'search_for_term' => 'Pesquisar por :term',
     'search_more' => 'Mais Resultados',
-    'search_filters' => 'Filtros de Pesquisa',
+    'search_advanced' => 'Pesquisa Avançada',
+    'search_terms' => 'Termos da Pesquisa',
     'search_content_type' => 'Tipo de Conteúdo',
     'search_exact_matches' => 'Correspondências Exatas',
-    'search_tags' => 'Tags',
+    'search_tags' => 'Persquisar Tags',
     'search_options' => 'Opções',
-    'search_viewed_by_me' => 'Visto por mim',
-    'search_not_viewed_by_me' => 'Não visto por mim',
+    'search_viewed_by_me' => 'Visualizado por mim',
+    'search_not_viewed_by_me' => 'Não visualizado por mim',
     'search_permissions_set' => 'Permissão definida',
     'search_created_by_me' => 'Criado por mim',
     'search_updated_by_me' => 'Atualizado por mim',
@@ -62,7 +65,7 @@ return [
     'search_updated_after' => 'Atualizado depois de',
     'search_created_before' => 'Criado antes de',
     'search_created_after' => 'Criado depois de',
-    'search_set_date' => 'Definir data',
+    'search_set_date' => 'Definir Data',
     'search_update' => 'Refazer Pesquisa',
 
     // Shelves
@@ -82,20 +85,20 @@ return [
     'shelves_add_books' => 'Adicionar livros a esta prateleira',
     'shelves_drag_books' => 'Arraste livros aqui para adicioná-los a esta prateleira',
     'shelves_empty_contents' => 'Esta prateleira não possui livros atribuídos a ela',
-    'shelves_edit_and_assign' => 'Edit shelf to assign books',
+    'shelves_edit_and_assign' => 'Editar prateleira para atribuir livros',
     'shelves_edit_named' => 'Editar Prateleira de Livros :name',
     'shelves_edit' => 'Edit Prateleira de Livros',
     'shelves_delete' => 'Excluir Prateleira de Livros',
     'shelves_delete_named' => 'Excluir Prateleira de Livros :name',
-    'shelves_delete_explain' => "A ação vai excluír a prateleira de livros com o nome ':name'. Livros contidos não serão excluídos",
+    'shelves_delete_explain' => "A ação vai excluír a prateleira com o nome ':name'. Livros contidos não serão excluídos.",
     'shelves_delete_confirmation' => 'Você tem certeza que quer excluir esta prateleira de livros?',
-    'shelves_permissions' => 'Permissões da Prateleira de Livros',
-    'shelves_permissions_updated' => 'Permissões da Prateleira de Livros Atualizada',
-    'shelves_permissions_active' => 'Permissões da Prateleira de Livros Ativadas',
+    'shelves_permissions' => 'Permissões da Prateleira',
+    'shelves_permissions_updated' => 'Permissões da Prateleira Atualizadas',
+    'shelves_permissions_active' => 'Permissões da Prateleira Ativas',
     'shelves_copy_permissions_to_books' => 'Copiar Permissões para Livros',
     'shelves_copy_permissions' => 'Copiar Permissões',
-    'shelves_copy_permissions_explain' => 'Isto aplicará as configurações de permissões atuais desta prateleira de livros a todos os livros contidos nela. Antes de ativar, assegure-se de que quaisquer alterações nas permissões desta prateleira de livros tenham sido salvas.',
-    'shelves_copy_permission_success' => 'Permissões da prateleira de livros copiada para :count livros',
+    'shelves_copy_permissions_explain' => 'Isto aplicará as configurações de permissões atuais desta prateleira a todos os livros contidos nela. Antes de ativar, assegure-se de que quaisquer alterações nas permissões desta prateleira tenham sido salvas.',
+    'shelves_copy_permission_success' => 'Permissões da prateleira copiadas para :count livros',
 
     // Books
     'book' => 'Livro',
@@ -108,23 +111,23 @@ return [
     'books_new_action' => 'Novo Livro',
     'books_popular_empty' => 'Os livros mais populares aparecerão aqui.',
     'books_new_empty' => 'Os livros criados mais recentemente aparecerão aqui.',
-    'books_create' => 'Criar novo Livro',
+    'books_create' => 'Criar Novo Livro',
     'books_delete' => 'Excluir Livro',
     'books_delete_named' => 'Excluir Livro :bookName',
-    'books_delete_explain' => 'A ação vai excluír o livro com o nome \':bookName\'. Todas as páginas e capítulos serão removidos.',
-    'books_delete_confirmation' => 'Você tem certeza que quer excluír o Livro?',
+    'books_delete_explain' => 'A ação vai excluir o livro com o nome \':bookName\'. Todas as páginas e capítulos serão removidos.',
+    'books_delete_confirmation' => 'Você tem certeza que quer excluir o Livro?',
     'books_edit' => 'Editar Livro',
     'books_edit_named' => 'Editar Livro :bookName',
     'books_form_book_name' => 'Nome do Livro',
     'books_save' => 'Salvar Livro',
     'books_permissions' => 'Permissões do Livro',
     'books_permissions_updated' => 'Permissões do Livro Atualizadas',
-    'books_empty_contents' => 'Nenhuma página ou capítulo criado para esse livro.',
+    'books_empty_contents' => 'Nenhuma página ou capítulo foram criados para este livro.',
     'books_empty_create_page' => 'Criar uma nova página',
     'books_empty_sort_current_book' => 'Ordenar o livro atual',
     'books_empty_add_chapter' => 'Adicionar um capítulo',
-    'books_permissions_active' => 'Permissões do Livro Ativadas',
-    'books_search_this' => 'Pesquisar esse livro',
+    'books_permissions_active' => 'Permissões do Livro Ativas',
+    'books_search_this' => 'Pesquisar neste livro',
     'books_navigation' => 'Navegação do Livro',
     'books_sort' => 'Ordenar Conteúdos do Livro',
     'books_sort_named' => 'Ordenar Livro :bookName',
@@ -143,10 +146,10 @@ return [
     'chapters_popular' => 'Capítulos Populares',
     'chapters_new' => 'Novo Capítulo',
     'chapters_create' => 'Criar Novo Capítulo',
-    'chapters_delete' => 'Excluír Capítulo',
+    'chapters_delete' => 'Excluir Capítulo',
     'chapters_delete_named' => 'Excluir Capítulo :chapterName',
-    'chapters_delete_explain' => 'A ação vai excluír o capítulo de nome \':chapterName\'. Todas as páginas do capítulo serão removidas e adicionadas diretamente ao livro pai.',
-    'chapters_delete_confirm' => 'Tem certeza que deseja excluír o capítulo?',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_confirm' => 'Tem certeza que deseja excluir o capítulo?',
     'chapters_edit' => 'Editar Capítulo',
     'chapters_edit_named' => 'Editar Capítulo :chapterName',
     'chapters_save' => 'Salvar Capítulo',
@@ -155,86 +158,87 @@ return [
     'chapter_move_success' => 'Capítulo movido para :bookName',
     'chapters_permissions' => 'Permissões do Capítulo',
     'chapters_empty' => 'Nenhuma página existente nesse capítulo.',
-    'chapters_permissions_active' => 'Permissões de Capítulo Ativadas',
+    'chapters_permissions_active' => 'Permissões de Capítulo Ativas',
     'chapters_permissions_success' => 'Permissões de Capítulo Atualizadas',
-    'chapters_search_this' => 'Pesquisar este Capítulo',
+    'chapters_search_this' => 'Pesquisar neste Capítulo',
 
     // Pages
     'page' => 'Página',
     'pages' => 'Páginas',
     'x_pages' => ':count Página|:count Páginas',
-    'pages_popular' => 'Páginas Popular',
+    'pages_popular' => 'Páginas Populares',
     'pages_new' => 'Nova Página',
     'pages_attachments' => 'Anexos',
-    'pages_navigation' => 'Página de Navegação',
-    'pages_delete' => 'Excluír Página',
-    'pages_delete_named' => 'Excluír Página :pageName',
-    'pages_delete_draft_named' => 'Excluir rascunho de Página de nome :pageName',
-    'pages_delete_draft' => 'Excluir rascunho de Página',
+    'pages_navigation' => 'Navegação da Página',
+    'pages_delete' => 'Excluir Página',
+    'pages_delete_named' => 'Excluir Página :pageName',
+    'pages_delete_draft_named' => 'Excluir Rascunho de Página de nome :pageName',
+    'pages_delete_draft' => 'Excluir Rascunho de Página',
     'pages_delete_success' => 'Página excluída',
-    'pages_delete_draft_success' => 'Página de rascunho excluída',
+    'pages_delete_draft_success' => 'Rascunho de página excluído',
     'pages_delete_confirm' => 'Tem certeza que deseja excluir a página?',
     'pages_delete_draft_confirm' => 'Tem certeza que deseja excluir o rascunho de página?',
     'pages_editing_named' => 'Editando a Página :pageName',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Opções de Rascunho',
     'pages_edit_save_draft' => 'Salvar Rascunho',
-    'pages_edit_draft' => 'Editar rascunho de Página',
+    'pages_edit_draft' => 'Editar Rascunho de Página',
     'pages_editing_draft' => 'Editando Rascunho',
     'pages_editing_page' => 'Editando Página',
     'pages_edit_draft_save_at' => 'Rascunho salvo em ',
-    'pages_edit_delete_draft' => 'Excluir rascunho',
-    'pages_edit_discard_draft' => 'Descartar rascunho',
-    'pages_edit_set_changelog' => 'Definir Changelog',
-    'pages_edit_enter_changelog_desc' => 'Digite uma breve descrição das mudanças efetuadas por você',
-    'pages_edit_enter_changelog' => 'Entrar no  Changelog',
+    'pages_edit_delete_draft' => 'Excluir Rascunho',
+    'pages_edit_discard_draft' => 'Descartar Rascunho',
+    'pages_edit_set_changelog' => 'Relatar Alterações',
+    'pages_edit_enter_changelog_desc' => 'Digite uma breve descrição das alterações efetuadas por você',
+    'pages_edit_enter_changelog' => 'Insira Alterações',
     'pages_save' => 'Salvar Página',
-    'pages_title' => 'Título de Página',
+    'pages_title' => 'Título da Página',
     'pages_name' => 'Nome da Página',
     'pages_md_editor' => 'Editor',
-    'pages_md_preview' => 'Preview',
+    'pages_md_preview' => 'Pré-Visualização',
     'pages_md_insert_image' => 'Inserir Imagem',
     'pages_md_insert_link' => 'Inserir Link para Entidade',
     'pages_md_insert_drawing' => 'Inserir Desenho',
-    'pages_not_in_chapter' => 'Página não está dentro de um Capítulo',
+    'pages_not_in_chapter' => 'Página não está dentro de um capítulo',
     'pages_move' => 'Mover Página',
     'pages_move_success' => 'Pagina movida para ":parentName"',
     'pages_copy' => 'Copiar Página',
     'pages_copy_desination' => 'Destino da Cópia',
     'pages_copy_success' => 'Página copiada com sucesso',
-    'pages_permissions' => 'Permissões de Página',
-    'pages_permissions_success' => 'Permissões de Página atualizadas',
+    'pages_permissions' => 'Permissões da Página',
+    'pages_permissions_success' => 'Permissões da Página atualizadas',
     'pages_revision' => 'Revisão',
-    'pages_revisions' => 'Revisões de Página',
+    'pages_revisions' => 'Revisões da Página',
     'pages_revisions_named' => 'Revisões de Página para :pageName',
     'pages_revision_named' => 'Revisão de Página para :pageName',
-    'pages_revisions_created_by' => 'Criado por',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions_created_by' => 'Criada por',
     'pages_revisions_date' => 'Data da Revisão',
     'pages_revisions_number' => '#',
     'pages_revisions_numbered' => 'Revisão #:id',
     'pages_revisions_numbered_changes' => 'Alterações da Revisão #:id',
-    'pages_revisions_changelog' => 'Changelog',
-    'pages_revisions_changes' => 'Mudanças',
-    'pages_revisions_current' => 'Versão atual',
-    'pages_revisions_preview' => 'Preview',
+    'pages_revisions_changelog' => 'Relatório de Alterações',
+    'pages_revisions_changes' => 'Alterações',
+    'pages_revisions_current' => 'Versão Atual',
+    'pages_revisions_preview' => 'Pré-Visualização',
     'pages_revisions_restore' => 'Restaurar',
     'pages_revisions_none' => 'Essa página não tem revisões',
-    'pages_copy_link' => 'Copia Link',
-    'pages_edit_content_link' => 'Editar conteúdo',
+    'pages_copy_link' => 'Copiar Link',
+    'pages_edit_content_link' => 'Editar Conteúdo',
     'pages_permissions_active' => 'Permissões de Página Ativas',
     'pages_initial_revision' => 'Publicação Inicial',
     'pages_initial_name' => 'Nova Página',
     'pages_editing_draft_notification' => 'Você está atualmente editando um rascunho que foi salvo da última vez em :timeDiff.',
     'pages_draft_edited_notification' => 'Essa página foi atualizada desde então. É recomendado que você descarte esse rascunho.',
     'pages_draft_edit_active' => [
-        'start_a' => ':count usuários que iniciaram edição dessa página',
+        'start_a' => ':count usuários iniciaram a edição dessa página',
         'start_b' => ':userName iniciou a edição dessa página',
         'time_a' => 'desde que a página foi atualizada pela última vez',
         'time_b' => 'nos últimos :minCount minutos',
         'message' => ':start :time. Tome cuidado para não sobrescrever atualizações de outras pessoas!',
     ],
-    'pages_draft_discarded' => 'Rascunho descartado. O editor foi atualizado com a página atualizada',
+    'pages_draft_discarded' => 'Rascunho descartado. O editor foi atualizado com o conteúdo atual da página',
     'pages_specific' => 'Página Específica',
-    'pages_is_template' => 'Page Template',
+    'pages_is_template' => 'Modelo de Página',
 
     // Editor Sidebar
     'page_tags' => 'Tags de Página',
@@ -243,42 +247,43 @@ return [
     'shelf_tags' => 'Tags de Prateleira',
     'tag' => 'Tag',
     'tags' =>  'Tags',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'Nome da Tag',
     'tag_value' => 'Valor da Tag (Opcional)',
-    'tags_explain' => "Adicione algumas tags para melhor categorizar seu conteúdo. \n Você pode atrelar um valor para uma tag para uma organização mais consistente.",
+    'tags_explain' => "Adicione algumas tags para melhor categorizar seu conteúdo. \n Você pode atribuir valores às tags para uma organização mais complexa.",
     'tags_add' => 'Adicionar outra tag',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Remover essa tag',
     'attachments' => 'Anexos',
-    'attachments_explain' => 'Faça o Upload de alguns arquivos ou anexo algum link para ser mostrado na sua página. Eles estarão visíveis na barra lateral à direita da página.',
+    'attachments_explain' => 'Faça o upload de alguns arquivos ou anexe links para serem exibidos na sua página. Eles estarão visíveis na barra lateral à direita.',
     'attachments_explain_instant_save' => 'Mudanças são salvas instantaneamente.',
     'attachments_items' => 'Itens Anexados',
-    'attachments_upload' => 'Upload de arquivos',
+    'attachments_upload' => 'Upload de Arquivos',
     'attachments_link' => 'Links Anexados',
     'attachments_set_link' => 'Definir Link',
-    'attachments_delete_confirm' => 'Clique novamente em Excluir para confirmar a exclusão desse anexo.',
+    'attachments_delete' => 'Tem certeza de que deseja excluir esse anexo?',
     'attachments_dropzone' => 'Arraste arquivos para cá ou clique para anexar arquivos',
     'attachments_no_files' => 'Nenhum arquivo foi enviado',
-    'attachments_explain_link' => 'Você pode anexar um link se preferir não fazer o upload do arquivo. O link poderá ser para uma outra página ou link para um arquivo na nuvem.',
+    'attachments_explain_link' => 'Você pode anexar um link se preferir não fazer o upload do arquivo. O link poderá ser para uma outra página ou para um arquivo na nuvem.',
     'attachments_link_name' => 'Nome do Link',
     'attachment_link' => 'Link para o Anexo',
     'attachments_link_url' => 'Link para o Arquivo',
     'attachments_link_url_hint' => 'URL do site ou arquivo',
     'attach' => 'Anexar',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Editar Arquivo',
     'attachments_edit_file_name' => 'Nome do Arquivo',
-    'attachments_edit_drop_upload' => 'Arraste arquivos para cá ou clique para anexar arquivos e sobrescreve-lo',
+    'attachments_edit_drop_upload' => 'Arraste arquivos para cá ou clique para anexar arquivos e sobrescreve-los',
     'attachments_order_updated' => 'Ordem dos anexos atualizada',
     'attachments_updated_success' => 'Detalhes dos anexos atualizados',
     'attachments_deleted' => 'Anexo excluído',
     'attachments_file_uploaded' => 'Upload de arquivo efetuado com sucesso',
     'attachments_file_updated' => 'Arquivo atualizado com sucesso',
     'attachments_link_attached' => 'Link anexado com sucesso à página',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Modelos',
+    'templates_set_as_template' => 'A Página é um Modelo',
+    'templates_explain_set_as_template' => 'Você pode definir esta página como um modelo para que seu conteúdo possa ser utilizado para criar outras páginas. Outros usuários poderão utilizar esta página como modelo se tiverem permissão para visualiza-la.',
+    'templates_replace_content' => 'Substituir conteúdo da página',
+    'templates_append_content' => 'Adicionar ao fim do conteúdo da página',
+    'templates_prepend_content' => 'Adicionar ao início do conteúdo da página',
 
     // Profile View
     'profile_user_for_x' => 'Usuário por :time',
@@ -297,13 +302,13 @@ return [
     'comment_save' => 'Salvar comentário',
     'comment_saving' => 'Salvando comentário...',
     'comment_deleting' => 'Removendo comentário...',
-    'comment_new' => 'Novo comentário',
+    'comment_new' => 'Novo Comentário',
     'comment_created' => 'comentado :createDiff',
     'comment_updated' => 'Editado :updateDiff por :username',
     'comment_deleted_success' => 'Comentário removido',
     'comment_created_success' => 'Comentário adicionado',
     'comment_updated_success' => 'Comentário editado',
-    'comment_delete_confirm' => 'Você tem certeza de que quer deletar este comentário?',
+    'comment_delete_confirm' => 'Você tem certeza de que deseja excluir este comentário?',
     'comment_in_reply_to' => 'Em resposta à :commentId',
 
     // Revision
index 150cb59112ba1503c56591d407ca2978ed13582c..c65d7f4969e14573eec31203e3f45a2dc955a0c8 100644 (file)
@@ -5,47 +5,52 @@
 return [
 
     // Permissions
-    'permission' => 'Você não tem permissões para acessar a página requerida.',
+    'permission' => 'Você não tem permissão para acessar a página requerida.',
     'permissionJson' => 'Você não tem permissão para realizar a ação requerida.',
 
     // Auth
     'error_user_exists_different_creds' => 'Um usuário com o e-mail :email já existe mas com credenciais diferentes.',
     'email_already_confirmed' => 'E-mail já foi confirmado. Tente efetuar o login.',
-    'email_confirmation_invalid' => 'Esse token de confirmação não é válido ou já foi utilizado. Por favor, tente efetuar o registro novamente.',
+    'email_confirmation_invalid' => 'Esse token de confirmação não é válido ou já foi utilizado. Por favor, tente cadastrar-se novamente.',
     'email_confirmation_expired' => 'O token de confirmação já expirou. Um novo e-mail foi enviado.',
+    'email_confirmation_awaiting' => 'O endereço de e-mail da conta em uso precisa ser confirmado',
     'ldap_fail_anonymous' => 'O acesso LDAP falhou ao tentar usar o anonymous bind',
     'ldap_fail_authed' => 'O acesso LDAP falhou ao tentar os detalhes do dn e senha fornecidos',
-    'ldap_extension_not_installed' => 'As extensões LDAP PHP não estão instaladas',
+    'ldap_extension_not_installed' => 'A extensão LDAP PHP não está instalada',
     'ldap_cannot_connect' => 'Não foi possível conectar ao servidor LDAP. Conexão inicial falhou',
+    'saml_already_logged_in' => 'Login já efetuado',
+    'saml_user_not_registered' => 'O usuário :name não está cadastrado e o cadastro automático está desativado',
+    'saml_no_email_address' => 'Não foi possível encontrar um endereço de e-mail para este usuário nos dados providos pelo sistema de autenticação externa',
+    'saml_invalid_response_id' => 'A requisição do sistema de autenticação externa não foi reconhecia por um processo iniciado por esta aplicação. Após o login, navegar para o caminho anterior pode causar um problema.',
+    'saml_fail_authed' => 'Login utilizando :system falhou. Sistema não forneceu autorização bem sucedida',
     'social_no_action_defined' => 'Nenhuma ação definida',
     'social_login_bad_response' => "Erro recebido durante o login :socialAccount: \n:error",
-    'social_account_in_use' => 'Essa conta :socialAccount já está em uso. Por favor, tente se logar usando a opção :socialAccount',
-    'social_account_email_in_use' => 'O e-mail :email já está e muso. Se você já tem uma conta você poderá se conectar a conta :socialAccount a partir das configurações de seu perfil.',
-    'social_account_existing' => 'Essa conta :socialAccount já está atrelada a esse perfil.',
-    'social_account_already_used_existing' => 'Essa conta :socialAccount já está sendo usada por outro usuário.',
-    'social_account_not_used' => 'Essa conta :socialAccount não está atrelada a nenhum usuário. Por favor, faça o link da conta com suas configurações de perfil. ',
-    'social_account_register_instructions' => 'Se você não tem uma conta, você poderá fazer o registro usando a opção :socialAccount',
+    'social_account_in_use' => 'Essa conta :socialAccount já está em uso. Por favor, tente entrar utilizando a opção :socialAccount.',
+    'social_account_email_in_use' => 'O e-mail :email já está euso. Se você já tem uma conta você poderá se conectar a conta :socialAccount a partir das configurações de seu perfil.',
+    'social_account_existing' => 'Essa conta :socialAccount já está vinculada a esse perfil.',
+    'social_account_already_used_existing' => 'Essa conta :socialAccount já está sendo utilizada por outro usuário.',
+    'social_account_not_used' => 'Essa conta :socialAccount não está vinculada a nenhum usuário. Por favor vincule a conta nas suas configurações de perfil. ',
+    'social_account_register_instructions' => 'Se você não tem uma conta, você poderá se cadastrar usando a opção :socialAccount.',
     'social_driver_not_found' => 'Social driver não encontrado',
     'social_driver_not_configured' => 'Seus parâmetros socials de :socialAccount não estão configurados corretamente.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Esse link de convite expirou. Alternativamente, você pode tentar redefinir a senha da sua conta.',
 
     // System
     'path_not_writable' => 'O caminho de destino (:filePath) de upload de arquivo não possui permissão de escrita. Certifique-se que ele possui direitos de escrita no servidor.',
-    'cannot_get_image_from_url' => 'Não foi possivel capturar a imagem a partir de :url',
+    'cannot_get_image_from_url' => 'Não foi possível obter a imagem a partir de :url',
     'cannot_create_thumbs' => 'O servidor não pôde criar as miniaturas de imagem. Por favor, verifique se a extensão GD PHP está instalada.',
     'server_upload_limit' => 'O servidor não permite o upload de arquivos com esse tamanho. Por favor, tente fazer o upload de arquivos de menor tamanho.',
     'uploaded'  => 'O servidor não permite o upload de arquivos com esse tamanho. Por favor, tente fazer o upload de arquivos de menor tamanho.',
     'image_upload_error' => 'Um erro aconteceu enquanto o servidor tentava efetuar o upload da imagem',
-    'image_upload_type_error' => 'O tipo de imagem que está sendo feito upload é inválido',
+    'image_upload_type_error' => 'O tipo de imagem que está sendo enviada é inválido',
     'file_upload_timeout' => 'O upload do arquivo expirou.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Erro de \'Page mismatch\' durante a atualização do anexo',
     'attachment_not_found' => 'Anexo não encontrado',
 
     // Pages
-    'page_draft_autosave_fail' => 'Falhou ao tentar salvar o rascunho. Certifique-se que a conexão de internet está funcional antes de tentar salvar essa página',
-    'page_custom_home_deletion' => 'Não pode deletar uma página que está definida como página inicial',
+    'page_draft_autosave_fail' => 'Falha ao tentar salvar o rascunho. Certifique-se que a conexão de internet está funcional antes de tentar salvar essa página',
+    'page_custom_home_deletion' => 'Não é possível excluir uma página que está definida como página inicial',
 
     // Entities
     'entity_not_found' => 'Entidade não encontrada',
@@ -54,18 +59,18 @@ return [
     'page_not_found' => 'Página não encontrada',
     'chapter_not_found' => 'Capítulo não encontrado',
     'selected_book_not_found' => 'O livro selecionado não foi encontrado',
-    'selected_book_chapter_not_found' => 'O Livro selecionado ou Capítulo não foi encontrado',
+    'selected_book_chapter_not_found' => 'O Livro ou Capítulo selecionado não foi encontrado',
     'guests_cannot_save_drafts' => 'Convidados não podem salvar rascunhos',
 
     // Users
-    'users_cannot_delete_only_admin' => 'Você não pode excluir o conteúdo, apenas o admin.',
+    'users_cannot_delete_only_admin' => 'Você não pode excluir o único admin',
     'users_cannot_delete_guest' => 'Você não pode excluir o usuário convidado',
 
     // Roles
-    'role_cannot_be_edited' => 'Esse perfil não pode ser editado',
-    'role_system_cannot_be_deleted' => 'Esse perfil é um perfil de sistema e não pode ser excluído',
-    'role_registration_default_cannot_delete' => 'Esse perfil não poderá se excluído enquando estiver registrado como o perfil padrão',
-    'role_cannot_remove_only_admin' => 'Este usuário é o único usuário atribuído ao perfil de administrador. Atribua o perfil de administrador a outro usuário antes de tentar removê-lo aqui.',
+    'role_cannot_be_edited' => 'Esse cargo não pode ser editado',
+    'role_system_cannot_be_deleted' => 'Esse cargo é um cargo do sistema e não pode ser excluído',
+    'role_registration_default_cannot_delete' => 'Esse cargo não poderá se excluído enquanto estiver registrado como o cargo padrão',
+    'role_cannot_remove_only_admin' => 'Este usuário é o único usuário vinculado ao cargo de administrador. Atribua o cargo de administrador a outro usuário antes de tentar removê-lo aqui.',
 
     // Comments
     'comment_list' => 'Ocorreu um erro ao buscar os comentários.',
@@ -75,11 +80,23 @@ return [
     'empty_comment' => 'Não é possível adicionar um comentário vazio.',
 
     // Error pages
-    '404_page_not_found' => 'Página não encontrada',
+    '404_page_not_found' => 'Página Não Encontrada',
     'sorry_page_not_found' => 'Desculpe, a página que você está procurando não pôde ser encontrada.',
-    'return_home' => 'Retornar à página principal',
-    'error_occurred' => 'Um erro ocorreu',
+    'sorry_page_not_found_permission_warning' => 'Se você esperava que esta página existisse, talvez você não tenha permissão para visualizá-la.',
+    'return_home' => 'Retornar à página inicial',
+    'error_occurred' => 'Ocorreu um Erro',
     'app_down' => ':appName está fora do ar no momento',
-    'back_soon' => 'Voltaremos em seguida.',
+    'back_soon' => 'Voltaremos em breve.',
+
+    // API errors
+    'api_no_authorization_found' => 'Nenhum token de autorização encontrado na requisição',
+    'api_bad_authorization_format' => 'Um token de autorização foi encontrado na requisição, mas o formato parece incorreto',
+    'api_user_token_not_found' => 'Nenhum token de API correspondente foi encontrado para o token de autorização fornecido',
+    'api_incorrect_token_secret' => 'O segredo fornecido para o token de API usado está incorreto',
+    'api_user_no_api_permission' => 'O proprietário do token de API utilizado não tem permissão para fazer requisições de API',
+    'api_user_token_expired' => 'O token de autenticação expirou',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Erro encontrado ao enviar um e-mail de teste:',
 
 ];
index 61a49f57a1f0d240ee5cc28dc0d396cdda5b27c5..fde9e2937e4bf04c0e784cd5f89da77be1fd046e 100644 (file)
@@ -6,10 +6,10 @@
  */
 return [
 
-    'password' => 'Senhas devem ter ao menos 6 caraceres e combinar com os atributos mínimos para a senha.',
+    'password' => 'Senhas devem ter ao menos oito caracteres e ser iguais à confirmação.',
     'user' => "Não pudemos encontrar um usuário com o e-mail fornecido.",
-    'token' => 'O token de reset de senha é inválido.',
-    'sent' => 'Enviamos para seu e-mail o link de reset de senha!',
-    'reset' => 'Sua senha foi resetada com sucesso!',
+    'token' => 'O token de redefinição de senha é inválido para este endereço de e-mail.',
+    'sent' => 'Enviamos o link de redefinição de senha para o seu e-mail!',
+    'reset' => 'Sua senha foi redefinida com sucesso!',
 
 ];
index 03283d752e8d4d1b8a020a65e0ab3b8c2aeb9a32..a970e023117ba617662a4407447cbaa0bc699faf 100644 (file)
@@ -9,131 +9,215 @@ return [
     // Common Messages
     'settings' => 'Configurações',
     'settings_save' => 'Salvar Configurações',
-    'settings_save_success' => 'Configurações Salvas',
+    'settings_save_success' => 'Configurações salvas',
 
     // App Settings
     'app_customization' => 'Customização',
     'app_features_security' => 'Recursos & Segurança',
     'app_name' => 'Nome da Aplicação',
     'app_name_desc' => 'Esse nome será mostrado no cabeçalho e em e-mails.',
-    'app_name_header' => 'Mostrar o nome da Aplicação no cabeçalho?',
+    'app_name_header' => 'Mostrar o nome no cabeçalho',
     'app_public_access' => 'Acesso Público',
     'app_public_access_desc' => 'Habilitar esta opção irá permitir que visitantes, que não estão logados, acessem o conteúdo em sua instância do BookStack.',
     'app_public_access_desc_guest' => 'O acesso de visitantes públicos pode ser controlado através do usuário "Convidado".',
     'app_public_access_toggle' => 'Permitir acesso público',
     'app_public_viewing' => 'Permitir visualização pública?',
-    'app_secure_images' => 'Permitir upload de imagens com maior segurança?',
-    'app_secure_images_toggle' => 'Habilitar uploads de imagem de maior segurança',
-    'app_secure_images_desc' => 'Por questões de performance, todas as imagens são públicas. Essa opção adiciona uma string randômica na frente da imagem. Certifique-se de que os índices do diretórios permitem o acesso fácil.',
+    'app_secure_images' => 'Upload de Imagens mais Seguro',
+    'app_secure_images_toggle' => 'Habilitar uploads de imagem mais seguro',
+    'app_secure_images_desc' => 'Por razões de performance, todas as imagens são públicas. Esta opção adiciona uma string randômica na frente das URLs de imagens. Certifique-se de que os diretórios não possam ser indexados para prevenir acesso indesejado.',
     'app_editor' => 'Editor de Página',
-    'app_editor_desc' => 'Selecione qual editor a ser usado pelos usuários para editar páginas.',
-    'app_custom_html' => 'Conteúdo para tag HTML HEAD customizado',
-    'app_custom_html_desc' => 'Quaisquer conteúdos aqui inseridos serão inseridos no final da seção <head> do HTML de cada página. Essa é uma maneira útil de sobrescrever estilos e adicionar códigos de análise de site.',
-    'app_custom_html_disabled_notice' => 'O conteúdo personalizado do head do HTML está desabilitado nesta página de configurações para garantir que quaisquer alterações significativas possam ser revertidas.',
+    'app_editor_desc' => 'Selecione qual editor será utilizado pelos usuários ao editar páginas.',
+    'app_custom_html' => 'Conteúdo customizado para <head> HTML',
+    'app_custom_html_desc' => 'Quaisquer conteúdos aqui adicionados serão inseridos no final da seção <head> de cada página. Essa é uma maneira útil de sobrescrever estilos e adicionar códigos de análise de site.',
+    'app_custom_html_disabled_notice' => 'O conteúdo customizado do <head> HTML está desabilitado nesta página de configurações, para garantir que quaisquer alterações danosas possam ser revertidas.',
     'app_logo' => 'Logo da Aplicação',
-    'app_logo_desc' => 'A imagem deve ter 43px de altura. <br>Imagens mais largas devem ser reduzidas.',
-    'app_primary_color' => 'Cor primária da Aplicação',
-    'app_primary_color_desc' => 'Esse valor deverá ser Hexadecimal. <br>Deixe em branco para que o Bookstack assuma a cor padrão.',
-    'app_homepage' => 'Página incial',
-    'app_homepage_desc' => 'Selecione a página para ser usada como página inicial em vez da padrão. Permissões da página serão ignoradas.',
+    'app_logo_desc' => 'A imagem deve ter 43px de altura. <br>Imagens maiores serão reduzidas.',
+    'app_primary_color' => 'Cor Primária da Aplicação',
+    'app_primary_color_desc' => 'Define a cor primária para a aplicação, incluindo o banner, botões e links.',
+    'app_homepage' => 'Página Inicial',
+    'app_homepage_desc' => 'Selecione uma opção para ser exibida como página inicial em vez da padrão. Permissões de página serão ignoradas para as páginas selecionadas.',
     'app_homepage_select' => 'Selecione uma página',
     'app_disable_comments' => 'Desativar Comentários',
     'app_disable_comments_toggle' => 'Desativar comentários',
-    'app_disable_comments_desc' => 'Desativar comentários em todas as páginas no aplicativo. Os comentários existentes não são exibidos.',
+    'app_disable_comments_desc' => 'Desativar comentários em todas as páginas no aplicativo.<br> Comentários existentes não serão exibidos.',
+
+    // Color settings
+    'content_colors' => 'Cores do Conteúdo',
+    'content_colors_desc' => 'Define as cores para todos os elementos da hierarquia de organização de páginas. Escolher cores com brilho similar ao das cores padrão é aconselhável para a legibilidade.',
+    'bookshelf_color' => 'Cor da Prateleira',
+    'book_color' => 'Cor do Livro',
+    'chapter_color' => 'Cor do Capítulo',
+    'page_color' => 'Cor da Página',
+    'page_draft_color' => 'Cor do Rascunho',
 
     // Registration Settings
-    'reg_settings' => 'Registro',
-    'reg_enable' => 'Habilitar Registro',
-    'reg_enable_toggle' => 'Habilitar registro',
-    'reg_enable_desc' => 'Quando o registro é habilitado, o usuário poderá se registrar como usuário do aplicativo. No registro, eles recebem um único perfil padrão.',
-    'reg_default_role' => 'Perfil padrão para usuários após o registro',
+    'reg_settings' => 'Cadastro',
+    'reg_enable' => 'Habilitar Cadastro',
+    'reg_enable_toggle' => 'Habilitar cadastro',
+    'reg_enable_desc' => 'Quando o cadastro é habilitado, visitantes poderão cadastrar-se como usuários do aplicativo. Realizado o cadastro, recebem um único cargo padrão.',
+    'reg_default_role' => 'Cargo padrão para usuários após o cadastro',
+    'reg_enable_external_warning' => 'A opção acima é ignorada enquanto a autenticação externa LDAP ou SAML estiver ativa. Contas de usuários para membros não existentes serão criadas automaticamente se a autenticação pelo sistema externo em uso for bem sucedida.',
     'reg_email_confirmation' => 'Confirmação de E-mail',
-    'reg_email_confirmation_toggle' => 'Requer confirmação de e-mail',
-    'reg_confirm_email_desc' => 'Se restrições de domínio são usadas a confirmação por e-mail será requerida e o valor abaixo será ignorado.',
-    'reg_confirm_restrict_domain' => 'Restringir registro ao domínio',
-    'reg_confirm_restrict_domain_desc' => 'Entre com uma lista de domínios de e-mails separados por vírgula para os quais você deseja restringir os registros. Será enviado um e-mail de confirmação para o usuário validar o e-mail antes de ser permitido interação com a aplicação. <br> Note que os usuários serão capazes de alterar o e-mail cadastrado após o sucesso na confirmação do registro.',
-    'reg_confirm_restrict_domain_placeholder' => 'Nenhuma restrição configurada',
+    'reg_email_confirmation_toggle' => 'Requerer confirmação de e-mail',
+    'reg_confirm_email_desc' => 'Em caso da restrição de domínios estar em uso, a confirmação de e-mail será requerida e essa opção será ignorada.',
+    'reg_confirm_restrict_domain' => 'Restrição de Domínios',
+    'reg_confirm_restrict_domain_desc' => 'Entre com uma lista separada por vírgulas de domínios de e-mails aos quais você deseja restringir o cadastro. Um e-mail de confirmação será enviado para o usuário validar seu endereço de e-mail antes de ser permitido a interagir com a aplicação. <br> Note que os usuários serão capazes de alterar o seus endereços de e-mail após o sucesso na confirmação do cadastro.',
+    'reg_confirm_restrict_domain_placeholder' => 'Nenhuma restrição definida',
 
     // Maintenance settings
     'maint' => 'Manutenção',
     'maint_image_cleanup' => 'Limpeza de Imagens',
-    'maint_image_cleanup_desc' => "Examina páginas & revisa o conteúdo para verificar quais imagens e desenhos estão atualmente em uso e quais imagens são redundantes. Certifique-se de criar um backup completo do banco de dados e imagens antes de executar isso.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignorar imagens em revisões',
+    'maint_image_cleanup_desc' => "Examina páginas e revisa seus conteúdos para verificar quais imagens e desenhos estão atualmente em uso e quais são redundantes. Certifique-se de criar um backup completo do banco de dados e imagens antes de executar esta ação.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Executar Limpeza',
     'maint_image_cleanup_warning' => ':count imagens potencialmente não utilizadas foram encontradas. Tem certeza de que deseja excluir estas imagens?',
     'maint_image_cleanup_success' => ':count imagens potencialmente não utilizadas foram encontradas e excluídas!',
     'maint_image_cleanup_nothing_found' => 'Nenhuma imagem não utilizada foi encontrada, nada foi excluído!',
+    'maint_send_test_email' => 'Enviar um E-mail de Teste',
+    'maint_send_test_email_desc' => 'Esta opção envia um e-mail de teste para o endereço especificado no seu perfil.',
+    'maint_send_test_email_run' => 'Enviar e-mail de teste',
+    'maint_send_test_email_success' => 'E-mail enviado para :address',
+    'maint_send_test_email_mail_subject' => 'E-mail de Teste',
+    'maint_send_test_email_mail_greeting' => 'O envio de e-mails parece funcionar!',
+    'maint_send_test_email_mail_text' => 'Parabéns! Já que você recebeu esta notificação, suas opções de e-mail parecem estar configuradas corretamente.',
+    'maint_recycle_bin_desc' => 'Prateleiras, livros, capítulos e páginas deletados são mandados para a lixeira podendo assim ser restaurados ou excluídos permanentemente. Itens mais antigos da lixeira podem vir a ser automaticamente removidos da lixeira após um tempo dependendo da configuração do sistema.',
+    'maint_recycle_bin_open' => 'Abrir Lixeira',
+
+    // Recycle Bin
+    'recycle_bin' => 'Lixeira',
+    'recycle_bin_desc' => 'Aqui você pode restaurar itens que foram excluídos ou escolher removê-los permanentemente do sistema. Esta lista não é filtrada diferentemente de listas de atividades similares no sistema onde filtros de permissão são aplicados.',
+    'recycle_bin_deleted_item' => 'Item excluído',
+    'recycle_bin_deleted_by' => 'Excluído por',
+    'recycle_bin_deleted_at' => 'Momento de Exclusão',
+    'recycle_bin_permanently_delete' => 'Excluir permanentemente',
+    'recycle_bin_restore' => 'Restaurar',
+    'recycle_bin_contents_empty' => 'A lixeira está vazia',
+    'recycle_bin_empty' => 'Esvaziar Lixeira',
+    'recycle_bin_empty_confirm' => 'Isso irá destruir permanentemente todos os itens na lixeira inclusive o conteúdo de cada item. Tem certeza de que quer esvaziar a lixeira?',
+    'recycle_bin_destroy_confirm' => 'Esta ação irá excluir permanentemente do sistema este item junto com todos os elementos filhos listados abaixo. Você não poderá restaurar esse conteúdo. Tem certeza de que deseja excluir permanentemente este item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'Esta ação irá restaurar o item excluído, inclusive quaisquer elementos filhos, para seu local original. Se a localização original tiver, entretanto, sido eliminada e estiver agora na lixeira, o item pai também precisará ser restaurado.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Registro de auditoria',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'Sem filtro',
+    'audit_deleted_item' => 'Item excluído',
+    'audit_deleted_item_name' => 'Nome: :name',
+    'audit_table_user' => 'Usuário',
+    'audit_table_event' => 'Evento',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Data da Atividade',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
 
     // Role Settings
-    'roles' => 'Perfis',
-    'role_user_roles' => 'Perfis de Usuário',
-    'role_create' => 'Criar novo Perfil',
-    'role_create_success' => 'Perfil criado com sucesso',
-    'role_delete' => 'Excluir Perfil',
-    'role_delete_confirm' => 'A ação vai excluír o Perfil de nome \':roleName\'.',
-    'role_delete_users_assigned' => 'Esse Perfil tem :userCount usuários assinalados a ele. Se quiser migrar usuários desse Perfil para outro, selecione um novo Perfil.',
+    'roles' => 'Cargos',
+    'role_user_roles' => 'Cargos de Usuário',
+    'role_create' => 'Criar novo Cargo',
+    'role_create_success' => 'Cargo criado com sucesso',
+    'role_delete' => 'Excluir Cargo',
+    'role_delete_confirm' => 'A ação vai excluír o cargo de nome \':roleName\'.',
+    'role_delete_users_assigned' => 'Esse cargo tem :userCount usuários vinculados a ele. Se quiser migrar usuários desse cargo para outro, selecione um novo cargo.',
     'role_delete_no_migration' => "Não migre os usuários",
-    'role_delete_sure' => 'Tem certeza que deseja excluir esse Perfil?',
-    'role_delete_success' => 'Perfil excluído com sucesso',
-    'role_edit' => 'Editar Perfil',
-    'role_details' => 'Detalhes do Perfil',
-    'role_name' => 'Nome do Perfil',
-    'role_desc' => 'Descrição Curta do Perfil',
+    'role_delete_sure' => 'Tem certeza que deseja excluir esse cargo?',
+    'role_delete_success' => 'Cargo excluído com sucesso',
+    'role_edit' => 'Editar Cargo',
+    'role_details' => 'Detalhes do Cargo',
+    'role_name' => 'Nome do Cargo',
+    'role_desc' => 'Breve Descrição do Cargo',
     'role_external_auth_id' => 'IDs de Autenticação Externa',
     'role_system' => 'Permissões do Sistema',
-    'role_manage_users' => 'Gerenciar Usuários',
-    'role_manage_roles' => 'Gerenciar Perfis & Permissões de Perfis',
+    'role_manage_users' => 'Gerenciar usuários',
+    'role_manage_roles' => 'Gerenciar cargos e permissões de cargos',
     'role_manage_entity_permissions' => 'Gerenciar todos os livros, capítulos e permissões de páginas',
     'role_manage_own_entity_permissions' => 'Gerenciar permissões de seu próprio livro, capítulo e paginas',
-    'role_manage_page_templates' => 'Manage page templates',
-    'role_manage_settings' => 'Gerenciar configurações de app',
+    'role_manage_page_templates' => 'Gerenciar modelos de página',
+    'role_access_api' => 'Acessar API do sistema',
+    'role_manage_settings' => 'Gerenciar configurações da aplicação',
     'role_asset' => 'Permissões de Ativos',
+    'roles_system_warning' => 'Esteja ciente de que o acesso a qualquer uma das três permissões acima pode permitir que um usuário altere seus próprios privilégios ou privilégios de outros usuários no sistema. Apenas atribua cargos com essas permissões para usuários confiáveis.',
     'role_asset_desc' => 'Essas permissões controlam o acesso padrão para os ativos dentro do sistema. Permissões em Livros, Capítulos e Páginas serão sobrescritas por essas permissões.',
-    'role_asset_admins' => 'Administradores recebem automaticamente acesso a todo o conteúdo, mas essas opções podem mostrar ou ocultar as opções da UI.',
+    'role_asset_admins' => 'Administradores recebem automaticamente acesso a todo o conteúdo, mas essas opções podem mostrar ou ocultar as opções da Interface de Usuário.',
     'role_all' => 'Todos',
     'role_own' => 'Próprio',
-    'role_controlled_by_asset' => 'Controlado pelos ativos que você fez upload',
-    'role_save' => 'Salvar Perfil',
-    'role_update_success' => 'Perfil atualizado com sucesso',
-    'role_users' => 'Usuários neste Perfil',
-    'role_users_none' => 'Nenhum usuário está atualmente atrelado a esse Perfil',
+    'role_controlled_by_asset' => 'Controlado pelos ativos nos quais o upload foi realizado',
+    'role_save' => 'Salvar Cargo',
+    'role_update_success' => 'Cargo atualizado com sucesso',
+    'role_users' => 'Usuários com este cargo',
+    'role_users_none' => 'Nenhum usuário está atualmente vinculado a este cargo',
 
     // Users
     'users' => 'Usuários',
     'user_profile' => 'Perfil do Usuário',
     'users_add_new' => 'Adicionar Novo Usuário',
     'users_search' => 'Pesquisar Usuários',
+    'users_latest_activity' => 'Latest Activity',
     'users_details' => 'Detalhes do Usuário',
     'users_details_desc' => 'Defina um nome de exibição e um endereço de e-mail para este usuário. O endereço de e-mail será usado para fazer login na aplicação.',
     'users_details_desc_no_email' => 'Defina um nome de exibição para este usuário para que outros usuários possam reconhecê-lo',
-    'users_role' => 'Perfis do Usuário',
-    'users_role_desc' => 'Selecione os perfis para os quais este usuário será atribuído. Se um usuário for atribuído a multiplos perfis, as permissões destes perfis serão empilhadas e eles receberão todas as habilidades dos perfis atribuídos.',
+    'users_role' => 'Cargos do Usuário',
+    'users_role_desc' => 'Selecione os cargos aos quais este usuário será vinculado. Se um usuário for vinculado a múltiplos cargos, suas permissões serão empilhadas e ele receberá todas as habilidades dos cargos atribuídos.',
     'users_password' => 'Senha do Usuário',
-    'users_password_desc' => 'Defina uma senha usada para fazer login na aplicação. Esta deve ter pelo menos 5 caracteres.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
+    'users_password_desc' => 'Defina uma senha usada para fazer login na aplicação. Esta deve ter pelo menos 6 caracteres.',
+    'users_send_invite_text' => 'Você pode escolher enviar a este usuário um convite por e-mail que o possibilitará definir sua própria senha, ou defina você uma senha.',
+    'users_send_invite_option' => 'Enviar convite por e-mail',
     'users_external_auth_id' => 'ID de Autenticação Externa',
-    'users_external_auth_id_desc' => 'Este é o ID usado para corresponder a este usuário ao se comunicar com seu sistema LDAP.',
-    'users_password_warning' => 'Preencha os dados abaixo caso queira modificar a sua senha:',
-    'users_system_public' => 'Esse usuário representa quaisquer convidados que visitam o aplicativo. Ele não pode ser usado para login.',
+    'users_external_auth_id_desc' => 'Este ID é usado para relacionar o usuário quando comunicando com algum sistema de autenticação externo.',
+    'users_password_warning' => 'Apenas preencha os dados abaixo caso queira modificar a sua senha.',
+    'users_system_public' => 'Esse usuário representa quaisquer convidados que visitam o aplicativo. Ele não pode ser usado para login mas é automaticamente atribuído.',
     'users_delete' => 'Excluir Usuário',
     'users_delete_named' => 'Excluir :userName',
     'users_delete_warning' => 'A ação vai excluir completamente o usuário de nome \':userName\' do sistema.',
     'users_delete_confirm' => 'Tem certeza que deseja excluir esse usuário?',
-    'users_delete_success' => 'Usuários excluídos com sucesso',
-    'users_edit' => 'Editar usuário',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
+    'users_edit' => 'Editar Usuário',
     'users_edit_profile' => 'Editar Perfil',
     'users_edit_success' => 'Usuário atualizado com sucesso',
     'users_avatar' => 'Imagem de Usuário',
-    'users_avatar_desc' => 'Essa imagem deve ser um quadrado com aproximadamente 256px de altura e largura.',
+    'users_avatar_desc' => 'Defina uma imagem para representar este usuário. Essa imagem deve ser um quadrado com aproximadamente 256px de altura e largura.',
     'users_preferred_language' => 'Linguagem de Preferência',
-    'users_preferred_language_desc' => 'Esta opção irá alterar o idioma usado para a interface de usuário da aplicação. Isto não afetará nenhum conteúdo criado pelo usuário.',
+    'users_preferred_language_desc' => 'Esta opção irá alterar o idioma utilizado para a interface de usuário da aplicação. Isto não afetará nenhum conteúdo criado por usuários.',
     'users_social_accounts' => 'Contas Sociais',
     'users_social_accounts_info' => 'Aqui você pode conectar outras contas para acesso mais rápido. Desconectar uma conta não retira a possibilidade de acesso usando-a. Para revogar o acesso ao perfil através da conta social, você deverá fazê-lo na sua conta social.',
-    'users_social_connect' => 'Contas conectadas',
+    'users_social_connect' => 'Contas Conectadas',
     'users_social_disconnect' => 'Desconectar Conta',
     'users_social_connected' => 'Conta :socialAccount foi conectada com sucesso ao seu perfil.',
     'users_social_disconnected' => 'Conta :socialAccount foi desconectada com sucesso de seu perfil.',
+    'users_api_tokens' => 'Tokens de API',
+    'users_api_tokens_none' => 'Nenhum token de API foi criado para este usuário',
+    'users_api_tokens_create' => 'Criar Token',
+    'users_api_tokens_expires' => 'Expira',
+    'users_api_tokens_docs' => 'Documentação da API',
+
+    // API Tokens
+    'user_api_token_create' => 'Criar Token de API',
+    'user_api_token_name' => 'Nome',
+    'user_api_token_name_desc' => 'Dê ao seu token um nome legível como um futuro lembrete de seu propósito.',
+    'user_api_token_expiry' => 'Data de Expiração',
+    'user_api_token_expiry_desc' => 'Defina uma data em que este token expira. Depois desta data, as requisições feitas usando este token não funcionarão mais. Deixar este campo em branco definirá um prazo de 100 anos futuros.',
+    'user_api_token_create_secret_message' => 'Imediatamente após a criação deste token, um "ID de token" e "Secreto de token" serão gerados e exibidos. O segredo só será mostrado uma única vez, portanto, certifique-se de copiar o valor para algum lugar seguro antes de prosseguir.',
+    'user_api_token_create_success' => 'Token de API criado com sucesso',
+    'user_api_token_update_success' => 'Token de API atualizado com sucesso',
+    'user_api_token' => 'Token de API',
+    'user_api_token_id' => 'ID do Token',
+    'user_api_token_id_desc' => 'Este é um identificador de sistema não editável, gerado para este token, que precisará ser fornecido em solicitações de API.',
+    'user_api_token_secret' => 'Segredo do Token',
+    'user_api_token_secret_desc' => 'Este é um segredo de sistema gerado para este token que precisará ser fornecido em requisições de API. Isto só será mostrado nesta única vez, portanto, copie este valor para um lugar seguro.',
+    'user_api_token_created' => 'Token Criado :timeAgo',
+    'user_api_token_updated' => 'Token Atualizado :timeAgo',
+    'user_api_token_delete' => 'Excluir Token',
+    'user_api_token_delete_warning' => 'Isto irá excluir completamente este token de API com o nome \':tokenName\' do sistema.',
+    'user_api_token_delete_confirm' => 'Você tem certeza que deseja excluir este token de API?',
+    'user_api_token_delete_success' => 'Token de API excluído com sucesso',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index c087fd0ab129c66f51b17bcab94501b80eb2a77b..9777eacac5c5ff756f94a776437fee19ed9a48d2 100644 (file)
@@ -8,62 +8,62 @@
 return [
 
     // Standard laravel validation lines
-    'accepted'             => 'O :attribute deve ser aceito.',
-    'active_url'           => 'O :attribute não é uma URL válida.',
-    'after'                => 'O :attribute deve ser uma data posterior à data :date.',
-    'alpha'                => 'O :attribute deve conter apenas letras.',
-    'alpha_dash'           => 'O :attribute deve conter apenas letras, números e traços.',
-    'alpha_num'            => 'O :attribute deve conter apenas letras e números.',
-    'array'                => 'O :attribute deve ser uma array.',
-    'before'               => 'O :attribute deve ser uma data anterior à data :date.',
+    'accepted'             => 'O campo :attribute deve ser aceito.',
+    'active_url'           => 'O campo :attribute não é uma URL válida.',
+    'after'                => 'O campo :attribute deve ser uma data posterior à data :date.',
+    'alpha'                => 'O campo :attribute deve conter apenas letras.',
+    'alpha_dash'           => 'O campo :attribute deve conter apenas letras, números, traços e underlines.',
+    'alpha_num'            => 'O campo :attribute deve conter apenas letras e números.',
+    'array'                => 'O campo :attribute deve ser uma array.',
+    'before'               => 'O campo :attribute deve ser uma data anterior à data :date.',
     'between'              => [
-        'numeric' => 'O :attribute deve ter tamanho entre :min e :max.',
-        'file'    => 'O :attribute deve ter entre :min e :max kilobytes.',
-        'string'  => 'O :attribute deve ter entre :min e :max caracteres.',
-        'array'   => 'O :attribute deve ter entre :min e :max itens.',
+        'numeric' => 'O campo :attribute deve estar entre :min e :max.',
+        'file'    => 'O campo :attribute deve ter entre :min e :max kilobytes.',
+        'string'  => 'O campo :attribute deve ter entre :min e :max caracteres.',
+        'array'   => 'O campo :attribute deve ter entre :min e :max itens.',
     ],
     'boolean'              => 'O campo :attribute deve ser verdadeiro ou falso.',
-    'confirmed'            => 'O campo :attribute de confirmação não é igual.',
+    'confirmed'            => 'O campo :attribute não é igual à sua confirmação.',
     'date'                 => 'O campo :attribute não está em um formato de data válido.',
     'date_format'          => 'O campo :attribute não tem a formatação :format.',
     'different'            => 'O campo :attribute e o campo :other devem ser diferentes.',
     'digits'               => 'O campo :attribute deve ter :digits dígitos.',
     'digits_between'       => 'O campo :attribute deve ter entre :min e :max dígitos.',
     'email'                => 'O campo :attribute deve ser um e-mail válido.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => 'O campo :attribute deve terminar com um dos seguintes: :values',
     'filled'               => 'O campo :attribute é requerido.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => 'O campo :attribute deve ser maior que :value.',
+        'file'    => 'O campo :attribute deve ser maior que :value kilobytes.',
+        'string'  => 'O campo :attribute deve ser maior que :value caracteres.',
+        'array'   => 'O campo :attribute deve ter mais que :value itens.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => 'O campo :attribute deve ser maior ou igual a :value.',
+        'file'    => 'O campo :attribute deve ser maior ou igual a :value kilobytes.',
+        'string'  => 'O campo :attribute deve ser maior ou igual a :value caracteres.',
+        'array'   => 'O campo :attribute deve ter :value itens ou mais.',
     ],
-    'exists'               => 'O atributo :attribute selecionado não é válido.',
+    'exists'               => 'O campo :attribute selecionado não é válido.',
     'image'                => 'O campo :attribute deve ser uma imagem.',
-    'image_extension'      => 'O campo :attribute deve ter uma extensão de imagem válida & suportada.',
-    'in'                   => 'The selected :attribute is invalid.',
+    'image_extension'      => 'O campo :attribute deve ter uma extensão de imagem válida e suportada.',
+    'in'                   => 'O campo :attribute selecionado não é válido.',
     'integer'              => 'O campo :attribute deve ser um número inteiro.',
-    'ip'                   => 'O campo :attribute deve ser um IP válido.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ip'                   => 'O campo :attribute deve ser um endereço IP válido.',
+    'ipv4'                 => 'O campo :attribute deve ser um endereço IPv4 válido.',
+    'ipv6'                 => 'O campo :attribute deve ser um endereço IPv6 válido.',
+    'json'                 => 'O campo :attribute deve ser uma string JSON válida.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => 'O campo :attribute deve ser menor que :value.',
+        'file'    => 'O campo :attribute deve ser menor que :value kilobytes.',
+        'string'  => 'O campo :attribute deve ser menor que :value caracteres.',
+        'array'   => 'O campo :attribute deve conter menos que :value itens.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => 'O campo :attribute deve ser menor ou igual a :value.',
+        'file'    => 'O campo :attribute deve ser menor ou igual a :value kilobytes.',
+        'string'  => 'O campo :attribute deve ser menor ou igual a :value caracteres.',
+        'array'   => 'O campo :attribute não deve conter mais que :value itens.',
     ],
     'max'                  => [
         'numeric' => 'O valor para o campo :attribute não deve ser maior que :max.',
@@ -71,16 +71,16 @@ return [
         'string'  => 'O valor para o campo :attribute não deve ter mais que :max caracteres.',
         'array'   => 'O valor para o campo :attribute não deve ter mais que :max itens.',
     ],
-    'mimes'                => 'O :attribute deve ser do tipo type: :values.',
+    'mimes'                => 'O campo :attribute deve ser do tipo type: :values.',
     'min'                  => [
-        'numeric' => 'O valor para o campo :attribute não deve ser menor que :min.',
-        'file'    => 'O valor para o campo :attribute não deve ter tamanho menor que :min kilobytes.',
-        'string'  => 'O valor para o campo :attribute não deve ter menos que :min caracteres.',
-        'array'   => 'O valor para o campo :attribute não deve ter menos que :min itens.',
+        'numeric' => 'O campo :attribute não deve ser menor que :min.',
+        'file'    => 'O campo :attribute não deve ter tamanho menor que :min kilobytes.',
+        'string'  => 'O campo :attribute não deve ter menos que :min caracteres.',
+        'array'   => 'O campo :attribute não deve ter menos que :min itens.',
     ],
     'no_double_extension'  => 'O campo :attribute deve ter apenas uma extensão de arquivo.',
     'not_in'               => 'O campo selecionado :attribute é inválido.',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => 'O formato do campo :attribute é inválido.',
     'numeric'              => 'O campo :attribute deve ser um número.',
     'regex'                => 'O formato do campo :attribute é inválido.',
     'required'             => 'O campo :attribute é requerido.',
@@ -90,6 +90,7 @@ return [
     'required_without'     => 'O campo :attribute é requerido quando os valores :values não estiverem presentes.',
     'required_without_all' => 'O campo :attribute é requerido quando nenhum dos valores :values estiverem presentes.',
     'same'                 => 'O campo :attribute e o campo :other devem ser iguais.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
         'numeric' => 'O tamanho do campo :attribute deve ser :size.',
         'file'    => 'O tamanho do arquivo :attribute deve ser de :size kilobytes.',
index 8be36b8bd2459f6d6faee1234de3c7e2c0495bd4..af5a7af1d93fc7e8d8c4a91ccf593c5fc854fa22 100644 (file)
@@ -18,7 +18,7 @@ return [
 
     // Chapters
     'chapter_create'              => 'создал главу',
-    'chapter_create_notification' => 'глава успешно создана',
+    'chapter_create_notification' => 'Ð\93лава успешно создана',
     'chapter_update'              => 'обновил главу',
     'chapter_update_notification' => 'Глава успешно обновлена',
     'chapter_delete'              => 'удалил главу',
@@ -36,13 +36,14 @@ return [
     'book_sort_notification'      => 'Книга успешно отсортирована',
 
     // Bookshelves
-    'bookshelf_create'            => 'created Bookshelf',
-    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
-    'bookshelf_update'                 => 'updated bookshelf',
-    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
-    'bookshelf_delete'                 => 'deleted bookshelf',
-    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+    'bookshelf_create'            => 'создал полку',
+    'bookshelf_create_notification'    => 'Полка успешно создана',
+    'bookshelf_update'                 => 'обновил полку',
+    'bookshelf_update_notification'    => 'Полка успешно обновлена',
+    'bookshelf_delete'                 => 'удалил полку',
+    'bookshelf_delete_notification'    => 'Полка успешно удалена',
 
     // Other
     'commented_on'                => 'прокомментировал',
+    'permissions_update'          => 'обновил разрешения',
 ];
index e493ec1972aa193b9fa3375532dcc73e195c39c7..1f0ec6b802d707e4f41c16f3120619bfce72f6a1 100644 (file)
@@ -7,7 +7,7 @@
 return [
 
     'failed' => 'Учетная запись не найдена.',
-    'throttle' => 'СлиÑ\88ком Ð¼Ð½Ð¾Ð³Ð¾ Ð¿Ð¾Ð¿Ñ\8bÑ\82ок Ð²Ñ\85ода. Ð\9fожалÑ\83йÑ\81Ñ\82а, Ð¿Ð¾Ð¿Ñ\80обÑ\83йÑ\82е Ð¿Ð¾Ð·Ð¶Ðµ через :seconds секунд.',
+    'throttle' => 'СлиÑ\88ком Ð¼Ð½Ð¾Ð³Ð¾ Ð¿Ð¾Ð¿Ñ\8bÑ\82ок Ð²Ñ\85ода. Ð\9fожалÑ\83йÑ\81Ñ\82а, Ð¿Ð¾Ð²Ñ\82оÑ\80иÑ\82е Ð¿Ð¾Ð¿Ñ\8bÑ\82кÑ\83 через :seconds секунд.',
 
     // Login & Register
     'sign_up' => 'Регистрация',
@@ -18,13 +18,13 @@ return [
 
     'name' => 'Имя',
     'username' => 'Логин',
-    'email' => 'Email',
+    'email' => 'Адрес электронной почты',
     'password' => 'Пароль',
     'password_confirm' => 'Подтверждение пароля',
-    'password_hint' => 'Ð\94олжен Ð±Ñ\8bÑ\82Ñ\8c Ð±Ð¾Ð»Ñ\8cÑ\88е 7 символов',
+    'password_hint' => 'Ð\9cинимÑ\83м 8 символов',
     'forgot_password' => 'Забыли пароль?',
     'remember_me' => 'Запомнить меня',
-    'ldap_email_hint' => 'Введите email адрес для данной учетной записи.',
+    'ldap_email_hint' => 'Введите адрес электронной почты для этой учетной записи.',
     'create_account' => 'Создать аккаунт',
     'already_have_account' => 'Уже есть аккаунт?',
     'dont_have_account' => 'У вас нет аккаунта?',
@@ -41,11 +41,11 @@ return [
 
     // Password Reset
     'reset_password' => 'Сброс пароля',
-    'reset_password_send_instructions' => 'Введите свой email ниже, и вам будет отправлено письмо со ссылкой для сброса пароля.',
-    'reset_password_send_button' => 'Ð\9eÑ\82пÑ\80авиÑ\82Ñ\8c Ñ\81Ñ\81Ñ\8bлкÑ\83 Ð´Ð»Ñ\8f Ñ\81бÑ\80оÑ\81а',
-    'reset_password_sent_success' => 'Ссылка для сброса была отправлена на :email.',
+    'reset_password_send_instructions' => 'Введите свой адрес электронной почты ниже, и вам будет отправлено письмо со ссылкой для сброса пароля.',
+    'reset_password_send_button' => 'СбÑ\80оÑ\81иÑ\82Ñ\8c Ð¿Ð°Ñ\80олÑ\8c',
+    'reset_password_sent' => 'Ссылка для сброса пароля будет выслана на :email, если этот адрес находится в системе.',
     'reset_password_success' => 'Ваш пароль был успешно сброшен.',
-    'email_reset_subject' => 'Сбросить ваш :appName пароль',
+    'email_reset_subject' => 'Сброс пароля от :appName',
     'email_reset_text' => 'Вы получили это письмо, потому что запросили сброс пароля для вашей учетной записи.',
     'email_reset_not_requested' => 'Если вы не запрашивали сброса пароля, то никаких дополнительных действий не требуется.',
 
@@ -53,23 +53,23 @@ return [
     // Email Confirmation
     'email_confirm_subject' => 'Подтвердите ваш почтовый адрес на :appName',
     'email_confirm_greeting' => 'Благодарим за участие :appName!',
-    'email_confirm_text' => 'Пожалуйста, подтвердите ваш email адрес кликнув на кнопку ниже:',
-    'email_confirm_action' => 'Подтвердить email',
+    'email_confirm_text' => 'Пожалуйста, подтвердите свой адрес электронной почты нажав на кнопку ниже:',
+    'email_confirm_action' => 'Подтвердить адрес электронной почты',
     'email_confirm_send_error' => 'Требуется подтверждение электронной почты, но система не может отправить письмо. Свяжитесь с администратором, чтобы убедиться, что адрес электронной почты настроен правильно.',
-    'email_confirm_success' => 'Ваш email был подтвержден!',
+    'email_confirm_success' => 'Ваш адрес подтвержден!',
     'email_confirm_resent' => 'Письмо с подтверждение выслано снова. Пожалуйста, проверьте ваш почтовый ящик.',
 
-    'email_not_confirmed' => 'email не подтвержден',
+    'email_not_confirmed' => 'Адрес электронной почты не подтвержден',
     'email_not_confirmed_text' => 'Ваш email адрес все еще не подтвержден.',
     'email_not_confirmed_click_link' => 'Пожалуйста, нажмите на ссылку в письме, которое было отправлено при регистрации.',
-    'email_not_confirmed_resend' => 'Ð\95Ñ\81ли Ð²Ñ\8b Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ\82е Ð½Ð°Ð¹Ñ\82и Ñ\8dлекÑ\82Ñ\80онное Ð¿Ð¸Ñ\81Ñ\8cмо, Ð²Ñ\8b Ð¼Ð¾Ð¶ÐµÑ\82е Ñ\81нова Ð¾Ñ\82пÑ\80авиÑ\82Ñ\8c Ð¿Ð¸Ñ\81Ñ\8cмо с подтверждением по форме ниже.',
+    'email_not_confirmed_resend' => 'Ð\95Ñ\81ли Ð²Ñ\8b Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ\82е Ð½Ð°Ð¹Ñ\82и Ñ\8dлекÑ\82Ñ\80онное Ð¿Ð¸Ñ\81Ñ\8cмо, Ð²Ñ\8b Ð¼Ð¾Ð¶ÐµÑ\82е Ñ\81нова Ð¾Ñ\82пÑ\80авиÑ\82Ñ\8c ÐµÐ³о с подтверждением по форме ниже.',
     'email_not_confirmed_resend_button' => 'Переотправить письмо с подтверждением',
 
     // User Invite
     'user_invite_email_subject' => 'Вас приглашают присоединиться к :appName!',
     'user_invite_email_greeting' => 'Для вас создан аккаунт в :appName.',
     'user_invite_email_text' => 'Нажмите кнопку ниже, чтобы задать пароль и получить доступ:',
-    'user_invite_email_action' => 'УÑ\81Ñ\82ановиÑ\82Ñ\8c Ð¿Ð°Ñ\80олÑ\8c Ð°ÐºÐºÐ°Ñ\83нÑ\82Ñ\83.',
+    'user_invite_email_action' => 'УÑ\81Ñ\82ановиÑ\82Ñ\8c Ð¿Ð°Ñ\80олÑ\8c Ð´Ð»Ñ\8f Ð°ÐºÐºÐ°Ñ\83нÑ\82а',
     'user_invite_page_welcome' => 'Добро пожаловать в :appName!',
     'user_invite_page_text' => 'Завершите настройку аккаунта, установите пароль для дальнейшего входа в :appName.',
     'user_invite_page_confirm_button' => 'Подтвердите пароль',
index 9ead2736ae6f2995e6f452427315da5f95e24e9c..12eac6912df6f4ead8089965516d8c90ad58527b 100644 (file)
@@ -33,15 +33,17 @@ return [
     'copy' => 'Скопировать',
     'reply' => 'Ответить',
     'delete' => 'Удалить',
+    'delete_confirm' => 'Подтвердить удаление',
     'search' => 'Поиск',
     'search_clear' => 'Очистить поиск',
     'reset' => 'Сбросить',
     'remove' => 'Удалить',
     'add' => 'Добавить',
+    'fullscreen' => 'На весь экран',
 
     // Sort Options
     'sort_options' => 'Параметры сортировки',
-    'sort_direction_toggle' => 'Переключить направления сортировки',
+    'sort_direction_toggle' => 'Переключить направление сортировки',
     'sort_ascending' => 'По возрастанию',
     'sort_descending' => 'По убыванию',
     'sort_name' => 'По имени',
@@ -63,14 +65,16 @@ return [
 
     // Header
     'profile_menu' => 'Меню профиля',
-    'view_profile' => 'Просмотреть профиль',
+    'view_profile' => 'Посмотреть профиль',
     'edit_profile' => 'Редактировать профиль',
+    'dark_mode' => 'Темный режим',
+    'light_mode' => 'Светлый режим',
 
     // Layout tabs
     'tab_info' => 'Информация',
     'tab_content' => 'Содержание',
 
     // Email Content
-    'email_action_help' => 'Ð\95Ñ\81ли Ñ\83 Ð²Ð°Ñ\81 Ð²Ð¾Ð·Ð½Ð¸ÐºÐ»Ð¸ Ð¿Ñ\80облемÑ\8b Ñ\81 Ð½Ð°Ð¶Ð°Ñ\82ием ÐºÐ½Ð¾Ð¿ÐºÐ¸ \':actionText\', Ñ\82о Ñ\81копиÑ\80Ñ\83йÑ\82е Ð¸ Ð²Ñ\81Ñ\82авÑ\8cÑ\82е Ñ\83казаннÑ\8bй URL-адÑ\80еÑ\81 Ð² Ñ\81вой Ð²ÐµÐ±-бÑ\80аÑ\83зеÑ\80:',
-    'email_rights' => 'Ð\92Ñ\81е Ð¿Ñ\80ава Ð·Ð°Ñ\80езеÑ\80виÑ\80ованы',
+    'email_action_help' => 'Если у вас возникли проблемы с нажатием кнопки \':actionText\', то скопируйте и вставьте указанный URL-адрес в свой браузер:',
+    'email_rights' => 'Ð\92Ñ\81е Ð¿Ñ\80ава Ð·Ð°Ñ\89иÑ\89ены',
 ];
index e9481f791466520021fedc76a7aa5593b0bb3752..12b1dd7cfbdd13723381068facf0aa7a66650953 100644 (file)
@@ -7,20 +7,20 @@ return [
     // Image Manager
     'image_select' => 'Выбрать изображение',
     'image_all' => 'Все',
-    'image_all_title' => 'Простмотр всех изображений',
+    'image_all_title' => 'Просмотр всех изображений',
     'image_book_title' => 'Просмотр всех изображений, загруженных в эту книгу',
     'image_page_title' => 'Просмотр всех изображений, загруженных на эту страницу',
-    'image_search_hint' => 'Ð\9fоиÑ\81к Ð¿Ð¾ Ð¸Ð¼ÐµÐ½Ð¸ изображения',
-    'image_uploaded' => 'Ð\97агÑ\80Ñ\83женно :uploadedDate',
+    'image_search_hint' => 'Ð\9fоиÑ\81к Ð¿Ð¾ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ\8e изображения',
+    'image_uploaded' => 'Загружено :uploadedDate',
     'image_load_more' => 'Загрузить еще',
-    'image_image_name' => 'Ð\98мÑ\8f изображения',
+    'image_image_name' => 'Ð\9dазвание изображения',
     'image_delete_used' => 'Это изображение используется на странице ниже.',
-    'image_delete_confirm' => 'Нажмите \'Удалить\' еще раз для подтверждения удаления.',
+    'image_delete_confirm_text' => 'Вы уверены, что хотите удалить это изображение?',
     'image_select_image' => 'Выбрать изображение',
     'image_dropzone' => 'Перетащите изображение или кликните для загрузки',
     'images_deleted' => 'Изображения удалены',
-    'image_preview' => 'Предосмотр изображения',
-    'image_upload_success' => 'Изображение загружено успешно',
+    'image_preview' => 'Ð\9fÑ\80едпÑ\80оÑ\81моÑ\82Ñ\80 Ð¸Ð·Ð¾Ð±Ñ\80ажениÑ\8f',
+    'image_upload_success' => 'Изображение успешно загружено',
     'image_update_success' => 'Детали изображения успешно обновлены',
     'image_delete_success' => 'Изображение успешно удалено',
     'image_upload_remove' => 'Удалить изображение',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Изменить код',
     'code_language' => 'Язык кода',
     'code_content' => 'Содержимое кода',
+    'code_session_history' => 'История сессии',
     'code_save' => 'Сохранить код',
 ];
index 475b454a0c4be688cb01f4dbe110b28b92398ded..e78ffde6563acbd8cce09ef059a1616656f4a920 100644 (file)
@@ -16,12 +16,13 @@ return [
     'recently_viewed' => 'Недавно просмотренные',
     'recent_activity' => 'Недавние действия',
     'create_now' => 'Создать сейчас',
-    'revisions' => 'Версия',
+    'revisions' => 'Версии',
     'meta_revision' => 'Версия #:revisionCount',
     'meta_created' => 'Создано :timeLength',
     'meta_created_name' => ':user создал :timeLength',
     'meta_updated' => 'Обновлено :timeLength',
     'meta_updated_name' => ':user обновил :timeLength',
+    'meta_owned_name' => 'Владелец :user',
     'entity_select' => 'Выбор объекта',
     'images' => 'Изображения',
     'my_recent_drafts' => 'Мои последние черновики',
@@ -36,18 +37,20 @@ return [
 
     // Permissions and restrictions
     'permissions' => 'Разрешения',
-    'permissions_intro' => 'После включения эти разрешения будут иметь приоритет над любыми установленными полномочиями.',
+    'permissions_intro' => 'После включения опции эти разрешения будут иметь приоритет над любыми установленными разрешениями роли.',
     'permissions_enable' => 'Включение пользовательских разрешений',
     'permissions_save' => 'Сохранить разрешения',
+    'permissions_owner' => 'Владелец',
 
     // Search
     'search_results' => 'Результаты поиска',
-    'search_total_results_found' => ':count результатов найдено|:count всего результатов найдено',
+    'search_total_results_found' => 'Найден :count результат|Найдено :count результата|Найдено :count результатов',
     'search_clear' => 'Очистить поиск',
-    'search_no_pages' => 'Нет страниц, соответствующих этому поиску.',
+    'search_no_pages' => 'Нет страниц, соответствующих этому поиску',
     'search_for_term' => 'Искать :term',
     'search_more' => 'Еще результаты',
-    'search_filters' => 'Фильтры поиска',
+    'search_advanced' => 'Расширенный поиск',
+    'search_terms' => 'Поисковые запросы',
     'search_content_type' => 'Тип содержимого',
     'search_exact_matches' => 'Точные соответствия',
     'search_tags' => 'Поиск по тегам',
@@ -68,7 +71,7 @@ return [
     // Shelves
     'shelf' => 'Полка',
     'shelves' => 'Полки',
-    'x_shelves' => ':count Ð¿Ð¾Ð»Ð¾Ðº|:count полок',
+    'x_shelves' => ':count Ð¿Ð¾Ð»ÐºÐ°|:count Ð¿Ð¾Ð»ÐºÐ¸|:count полок',
     'shelves_long' => 'Книжные полки',
     'shelves_empty' => 'Полки не созданы',
     'shelves_create' => 'Создать новую полку',
@@ -80,7 +83,7 @@ return [
     'shelves_save' => 'Сохранить полку',
     'shelves_books' => 'Книги из этой полки',
     'shelves_add_books' => 'Добавить книгу в эту полку',
-    'shelves_drag_books' => 'Перетащите книгу сюда, чтобы добавить на эту полку',
+    'shelves_drag_books' => 'Перетащите книги сюда, чтобы добавить их на эту полку',
     'shelves_empty_contents' => 'На этой полке нет книг',
     'shelves_edit_and_assign' => 'Изменить полку для привязки книг',
     'shelves_edit_named' => 'Редактировать полку :name',
@@ -91,16 +94,16 @@ return [
     'shelves_delete_confirmation' => 'Вы уверены, что хотите удалить эту полку?',
     'shelves_permissions' => 'Доступы к книжной полке',
     'shelves_permissions_updated' => 'Доступы к книжной полке обновлены',
-    'shelves_permissions_active' => 'Ð\94оÑ\81Ñ\82Ñ\83пÑ\8b Ðº ÐºÐ½Ð¸Ð¶Ð½Ð¾Ð¹ Ð¿Ð¾Ð»ÐºÐµ Ð°ÐºÑ\82ивнÑ\8b',
+    'shelves_permissions_active' => 'Ð\94ейÑ\81Ñ\82вÑ\83Ñ\8eÑ\89ие Ñ\80азÑ\80еÑ\88ениÑ\8f ÐºÐ½Ð¸Ð¶Ð½Ð¾Ð¹ Ð¿Ð¾Ð»ÐºÐ¸',
     'shelves_copy_permissions_to_books' => 'Наследовать доступы книгам',
     'shelves_copy_permissions' => 'Копировать доступы',
     'shelves_copy_permissions_explain' => 'Это применит текущие настройки доступов этой книжной полки ко всем книгам, содержащимся внутри. Перед активацией убедитесь, что все изменения в доступах этой книжной полки сохранены.',
-    'shelves_copy_permission_success' => 'Доступы книжной полки скопированы для :count books',
+    'shelves_copy_permission_success' => 'Доступы книжной полки скопированы для :count книг',
 
     // Books
     'book' => 'Книга',
     'books' => 'Книги',
-    'x_books' => ':count книга|:count книг',
+    'x_books' => ':count книга|:count книги|:count книг',
     'books_empty' => 'Нет созданных книг',
     'books_popular' => 'Популярные книги',
     'books_recent' => 'Недавние книги',
@@ -115,7 +118,7 @@ return [
     'books_delete_confirmation' => 'Вы действительно хотите удалить эту книгу?',
     'books_edit' => 'Редактировать книгу',
     'books_edit_named' => 'Редактировать книгу :bookName',
-    'books_form_book_name' => 'Ð\98мÑ\8f книги',
+    'books_form_book_name' => 'Ð\9dазвание книги',
     'books_save' => 'Сохранить книгу',
     'books_permissions' => 'Разрешения на книгу',
     'books_permissions_updated' => 'Разрешения на книгу обновлены',
@@ -123,32 +126,32 @@ return [
     'books_empty_create_page' => 'Создать новую страницу',
     'books_empty_sort_current_book' => 'Сортировка текущей книги',
     'books_empty_add_chapter' => 'Добавить главу',
-    'books_permissions_active' => 'дейÑ\81Ñ\82вÑ\83Ñ\8eÑ\89ие Ñ\80азÑ\80еÑ\88ениÑ\8f Ð½Ð° ÐºÐ½Ð¸Ð³Ñ\83',
+    'books_permissions_active' => 'Ð\94ейÑ\81Ñ\82вÑ\83Ñ\8eÑ\89ие Ñ\80азÑ\80еÑ\88ениÑ\8f ÐºÐ½Ð¸Ð³Ð¸',
     'books_search_this' => 'Поиск в этой книге',
     'books_navigation' => 'Навигация по книге',
     'books_sort' => 'Сортировка содержимого книги',
     'books_sort_named' => 'Сортировка книги :bookName',
-    'books_sort_name' => 'СоÑ\80Ñ\82иÑ\80оваÑ\82Ñ\8c Ð¿о имени',
-    'books_sort_created' => 'СоÑ\80Ñ\82иÑ\80оваÑ\82Ñ\8c Ð¿о дате создания',
-    'books_sort_updated' => 'СоÑ\80Ñ\82иÑ\80оваÑ\82Ñ\8c Ð¿о дате обновления',
-    'books_sort_chapters_first' => 'СнаÑ\87ала Ð³Ð»Ð°Ð²Ñ\8b',
-    'books_sort_chapters_last' => 'Ð\93лавÑ\8b Ð¿Ð¾Ñ\81ледние',
+    'books_sort_name' => 'Ð\9fо имени',
+    'books_sort_created' => 'Ð\9fо дате создания',
+    'books_sort_updated' => 'Ð\9fо дате обновления',
+    'books_sort_chapters_first' => 'Ð\93лавÑ\8b Ð² Ð½Ð°Ñ\87але',
+    'books_sort_chapters_last' => 'Ð\93лавÑ\8b Ð² ÐºÐ¾Ð½Ñ\86е',
     'books_sort_show_other' => 'Показать другие книги',
     'books_sort_save' => 'Сохранить новый порядок',
 
     // Chapters
     'chapter' => 'Глава',
     'chapters' => 'Главы',
-    'x_chapters' => ':count глава|:count главы',
+    'x_chapters' => ':count глава|:count главы|:count глав',
     'chapters_popular' => 'Популярные главы',
     'chapters_new' => 'Новая глава',
     'chapters_create' => 'Создать новую главу',
     'chapters_delete' => 'Удалить главу',
     'chapters_delete_named' => 'Удалить главу :chapterName',
-    'chapters_delete_explain' => 'Это удалит главу с именем \':chapterName\'. Все страницы главы будут удалены и перемещены напрямую в книгу.',
+    'chapters_delete_explain' => 'Это действие удалит главу с названием \':chapterName\'. Все страницы, которые существуют в этой главе, также будут удалены.',
     'chapters_delete_confirm' => 'Вы действительно хотите удалить эту главу?',
     'chapters_edit' => 'Редактировать главу',
-    'chapters_edit_named' => 'редактировать главу :chapterName',
+    'chapters_edit_named' => 'Редактировать главу :chapterName',
     'chapters_save' => 'Сохранить главу',
     'chapters_move' => 'Переместить главу',
     'chapters_move_named' => 'Переместить главу :chapterName',
@@ -162,12 +165,12 @@ return [
     // Pages
     'page' => 'Страница',
     'pages' => 'Страницы',
-    'x_pages' => ':count страница|:count страниц',
+    'x_pages' => ':count страница|:count страницы|:count страниц',
     'pages_popular' => 'Популярные страницы',
     'pages_new' => 'Новая страница',
     'pages_attachments' => 'Вложения',
     'pages_navigation' => 'Навигация на странице',
-    'pages_delete' => 'УдалиÑ\82Ñ\8c Ñ\83Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83',
+    'pages_delete' => 'Удалить страницу',
     'pages_delete_named' => 'Удалить страницу :pageName',
     'pages_delete_draft_named' => 'Удалить черновик :pageName',
     'pages_delete_draft' => 'Удалить черновик',
@@ -176,20 +179,20 @@ return [
     'pages_delete_confirm' => 'Вы действительно хотите удалить эту страницу?',
     'pages_delete_draft_confirm' => 'Вы действительно хотите удалить этот черновик?',
     'pages_editing_named' => 'Редактирование страницы :pageName',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Параметры черновика',
     'pages_edit_save_draft' => 'Сохранить черновик',
     'pages_edit_draft' => 'Редактировать черновик',
     'pages_editing_draft' => 'Редактирование черновика',
     'pages_editing_page' => 'Редактирование страницы',
-    'pages_edit_draft_save_at' => 'Черновик сохранить в ',
+    'pages_edit_draft_save_at' => 'Черновик сохранён в ',
     'pages_edit_delete_draft' => 'Удалить черновик',
-    'pages_edit_discard_draft' => 'отменить черновик',
+    'pages_edit_discard_draft' => 'Ð\9eтменить черновик',
     'pages_edit_set_changelog' => 'Задать список изменений',
-    'pages_edit_enter_changelog_desc' => 'Ð\92ведиÑ\82е ÐºÑ\80аÑ\82кое Ð¾Ð¿Ð¸Ñ\81ание Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹, ÐºÐ¾Ñ\82оÑ\80Ñ\8bе Ð²Ñ\8b Ñ\81делали',
+    'pages_edit_enter_changelog_desc' => 'Ð\92ведиÑ\82е ÐºÑ\80аÑ\82кое Ð¾Ð¿Ð¸Ñ\81ание Ð²Ð½ÐµÑ\81еннÑ\8bÑ\85 Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹',
     'pages_edit_enter_changelog' => 'Введите список изменений',
     'pages_save' => 'Сохранить страницу',
     'pages_title' => 'Заголовок страницы',
-    'pages_name' => 'Ð\98мÑ\8f страницы',
+    'pages_name' => 'Ð\9dазвание страницы',
     'pages_md_editor' => 'Редактор',
     'pages_md_preview' => 'Просмотр',
     'pages_md_insert_image' => 'Вставить изображение',
@@ -204,14 +207,15 @@ return [
     'pages_permissions' => 'Разрешения страницы',
     'pages_permissions_success' => 'Pазрешения страницы обновлены',
     'pages_revision' => 'Версия',
-    'pages_revisions' => 'Версия страницы',
+    'pages_revisions' => 'Версии страницы',
     'pages_revisions_named' => 'Версии страницы для :pageName',
     'pages_revision_named' => 'Версия страницы для :pageName',
+    'pages_revision_restored_from' => 'Восстановлено из #:id; :summary',
     'pages_revisions_created_by' => 'Создана',
     'pages_revisions_date' => 'Дата версии',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Ревизия #:id',
-    'pages_revisions_numbered_changes' => 'РевизиÑ\8f #:id Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f',
+    'pages_revisions_numbered' => 'Ð\92еÑ\80Ñ\81ия #:id',
+    'pages_revisions_numbered_changes' => 'Ð\98зменениÑ\8f Ð² Ð²ÐµÑ\80Ñ\81ии #:id',
     'pages_revisions_changelog' => 'Список изменений',
     'pages_revisions_changes' => 'Изменения',
     'pages_revisions_current' => 'Текущая версия',
@@ -223,8 +227,8 @@ return [
     'pages_permissions_active' => 'Действующие разрешения на страницу',
     'pages_initial_revision' => 'Первоначальное издание',
     'pages_initial_name' => 'Новая страница',
-    'pages_editing_draft_notification' => 'Вы в настоящее время редактируете черновик, который был сохранен :timeDiff.',
-    'pages_draft_edited_notification' => 'Эта страница была обновлена до этого момента. Рекомендуется отменить этот черновик',
+    'pages_editing_draft_notification' => 'В настоящее время вы редактируете черновик, который был сохранён :timeDiff.',
+    'pages_draft_edited_notification' => 'Эта страница была обновлена до этого момента. Рекомендуется отменить этот черновик.',
     'pages_draft_edit_active' => [
         'start_a' => ':count пользователей начали редактирование этой страницы',
         'start_b' => ':userName начал редактирование этой страницы',
@@ -247,45 +251,46 @@ return [
     'tag_value' => 'Значение тега (опционально)',
     'tags_explain' => "Добавьте теги, чтобы лучше классифицировать ваш контент. \\n Вы можете присвоить значение тегу для более глубокой организации.",
     'tags_add' => 'Добавить тег',
-    'tags_remove' => 'Удалить этот тэг',
-    'attachments' => 'Вложение',
+    'tags_remove' => 'Удалить этот тег',
+    'attachments' => 'Вложения',
     'attachments_explain' => 'Загрузите несколько файлов или добавьте ссылку для отображения на своей странице. Они видны на боковой панели страницы.',
     'attachments_explain_instant_save' => 'Изменения здесь сохраняются мгновенно.',
     'attachments_items' => 'Прикрепленные элементы',
     'attachments_upload' => 'Загрузить файл',
     'attachments_link' => 'Присоединить ссылку',
     'attachments_set_link' => 'Установить ссылку',
-    'attachments_delete_confirm' => 'Нажмите \'Удалить\' еще раз, чтобы подтвердить удаление этого файла.',
+    'attachments_delete' => 'Вы уверены, что хотите удалить это вложение?',
     'attachments_dropzone' => 'Перетащите файл сюда или нажмите здесь, чтобы загрузить файл',
     'attachments_no_files' => 'Файлы не загружены',
-    'attachments_explain_link' => 'Вы можете присоединить ссылку, если вы предпочитаете не загружать файл. Это может быть ссылка на другую страницу или ссылку на файл в облаке',
-    'attachments_link_name' => 'Ð\98мÑ\8f ссылки',
+    'attachments_explain_link' => 'Вы можете присоединить ссылку, если вы предпочитаете не загружать файл. Это может быть ссылка на другую страницу или ссылка на файл в облаке.',
+    'attachments_link_name' => 'Ð\9dазвание ссылки',
     'attachment_link' => 'Ссылка на вложение',
     'attachments_link_url' => 'Ссылка на файл',
     'attachments_link_url_hint' => 'URL-адрес сайта или файла',
     'attach' => 'Прикрепить',
+    'attachments_insert_link' => 'Добавить ссылку на вложение',
     'attachments_edit_file' => 'Редактировать файл',
-    'attachments_edit_file_name' => 'Ð\98мÑ\8f файла',
-    'attachments_edit_drop_upload' => 'перетащите файлы или нажмите здесь, чтобы загрузить и перезаписать',
-    'attachments_order_updated' => 'Прикрепленный файл обновлен',
-    'attachments_updated_success' => 'Детали файла обновлены',
-    'attachments_deleted' => 'Ð\9fÑ\80иложение удалено',
+    'attachments_edit_file_name' => 'Ð\9dазвание файла',
+    'attachments_edit_drop_upload' => 'Ð\9fеретащите файлы или нажмите здесь, чтобы загрузить и перезаписать',
+    'attachments_order_updated' => 'Порядок вложений обновлен',
+    'attachments_updated_success' => 'Детали вложения обновлены',
+    'attachments_deleted' => 'Ð\92ложение удалено',
     'attachments_file_uploaded' => 'Файл успешно загружен',
     'attachments_file_updated' => 'Файл успешно обновлен',
     'attachments_link_attached' => 'Ссылка успешно присоединена к странице',
     'templates' => 'Шаблоны',
-    'templates_set_as_template' => 'СÑ\82Ñ\80аниÑ\86а Ñ\8dÑ\82о Ñ\88аблон',
+    'templates_set_as_template' => 'СÑ\82Ñ\80аниÑ\86а Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ñ\88аблоном',
     'templates_explain_set_as_template' => 'Вы можете назначить эту страницу в качестве шаблона, её содержимое будет использоваться при создании других страниц. Пользователи смогут использовать этот шаблон в случае, если имеют разрешения на просмотр этой страницы.',
     'templates_replace_content' => 'Заменить содержимое страницы',
     'templates_append_content' => 'Добавить к содержанию страницы',
     'templates_prepend_content' => 'Добавить в начало содержимого страницы',
 
     // Profile View
-    'profile_user_for_x' => 'пользователь уже :time',
+    'profile_user_for_x' => 'Ð\9fользователь уже :time',
     'profile_created_content' => 'Созданный контент',
-    'profile_not_created_pages' => ':userName Ð½Ðµ Ñ\81оздавал Ñ\81Ñ\82Ñ\80аниÑ\86',
-    'profile_not_created_chapters' => ':userName Ð½Ðµ Ñ\81оздавал Ð³Ð»Ð°Ð²',
-    'profile_not_created_books' => ':userName Ð½Ðµ Ñ\81оздавал Ð½Ð¸ Ð¾Ð´Ð½Ð¾Ð¹ ÐºÐ½Ð¸Ð³Ð¸',
+    'profile_not_created_pages' => ':userName Ð½Ðµ Ñ\81оздал Ð½Ð¸ Ð¾Ð´Ð½Ð¾Ð¹ Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b',
+    'profile_not_created_chapters' => ':userName Ð½Ðµ Ñ\81оздал Ð½Ð¸ Ð¾Ð´Ð½Ð¾Ð¹ Ð³Ð»Ð°Ð²Ñ\8b',
+    'profile_not_created_books' => ':userName не создал ни одной книги',
     'profile_not_created_shelves' => ':userName не создал ни одной полки',
 
     // Comments
@@ -295,7 +300,7 @@ return [
     'comment_placeholder' => 'Оставить комментарий здесь',
     'comment_count' => '{0} Нет комментариев|{1} 1 комментарий|[2,*] :count комментария',
     'comment_save' => 'Сохранить комментарий',
-    'comment_saving' => 'СоÑ\85Ñ\80аниение ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ\82аÑ\80иÑ\8f...',
+    'comment_saving' => 'Сохранение комментария...',
     'comment_deleting' => 'Удаление комментария...',
     'comment_new' => 'Новый комментарий',
     'comment_created' => 'прокомментировал :createDiff',
@@ -307,8 +312,8 @@ return [
     'comment_in_reply_to' => 'В ответ на :commentId',
 
     // Revision
-    'revision_delete_confirm' => 'Удалить эту ревизию?',
-    'revision_restore_confirm' => 'Восстановить эту ревизию? Текущее содержимое будет заменено.',
-    'revision_delete_success' => 'Ревизия удалена',
+    'revision_delete_confirm' => 'Удалить эту версию?',
+    'revision_restore_confirm' => 'Вы уверены, что хотите восстановить эту версию? Текущее содержимое страницы будет заменено.',
+    'revision_delete_success' => 'Ð\92еÑ\80Ñ\81ия удалена',
     'revision_cannot_delete_latest' => 'Нельзя удалить последнюю версию.'
 ];
\ No newline at end of file
index 69c63b5244bb813d042f1bb8effdb3c39eca44b7..e8f537ecdaa9e4b22b5b3fd467959cba88d09f66 100644 (file)
@@ -9,17 +9,23 @@ return [
     'permissionJson' => 'У вас нет разрешения для запрашиваемого действия.',
 
     // Auth
-    'error_user_exists_different_creds' => 'Пользователь с электронной почтой: :email уже существует, но с другими учетными данными.',
-    'email_already_confirmed' => 'ЭлекÑ\82Ñ\80оннаÑ\8f Ð¿Ð¾Ñ\87Ñ\82а Ñ\83же Ð¿Ð¾Ð´Ñ\82веÑ\80ждена, попробуйте войти в систему.',
+    'error_user_exists_different_creds' => 'Пользователь с электронной почтой :email уже существует, но с другими учетными данными.',
+    'email_already_confirmed' => 'Ð\90дÑ\80еÑ\81 Ñ\8dлекÑ\82Ñ\80онной Ð¿Ð¾Ñ\87Ñ\82Ñ\8b Ñ\83же Ð±Ñ\8bл Ð¿Ð¾Ð´Ñ\82веÑ\80жден, попробуйте войти в систему.',
     'email_confirmation_invalid' => 'Этот токен подтверждения недействителен или уже используется. Повторите попытку регистрации.',
     'email_confirmation_expired' => 'Истек срок действия токена. Отправлено новое письмо с подтверждением.',
+    'email_confirmation_awaiting' => 'Для используемой учетной записи необходимо подтвердить адрес электронной почты',
     'ldap_fail_anonymous' => 'Недопустимый доступ LDAP с использованием анонимной привязки',
     'ldap_fail_authed' => 'Не удалось получить доступ к LDAP, используя данные dn & password',
-    'ldap_extension_not_installed' => 'LDAP расширения для PHP не установлено',
-    'ldap_cannot_connect' => 'Не удается подключиться к серверу ldap, не удалось выполнить начальное соединение',
+    'ldap_extension_not_installed' => 'LDAP расширение для PHP не установлено',
+    'ldap_cannot_connect' => 'Не удается подключиться к серверу LDAP, не удалось выполнить начальное соединение',
+    'saml_already_logged_in' => 'Уже вошли в систему',
+    'saml_user_not_registered' => 'Пользователь :name не зарегистрирован. Автоматическая регистрация отключена',
+    'saml_no_email_address' => 'Не удалось найти email для этого пользователя в данных, предоставленных внешней системой аутентификации',
+    'saml_invalid_response_id' => 'Запрос от внешней системы аутентификации не распознается процессом, запущенным этим приложением. Переход назад после входа в систему может вызвать эту проблему.',
+    'saml_fail_authed' => 'Вход с помощью :system не удался, система не предоставила успешную авторизацию',
     'social_no_action_defined' => 'Действие не определено',
     'social_login_bad_response' => "При попытке входа с :socialAccount произошла ошибка: \\n:error",
-    'social_account_in_use' => 'ЭÑ\82оÑ\82 :socialAccount Ð°ÐºÐºÐ°Ñ\83нÑ\82 Ñ\83же Ð¸Ñ\81опльзуется, попробуйте войти с параметрами :socialAccount.',
+    'social_account_in_use' => 'ЭÑ\82оÑ\82 :socialAccount Ð°ÐºÐºÐ°Ñ\83нÑ\82 Ñ\83же Ð¸Ñ\81пользуется, попробуйте войти с параметрами :socialAccount.',
     'social_account_email_in_use' => 'Электронный ящик :email уже используется. Если у вас уже есть учетная запись, вы можете подключить свою учетную запись :socialAccount из настроек своего профиля.',
     'social_account_existing' => 'Этот :socialAccount уже привязан к вашему профилю.',
     'social_account_already_used_existing' => 'Этот :socialAccount уже используется другим пользователем.',
@@ -30,22 +36,21 @@ return [
     'invite_token_expired' => 'Срок действия приглашения истек. Вместо этого вы можете попытаться сбросить пароль своей учетной записи.',
 
     // System
-    'path_not_writable' => 'Невозможно загрузить файл по пути :filePath . Убедитесь что сервер доступен для записи.',
+    'path_not_writable' => 'Невозможно загрузить файл по пути :filePath. Убедитесь что сервер доступен для записи.',
     'cannot_get_image_from_url' => 'Не удается получить изображение из :url',
     'cannot_create_thumbs' => 'Сервер не может создавать эскизы. Убедитесь, что у вас установлено расширение GD PHP.',
-    'server_upload_limit' => 'Сервер не разрешает загрузку такого размера. Попробуйте уменьшить размер файла.',
+    'server_upload_limit' => 'СеÑ\80веÑ\80 Ð½Ðµ Ñ\80азÑ\80еÑ\88аеÑ\82 Ð·Ð°Ð³Ñ\80Ñ\83зкÑ\83 Ñ\84айлов Ñ\82акого Ñ\80азмеÑ\80а. Ð\9fопÑ\80обÑ\83йÑ\82е Ñ\83менÑ\8cÑ\88иÑ\82Ñ\8c Ñ\80азмеÑ\80 Ñ\84айла.',
     'uploaded'  => 'Сервер не позволяет загружать файлы такого размера. Пожалуйста, попробуйте файл меньше.',
-    'image_upload_error' => 'Произошла ошибка при загрузке изображения.',
+    'image_upload_error' => 'Произошла ошибка при загрузке изображения',
     'image_upload_type_error' => 'Неправильный тип загружаемого изображения',
-    'file_upload_timeout' => 'Ð\92Ñ\8bгÑ\80Ñ\83зка Ñ\84айла Ð·Ð°ÐºÐ¾Ð½Ñ\87илаÑ\81Ñ\8c.',
+    'file_upload_timeout' => 'Ð\92Ñ\80емÑ\8f Ð·Ð°Ð³Ñ\80Ñ\83зки Ñ\84айла Ð¸Ñ\81Ñ\82екло.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Несоответствие страницы во время обновления вложения',
     'attachment_not_found' => 'Вложение не найдено',
 
     // Pages
     'page_draft_autosave_fail' => 'Не удалось сохранить черновик. Перед сохранением этой страницы убедитесь, что у вас есть подключение к Интернету.',
-    'page_custom_home_deletion' => 'Ð\9dелÑ\8cзÑ\8f Ñ\83далиÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83, Ñ\83Ñ\81Ñ\82ановленнÑ\83Ñ\8e Ð²Ð¼ÐµÑ\81Ñ\82о Ð³Ð»Ð°Ð²Ð½Ð¾Ð¹ Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b',
+    'page_custom_home_deletion' => 'Ð\9dевозможно Ñ\83далиÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83, Ð¿Ð¾ÐºÐ° Ð¾Ð½Ð° Ñ\83Ñ\81Ñ\82ановлена ÐºÐ°Ðº Ð´Ð¾Ð¼Ð°Ñ\88нÑ\8fÑ\8f Ñ\81Ñ\82Ñ\80аниÑ\86а',
 
     // Entities
     'entity_not_found' => 'Объект не найден',
@@ -55,31 +60,43 @@ return [
     'chapter_not_found' => 'Глава не найдена',
     'selected_book_not_found' => 'Выбранная книга не найдена',
     'selected_book_chapter_not_found' => 'Выбранная книга или глава не найдена',
-    'guests_cannot_save_drafts' => 'Гости не могут сохранить черновики',
+    'guests_cannot_save_drafts' => 'Гости не могут сохранять черновики',
 
     // Users
     'users_cannot_delete_only_admin' => 'Вы не можете удалить единственного администратора',
     'users_cannot_delete_guest' => 'Вы не можете удалить гостевого пользователя',
 
     // Roles
-    'role_cannot_be_edited' => 'Ð\9dевозможно Ð¾Ñ\82Ñ\80едакÑ\82иÑ\80оваÑ\82Ñ\8c Ð´Ð°Ð½Ð½Ñ\83Ñ\8e Ñ\80олÑ\8c',
+    'role_cannot_be_edited' => 'ЭÑ\82а Ñ\80олÑ\8c Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ\82 Ð±Ñ\8bÑ\82Ñ\8c Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð°',
     'role_system_cannot_be_deleted' => 'Эта роль является системной и не может быть удалена',
-    'role_registration_default_cannot_delete' => 'Эта роль не может быть удалена, так как она устанолена в качестве роли по умолчанию',
+    'role_registration_default_cannot_delete' => 'ЭÑ\82а Ñ\80олÑ\8c Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ\82 Ð±Ñ\8bÑ\82Ñ\8c Ñ\83далена, Ñ\82ак ÐºÐ°Ðº Ð¾Ð½Ð° Ñ\83Ñ\81Ñ\82ановлена Ð² ÐºÐ°Ñ\87еÑ\81Ñ\82ве Ñ\80оли Ð¿Ð¾ Ñ\83молÑ\87аниÑ\8e',
     'role_cannot_remove_only_admin' => 'Этот пользователь единственный с правами администратора. Назначьте роль администратора другому пользователю, прежде чем удалить этого.',
 
     // Comments
-    'comment_list' => 'Ð\9fÑ\80и Ð¿Ð¾Ð»Ñ\83Ñ\87ении ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ\82аÑ\80иев Ð¿Ñ\80оизоÑ\88ла Ð¾Ñ\88ибка.',
+    'comment_list' => 'Ð\9fÑ\80оизоÑ\88ла Ð¾Ñ\88ибка Ð¿Ñ\80и Ð¿Ð¾Ð»Ñ\83Ñ\87ении ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ\82аÑ\80иев.',
     'cannot_add_comment_to_draft' => 'Вы не можете добавлять комментарии к черновику.',
-    'comment_add' => 'Ð\9fÑ\80и Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ð¸ / Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ð¸ ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ\82аÑ\80иÑ\8f Ð¿Ñ\80оизоÑ\88ла Ð¾Ñ\88ибка.',
-    'comment_delete' => 'Ð\9fÑ\80и Ñ\83далении ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ\82аÑ\80иÑ\8f Ð¿Ñ\80оизоÑ\88ла Ð¾Ñ\88ибка.',
+    'comment_add' => 'Ð\9fÑ\80оизоÑ\88ла Ð¾Ñ\88ибка Ð¿Ñ\80и Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ð¸ / Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ð¸ ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ\82аÑ\80иÑ\8f.',
+    'comment_delete' => 'Ð\9fÑ\80оизоÑ\88ла Ð¾Ñ\88ибка Ð¿Ñ\80и Ñ\83далении ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ\82аÑ\80иÑ\8f.',
     'empty_comment' => 'Нельзя добавить пустой комментарий.',
 
     // Error pages
     '404_page_not_found' => 'Страница не найдена',
     'sorry_page_not_found' => 'Извините, страница, которую вы искали, не найдена.',
+    'sorry_page_not_found_permission_warning' => 'Если вы ожидали что страница существует, возможно у вас нет прав для её просмотра.',
     'return_home' => 'вернуться на главную страницу',
     'error_occurred' => 'Произошла ошибка',
-    'app_down' => ':appName в данный момент не достпуно',
+    'app_down' => ':appName в данный момент не доступно',
     'back_soon' => 'Скоро восстановится.',
 
+    // API errors
+    'api_no_authorization_found' => 'Отсутствует токен авторизации в запросе',
+    'api_bad_authorization_format' => 'Токен авторизации найден, но формат запроса неверен',
+    'api_user_token_not_found' => 'Отсутствует соответствующий API токен для предоставленного токена авторизации',
+    'api_incorrect_token_secret' => 'Секрет, предоставленный для данного использованного API токена неверен',
+    'api_user_no_api_permission' => 'Владелец используемого API токена не имеет прав на выполнение вызовов API',
+    'api_user_token_expired' => 'Срок действия используемого токена авторизации истек',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Ошибка при отправке тестового письма:',
+
 ];
index c3324e8de76438aa8a72b81aa7ab8f24f2aea100..433c7045411dd0e8d0487b7314036be24c39b3e9 100644 (file)
@@ -6,10 +6,10 @@
  */
 return [
 
-    'password' => 'Пароль должен содержать не менее шести символов и совпадать с подтверждением.',
-    'user' => "Ð\9fолÑ\8cзоваÑ\82елÑ\8c Ñ\81 Ñ\83казанÑ\8bм email Ð¾Ñ\82Ñ\81Ñ\83Ñ\82ствует.",
-    'token' => 'Токен сброса пароля недействителен.',
-    'sent' => 'Ссылка для сброса пароля отправлена на email!',
+    'password' => 'Пароль должен содержать не менее восьми символов и совпадать с подтверждением.',
+    'user' => "Ð\9fолÑ\8cзоваÑ\82елÑ\8f Ñ\81 Ð´Ð°Ð½Ð½Ñ\8bм Ð°Ð´Ñ\80еÑ\81ом Ñ\8dлекÑ\82Ñ\80онной Ð¿Ð¾Ñ\87Ñ\82Ñ\8b Ð½Ðµ Ñ\81Ñ\83Ñ\89ествует.",
+    'token' => 'Токен сброса пароля недействителен для этого адреса электронной почты.',
+    'sent' => 'Ссылка для сброса пароля отправлена на вашу почту!',
     'reset' => 'Ваш пароль был сброшен!',
 
 ];
index f268c7e4ca48e522c803aa1456148c08ad2006c4..472973ca2a2c1b08c45a9da9579be29ef8084880 100755 (executable)
@@ -13,81 +13,135 @@ return [
 
     // App Settings
     'app_customization' => 'Настройки',
-    'app_features_security' => 'Функционал & Безопасность',
-    'app_name' => 'Ð\98мÑ\8f приложения',
-    'app_name_desc' => 'Ð\98мÑ\8f Ð¾Ñ\82обÑ\80ажаеÑ\82Ñ\81Ñ\8f Ð² Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐµ email отправленных системой.',
-    'app_name_header' => 'Ð\9eÑ\82обÑ\80ажаÑ\82Ñ\8c Ð¸Ð¼Ñ\8f приложения в заголовке',
+    'app_features_security' => 'Функционал и безопасность',
+    'app_name' => 'Ð\9dазвание приложения',
+    'app_name_desc' => 'Ð\9dазвание Ð¾Ñ\82обÑ\80ажаеÑ\82Ñ\81Ñ\8f Ð² Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°Ñ\85 Ð¸ Ñ\81ообÑ\89ениÑ\8fÑ\85 Ñ\8dлекÑ\82Ñ\80онной Ð¿Ð¾Ñ\87Ñ\82Ñ\8b отправленных системой.',
+    'app_name_header' => 'Ð\9eÑ\82обÑ\80ажаÑ\82Ñ\8c Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ приложения в заголовке',
     'app_public_access' => 'Публичный доступ',
     'app_public_access_desc' => 'Включение этой опции позволит неавторизованным посетителям получить доступ к содержимому вашего BookStack.',
     'app_public_access_desc_guest' => 'Публичный доступ контролируется через настройки пользователя "Guest"',
     'app_public_access_toggle' => 'Разрешить публичный доступ',
     'app_public_viewing' => 'Разрешить публичный просмотр?',
-    'app_secure_images' => 'Загрузка изображений с высоким уровнем безопасности.',
+    'app_secure_images' => 'Загрузка изображений с высоким уровнем безопасности',
     'app_secure_images_toggle' => 'Включить загрузку изображений с высоким уровнем безопасности',
     'app_secure_images_desc' => 'Для высокой производительности все изображения являются общедоступными. Этот параметр добавляет случайную строку перед URL изображения. Убедитесь, что индексация каталогов отключена, для предотвращения легкого доступа.',
-    'app_editor' => 'Редактор страницы',
+    'app_editor' => 'Редактор страниц',
     'app_editor_desc' => 'Выберите, какой редактор будет использоваться всеми пользователями для редактирования страниц.',
     'app_custom_html' => 'Пользовательский контент заголовка HTML',
     'app_custom_html_desc' => 'Любой контент, добавленный здесь, будет вставлен в нижнюю часть раздела <head> каждой страницы. Это удобно для переопределения стилей или добавления кода аналитики.',
     'app_custom_html_disabled_notice' => 'Пользовательский контент заголовка HTML отключен на этой странице, чтобы гарантировать отмену любых критических изменений',
-    'app_logo' => 'Лого приложения',
+    'app_logo' => 'Логотип приложения',
     'app_logo_desc' => 'Это изображение должно быть 43px в высоту. <br>Большое изображение будет уменьшено.',
     'app_primary_color' => 'Основной цвет приложения',
     'app_primary_color_desc' => 'Значение должно быть указано в hex-формате. <br>Оставьте пустым чтобы использовать цвет по умолчанию.',
     'app_homepage' => 'Стартовая страница приложения',
     'app_homepage_desc' => 'Выберите страницу, которая будет отображаться на главной странице вместо стандартной. Права на страницы игнорируются для выбранных страниц.',
     'app_homepage_select' => 'Выберите страницу',
-    'app_disable_comments' => 'Ð\9eÑ\82клÑ\8eÑ\87ение ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ\82ов',
+    'app_disable_comments' => 'Ð\9eÑ\82клÑ\8eÑ\87ение ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ\82аÑ\80иев',
     'app_disable_comments_toggle' => 'Отключить комментарии',
-    'app_disable_comments_desc' => 'Отключение комментов на всех страницах. Существующие комментарии отображаться не будут.',
+    'app_disable_comments_desc' => 'Отключение комментариев на всех страницах. Существующие комментарии будут скрыты.',
+
+    // Color settings
+    'content_colors' => 'Цвета элементов иерархии',
+    'content_colors_desc' => 'Задает цвета для всех элементов организационной иерархии страницы. Для удобства чтения рекомендуется выбирать цвета, яркость которых близка к цветам по умолчанию.',
+    'bookshelf_color' => 'Цвет полки',
+    'book_color' => 'Цвет книги',
+    'chapter_color' => 'Цвет главы',
+    'page_color' => 'Цвет страницы',
+    'page_draft_color' => 'Цвет черновика страницы',
 
     // Registration Settings
     'reg_settings' => 'Настройки регистрации',
     'reg_enable' => 'Разрешить регистрацию',
     'reg_enable_toggle' => 'Разрешить регистрацию',
-    'reg_enable_desc' => 'Если регистрация разрешена, пользователь сможет зарегистрироваться в системе самостоятельно. При регистрации назначается роль пользователя по умолчанию',
+    'reg_enable_desc' => 'Если регистрация разрешена, пользователь сможет зарегистрироваться в системе самостоятельно. При регистрации назначается роль пользователя по умолчанию.',
     'reg_default_role' => 'Роль пользователя по умолчанию после регистрации',
-    'reg_email_confirmation' => 'Подтверждение электонной почты',
+    'reg_enable_external_warning' => 'Вышеуказанный параметр игнорируется, пока активна внешняя аутентификация LDAP или SAML. Учетные записи для несуществующих пользователей будут создаваться автоматически при условии успешной аутентификации на внешнем сервере.',
+    'reg_email_confirmation' => 'Подтверждение электронной почты',
     'reg_email_confirmation_toggle' => 'Требовать подтверждение по электронной почте',
     'reg_confirm_email_desc' => 'При использовании ограничения по домену - подтверждение обязательно, этот пункт игнорируется.',
     'reg_confirm_restrict_domain' => 'Ограничить регистрацию по домену',
-    'reg_confirm_restrict_domain_desc' => 'Ð\92ведиÑ\82е Ñ\81пиÑ\81ок Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð² Ð¿Ð¾Ñ\87Ñ\82Ñ\8b Ñ\87еÑ\80ез Ð·Ð°Ð¿Ñ\8fÑ\82Ñ\83Ñ\8e, Ð´Ð»Ñ\8f ÐºÐ¾Ñ\82оÑ\80Ñ\8bÑ\85 Ñ\80азÑ\80еÑ\88ена Ñ\80егиÑ\81Ñ\82Ñ\80аÑ\86иÑ\8f. Ð\9fолÑ\8cзоваÑ\82елÑ\8fм Ð±Ñ\83деÑ\82 Ð¾Ñ\82пÑ\80авлено Ð¿Ð¸Ñ\81Ñ\8cмо Ð´Ð»Ñ\8f Ð¿Ð¾Ð´Ñ\82веÑ\80ждениÑ\8f Ð°Ð´Ñ\80еÑ\81а Ð¿ÐµÑ\80ед Ð²Ñ\85одом Ð² Ð¿Ñ\80иложение. <br> Ð\9eбÑ\80аÑ\82иÑ\82е Ð²Ð½Ð¸Ð¼Ð°Ð½Ð¸Ðµ, Ñ\87Ñ\82о Ð¿Ð¾Ð»Ñ\8cзоваÑ\82ели Ñ\81могÑ\83Ñ\82 Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ\82Ñ\8c Ñ\81вои Ð°Ð´Ñ\80еÑ\81а Ñ\83же после регистрации.',
+    'reg_confirm_restrict_domain_desc' => 'Ð\92ведиÑ\82е Ñ\81пиÑ\81ок Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð² Ð¿Ð¾Ñ\87Ñ\82Ñ\8b Ñ\87еÑ\80ез Ð·Ð°Ð¿Ñ\8fÑ\82Ñ\83Ñ\8e, Ð´Ð»Ñ\8f ÐºÐ¾Ñ\82оÑ\80Ñ\8bÑ\85 Ñ\80азÑ\80еÑ\88ена Ñ\80егиÑ\81Ñ\82Ñ\80аÑ\86иÑ\8f. Ð\9fолÑ\8cзоваÑ\82елÑ\8fм Ð±Ñ\83деÑ\82 Ð¾Ñ\82пÑ\80авлено Ð¿Ð¸Ñ\81Ñ\8cмо Ð´Ð»Ñ\8f Ð¿Ð¾Ð´Ñ\82веÑ\80ждениÑ\8f Ð°Ð´Ñ\80еÑ\81а Ð¿ÐµÑ\80ед Ð²Ñ\85одом Ð² Ð¿Ñ\80иложение. <br> Ð\9eбÑ\80аÑ\82иÑ\82е Ð²Ð½Ð¸Ð¼Ð°Ð½Ð¸Ðµ, Ñ\87Ñ\82о Ð¿Ð¾Ð»Ñ\8cзоваÑ\82ели Ñ\81могÑ\83Ñ\82 Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ\82Ñ\8c Ñ\81вой Ð°Ð´Ñ\80еÑ\81 после регистрации.',
     'reg_confirm_restrict_domain_placeholder' => 'Без ограничений',
 
     // Maintenance settings
     'maint' => 'Обслуживание',
     'maint_image_cleanup' => 'Очистка изображений',
     'maint_image_cleanup_desc' => "Сканирует содержимое страниц и предыдущих версий и определяет изображения, которые не используются. Убедитесь, что у вас есть резервная копия базы данных и папки изображений перед запуском этой функции.",
-    'maint_image_cleanup_ignore_revisions' => 'Пропускать изображения в версиях',
+    'maint_delete_images_only_in_revisions' => 'Также удалять изображения, которые существуют только в старой версии страницы',
     'maint_image_cleanup_run' => 'Выполнить очистку',
     'maint_image_cleanup_warning' => 'Найдено :count возможно бесполезных изображений. Вы уверены, что хотите удалить эти изображения?',
     'maint_image_cleanup_success' => ':count возможно бесполезных изображений было найдено и удалено!',
     'maint_image_cleanup_nothing_found' => 'Не найдено ни одного бесполезного изображения!',
+    'maint_send_test_email' => 'Отправить тестовое письмо',
+    'maint_send_test_email_desc' => 'Отправить тестовое письмо на адрес электронной почты, указанный в профиле.',
+    'maint_send_test_email_run' => 'Отправить проверочное письмо',
+    'maint_send_test_email_success' => 'На адрес :address отравлено письмо',
+    'maint_send_test_email_mail_subject' => 'Проверка электронной почты',
+    'maint_send_test_email_mail_greeting' => 'Доставка электронной почты работает!',
+    'maint_send_test_email_mail_text' => 'Поздравляем! Поскольку вы получили это письмо, электронная почта настроена правильно.',
+    'maint_recycle_bin_desc' => 'Удаленные полки, книги, главы и страницы отправляются в корзину, чтобы они могли быть восстановлены или удалены навсегда. Более старые элементы в корзине могут быть автоматически удалены через некоторое время в зависимости от системной конфигурации.',
+    'maint_recycle_bin_open' => 'Открыть корзину',
+
+    // Recycle Bin
+    'recycle_bin' => 'Корзина',
+    'recycle_bin_desc' => 'Здесь вы можете восстановить удаленные элементы или навсегда удалить их из системы. Этот список не отфильтрован в отличие от аналогичных списков действий в системе, где применяются фильтры.',
+    'recycle_bin_deleted_item' => 'Удаленный элемент',
+    'recycle_bin_deleted_by' => 'Удалён',
+    'recycle_bin_deleted_at' => 'Время удаления',
+    'recycle_bin_permanently_delete' => 'Удалить навсегда',
+    'recycle_bin_restore' => 'Восстановить',
+    'recycle_bin_contents_empty' => 'На данный момент корзина пуста',
+    'recycle_bin_empty' => 'Очистить корзину',
+    'recycle_bin_empty_confirm' => 'Это действие навсегда уничтожит все элементы в корзине, включая содержимое, содержащееся в каждом элементе. Вы уверены, что хотите очистить корзину?',
+    'recycle_bin_destroy_confirm' => 'Это действие удалит этот элемент навсегда вместе с любыми дочерними элементами, перечисленными ниже, и вы не сможете восстановить этот контент. Вы уверены, что хотите навсегда удалить этот элемент?',
+    'recycle_bin_destroy_list' => 'Элементы для удаления',
+    'recycle_bin_restore_list' => 'Элементы для восстановления',
+    'recycle_bin_restore_confirm' => 'Это действие восстановит удаленный элемент, включая дочерние, в исходное место. Если исходное место было удалено и теперь находится в корзине, родительский элемент также необходимо будет восстановить.',
+    'recycle_bin_restore_deleted_parent' => 'Родитель этого элемента также был удален. Элементы будут удалены до тех пор, пока этот родитель не будет восстановлен.',
+    'recycle_bin_destroy_notification' => 'Удалено :count элементов из корзины.',
+    'recycle_bin_restore_notification' => 'Восстановлено :count элементов из корзины',
+
+    // Audit Log
+    'audit' => 'Журнал аудита',
+    'audit_desc' => 'Этот журнал аудита отображает список действий, отслеживаемых в системе. Этот список не отфильтрован в отличие от аналогичных списков действий в системе, где применяются фильтры.',
+    'audit_event_filter' => 'Фильтр событий',
+    'audit_event_filter_no_filter' => 'Без фильтра',
+    'audit_deleted_item' => 'Удаленный элемент',
+    'audit_deleted_item_name' => 'Имя: :name',
+    'audit_table_user' => 'Пользователь',
+    'audit_table_event' => 'Событие',
+    'audit_table_related' => 'Связанный элемент',
+    'audit_table_date' => 'Дата действия',
+    'audit_date_from' => 'Диапазон даты от',
+    'audit_date_to' => 'Диапазон даты до',
 
     // Role Settings
     'roles' => 'Роли',
     'role_user_roles' => 'Роли пользователя',
     'role_create' => 'Добавить роль',
-    'role_create_success' => 'Роль упешно добавлена',
+    'role_create_success' => 'Роль успешно добавлена',
     'role_delete' => 'Удалить роль',
     'role_delete_confirm' => 'Это удалит роль с именем \':roleName\'.',
-    'role_delete_users_assigned' => 'Эта роль назначена :userCount пользователям. Если вы хотите перенести их из этой роли, выберите новую роль ниже.',
-    'role_delete_no_migration' => "Ð\9dе Ð¼Ð¸Ð³Ñ\80иÑ\80овать пользователей",
+    'role_delete_users_assigned' => 'Эта роль назначена :userCount пользователям. Если вы хотите перенести их, выберите новую роль ниже.',
+    'role_delete_no_migration' => "Ð\9dе Ð¿ÐµÑ\80еноÑ\81ить пользователей",
     'role_delete_sure' => 'Вы уверены что хотите удалить данную роль?',
     'role_delete_success' => 'Роль успешно удалена',
     'role_edit' => 'Редактировать роль',
     'role_details' => 'Детали роли',
-    'role_name' => 'Ð\98мÑ\8f роли',
+    'role_name' => 'Ð\9dазвание роли',
     'role_desc' => 'Краткое описание роли',
     'role_external_auth_id' => 'Внешние ID авторизации',
     'role_system' => 'Системные разрешения',
     'role_manage_users' => 'Управление пользователями',
     'role_manage_roles' => 'Управление ролями и правами на роли',
     'role_manage_entity_permissions' => 'Управление правами на все книги, главы и страницы',
-    'role_manage_own_entity_permissions' => 'Управление разрешениями для собственных книг, разделов и страниц',
+    'role_manage_own_entity_permissions' => 'Управление разрешениями для собственных книг, глав и страниц',
     'role_manage_page_templates' => 'Управление шаблонами страниц',
+    'role_access_api' => 'Доступ к системному API',
     'role_manage_settings' => 'Управление настройками приложения',
-    'role_asset' => 'Разрешение для активации',
+    'role_asset' => 'Права доступа к материалам',
+    'roles_system_warning' => 'Имейте в виду, что доступ к любому из указанных выше трех разрешений может позволить пользователю изменить свои собственные привилегии или привилегии других пользователей системы. Назначить роли с этими правами только доверенным пользователям.',
     'role_asset_desc' => 'Эти разрешения контролируют доступ по умолчанию к параметрам внутри системы. Разрешения на книги, главы и страницы перезапишут эти разрешения.',
     'role_asset_admins' => 'Администраторы автоматически получают доступ ко всему контенту, но эти опции могут отображать или скрывать параметры пользовательского интерфейса.',
     'role_all' => 'Все',
@@ -103,24 +157,28 @@ return [
     'user_profile' => 'Профиль пользователя',
     'users_add_new' => 'Добавить пользователя',
     'users_search' => 'Поиск пользователей',
+    'users_latest_activity' => 'Последние действия',
     'users_details' => 'Данные пользователя',
-    'users_details_desc' => 'Ð\97адайÑ\82е Ð¸Ð¼Ñ\8f Ð´Ð»Ñ\8f Ñ\8dÑ\82ого Ð¿Ð¾Ð»Ñ\8cзоваÑ\82елÑ\8f, Ñ\87Ñ\82обÑ\8b Ð´Ñ\80Ñ\83гие Ð¼Ð¾Ð³Ð»Ð¸ ÐµÐ³Ð¾ Ñ\83знаÑ\82Ñ\8c',
-    'users_details_desc_no_email' => 'Задайте имя для этого пользователя, чтобы другие могли его узнать',
+    'users_details_desc' => 'УкажиÑ\82е Ð¸Ð¼Ñ\8f Ð¸ Ð°Ð´Ñ\80еÑ\81 Ñ\8dлекÑ\82Ñ\80онной Ð¿Ð¾Ñ\87Ñ\82Ñ\8b Ð´Ð»Ñ\8f Ñ\8dÑ\82ого Ð¿Ð¾Ð»Ñ\8cзоваÑ\82елÑ\8f. Ð\90дÑ\80еÑ\81 Ñ\8dлекÑ\82Ñ\80онной Ð¿Ð¾Ñ\87Ñ\82Ñ\8b Ð±Ñ\83деÑ\82 Ð¸Ñ\81полÑ\8cзоваÑ\82Ñ\8cÑ\81Ñ\8f Ð´Ð»Ñ\8f Ð²Ñ\85ода Ð² Ð¿Ñ\80иложение.',
+    'users_details_desc_no_email' => 'Задайте имя для этого пользователя, чтобы другие могли его узнать.',
     'users_role' => 'Роли пользователя',
     'users_role_desc' => 'Назначьте роли пользователю. Если назначено несколько ролей, разрешения будут суммироваться и пользователь получит все права назначенных ролей.',
     'users_password' => 'Пароль пользователя',
-    'users_password_desc' => 'УÑ\81Ñ\82ановиÑ\82е Ð¿Ð°Ñ\80олÑ\8c Ð´Ð»Ñ\8f Ð²Ñ\85ода Ð² Ð¿Ñ\80иложение. Ð\94олжно быть не менее 6 символов.',
-    'users_send_invite_text' => 'Вы можете отправить этому пользователю email с приглашением, которое позволит ему установить пароль самостоятельно или задайте пароль сами.',
-    'users_send_invite_option' => 'Отправить пользователю email с приглашением.',
+    'users_password_desc' => 'УÑ\81Ñ\82ановиÑ\82е Ð¿Ð°Ñ\80олÑ\8c Ð´Ð»Ñ\8f Ð²Ñ\85ода Ð² Ð¿Ñ\80иложение. Ð\94лина Ð¿Ð°Ñ\80олÑ\8f Ð´Ð¾Ð»Ð¶Ð½Ð° быть не менее 6 символов.',
+    'users_send_invite_text' => 'Вы можете отправить этому пользователю письмо с приглашением, которое позволит ему установить пароль самостоятельно или задайте пароль сами.',
+    'users_send_invite_option' => 'Отправить пользователю письмо с приглашением',
     'users_external_auth_id' => 'Внешний ID аутентификации',
-    'users_external_auth_id_desc' => 'Этот ID используется для связи с вашей LDAP системой.',
-    'users_password_warning' => 'Ð\97аполниÑ\82е Ð½Ð¸Ð¶Ðµ Ñ\82олÑ\8cко ÐµÑ\81ли Ð²Ñ\8b Ñ\85оÑ\82иÑ\82е Ñ\81мениÑ\82Ñ\8c Ñ\81вой пароль.',
+    'users_external_auth_id_desc' => 'Этот ID используется для связи с вашей внешней системой аутентификации.',
+    'users_password_warning' => 'Ð\97аполниÑ\82е Ð¿Ð¾Ð»Ñ\8f Ð½Ð¸Ð¶Ðµ Ñ\82олÑ\8cко ÐµÑ\81ли Ð²Ñ\8b Ñ\85оÑ\82иÑ\82е Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ\82Ñ\8c пароль.',
     'users_system_public' => 'Этот пользователь представляет любых гостевых пользователей, которые посещают ваше приложение. Он не может использоваться для входа в систему и назначается автоматически.',
     'users_delete' => 'Удалить пользователя',
     'users_delete_named' => 'Удалить пользователя :userName',
-    'users_delete_warning' => 'Это полностью удалит пользователя с именем \':userName\' из системы.',
+    'users_delete_warning' => 'Это полностью удалит пользователя \':userName\' из системы.',
     'users_delete_confirm' => 'Вы уверены что хотите удалить этого пользователя?',
-    'users_delete_success' => 'Пользователи успешно удалены',
+    'users_migrate_ownership' => 'Наследник контента',
+    'users_migrate_ownership_desc' => 'Выберите пользователя, если вы хотите, чтобы он стал владельцем всех элементов, в настоящее время принадлежащих удаляемому пользователю.',
+    'users_none_selected' => 'Пользователь не выбран',
+    'users_delete_success' => 'Пользователь успешно удален',
     'users_edit' => 'Редактировать пользователя',
     'users_edit_profile' => 'Редактировать профиль',
     'users_edit_success' => 'Пользователь успешно обновлен',
@@ -128,12 +186,38 @@ return [
     'users_avatar_desc' => 'Выберите изображение. Изображение должно быть квадратным, размером около 256px.',
     'users_preferred_language' => 'Предпочитаемый язык',
     'users_preferred_language_desc' => 'Этот параметр изменит язык интерфейса приложения. Это не влияет на созданный пользователем контент.',
-    'users_social_accounts' => 'Аккаунты Соцсетей',
-    'users_social_accounts_info' => 'Здесь вы можете подключить другие учетные записи для более быстрого и легкого входа в систему. Отключение учетной записи здесь не возможно. Отмените доступ к настройкам вашего профиля в подключенном аккаунте соцсети.',
+    'users_social_accounts' => 'Аккаунты социальных сетей',
+    'users_social_accounts_info' => 'Здесь вы можете подключить другие учетные записи для более быстрого и легкого входа в систему. Отключение учетной записи здесь не возможно. Отмените доступ к настройкам вашего профиля в подключенном социальном аккаунте.',
     'users_social_connect' => 'Подключить аккаунт',
     'users_social_disconnect' => 'Отключить аккаунт',
-    'users_social_connected' => ':socialAccount аккаунт упешно подключен к вашему профилю.',
+    'users_social_connected' => ':socialAccount аккаунт успешно подключен к вашему профилю.',
     'users_social_disconnected' => ':socialAccount аккаунт успешно отключен от вашего профиля.',
+    'users_api_tokens' => 'API токены',
+    'users_api_tokens_none' => 'Для этого пользователя не создано API токенов',
+    'users_api_tokens_create' => 'Создать токен',
+    'users_api_tokens_expires' => 'Истекает',
+    'users_api_tokens_docs' => 'Документация',
+
+    // API Tokens
+    'user_api_token_create' => 'Создать токен',
+    'user_api_token_name' => 'Имя',
+    'user_api_token_name_desc' => 'Присвойте вашему токену читаемое имя, в качестве напоминания о его назначении в будущем.',
+    'user_api_token_expiry' => 'Истекает',
+    'user_api_token_expiry_desc' => 'Установите дату истечения срока действия этого токена. После этой даты запросы, сделанные с использованием этого токена, больше не будут работать. Если оставить это поле пустым, срок действия истечет через 100 лет.',
+    'user_api_token_create_secret_message' => 'Сразу после создания этого токена будут сгенерированы и отображены идентификатор токена и секретный ключ. Секретный ключ будет показан только один раз, поэтому перед продолжением обязательно скопируйте значение в безопасное и надежное место.',
+    'user_api_token_create_success' => 'API токен успешно создан',
+    'user_api_token_update_success' => 'API токен успешно обновлен',
+    'user_api_token' => 'API токен',
+    'user_api_token_id' => 'Идентификатор токена',
+    'user_api_token_id_desc' => 'Это нередактируемый системный идентификатор для этого токена, который необходимо указывать в запросах API.',
+    'user_api_token_secret' => 'Секретный ключ',
+    'user_api_token_secret_desc' => 'Это сгенерированный системой секретный ключ для этого токена, который необходимо будет указывать в запросах API. Он будет показан только один раз, поэтому скопируйте это значение в безопасное и надежное место.',
+    'user_api_token_created' => 'Токен создан :timeAgo',
+    'user_api_token_updated' => 'Токен обновлён :timeAgo',
+    'user_api_token_delete' => 'Удалить токен',
+    'user_api_token_delete_warning' => 'Это полностью удалит API токен с именем \':tokenName\' из системы.',
+    'user_api_token_delete_confirm' => 'Вы уверены, что хотите удалить этот API токен?',
+    'user_api_token_delete_success' => 'API токен успешно удален',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 80cd89fd5f8c5feddad658fd1f25787ffd4749bd..d16ead5d843e737384416fd8152f9519cf83c337 100644 (file)
@@ -19,8 +19,8 @@ return [
     'between'              => [
         'numeric' => ':attribute должен быть между :min и :max.',
         'file'    => ':attribute должен быть между :min и :max килобайт.',
-        'string'  => 'длина :attribute Ð´Ð¾Ð»Ð¶ÐµÐ½Ð° Ð±Ñ\8bÑ\82Ñ\8c Ð¼ÐµÐ¶Ð´Ñ\83 :min Ð¸ :max Ñ\81имволами.',
-        'array'   => ':attribute должен содержать не менее :min и не более:max элементов.',
+        'string'  => 'длина :attribute должна быть между :min и :max символами.',
+        'array'   => ':attribute должен содержать не менее :min и не более :max элементов.',
     ],
     'boolean'              => ':attribute поле может быть только true или false.',
     'confirmed'            => ':attribute подтверждение не совпадает.',
@@ -30,19 +30,19 @@ return [
     'digits'               => ':attribute должен состоять из :digits цифр.',
     'digits_between'       => ':attribute должен иметь от :min до :max цифр.',
     'email'                => ':attribute должен быть корректным email адресом.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ':attribute должен заканчиваться одним из следующих: :values',
     'filled'               => ':attribute поле необходимо.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => 'Значение :attribute должно быть больше чем :value.',
+        'file'    => 'Значение :attribute должно быть больше :value килобайт.',
+        'string'  => 'Значение :attribute должно быть больше :value символов.',
+        'array'   => 'Значение :attribute должно содержать больше :value элементов.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => 'Значение :attribute должно быть больше или равно :value.',
+        'file'    => 'Значение :attribute должно быть больше или равно :value килобайт.',
+        'string'  => 'Значение :attribute должно быть больше или равно :value символам.',
+        'array'   => ':attribute должен иметь :value элементов или больше.',
     ],
     'exists'               => 'выделенный :attribute некорректен.',
     'image'                => ':attribute должен быть изображением.',
@@ -50,20 +50,20 @@ return [
     'in'                   => 'выделенный :attribute некорректен.',
     'integer'              => ':attribute должно быть целое число.',
     'ip'                   => ':attribute должен быть корректным IP адресом.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ipv4'                 => ':attribute должен быть корректным IPv4-адресом.',
+    'ipv6'                 => ':attribute должен быть корректным IPv6-адресом.',
+    'json'                 => ':attribute должен быть допустимой строкой JSON.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => 'Значение :attribute должно быть меньше, чем :value.',
+        'file'    => 'Значение :attribute должно быть меньше :value килобайт.',
+        'string'  => 'Значение :attribute должно быть меньше :value символов.',
+        'array'   => 'Значение :attribute должно быть меньше :value элементов.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => 'Значение :attribute должно быть меньше или равно :value.',
+        'file'    => 'Значение :attribute должно быть меньше или равно :value килобайт.',
+        'string'  => 'Значение :attribute должно быть меньше или равно :value символам.',
+        'array'   => 'Поле :attribute не должно содержать больше :value элементов.',
     ],
     'max'                  => [
         'numeric' => ':attribute не может быть больше чем :max.',
@@ -73,16 +73,16 @@ return [
     ],
     'mimes'                => ':attribute должен быть файлом с типом: :values.',
     'min'                  => [
-        'numeric' => ':attribute должен быть хотя бы :min.',
+        'numeric' => 'Поле :attribute должно быть не менее :min.',
         'file'    => ':attribute должен быть минимум :min килобайт.',
         'string'  => ':attribute должен быть минимум :min символов.',
         'array'   => ':attribute должен содержать хотя бы :min элементов.',
     ],
     'no_double_extension'  => ':attribute должен иметь только одно расширение файла.',
     'not_in'               => 'Выбранный :attribute некорректен.',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => 'Формат :attribute некорректен.',
     'numeric'              => ':attribute должен быть числом.',
-    'regex'                => ':attribute неправильный формат.',
+    'regex'                => 'Формат :attribute некорректен.',
     'required'             => ':attribute обязательное поле.',
     'required_if'          => ':attribute обязательное поле когда :other со значением :value.',
     'required_with'        => ':attribute обязательное поле когда :values установлено.',
@@ -90,6 +90,7 @@ return [
     'required_without'     => ':attribute обязательное поле когда :values не установлены.',
     'required_without_all' => ':attribute обязательное поле когда ни одно из :values не установлены.',
     'same'                 => ':attribute и :other должны совпадать.',
+    'safe_url'             => 'Предоставленная ссылка может быть небезопасной.',
     'size'                 => [
         'numeric' => ':attribute должен быть :size.',
         'file'    => ':attribute должен быть :size килобайт.',
@@ -99,7 +100,7 @@ return [
     'string'               => ':attribute должен быть строкой.',
     'timezone'             => ':attribute должен быть корректным часовым поясом.',
     'unique'               => ':attribute уже есть.',
-    'url'                  => ':attribute имеет неправильный формат.',
+    'url'                  => 'Формат :attribute некорректен.',
     'uploaded'             => 'Не удалось загрузить файл. Сервер не может принимать файлы такого размера.',
 
     // Custom validation lines
index 444d78e95d45586755aecba945a578d8663bd084..bbdec3cf7543c2c3d0206096397b41c9e8533336 100644 (file)
@@ -6,43 +6,44 @@
 return [
 
     // Pages
-    'page_create'                 => 'vytvoril stránku',
+    'page_create'                 => 'vytvoril(a) stránku',
     'page_create_notification'    => 'Stránka úspešne vytvorená',
     'page_update'                 => 'aktualizoval stránku',
     'page_update_notification'    => 'Stránka úspešne aktualizovaná',
-    'page_delete'                 => 'odstránil stránku',
+    'page_delete'                 => 'odstránil(a) stránku',
     'page_delete_notification'    => 'Stránka úspešne odstránená',
-    'page_restore'                => 'obnovil stránku',
+    'page_restore'                => 'obnovil(a) stránku',
     'page_restore_notification'   => 'Stránka úspešne obnovená',
-    'page_move'                   => 'presunul stránku',
+    'page_move'                   => 'presunul(a) stránku',
 
     // Chapters
-    'chapter_create'              => 'vytvoril kapitolu',
+    'chapter_create'              => 'vytvoril(a) kapitolu',
     'chapter_create_notification' => 'Kapitola úspešne vytvorená',
-    'chapter_update'              => 'aktualizoval kapitolu',
+    'chapter_update'              => 'aktualizoval(a) kapitolu',
     'chapter_update_notification' => 'Kapitola úspešne aktualizovaná',
-    'chapter_delete'              => 'odstránil kapitolu',
+    'chapter_delete'              => 'odstránil(a) kapitolu',
     'chapter_delete_notification' => 'Kapitola úspešne odstránená',
-    'chapter_move'                => 'presunul kapitolu',
+    'chapter_move'                => 'presunul(a) kapitolu',
 
     // Books
-    'book_create'                 => 'vytvoril knihu',
+    'book_create'                 => 'vytvoril(a) knihu',
     'book_create_notification'    => 'Kniha úspešne vytvorená',
-    'book_update'                 => 'aktualizoval knihu',
+    'book_update'                 => 'aktualizoval(a) knihu',
     'book_update_notification'    => 'Kniha úspešne aktualizovaná',
-    'book_delete'                 => 'odstránil knihu',
+    'book_delete'                 => 'odstránil(a) knihu',
     'book_delete_notification'    => 'Kniha úspešne odstránená',
-    'book_sort'                   => 'zoradil knihu',
+    'book_sort'                   => 'zoradil(a) knihu',
     'book_sort_notification'      => 'Kniha úspešne znovu zoradená',
 
     // Bookshelves
-    'bookshelf_create'            => 'created Bookshelf',
-    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
-    'bookshelf_update'                 => 'updated bookshelf',
-    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
-    'bookshelf_delete'                 => 'deleted bookshelf',
-    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+    'bookshelf_create'            => 'vytvoril(a) knižnicu',
+    'bookshelf_create_notification'    => 'Knižnica úspešne vytvorená',
+    'bookshelf_update'                 => 'aktualizoval(a) knižnicu',
+    'bookshelf_update_notification'    => 'Knižnica úspešne aktualizovaná',
+    'bookshelf_delete'                 => 'odstránil(a) knižnicu',
+    'bookshelf_delete_notification'    => 'Knižnica úspešne odstránená',
 
     // Other
-    'commented_on'                => 'commented on',
+    'commented_on'                => 'komentoval(a)',
+    'permissions_update'          => 'aktualizované oprávnenia',
 ];
index fcce42bbde733d1309a31be2d1908a599a28ed3c..0d96811a3671aa6bcad8ccf2e80d4ba537c0b464 100644 (file)
@@ -6,7 +6,7 @@
  */
 return [
 
-    'failed' => 'Tieto údaje nesedia s našimi záznamami.',
+    'failed' => 'Tieto údaje sa nezhodujú s našimi záznamami.',
     'throttle' => 'Priveľa pokusov o prihlásenie. Skúste znova o :seconds sekúnd.',
 
     // Login & Register
@@ -18,60 +18,60 @@ return [
 
     'name' => 'Meno',
     'username' => 'Používateľské meno',
-    'email' => 'Email',
+    'email' => 'E-mail',
     'password' => 'Heslo',
     'password_confirm' => 'Potvrdiť heslo',
     'password_hint' => 'Musí mať viac ako 7 znakov',
     'forgot_password' => 'Zabudli ste heslo?',
     'remember_me' => 'Zapamätať si ma',
-    'ldap_email_hint' => 'Zadajte prosím email, ktorý sa má použiť pre tento účet.',
+    'ldap_email_hint' => 'Zadajte prosím e-mail, ktorý sa má použiť pre tento účet.',
     'create_account' => 'Vytvoriť účet',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
+    'already_have_account' => 'Už máte svoj ​​účet?',
+    'dont_have_account' => 'Nemáte účet?',
     'social_login' => 'Sociálne prihlásenie',
     'social_registration' => 'Sociálna registrácia',
-    'social_registration_text' => 'Registrovať sa a prihlásiť sa použitím inej služby.',
+    'social_registration_text' => 'Registrácia a prihlásenie pomocou inej služby.',
 
-    'register_thanks' => 'Ďakujeme zaregistráciu!',
-    'register_confirm' => 'Skontrolujte prosím svoj email a kliknite na potvrdzujúce tlačidlo pre prístup k :appName.',
+    'register_thanks' => 'Ďakujeme za registráciu!',
+    'register_confirm' => 'Prosím, skontrolujte svoj e-mail a kliknite na potvrdzujúce tlačidlo pre prístup k :appName.',
     'registrations_disabled' => 'Registrácie sú momentálne zablokované',
-    'registration_email_domain_invalid' => 'Táto emailová doména nemá prístup k tejto aplikácii',
+    'registration_email_domain_invalid' => 'Táto e-mailová doména nemá prístup k tejto aplikácii',
     'register_success' => 'Ďakujeme za registráciu! Teraz ste registrovaný a prihlásený.',
 
 
     // Password Reset
-    'reset_password' => 'Reset hesla',
-    'reset_password_send_instructions' => 'Zadajte svoj email nižšie a bude Vám odoslaný email s odkazom pre reset hesla.',
-    'reset_password_send_button' => 'Poslať odkaz na reset hesla',
-    'reset_password_sent_success' => 'Odkaz na reset hesla bol poslaný na :email.',
+    'reset_password' => 'Resetovanie hesla',
+    'reset_password_send_instructions' => 'Nižšie zadajte svoj e-mail, na ktorý Vám zašleme odkaz pre resetovanie hesla.',
+    'reset_password_send_button' => 'Poslať odkaz na resetovanie hesla',
+    'reset_password_sent' => 'Odkaz na resetovanie hesla bude odoslaný na :email, ak sa táto e-mailová adresa nachádza v systéme.',
     'reset_password_success' => 'Vaše heslo bolo úspešne resetované.',
-    'email_reset_subject' => 'Reset Vášho :appName hesla',
-    'email_reset_text' => 'Tento email Ste dostali pretože sme dostali požiadavku na reset hesla pre Váš účet.',
-    'email_reset_not_requested' => 'Ak ste nepožiadali o reset hesla, nemusíte nič robiť.',
+    'email_reset_subject' => 'Resetovanie Vášho hesla do :appName',
+    'email_reset_text' => 'Tento e-mail ste obdržali, pretože sme dostali požiadavku na resetovanie hesla pre Váš účet.',
+    'email_reset_not_requested' => 'Ak ste nepožiadali o resetovanie hesla, nemusíte robiť nič.',
 
 
     // Email Confirmation
-    'email_confirm_subject' => 'Potvrdiť email na :appName',
-    'email_confirm_greeting' => 'Ďakujeme za pridanie sa k :appName!',
-    'email_confirm_text' => 'Prosím potvrďte Vašu emailovú adresu kliknutím na tlačidlo nižšie:',
-    'email_confirm_action' => 'Potvrdiť email',
-    'email_confirm_send_error' => 'Je požadované overenie emailu, ale systém nemohol odoslať email. Kontaktujte administrátora by ste sa uistili, že email je nastavený správne.',
+    'email_confirm_subject' => 'Potvrdiť e-mail na :appName',
+    'email_confirm_greeting' => 'Ďakujeme, že ste sa pridali k :appName!',
+    'email_confirm_text' => 'Prosím, potvrďte Vašu e-mailovú adresu kliknutím na tlačidlo nižšie:',
+    'email_confirm_action' => 'Potvrdiť e-mail',
+    'email_confirm_send_error' => 'Je požadované overenie e-mailu, ale systém nemohol e-mail odoslať. Kontaktujte administrátora, aby ste sa uistili, že je e-mail nastavený správne.',
     'email_confirm_success' => 'Váš email bol overený!',
-    'email_confirm_resent' => 'Potvrdzujúci email bol poslaný znovu, skontrolujte prosím svoju emailovú schránku.',
+    'email_confirm_resent' => 'Potvrdzujúci e-mail bol poslaný znovu, skontrolujte prosím svoju e-mailovú schránku.',
 
-    'email_not_confirmed' => 'Emailová adresa nebola overená',
-    'email_not_confirmed_text' => 'Vaša emailová adresa nebola zatiaľ overená.',
-    'email_not_confirmed_click_link' => 'Prosím, kliknite na odkaz v emaili, ktorý bol poslaný krátko po Vašej registrácii.',
-    'email_not_confirmed_resend' => 'Ak nemôžete násť email, môžete znova odoslať overovací email odoslaním doleuvedeného formulára.',
-    'email_not_confirmed_resend_button' => 'Znova odoslať overovací email',
+    'email_not_confirmed' => 'E-mailová adresa nebola overená',
+    'email_not_confirmed_text' => 'Vaša e-mailová adresa nebola zatiaľ overená.',
+    'email_not_confirmed_click_link' => 'Prosím, kliknite na odkaz v e-maili, ktorý bol poslaný krátko po Vašej registrácii.',
+    'email_not_confirmed_resend' => 'Ak nemôžete nájsť e-mail, môžete znova odoslať overovací e-mail odoslaním doleuvedeného formulára.',
+    'email_not_confirmed_resend_button' => 'Znova odoslať overovací e-mail',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Dostali ste pozvánku na pripojenie sa k aplikácii :appName!',
+    'user_invite_email_greeting' => 'Účet pre :appName bol pre vás vytvorený.',
+    'user_invite_email_text' => 'Kliknutím na tlačidlo nižšie nastavíte heslo k účtu a získate prístup:',
+    'user_invite_email_action' => 'Nastaviť heslo k účtu',
+    'user_invite_page_welcome' => 'Vitajte v :appName!',
+    'user_invite_page_text' => 'Ak chcete dokončiť svoj účet a získať prístup, musíte nastaviť heslo, ktoré sa použije na prihlásenie do aplikácie :appName pri budúcich návštevách.',
+    'user_invite_page_confirm_button' => 'Potvrdiť heslo',
+    'user_invite_success' => 'Heslo bolo nastavené, teraz máte prístup k :appName!'
 ];
\ No newline at end of file
index 1f915c71e7d24dfdbc15863ecc21a70316a76b53..555c2c6f2c65c71427763c8f52f17f2b4554dd74 100644 (file)
@@ -11,8 +11,8 @@ return [
     'save' => 'Uložiť',
     'continue' => 'Pokračovať',
     'select' => 'Vybrať',
-    'toggle_all' => 'Toggle All',
-    'more' => 'More',
+    'toggle_all' => 'Prepnúť všetko',
+    'more' => 'Viac',
 
     // Form Labels
     'name' => 'Meno',
@@ -24,29 +24,31 @@ return [
     // Actions
     'actions' => 'Akcie',
     'view' => 'Zobraziť',
-    'view_all' => 'View All',
+    'view_all' => 'Zobraziť všetko',
     'create' => 'Vytvoriť',
     'update' => 'Aktualizovať',
     'edit' => 'Editovať',
     'sort' => 'Zoradiť',
     'move' => 'Presunúť',
-    'copy' => 'Copy',
-    'reply' => 'Reply',
+    'copy' => 'Kopírovať',
+    'reply' => 'Odpovedať',
     'delete' => 'Zmazať',
-    'search' => 'Hľadť',
+    'delete_confirm' => 'Potvrdiť zmazanie',
+    'search' => 'Hľadať',
     'search_clear' => 'Vyčistiť hľadanie',
-    'reset' => 'Reset',
+    'reset' => 'Resetovať',
     'remove' => 'Odstrániť',
-    'add' => 'Add',
+    'add' => 'Pridať',
+    'fullscreen' => 'Celá obrazovka',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
-    'sort_name' => 'Name',
-    'sort_created_at' => 'Created Date',
-    'sort_updated_at' => 'Updated Date',
+    'sort_options' => 'Možnosti triedenia',
+    'sort_direction_toggle' => 'Zoradiť smerový prepínač',
+    'sort_ascending' => 'Zoradiť vzostupne',
+    'sort_descending' => 'Zoradiť zostupne',
+    'sort_name' => 'Meno',
+    'sort_created_at' => 'Dátum vytvorenia',
+    'sort_updated_at' => 'Aktualizované dňa',
 
     // Misc
     'deleted_user' => 'Odstránený používateľ',
@@ -55,20 +57,22 @@ return [
     'back_to_top' => 'Späť nahor',
     'toggle_details' => 'Prepnúť detaily',
     'toggle_thumbnails' => 'Prepnúť náhľady',
-    'details' => 'Details',
-    'grid_view' => 'Grid View',
-    'list_view' => 'List View',
-    'default' => 'Default',
+    'details' => 'Podrobnosti',
+    'grid_view' => 'Zobrazenie v mriežke',
+    'list_view' => 'Zobraziť ako zoznam',
+    'default' => 'Predvolené',
     'breadcrumb' => 'Breadcrumb',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Menu profilu',
     'view_profile' => 'Zobraziť profil',
     'edit_profile' => 'Upraviť profil',
+    'dark_mode' => 'Tmavý režim',
+    'light_mode' => 'Svetlý režim',
 
     // Layout tabs
-    'tab_info' => 'Info',
-    'tab_content' => 'Content',
+    'tab_info' => 'Informácie',
+    'tab_content' => 'Obsah',
 
     // Email Content
     'email_action_help' => 'Ak máte problém klinkúť na tlačidlo ":actionText", skopírujte a vložte URL uvedenú nižšie do Vášho prehliadača:',
index 726d8361438ec26712eaaf5611b8de49868a16b7..a6bae619c47be8ecc19bf88725909c61634d2985 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Načítať viac',
     'image_image_name' => 'Názov obrázka',
     'image_delete_used' => 'Tento obrázok je použitý na stránkach uvedených nižšie.',
-    'image_delete_confirm' => 'Kliknite znova na zmazať pre potvrdenie zmazania tohto obrázka.',
+    'image_delete_confirm_text' => 'Naozaj chcete vymazať tento obrázok?',
     'image_select_image' => 'Vybrať obrázok',
     'image_dropzone' => 'Presuňte obrázky sem alebo kliknite sem pre nahranie',
     'images_deleted' => 'Obrázky zmazané',
@@ -23,11 +23,12 @@ return [
     'image_upload_success' => 'Obrázok úspešne nahraný',
     'image_update_success' => 'Detaily obrázka úspešne aktualizované',
     'image_delete_success' => 'Obrázok úspešne zmazaný',
-    'image_upload_remove' => 'Remove',
+    'image_upload_remove' => 'Odstrániť',
 
     // Code Editor
-    'code_editor' => 'Edit Code',
-    'code_language' => 'Code Language',
-    'code_content' => 'Code Content',
-    'code_save' => 'Save Code',
+    'code_editor' => 'Upraviť kód',
+    'code_language' => 'Kód jazyka',
+    'code_content' => 'Obsah kódu',
+    'code_session_history' => 'História relácií',
+    'code_save' => 'Ulož kód',
 ];
index 2b42ca86cd43c89238b18d808ad1411c4807b8dd..4b026c506c5a0a0f8c84e649feea1f2ea5968118 100644 (file)
@@ -11,17 +11,18 @@ return [
     'recently_updated_pages' => 'Nedávno aktualizované stránky',
     'recently_created_chapters' => 'Nedávno vytvorené kapitoly',
     'recently_created_books' => 'Nedávno vytvorené knihy',
-    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_created_shelves' => 'Nedávno vytvorené knižnice',
     'recently_update' => 'Nedávno aktualizované',
     'recently_viewed' => 'Nedávno zobrazené',
     'recent_activity' => 'Nedávna aktivita',
     'create_now' => 'Vytvoriť teraz',
     'revisions' => 'Revízie',
-    'meta_revision' => 'Revision #:revisionCount',
+    'meta_revision' => 'Upravené vydanie #:revisionCount',
     'meta_created' => 'Vytvorené :timeLength',
     'meta_created_name' => 'Vytvorené :timeLength používateľom :user',
     'meta_updated' => 'Aktualizované :timeLength',
     'meta_updated_name' => 'Aktualizované :timeLength používateľom :user',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Entita vybraná',
     'images' => 'Obrázky',
     'my_recent_drafts' => 'Moje nedávne koncepty',
@@ -29,8 +30,8 @@ return [
     'no_pages_viewed' => 'Nepozreli ste si žiadne stránky',
     'no_pages_recently_created' => 'Žiadne stránky neboli nedávno vytvorené',
     'no_pages_recently_updated' => 'Žiadne stránky neboli nedávno aktualizované',
-    'export' => 'Export',
-    'export_html' => 'Contained Web File',
+    'export' => 'Exportovať',
+    'export_html' => 'Obsahovaný webový súbor',
     'export_pdf' => 'PDF súbor',
     'export_text' => 'Súbor s čistým textom',
 
@@ -39,59 +40,61 @@ return [
     'permissions_intro' => 'Ak budú tieto oprávnenia povolené, budú mať prioritu pred oprávneniami roly.',
     'permissions_enable' => 'Povoliť vlastné oprávnenia',
     'permissions_save' => 'Uložiť oprávnenia',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Výsledky hľadania',
-    'search_total_results_found' => ':count result found|:count total results found',
+    'search_total_results_found' => ':count výsledok found|:počet nájdených výsledkov',
     'search_clear' => 'Vyčistiť hľadanie',
     'search_no_pages' => 'Žiadne stránky nevyhovujú tomuto hľadaniu',
     'search_for_term' => 'Hľadať :term',
-    'search_more' => 'More Results',
-    'search_filters' => 'Search Filters',
-    'search_content_type' => 'Content Type',
-    'search_exact_matches' => 'Exact Matches',
-    'search_tags' => 'Tag Searches',
-    'search_options' => 'Options',
-    'search_viewed_by_me' => 'Viewed by me',
-    'search_not_viewed_by_me' => 'Not viewed by me',
-    'search_permissions_set' => 'Permissions set',
-    'search_created_by_me' => 'Created by me',
-    'search_updated_by_me' => 'Updated by me',
-    'search_date_options' => 'Date Options',
-    'search_updated_before' => 'Updated before',
-    'search_updated_after' => 'Updated after',
-    'search_created_before' => 'Created before',
-    'search_created_after' => 'Created after',
-    'search_set_date' => 'Set Date',
-    'search_update' => 'Update Search',
+    'search_more' => 'Načítať ďalšie výsledky',
+    'search_advanced' => 'Rozšírené vyhľadávanie',
+    'search_terms' => 'Hľadané výrazy',
+    'search_content_type' => 'Typ obsahu',
+    'search_exact_matches' => 'Presná zhoda',
+    'search_tags' => 'Vyhľadávanie značiek',
+    'search_options' => 'Možnosti',
+    'search_viewed_by_me' => 'Videné mnou',
+    'search_not_viewed_by_me' => 'Nevidené mnou',
+    'search_permissions_set' => 'Oprávnenia',
+    'search_created_by_me' => 'Vytvorené mnou',
+    'search_updated_by_me' => 'Aktualizované mnou',
+    'search_date_options' => 'Možnosti dátumu',
+    'search_updated_before' => 'Aktualizované pred',
+    'search_updated_after' => 'Aktualizované po',
+    'search_created_before' => 'Vytvorené pred',
+    'search_created_after' => 'Vytvorené po',
+    'search_set_date' => 'Nastaviť Dátum',
+    'search_update' => 'Aktualizujte vyhľadávanie',
 
     // Shelves
-    'shelf' => 'Shelf',
-    'shelves' => 'Shelves',
-    'x_shelves' => ':count Shelf|:count Shelves',
-    'shelves_long' => 'Bookshelves',
-    'shelves_empty' => 'No shelves have been created',
-    'shelves_create' => 'Create New Shelf',
-    'shelves_popular' => 'Popular Shelves',
-    'shelves_new' => 'New Shelves',
-    'shelves_new_action' => 'New Shelf',
-    'shelves_popular_empty' => 'The most popular shelves will appear here.',
-    'shelves_new_empty' => 'The most recently created shelves will appear here.',
-    'shelves_save' => 'Save Shelf',
-    'shelves_books' => 'Books on this shelf',
-    'shelves_add_books' => 'Add books to this shelf',
-    'shelves_drag_books' => 'Drag books here to add them to this shelf',
-    'shelves_empty_contents' => 'This shelf has no books assigned to it',
-    'shelves_edit_and_assign' => 'Edit shelf to assign books',
-    'shelves_edit_named' => 'Edit Bookshelf :name',
-    'shelves_edit' => 'Edit Bookshelf',
-    'shelves_delete' => 'Delete Bookshelf',
-    'shelves_delete_named' => 'Delete Bookshelf :name',
-    'shelves_delete_explain' => "This will delete the bookshelf with the name ':name'. Contained books will not be deleted.",
-    'shelves_delete_confirmation' => 'Are you sure you want to delete this bookshelf?',
-    'shelves_permissions' => 'Bookshelf Permissions',
-    'shelves_permissions_updated' => 'Bookshelf Permissions Updated',
-    'shelves_permissions_active' => 'Bookshelf Permissions Active',
+    'shelf' => 'Polica',
+    'shelves' => 'Police',
+    'x_shelves' => ':count Shelf|:count Police',
+    'shelves_long' => 'Poličky na knihy',
+    'shelves_empty' => 'Neboli vytvorené žiadne police',
+    'shelves_create' => 'Vytvoriť novú policu',
+    'shelves_popular' => 'Populárne police',
+    'shelves_new' => 'Nové police',
+    'shelves_new_action' => 'Nová polica',
+    'shelves_popular_empty' => 'Najpopulárnejšie police sa objavia tu.',
+    'shelves_new_empty' => 'Najpopulárnejšie police sa objavia tu.',
+    'shelves_save' => 'Uložiť policu',
+    'shelves_books' => 'Knihy na tejto polici',
+    'shelves_add_books' => 'Pridať knihy do tejto police',
+    'shelves_drag_books' => 'Potiahnite knihy sem a pridajte ich do tejto police',
+    'shelves_empty_contents' => 'Táto polica nemá priradené žiadne knihy',
+    'shelves_edit_and_assign' => 'Uprav policu a priraď knihy',
+    'shelves_edit_named' => 'Upraviť poličku::name',
+    'shelves_edit' => 'Upraviť policu',
+    'shelves_delete' => 'Odstrániť knižnicu',
+    'shelves_delete_named' => 'Odstrániť knižnicu :name',
+    'shelves_delete_explain' => "Týmto vymažete policu s názvom ': name'. Obsahované knihy sa neodstránia.",
+    'shelves_delete_confirmation' => 'Ste si istý, že chcete zmazať túto knižnicu?',
+    'shelves_permissions' => 'Oprávnenia knižnice',
+    'shelves_permissions_updated' => 'Oprávnenia knižnice aktualizované',
+    'shelves_permissions_active' => 'Oprávnenia knižnice aktívne',
     'shelves_copy_permissions_to_books' => 'Copy Permissions to Books',
     'shelves_copy_permissions' => 'Copy Permissions',
     'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this bookshelf to all books contained within. Before activating, ensure any changes to the permissions of this bookshelf have been saved.',
@@ -145,8 +148,7 @@ return [
     'chapters_create' => 'Vytvoriť novú kapitolu',
     'chapters_delete' => 'Zmazať kapitolu',
     'chapters_delete_named' => 'Zmazať kapitolu :chapterName',
-    'chapters_delete_explain' => 'Toto zmaže kapitolu menom \':chapterName\', všetky stránky budú ostránené
-        a pridané priamo do rodičovskej knihy.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'Ste si istý, že chcete zmazať túto kapitolu?',
     'chapters_edit' => 'Upraviť kapitolu',
     'chapters_edit_named' => 'Upraviť kapitolu :chapterName',
@@ -208,6 +210,7 @@ return [
     'pages_revisions' => 'Revízie stránky',
     'pages_revisions_named' => 'Revízie stránky :pageName',
     'pages_revision_named' => 'Revízia stránky :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Vytvoril',
     'pages_revisions_date' => 'Dátum revízie',
     'pages_revisions_number' => '#',
@@ -256,7 +259,7 @@ return [
     'attachments_upload' => 'Nahrať súbor',
     'attachments_link' => 'Priložiť odkaz',
     'attachments_set_link' => 'Nastaviť odkaz',
-    'attachments_delete_confirm' => 'Kliknite znova na zmazať pre potvrdenie zmazania prílohy.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Presuňte súbory alebo klinknite sem pre priloženie súboru',
     'attachments_no_files' => 'Žiadne súbory neboli nahrané',
     'attachments_explain_link' => 'Ak nechcete priložiť súbor, môžete priložiť odkaz. Môže to byť odkaz na inú stránku alebo odkaz na súbor v cloude.',
@@ -265,6 +268,7 @@ return [
     'attachments_link_url' => 'Odkaz na súbor',
     'attachments_link_url_hint' => 'Url stránky alebo súboru',
     'attach' => 'Priložiť',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Upraviť súbor',
     'attachments_edit_file_name' => 'Názov súboru',
     'attachments_edit_drop_upload' => 'Presuňte súbory sem alebo klinknite pre nahranie a prepis',
@@ -304,12 +308,12 @@ return [
     'comment_deleted_success' => 'Comment deleted',
     'comment_created_success' => 'Comment added',
     'comment_updated_success' => 'Comment updated',
-    'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
-    'comment_in_reply_to' => 'In reply to :commentId',
+    'comment_delete_confirm' => 'Ste si istý, že chcete odstrániť tento komentár?',
+    'comment_in_reply_to' => 'Odpovedať na :commentId',
 
     // Revision
     'revision_delete_confirm' => 'Naozaj chcete túto revíziu odstrániť?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_restore_confirm' => 'Naozaj chcete obnoviť túto revíziu? Aktuálny obsah stránky sa nahradí.',
     'revision_delete_success' => 'Revízia bola vymazaná',
     'revision_cannot_delete_latest' => 'Nie je možné vymazať poslednú revíziu.'
 ];
\ No newline at end of file
index 361711d9aae29f1c66264d33bfcc018310b858ef..c045d473d05e99e86971523018513dc50de667d8 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'Email bol už overený, skúste sa prihlásiť.',
     'email_confirmation_invalid' => 'Tento potvrdzujúci token nie je platný alebo už bol použitý, skúste sa prosím registrovať znova.',
     'email_confirmation_expired' => 'Potvrdzujúci token expiroval, bol odoslaný nový potvrdzujúci email.',
+    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
     'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
     'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
     'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
     'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
+    'saml_already_logged_in' => 'Already logged in',
+    'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
+    'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
+    'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
+    'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
     'social_no_action_defined' => 'Nebola definovaná žiadna akcia',
     'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
     'social_account_in_use' => 'Tento :socialAccount účet sa už používa, skúste sa prihlásiť pomocou možnosti :socialAccount.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'Nahrávanie súboru vypršalo.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'Attachment not found',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Stránka nenájdená',
     'sorry_page_not_found' => 'Prepáčte, stránka ktorú hľadáte nebola nájdená.',
+    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
     'return_home' => 'Vrátiť sa domov',
     'error_occurred' => 'Nastala chyba',
     'app_down' => ':appName je momentálne nedostupná',
     'back_soon' => 'Čoskoro bude opäť dostupná.',
 
+    // API errors
+    'api_no_authorization_found' => 'No authorization token found on the request',
+    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
+    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
+    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
+    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
+    'api_user_token_expired' => 'The authorization token used has expired',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+
 ];
index de7d24442212948ca4ec3009b07dd89fa555f6b1..5b80f62da63877abbf0ebb07084bcfb6efd14aa7 100644 (file)
@@ -6,9 +6,9 @@
  */
 return [
 
-    'password' => 'Heslo musí obsahovať aspoň šesť znakov a musí byť rovnaké ako potvrdzujúce.',
+    'password' => 'Heslo musí obsahovať aspoň osem znakov a musí byť rovnaké ako potvrdzujúce.',
     'user' => "Nenašli sme používateľa s takou emailovou adresou.",
-    'token' => 'Tento token pre reset hesla je neplatný.',
+    'token' => 'Token na obnovenie hesla je pre túto e-mailovú adresu neplatný.',
     'sent' => 'Poslali sme Vám email s odkazom na reset hesla!',
     'reset' => 'Vaše heslo bolo resetované!',
 
index dc1e06bc58f01dd82d7bef8ab78c9757632d5009..17e658a3fbe6413e82236840da0e2f5d6d024bab 100644 (file)
@@ -41,12 +41,22 @@ return [
     'app_disable_comments_toggle' => 'Disable comments',
     'app_disable_comments_desc' => 'Zakázať komentáre na všetkých stránkach aplikácie. Existujúce komentáre sa nezobrazujú.',
 
+    // Color settings
+    'content_colors' => 'Content Colors',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
     // Registration Settings
     'reg_settings' => 'Nastavenia registrácie',
     'reg_enable' => 'Enable Registration',
     'reg_enable_toggle' => 'Enable registration',
     'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
     'reg_default_role' => 'Prednastavená používateľská rola po registrácii',
+    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
     'reg_email_confirmation' => 'Email Confirmation',
     'reg_email_confirmation_toggle' => 'Require email confirmation',
     'reg_confirm_email_desc' => 'Ak je použité obmedzenie domény, potom bude vyžadované overenie emailu a hodnota nižšie bude ignorovaná.',
@@ -58,11 +68,53 @@ return [
     'maint' => 'Maintenance',
     'maint_image_cleanup' => 'Cleanup Images',
     'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Run Cleanup',
     'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
     'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
     'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
+    'maint_send_test_email' => 'Send a Test Email',
+    'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
+    'maint_send_test_email_run' => 'Send test email',
+    'maint_send_test_email_success' => 'Email sent to :address',
+    'maint_send_test_email_mail_subject' => 'Test Email',
+    'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
+    'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
 
     // Role Settings
     'roles' => 'Roly',
@@ -86,8 +138,10 @@ return [
     'role_manage_entity_permissions' => 'Spravovať všetky oprávnenia kníh, kapitol a stránok',
     'role_manage_own_entity_permissions' => 'Spravovať oprávnenia vlastných kníh, kapitol a stránok',
     'role_manage_page_templates' => 'Manage page templates',
+    'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Spravovať nastavenia aplikácie',
     'role_asset' => 'Oprávnenia majetku',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Tieto oprávnenia regulujú prednastavený prístup k zdroju v systéme. Oprávnenia pre knihy, kapitoly a stránky majú vyššiu prioritu.',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => 'Všetko',
@@ -103,6 +157,7 @@ return [
     'user_profile' => 'Profil používateľa',
     'users_add_new' => 'Pridať nového používateľa',
     'users_search' => 'Hľadať medzi používateľmi',
+    'users_latest_activity' => 'Latest Activity',
     'users_details' => 'User Details',
     'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
     'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
@@ -113,14 +168,17 @@ return [
     'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
     'users_send_invite_option' => 'Send user invite email',
     'users_external_auth_id' => 'Externé autentifikačné ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
     'users_password_warning' => 'Pole nižšie vyplňte iba ak chcete zmeniť heslo:',
     'users_system_public' => 'Tento účet reprezentuje každého hosťovského používateľa, ktorý navštívi Vašu inštanciu. Nedá sa pomocou neho prihlásiť a je priradený automaticky.',
     'users_delete' => 'Zmazať používateľa',
     'users_delete_named' => 'Zmazať používateľa :userName',
     'users_delete_warning' => ' Toto úplne odstráni používateľa menom \':userName\' zo systému.',
     'users_delete_confirm' => 'Ste si istý, že chcete zmazať tohoto používateľa?',
-    'users_delete_success' => 'Používateľ úspešne zmazaný',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Upraviť používateľa',
     'users_edit_profile' => 'Upraviť profil',
     'users_edit_success' => 'Používateľ úspešne upravený',
@@ -134,6 +192,32 @@ return [
     'users_social_disconnect' => 'Odpojiť účet',
     'users_social_connected' => ':socialAccount účet bol úspešne pripojený k Vášmu profilu.',
     'users_social_disconnected' => ':socialAccount účet bol úspešne odpojený od Vášho profilu.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
+    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 8dc26905dc4b5ee8c606385cc40dd876a7ceb992..c127b0623cc24158c006b7219ccf5f97546774c8 100644 (file)
@@ -90,6 +90,7 @@ return [
     'required_without'     => 'Políčko :attribute je povinné aj :values neexistuje.',
     'required_without_all' => 'Políčko :attribute je povinné ak ani jedno z :values neexistuje.',
     'same'                 => ':attribute a :other musia byť rovnaké.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
         'numeric' => ':attribute musí byť :size.',
         'file'    => ':attribute musí mať :size kilobajtov.',
diff --git a/resources/lang/sl/activities.php b/resources/lang/sl/activities.php
new file mode 100644 (file)
index 0000000..4ed1185
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'ustvarjena stran',
+    'page_create_notification'    => 'Stran uspešno ustvarjena',
+    'page_update'                 => 'posodobljena stran',
+    'page_update_notification'    => 'Stran uspešno posodobljena',
+    'page_delete'                 => 'izbrisana stran',
+    'page_delete_notification'    => 'Stran uspešno izbrisana',
+    'page_restore'                => 'obnovljena stran',
+    'page_restore_notification'   => 'Stran uspešno obnovljena',
+    'page_move'                   => 'premaknjena stran',
+
+    // Chapters
+    'chapter_create'              => 'ustvarjeno poglavje',
+    'chapter_create_notification' => 'Poglavje uspešno ustvarjeno',
+    'chapter_update'              => 'posodobljeno poglavje',
+    'chapter_update_notification' => 'Poglavje uspešno posodobljeno',
+    'chapter_delete'              => 'izbrisano poglavje',
+    'chapter_delete_notification' => 'Poglavje uspešno izbrisano',
+    'chapter_move'                => 'premaknjeno poglavje',
+
+    // Books
+    'book_create'                 => 'knjiga ustvarjena',
+    'book_create_notification'    => 'Knjiga uspešno usvarjena',
+    'book_update'                 => 'knjiga posodobljena',
+    'book_update_notification'    => 'Knjiga uspešno posodobljena',
+    'book_delete'                 => 'izbrisana knjiga',
+    'book_delete_notification'    => 'Knjiga uspešno izbrisana',
+    'book_sort'                   => 'razvrščena knjiga',
+    'book_sort_notification'      => 'Knjiga uspešno razvrščena',
+
+    // Bookshelves
+    'bookshelf_create'            => 'knjižna polica izdelana',
+    'bookshelf_create_notification'    => 'Knjižna polica uspešno ustvarjena',
+    'bookshelf_update'                 => 'knjižna polica posodobljena',
+    'bookshelf_update_notification'    => 'Knjižna polica uspešno posodobljena',
+    'bookshelf_delete'                 => 'knjižna polica izbrisana',
+    'bookshelf_delete_notification'    => 'Knjižna polica uspešno Izbrisana',
+
+    // Other
+    'commented_on'                => 'komentar na',
+    'permissions_update'          => 'pravice so posodobljene',
+];
diff --git a/resources/lang/sl/auth.php b/resources/lang/sl/auth.php
new file mode 100644 (file)
index 0000000..df6fb42
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'Poverilnice se ne ujemajo s podatki v naši bazi.',
+    'throttle' => 'Prekoračili ste število možnih prijav. Poskusite znova čez :seconds sekund.',
+
+    // Login & Register
+    'sign_up' => 'Registracija',
+    'log_in' => 'Prijavi se',
+    'log_in_with' => 'Prijavi se z :socialDriver',
+    'sign_up_with' => 'Registriraj se z :socialDriver',
+    'logout' => 'Odjavi se',
+
+    'name' => 'Ime',
+    'username' => 'Uporabniško ime',
+    'email' => 'E-pošta',
+    'password' => 'Geslo',
+    'password_confirm' => 'Potrdi geslo',
+    'password_hint' => 'Mora vebovati vsaj 8 znakov',
+    'forgot_password' => 'Pozabljeno geslo?',
+    'remember_me' => 'Zapomni si me',
+    'ldap_email_hint' => 'Prosimo vpišite e-poštni naslov za ta račun.',
+    'create_account' => 'Ustvari račun',
+    'already_have_account' => 'Že imate račun?',
+    'dont_have_account' => 'Nimate računa?',
+    'social_login' => 'Prijava z računi družbenih omrežij',
+    'social_registration' => 'Registracija z družbenim omrežjem',
+    'social_registration_text' => 'Registrirajte in prijavite se za uporabo drugih možnosti.',
+
+    'register_thanks' => 'Hvala za registracijo!',
+    'register_confirm' => 'Prosimo preverite vaš e-poštni predal in kliknite na potrditveni gumb za dostop :appName.',
+    'registrations_disabled' => 'Registracija trenutno ni mogoča',
+    'registration_email_domain_invalid' => 'Ta e-poštna domena nima dostopa do te aplikacije',
+    'register_success' => 'Hvala za registracijo! Sedaj ste registrirani in prijavljeni.',
+
+
+    // Password Reset
+    'reset_password' => 'Ponastavi geslo',
+    'reset_password_send_instructions' => 'Spodaj vpišite vaš e-poštni naslov in prejeli boste e-pošto s povezavo za ponastavitev gesla.',
+    'reset_password_send_button' => 'Pošlji povezavo za ponastavitev',
+    'reset_password_sent' => 'V kolikor e-poštni naslov :email obstaja v sistemu, bo nanj poslana povezava za ponastavitev gesla.',
+    'reset_password_success' => 'Vaše geslo je bilo uspešno spremenjeno.',
+    'email_reset_subject' => 'Ponastavi svoje :appName geslo',
+    'email_reset_text' => 'To e-poštno sporočilo ste prejeli, ker smo prejeli zahtevo za ponastavitev gesla za vaš račun.',
+    'email_reset_not_requested' => 'Če niste zahtevali ponastavitve gesla, vam ni potrebno ničesar storiti.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Potrdi svojo e-pošto za :appName',
+    'email_confirm_greeting' => 'Hvala ker ste se pridružili :appName!',
+    'email_confirm_text' => 'Potrdite svoj e-naslov s klikom spodnjega gumba:',
+    'email_confirm_action' => 'Potrdi e-pošto',
+    'email_confirm_send_error' => 'E-poštna potrditev je zahtevana ampak sistem ni mogel poslati e-pošte. Kontaktirajte administratorja, da zagotovite, da je e-pošta pravilno nastavljena.',
+    'email_confirm_success' => 'Vaš e-naslov je bil potrjen!',
+    'email_confirm_resent' => 'Poslali smo vam potrditveno sporočilo. Prosimo preverite svojo elektronsko pošto.',
+
+    'email_not_confirmed' => 'Elektronski naslov ni potrjen',
+    'email_not_confirmed_text' => 'Vaš e-naslov še ni bil potrjen.',
+    'email_not_confirmed_click_link' => 'Prosimo kliknite na link v e-poštnem sporočilu, ki ste ga prejeli kmalu po registraciji.',
+    'email_not_confirmed_resend' => 'Če ne najdete e-pošte jo lahko ponovno pošljete s potrditvijo obrazca.',
+    'email_not_confirmed_resend_button' => 'Ponovno pošlji potrditveno e-pošto',
+
+    // User Invite
+    'user_invite_email_subject' => 'Povabljen si bil da se pridružiš :appName!',
+    'user_invite_email_greeting' => 'Račun je bil ustvarjen zate na :appName.',
+    'user_invite_email_text' => 'Klikni na spodnji gumb, da si nastaviš geslo in dobiš dostop:',
+    'user_invite_email_action' => 'Nastavi geslo za račun',
+    'user_invite_page_welcome' => 'Dobrodošli na :appName!',
+    'user_invite_page_text' => 'Za zaključiti in pridobiti dostop si morate nastaviti geslo, ki bo uporabljeno za prijavo v :appName.',
+    'user_invite_page_confirm_button' => 'Potrdi geslo',
+    'user_invite_success' => 'Geslo nastavljeno, sedaj imaš dostop do :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/sl/common.php b/resources/lang/sl/common.php
new file mode 100644 (file)
index 0000000..65a4228
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Prekliči',
+    'confirm' => 'Potrdi',
+    'back' => 'Nazaj',
+    'save' => 'Shrani',
+    'continue' => 'Nadaljuj',
+    'select' => 'Izberi',
+    'toggle_all' => 'Vklopi vse',
+    'more' => 'Več',
+
+    // Form Labels
+    'name' => 'Naziv',
+    'description' => 'Opis',
+    'role' => 'Vloga',
+    'cover_image' => 'Naslovna slika',
+    'cover_image_description' => 'Slika naj bo velika približno 440x250px.',
+    
+    // Actions
+    'actions' => 'Dejanja',
+    'view' => 'Pogled',
+    'view_all' => 'Prikaži vse',
+    'create' => 'Ustvari',
+    'update' => 'Posodobi',
+    'edit' => 'Uredi',
+    'sort' => 'Razvrsti',
+    'move' => 'Premakni',
+    'copy' => 'Kopiraj',
+    'reply' => 'Odgovori',
+    'delete' => 'Izbriši',
+    'delete_confirm' => 'Potrdi brisanje',
+    'search' => 'Išči',
+    'search_clear' => 'Razveljavi iskanje',
+    'reset' => 'Ponastavi',
+    'remove' => 'Odstrani',
+    'add' => 'Dodaj',
+    'fullscreen' => 'Celozaslonski način',
+
+    // Sort Options
+    'sort_options' => 'Možnosti razvrščanja',
+    'sort_direction_toggle' => 'Preklopi smer razvrščanja',
+    'sort_ascending' => 'Razvrsti naraščajoče',
+    'sort_descending' => 'Razvrsti padajoče',
+    'sort_name' => 'Ime',
+    'sort_created_at' => 'Datum nastanka',
+    'sort_updated_at' => 'Datum posodobitve',
+
+    // Misc
+    'deleted_user' => 'Izbrisan uporabnik',
+    'no_activity' => 'Ni aktivnosti za prikaz',
+    'no_items' => 'Na voljo ni nobenega elementa',
+    'back_to_top' => 'Nazaj na vrh',
+    'toggle_details' => 'Preklopi podrobnosti',
+    'toggle_thumbnails' => 'Preklopi sličice',
+    'details' => 'Podrobnosti',
+    'grid_view' => 'Mrežni pogled',
+    'list_view' => 'Seznam',
+    'default' => 'Privzeto',
+    'breadcrumb' => 'Pot',
+
+    // Header
+    'profile_menu' => 'Meni profila',
+    'view_profile' => 'Ogled profila',
+    'edit_profile' => 'Uredi profil',
+    'dark_mode' => 'Način temnega zaslona',
+    'light_mode' => 'Način svetlega zaslona',
+
+    // Layout tabs
+    'tab_info' => 'Informacije',
+    'tab_content' => 'Vsebina',
+
+    // Email Content
+    'email_action_help' => 'V kolikor imate težave s klikom na gumb ":actionText", kopirajte in prilepite spodnjo povezavo v vaš brskalnik:',
+    'email_rights' => 'Vse pravice pridržane',
+];
diff --git a/resources/lang/sl/components.php b/resources/lang/sl/components.php
new file mode 100644 (file)
index 0000000..d1b454e
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Izberi slike',
+    'image_all' => 'Vse',
+    'image_all_title' => 'Prikaži vse slike',
+    'image_book_title' => 'Prikaži slike naložene v to knjigo',
+    'image_page_title' => 'Preglej slike naložene na to stran',
+    'image_search_hint' => 'Iskanje po nazivu slike',
+    'image_uploaded' => 'Naloženo :uploadedDate',
+    'image_load_more' => 'Dodatno naloži',
+    'image_image_name' => 'Ime slike',
+    'image_delete_used' => 'Ta slika je uporabljena na spodnjih straneh.',
+    'image_delete_confirm_text' => 'Ste prepričani, da želite izbrisati to sliko?',
+    'image_select_image' => 'Izberite sliko',
+    'image_dropzone' => 'Povlecite slike ali kliknite tukaj za nalaganje',
+    'images_deleted' => 'Slike so bile izbrisane',
+    'image_preview' => 'Predogled slike',
+    'image_upload_success' => 'Slika uspešno naložena',
+    'image_update_success' => 'Podatki slike uspešno posodobljeni',
+    'image_delete_success' => 'Slika uspešno izbrisana',
+    'image_upload_remove' => 'Odstrani',
+
+    // Code Editor
+    'code_editor' => 'Uredi kodo',
+    'code_language' => 'Koda jezika',
+    'code_content' => 'Koda vsebine',
+    'code_session_history' => 'Zgodovina seje',
+    'code_save' => 'Shrani kodo',
+];
diff --git a/resources/lang/sl/entities.php b/resources/lang/sl/entities.php
new file mode 100644 (file)
index 0000000..9e400bb
--- /dev/null
@@ -0,0 +1,319 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Nazadnje objavljeno',
+    'recently_created_pages' => 'Nazadnje objavljene strani',
+    'recently_updated_pages' => 'Nazadnje posodobljene strani',
+    'recently_created_chapters' => 'Nazadnje objavljena poglavja',
+    'recently_created_books' => 'Nazadnje objavljene knjige',
+    'recently_created_shelves' => 'Nazadnje ustvarjene police',
+    'recently_update' => 'Nazadnje posodobljeno',
+    'recently_viewed' => 'Nazadnje prikazano',
+    'recent_activity' => 'Nedavna dejavnost',
+    'create_now' => 'Ustvarite eno sedaj',
+    'revisions' => 'Revizije',
+    'meta_revision' => 'Številka revizije #:revisionCount',
+    'meta_created' => 'Ustvarjeno :timeLength',
+    'meta_created_name' => 'Ustvaril :timeLength uporabnik :user',
+    'meta_updated' => 'Posodobljeno :timeLength',
+    'meta_updated_name' => 'Posodobil :timeLength uporabnik :user',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'Izbira entitete',
+    'images' => 'Slike',
+    'my_recent_drafts' => 'Moji nedavni osnutki',
+    'my_recently_viewed' => 'Nedavno prikazano',
+    'no_pages_viewed' => 'Niste si ogledali še nobene strani',
+    'no_pages_recently_created' => 'Nedavno ni bila ustvarjena nobena stran',
+    'no_pages_recently_updated' => 'Nedavno ni bila posodobljena nobena stran',
+    'export' => 'Izvozi',
+    'export_html' => 'Vsebuje spletno datoteko',
+    'export_pdf' => 'PDF datoteka (.pdf)',
+    'export_text' => 'Navadna besedilna datoteka',
+
+    // Permissions and restrictions
+    'permissions' => 'Dovoljenja',
+    'permissions_intro' => 'V trenutku, ko bodo omogočena, bodo imela ta dovoljenja prednost pred dovoljenji za določanje vlog.',
+    'permissions_enable' => 'Omogoči dovoljenja po meri',
+    'permissions_save' => 'Shrani dovoljenja',
+    'permissions_owner' => 'Owner',
+
+    // Search
+    'search_results' => 'Rezultati iskanja',
+    'search_total_results_found' => ':count najdenih rezultatov|:count skupaj najdenih rezultatov',
+    'search_clear' => 'Počisti iskanje',
+    'search_no_pages' => 'Nobena stran se ne ujema z vašim iskanjem',
+    'search_for_term' => 'Išči :term',
+    'search_more' => 'Prikaži več rezultatov',
+    'search_advanced' => 'Napredno iskanje',
+    'search_terms' => 'Iskalni izrazi',
+    'search_content_type' => 'Vrsta vsebine',
+    'search_exact_matches' => 'Natančno ujemanje',
+    'search_tags' => 'Iskanje po oznakah',
+    'search_options' => 'Možnosti',
+    'search_viewed_by_me' => 'Ogledano',
+    'search_not_viewed_by_me' => 'Neogledano',
+    'search_permissions_set' => 'Nastavljena dovoljenja',
+    'search_created_by_me' => 'Ustvaril sem jaz',
+    'search_updated_by_me' => 'Posodobil sem jaz',
+    'search_date_options' => 'Možnosti datuma',
+    'search_updated_before' => 'Posodobljeno pred',
+    'search_updated_after' => 'Posodobljeno po',
+    'search_created_before' => 'Ustvarjeno pred',
+    'search_created_after' => 'Ustvarjeno po',
+    'search_set_date' => 'Nastavi datum',
+    'search_update' => 'Posodobi iskanje',
+
+    // Shelves
+    'shelf' => 'Polica',
+    'shelves' => 'Police',
+    'x_shelves' => ':count Polica|:count Police',
+    'shelves_long' => 'Knjižne police',
+    'shelves_empty' => 'Ustvarjena ni bila nobena polica',
+    'shelves_create' => 'Ustvari novo polico',
+    'shelves_popular' => 'Priljubljene police',
+    'shelves_new' => 'Nove police',
+    'shelves_new_action' => 'Nova polica',
+    'shelves_popular_empty' => 'Najbolj priljubljene police se bodo pojavile tukaj.',
+    'shelves_new_empty' => 'Nazadnje ustvarjene police se bodo pojavile tukaj.',
+    'shelves_save' => 'Shrani polico',
+    'shelves_books' => 'Knjige na tej polici',
+    'shelves_add_books' => 'Dodaj knjige na to polico',
+    'shelves_drag_books' => 'Povlecite knjige sem, da jih dodate na to polico',
+    'shelves_empty_contents' => 'Na tej polici ni nobene knjige',
+    'shelves_edit_and_assign' => 'Uredi knjižno polico za dodajanje knjig',
+    'shelves_edit_named' => 'Uredi knjižno polico :name',
+    'shelves_edit' => 'Uredi knjižno polico',
+    'shelves_delete' => 'Izbriši knjižno polico',
+    'shelves_delete_named' => 'Izbriši knjižno polico :name',
+    'shelves_delete_explain' => "S tem boste izbrisali knjižno polico z nazivom ':name'. Vsebovane knjige ne bodo izbrisane.",
+    'shelves_delete_confirmation' => 'Ali ste prepričani, da želite izbrisati ta knjižno polico?',
+    'shelves_permissions' => 'Dovoljenja knjižnih polic',
+    'shelves_permissions_updated' => 'Posodobljena dovoljenja knjižnih polic',
+    'shelves_permissions_active' => 'Aktivna dovoljenja knjižnih polic',
+    'shelves_copy_permissions_to_books' => 'Kopiraj dovoljenja na knjige',
+    'shelves_copy_permissions' => 'Dovoljenja kopiranja',
+    'shelves_copy_permissions_explain' => 'To bo uveljavilo trenutne nastavitve dovoljenj na knjižni polici za vse knjige, ki jih vsebuje ta polica. Pred aktiviranjem zagotovite, da so shranjene vse spremembe dovoljenj te knjižne police.',
+    'shelves_copy_permission_success' => 'Dovoljenja knjižne police kopirana na :count knjig',
+
+    // Books
+    'book' => 'Knjiga',
+    'books' => 'Knjige',
+    'x_books' => ':count Knjiga|:count Knjig',
+    'books_empty' => 'Ustvarjena ni bila nobena knjiga',
+    'books_popular' => 'Priljubjene knjige',
+    'books_recent' => 'Zadnje knjige',
+    'books_new' => 'Nove knjige',
+    'books_new_action' => 'Nova knjiga',
+    'books_popular_empty' => 'Tukaj bodo prikazane najbolj priljubljene knjige.',
+    'books_new_empty' => 'Tukaj bodo prikazane nazadnje ustvarjene knjige.',
+    'books_create' => 'Ustvari novo knjigo',
+    'books_delete' => 'Izbriši knjigo',
+    'books_delete_named' => 'Izbriši knjigo :bookName',
+    'books_delete_explain' => 'S tem boste izbrisali knjigo z nazivom \':bookName\'. Vse strani in poglavja bodo odstranjena.',
+    'books_delete_confirmation' => 'Ali ste prepričani, da želite izbrisati to knjigo?',
+    'books_edit' => 'Uredi knjigo',
+    'books_edit_named' => 'Uredi knjigo :bookName',
+    'books_form_book_name' => 'Ime knjige',
+    'books_save' => 'Shrani knjigo',
+    'books_permissions' => 'Dovoljenja knjige',
+    'books_permissions_updated' => 'Posodobljena dovoljenja knjige',
+    'books_empty_contents' => 'V tej knjigi ni bila ustvarjena še nobena stran ali poglavje.',
+    'books_empty_create_page' => 'Ustvari novo stran',
+    'books_empty_sort_current_book' => 'Razvrsti trenutno knjigo',
+    'books_empty_add_chapter' => 'Dodaj poglavje',
+    'books_permissions_active' => 'Aktivna dovoljenja knjige',
+    'books_search_this' => 'Išči v tej knjigi',
+    'books_navigation' => 'Navigacija po knjigi',
+    'books_sort' => 'Razvrsti vsebino knjige',
+    'books_sort_named' => 'Razvrsti knjigo :bookName',
+    'books_sort_name' => 'Razvrsti po imenu',
+    'books_sort_created' => 'Razvrsti po datumu nastanka',
+    'books_sort_updated' => 'Razvrsti po datumu posodobitve',
+    'books_sort_chapters_first' => 'Najprej poglavja',
+    'books_sort_chapters_last' => 'Nazadnje poglavja',
+    'books_sort_show_other' => 'Prikaži druge knjige',
+    'books_sort_save' => 'Shrani novo razvrstitev',
+
+    // Chapters
+    'chapter' => 'Poglavje',
+    'chapters' => 'Poglavja',
+    'x_chapters' => ':count Poglavje|:count Poglavja',
+    'chapters_popular' => 'Priljubljena poglavja',
+    'chapters_new' => 'Novo poglavje',
+    'chapters_create' => 'Ustvari novo poglavje',
+    'chapters_delete' => 'Izbriši poglavje',
+    'chapters_delete_named' => 'Izbriši poglavje :chapterName',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_confirm' => 'Ste prepričani, da želite izbrisati to poglavje?',
+    'chapters_edit' => 'Uredi poglavje',
+    'chapters_edit_named' => 'Uredi poglavje :chapterName',
+    'chapters_save' => 'Shrani poglavje',
+    'chapters_move' => 'Premakni poglavje',
+    'chapters_move_named' => 'Premakni poglavje :chapterName',
+    'chapter_move_success' => 'Poglavje premaknjeno v :bookName',
+    'chapters_permissions' => 'Dovoljenja poglavij',
+    'chapters_empty' => 'V tem poglavju trenutno ni strani.',
+    'chapters_permissions_active' => 'Dovoljenja poglavij so aktivirana',
+    'chapters_permissions_success' => 'Posodobljena dovoljenja poglavij',
+    'chapters_search_this' => 'Išči v tem poglavju',
+
+    // Pages
+    'page' => 'Stran',
+    'pages' => 'Strani',
+    'x_pages' => ':count Stran|:count Strani',
+    'pages_popular' => 'Priljubjene strani',
+    'pages_new' => 'Nova stran',
+    'pages_attachments' => 'Priponke',
+    'pages_navigation' => 'Navigacija po strani',
+    'pages_delete' => 'Izbriši stran',
+    'pages_delete_named' => 'Izbriši stran :pageName',
+    'pages_delete_draft_named' => 'Izbriši osnutek strani :pageName',
+    'pages_delete_draft' => 'Izbriši osnutek strani',
+    'pages_delete_success' => 'Stran izbirsana',
+    'pages_delete_draft_success' => 'Osnutek strani izbrisan',
+    'pages_delete_confirm' => 'Ste prepričani, da želite izbrisati to stran?',
+    'pages_delete_draft_confirm' => 'Ali ste prepričani, da želite izbrisati ta osnutek?',
+    'pages_editing_named' => 'Urejanje strani :pageName',
+    'pages_edit_draft_options' => 'Možnosti osnutka',
+    'pages_edit_save_draft' => 'Shrani osnutek',
+    'pages_edit_draft' => 'Uredi osnutek strani',
+    'pages_editing_draft' => 'Urejanje osnutka',
+    'pages_editing_page' => 'Urejanje strani',
+    'pages_edit_draft_save_at' => 'Osnutek shranjen ob ',
+    'pages_edit_delete_draft' => 'Izbriši osnutek',
+    'pages_edit_discard_draft' => 'Zavrzi osnutek',
+    'pages_edit_set_changelog' => 'Opiši spremembe na dokumentu',
+    'pages_edit_enter_changelog_desc' => 'Vnesite kratek opis sprememb, ki ste jih naredili',
+    'pages_edit_enter_changelog' => 'Vpišite vsebino sprememb',
+    'pages_save' => 'Shrani stran',
+    'pages_title' => 'Naslov strani',
+    'pages_name' => 'Ime strani',
+    'pages_md_editor' => 'Urejevalnik',
+    'pages_md_preview' => 'Predogled',
+    'pages_md_insert_image' => 'Vstavi sliko',
+    'pages_md_insert_link' => 'Vnesi povezavo do objekta',
+    'pages_md_insert_drawing' => 'Vstavi risbo',
+    'pages_not_in_chapter' => 'Stran ni v poglavju',
+    'pages_move' => 'Premakni stran',
+    'pages_move_success' => 'Stran premaknjena v ":parentName"',
+    'pages_copy' => 'Kopiraj stran',
+    'pages_copy_desination' => 'Destinacija kopije',
+    'pages_copy_success' => 'Stran uspešno kopirana',
+    'pages_permissions' => 'Dovoljenja strani',
+    'pages_permissions_success' => 'Posodobljena dovoljenja strani',
+    'pages_revision' => 'Revizija',
+    'pages_revisions' => 'Pregled strani',
+    'pages_revisions_named' => 'Pregledi strani za :pageName',
+    'pages_revision_named' => 'Pregled strani za :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions_created_by' => 'Ustvaril',
+    'pages_revisions_date' => 'Datum revizije',
+    'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'Revizija št. :id',
+    'pages_revisions_numbered_changes' => 'Revizija št. #:id Changes',
+    'pages_revisions_changelog' => 'Dnevnik sprememb',
+    'pages_revisions_changes' => 'Spremembe',
+    'pages_revisions_current' => 'Trenutna različica',
+    'pages_revisions_preview' => 'Predogled',
+    'pages_revisions_restore' => 'Obnovi',
+    'pages_revisions_none' => 'Ta stran nima popravkov',
+    'pages_copy_link' => 'Kopiraj povezavo',
+    'pages_edit_content_link' => 'Uredi vsebino',
+    'pages_permissions_active' => 'Aktivna dovoljenja strani',
+    'pages_initial_revision' => 'Prvotno objavljeno',
+    'pages_initial_name' => 'Nova stran',
+    'pages_editing_draft_notification' => 'Trenutno urejate osnutek, ki je bil nazadnje shranjen :timeDiff.',
+    'pages_draft_edited_notification' => 'Ta stran je odtlej posodobljena. Priporočamo, da zavržete ta osnutek.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count uporabnikov je začelo urejati to stran',
+        'start_b' => ':userName je začel urejati to stran',
+        'time_a' => 'odkar je bila stran nazandnje posodobljena',
+        'time_b' => 'v zadnjih :minCount minutah',
+        'message' => ':start :time. Pazite, da ne boste prepisali posodobitev drug drugega!',
+    ],
+    'pages_draft_discarded' => 'Osnutek zavržen, urejevalnik je bil posodobljen s trenutno vsebino strani',
+    'pages_specific' => 'Določena stran',
+    'pages_is_template' => 'Predloga strani',
+
+    // Editor Sidebar
+    'page_tags' => 'Oznake strani',
+    'chapter_tags' => 'Oznake poglavja',
+    'book_tags' => 'Oznake knjige',
+    'shelf_tags' => 'Oznake police',
+    'tag' => 'Oznaka',
+    'tags' =>  'Oznake',
+    'tag_name' =>  'Ime oznake',
+    'tag_value' => 'Vrednost oznake (opcijsko)',
+    'tags_explain' => "Dodajte nekaj oznak za boljšo kategorizacijo vaše vsebine.\nZ dodelitvijo oznake lahko poskrbite za bolj poglobljeno organizacijo.",
+    'tags_add' => 'Dodaj drugo oznako',
+    'tags_remove' => 'Odstrani to oznako',
+    'attachments' => 'Priponke',
+    'attachments_explain' => 'Naložite nekaj datotek ali pripnite nekaj povezav, da jih prikažete na vaši strani. Vidne so v stranski orodni vrstici.',
+    'attachments_explain_instant_save' => 'Spremembe tukaj so takoj shranjene.',
+    'attachments_items' => 'Priloženi elementi',
+    'attachments_upload' => 'Naloži datoteko',
+    'attachments_link' => 'Pripni povezavo',
+    'attachments_set_link' => 'Nastavi povezavo',
+    'attachments_delete' => 'Ali ste prepričani, da želite izbrisati to priponko?',
+    'attachments_dropzone' => 'Spustite datoteke ali kliknite tukaj, če želite priložiti datoteko',
+    'attachments_no_files' => 'Nobena datoteka ni bila naložena',
+    'attachments_explain_link' => 'Lahko pripnete povezavo, če ne želite naložiti datoteke. Lahko je povezava na drugo stran ali povezava do dateteke v oblaku.',
+    'attachments_link_name' => 'Ime povezave',
+    'attachment_link' => 'Povezava priponke',
+    'attachments_link_url' => 'Povezava do datoteke',
+    'attachments_link_url_hint' => 'Url spletnega mesta ali datoteke',
+    'attach' => 'Pripni',
+    'attachments_insert_link' => 'Dodaj povezavo na priponko na stran',
+    'attachments_edit_file' => 'Uredi datoteko',
+    'attachments_edit_file_name' => 'Ime datoteke',
+    'attachments_edit_drop_upload' => 'Spustite datoteke ali kliknite tukaj, če želite naložiti in prepisati',
+    'attachments_order_updated' => 'Razvrščanje priponk posodobljeno',
+    'attachments_updated_success' => 'Podrobnosti priloge posodobljene',
+    'attachments_deleted' => 'Priponka izbirsana',
+    'attachments_file_uploaded' => 'Datoteka uspešno naložena',
+    'attachments_file_updated' => 'Datoteka uspešno posodobljena',
+    'attachments_link_attached' => 'Povezava uspešno dodana na stran',
+    'templates' => 'Predloge',
+    'templates_set_as_template' => 'Stran je predloga',
+    'templates_explain_set_as_template' => 'To stran lahko nastavite kot predlogo in njeno vsebino uporabite pri izdelavi drugih strani. Ostali uporabniki bodo lahko uporabljali to predlogo, če imajo dovoljenja za to stran.',
+    'templates_replace_content' => 'Zamenjaj vsebino strani',
+    'templates_append_content' => 'Dodajte k vsebini strani',
+    'templates_prepend_content' => 'Dodaj predpono k vsebini strani',
+
+    // Profile View
+    'profile_user_for_x' => 'Uporabnik že :time',
+    'profile_created_content' => 'Ustvarjena vsebina',
+    'profile_not_created_pages' => ':userName ni izdelal nobene strani',
+    'profile_not_created_chapters' => ':userName ni izdelal nobenega poglavja',
+    'profile_not_created_books' => ':userName ni objavil nobene knjige',
+    'profile_not_created_shelves' => ':userName ni izdelal nobene knjižne police',
+
+    // Comments
+    'comment' => 'Komentar',
+    'comments' => 'Komentarji',
+    'comment_add' => 'Dodaj komentar',
+    'comment_placeholder' => 'Dodaj komentar',
+    'comment_count' => '{0} Ni komentarjev|{1} 1 Komentar|[2,*] :count Komentarji',
+    'comment_save' => 'Shrani komentar',
+    'comment_saving' => 'Shranjujem komentar...',
+    'comment_deleting' => 'Brišem komentar...',
+    'comment_new' => 'Nov kometar',
+    'comment_created' => 'komentirano :createDiff',
+    'comment_updated' => 'Posodobljeno :updateDiff od :username',
+    'comment_deleted_success' => 'Komentar je izbrisan',
+    'comment_created_success' => 'Komentar dodan',
+    'comment_updated_success' => 'Komentar posodobljen',
+    'comment_delete_confirm' => 'Ste prepričani, da želite izbrisati ta komentar?',
+    'comment_in_reply_to' => 'Odgovor na :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Ali ste prepričani, da želite izbrisati to revizijo?',
+    'revision_restore_confirm' => 'Ali ste prepričani da želite obnoviti to revizijo? Vsebina trenutne strani bo zamenjana.',
+    'revision_delete_success' => 'Revizija izbrisana',
+    'revision_cannot_delete_latest' => 'Ne morem izbrisati zadnje revizije.'
+];
\ No newline at end of file
diff --git a/resources/lang/sl/errors.php b/resources/lang/sl/errors.php
new file mode 100644 (file)
index 0000000..e291222
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'Nimate pravic za dostop do želene strani.',
+    'permissionJson' => 'Nimate dovoljenja za izvedbo zahtevanega dejanja.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'Uporabnik z e-pošto :email že obstaja, vendar z drugačnimi poverilnicami.',
+    'email_already_confirmed' => 'E-naslov je že bil potrjen, poskusite se prijaviti.',
+    'email_confirmation_invalid' => 'Ta potrditveni žeton ni veljaven ali je že bil uporabljen. Poizkusite znova.',
+    'email_confirmation_expired' => 'Potrditveni žeton je potekel. Nova potrditvena e-pošta je bila poslana.',
+    'email_confirmation_awaiting' => 'Potrebno je potrditi e-naslov',
+    'ldap_fail_anonymous' => 'Dostop do LDAP ni uspel z anonimno povezavo',
+    'ldap_fail_authed' => 'Neuspešen LDAP dostop z danimi podrobnostimi dn & gesla',
+    'ldap_extension_not_installed' => 'PHP razširitev za LDAP ni nameščena',
+    'ldap_cannot_connect' => 'Ne morem se povezati na LDAP strežnik, neuspešna začetna povezava',
+    'saml_already_logged_in' => 'Že prijavljen',
+    'saml_user_not_registered' => 'Uporabniško ime :name ni registrirano in avtomatska registracija je onemogočena',
+    'saml_no_email_address' => 'Nisem našel e-naslova za tega uporabnika v podatkih iz zunanjega sistema za preverjanje pristnosti',
+    'saml_invalid_response_id' => 'Zahteva iz zunanjega sistema za preverjanje pristnosti ni prepoznana s strani procesa zagnanega s strani te aplikacije. Pomik nazaj po prijavi je lahko vzrok teh težav.',
+    'saml_fail_authed' => 'Prijava z uporabo :system ni uspela, sistem ni zagotovil uspešne avtorizacije',
+    'social_no_action_defined' => 'Akcija ni določena',
+    'social_login_bad_response' => "Napaka pri :socialAccount prijavi:\n:error",
+    'social_account_in_use' => 'Ta :socialAccount je že v uporabi. Poskusite se prijaviti z :socialAccount možnostjo.',
+    'social_account_email_in_use' => 'Ta e-naslov :email je že v uporabi. Če že imate račun lahko povežete vaš :socialAccount v vaših nastavitvah profila.',
+    'social_account_existing' => 'Ta :socialAccount je že dodan vašemu profilu.',
+    'social_account_already_used_existing' => 'Ta :socialAccount je v uporabi s strani drugega uporabnika.',
+    'social_account_not_used' => 'Ta :socialAccount ni povezan z nobenim uporabnikom. Prosimo povežite ga v vaših nastavitvah profila. ',
+    'social_account_register_instructions' => 'Če še nimate računa, se lahko registrirate z uporabo :socialAccount.',
+    'social_driver_not_found' => 'Socialni vtičnik ni najden',
+    'social_driver_not_configured' => 'Vaše nastavitve :socialAccount niso pravilo nastavljene.',
+    'invite_token_expired' => 'Ta povezava je potekla. Namesto tega lahko ponastavite vaše geslo računa.',
+
+    // System
+    'path_not_writable' => 'Poti :filePath ni bilo mogoče naložiti. Prepričajte se, da je zapisljiva na strežnik.',
+    'cannot_get_image_from_url' => 'Ne morem pridobiti slike z :url',
+    'cannot_create_thumbs' => 'Strežnik ne more izdelati sličice. Prosimo preverite če imate GD PHP razširitev nameščeno.',
+    'server_upload_limit' => 'Strežnik ne dovoli nalaganj take velikosti. Prosimo poskusite z manjšo velikostjo datoteke.',
+    'uploaded'  => 'Strežnik ne dovoli nalaganj take velikosti. Prosimo poskusite zmanjšati velikost datoteke.',
+    'image_upload_error' => 'Prišlo je do napake med nalaganjem slike',
+    'image_upload_type_error' => 'Napačen tip (format) slike',
+    'file_upload_timeout' => 'Čas nalaganjanja datoteke je potekel.',
+
+    // Attachments
+    'attachment_not_found' => 'Priloga ni najdena',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Osnutka ni bilo mogoče shraniti. Pred shranjevanjem te strani se prepričajte, da imate internetno povezavo',
+    'page_custom_home_deletion' => 'Ne morem izbrisati strani, ki je nastavljena kot domača stran',
+
+    // Entities
+    'entity_not_found' => 'Ne najdem tega objekta',
+    'bookshelf_not_found' => 'Knjižna polica ni najdena',
+    'book_not_found' => 'Knjiga ni najdena',
+    'page_not_found' => 'Stran ni najdena',
+    'chapter_not_found' => 'Poglavje ni najdeno',
+    'selected_book_not_found' => 'Izbrana knjiga ni najdena',
+    'selected_book_chapter_not_found' => 'Izbrana knjiga ali poglavje ni najdeno',
+    'guests_cannot_save_drafts' => 'Gosti ne morejo shranjevati osnutkov',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'Ne morete odstraniti edinega administratorja',
+    'users_cannot_delete_guest' => 'Ne morete odstraniti uporabnika gost',
+
+    // Roles
+    'role_cannot_be_edited' => 'Te vloge mi možno urejati',
+    'role_system_cannot_be_deleted' => 'Ta vloga je sistemska in je ni možno brisati',
+    'role_registration_default_cannot_delete' => 'Te vloge ni možno brisati, dokler je nastavljena kot privzeta',
+    'role_cannot_remove_only_admin' => 'Ta uporabnik je edini administrator. Dodelite vlogo administratorja drugemu uporabniku, preden ga poskusite brisati.',
+
+    // Comments
+    'comment_list' => 'Napaka se je pojavila pri pridobivanju komentarjev.',
+    'cannot_add_comment_to_draft' => 'V osnutek ni možno dodajati komentarjev.',
+    'comment_add' => 'Napaka se je pojavila pri dodajanju / posodobitvi komentarjev.',
+    'comment_delete' => 'Napaka se je pojavila pri brisanju komentarja.',
+    'empty_comment' => 'Praznega komentarja ne morete objaviti.',
+
+    // Error pages
+    '404_page_not_found' => 'Strani ni mogoče najti',
+    'sorry_page_not_found' => 'Oprostite, strani ki jo iščete, ni mogoče najti.',
+    'sorry_page_not_found_permission_warning' => 'Če pričakujete, da ta stran obstaja, mogoče nimate pravic ogleda zanjo.',
+    'return_home' => 'Vrni se domov',
+    'error_occurred' => 'Prišlo je do napake',
+    'app_down' => ':appName trenutno ni dosegljiva',
+    'back_soon' => 'Kmalu bo ponovno dosegljiva.',
+
+    // API errors
+    'api_no_authorization_found' => 'Avtorizacija ni bila najdena',
+    'api_bad_authorization_format' => 'Avtorizacija je bila najdena, vendar je v napačni obliki',
+    'api_user_token_not_found' => 'Za dano avtorizacijo ni bil najden noben ustrezen API',
+    'api_incorrect_token_secret' => 'Skrivnost, ki je bila dana za uporabljeni žeton API, je napačna',
+    'api_user_no_api_permission' => 'Lastnik API nima pravic za klicanje API',
+    'api_user_token_expired' => 'Avtorizacijski žeton je pretečen',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Napaka se je pojavila pri pošiljanju testne e-pošte:',
+
+];
diff --git a/resources/lang/sl/pagination.php b/resources/lang/sl/pagination.php
new file mode 100644 (file)
index 0000000..e04a98d
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; Predhodno',
+    'next'     => 'Naslednje &raquo;',
+
+];
diff --git a/resources/lang/sl/passwords.php b/resources/lang/sl/passwords.php
new file mode 100644 (file)
index 0000000..e9e195f
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+    'password' => 'Gesla morajo biti najmanj osem znakov dolga in se morajo ujemati s potrditvijo.',
+    'user' => "Ne moremo najti uporabnika s tem e-poštnim naslovom.",
+    'token' => 'Žeton za ponastavitev gesla ni veljaven za ta e-poštni naslov.',
+    'sent' => 'Poslali smo vam povezavo za ponastavitev gesla!',
+    'reset' => 'Vaše geslo je bilo ponastavljeno!',
+
+];
diff --git a/resources/lang/sl/settings.php b/resources/lang/sl/settings.php
new file mode 100644 (file)
index 0000000..feae93b
--- /dev/null
@@ -0,0 +1,257 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Nastavitve',
+    'settings_save' => 'Shrani nastavitve',
+    'settings_save_success' => 'Nastavitve shranjene',
+
+    // App Settings
+    'app_customization' => 'Prilagoditev',
+    'app_features_security' => 'Lastnosti & Varnost',
+    'app_name' => 'Ime aplikacije',
+    'app_name_desc' => 'To ime je prikazano v glavi in vsaki sistemski e-pošti.',
+    'app_name_header' => 'Prikaži ime v glavi',
+    'app_public_access' => 'Javni dostop',
+    'app_public_access_desc' => 'Če omogočite to možnost, bo obiskovalcem, ki niso prijavljeni, omogočen dostop do vsebine v BookStack.',
+    'app_public_access_desc_guest' => 'Dostop za javne obiskovalce je mogoče nadzorovati prek uporabnika "Gost".',
+    'app_public_access_toggle' => 'Dovoli javni dostop',
+    'app_public_viewing' => 'Dovoli javni pregled?',
+    'app_secure_images' => 'Nalaganje slik z večjo varnostjo',
+    'app_secure_images_toggle' => 'Omogoči nalaganje slik z večjo varnostjo',
+    'app_secure_images_desc' => 'Zaradi delovanja so vse slike javne. Ta možnost doda naključni, hard-to-guess niz pred Url-ji slike. Prepričajte se, da indeksi imenikov niso omogočeni, da preprečite enostaven dostop.',
+    'app_editor' => 'Urejevalnik strani',
+    'app_editor_desc' => 'Izberite urejevalnik, ki bodo uporabniki uporabljali za urejanje strani.',
+    'app_custom_html' => 'Po meri HTML vsebina glave',
+    'app_custom_html_desc' => 'Katerakoli vsebina dodana tukaj, bo vstavljena na dno <head> dela vsake strani. To je uporabno za uporabo prevladujočih slogov ali dodajanje analitike.',
+    'app_custom_html_disabled_notice' => 'Po meri narejena HTML glava vsebine je onemogočena na tej strani z nastavitvami, da se zagotovi, da bodo morebitne zrušitve lahko povrnjene.',
+    'app_logo' => 'Logotip aplikacije',
+    'app_logo_desc' => 'Ta slika bi morala biti 43px visoka. <br>Velike slike bodo pomanjšane.',
+    'app_primary_color' => 'Osnovna barva aplikacije',
+    'app_primary_color_desc' => 'Nastavi osnovno barvo za aplikacijo vključno s pasico, gumbi in povezavami.',
+    'app_homepage' => 'Domača stran aplikacije',
+    'app_homepage_desc' => 'Izberi pogled, da se pokaže na domači strani, namesto osnovnega pogleda. Dovoljenja strani so prezrta za izbrane strani.',
+    'app_homepage_select' => 'Izberi stran',
+    'app_disable_comments' => 'Onemogoči komentarje',
+    'app_disable_comments_toggle' => 'Onemogoči komentarje',
+    'app_disable_comments_desc' => 'Onemogoči komentarje na vseh straneh v aplikaciji. <br> Obstoječi komentarji se ne prikazujejo.',
+
+    // Color settings
+    'content_colors' => 'Barve vsebine',
+    'content_colors_desc' => 'Nastavi barve za vse elemente v hierarhiji. Izbor barv s podobno barvno svetlostjo je priporočljivo za osnovne barve za branje.',
+    'bookshelf_color' => 'Barva police',
+    'book_color' => 'knjiga barv',
+    'chapter_color' => 'barvno poglavje',
+    'page_color' => 'Stran barv',
+    'page_draft_color' => 'stran osnutka barv',
+
+    // Registration Settings
+    'reg_settings' => 'registracija',
+    'reg_enable' => 'onemogočena registracija',
+    'reg_enable_toggle' => 'omogočena registracija',
+    'reg_enable_desc' => 'Ko je registracija omogočena, se  bo uporabnik lahko prijavil sam kot uporabnik aplikacije. Po registraciji je uporabniku dodeljena ena prevzeta vloga.',
+    'reg_default_role' => 'prevzeta uporabniška vloga po registraciji',
+    'reg_enable_external_warning' => 'Ta možnosti je ignorirana ko zunanja LDAP ali SAML avtentikacija je akitivna. Uporabniški računi za ne obstoječe uporabnike bodo avtomatsko izdelani, če avtentikacija zunanjih uporabljenih sistemov je uspešna.',
+    'reg_email_confirmation' => 'potrditev e-pošte',
+    'reg_email_confirmation_toggle' => 'potrebna potrditev e-pošte',
+    'reg_confirm_email_desc' => 'Če uporabite omejitev domene, bo potrebna potrditev e-pošte in ta možnost bo prezrta.',
+    'reg_confirm_restrict_domain' => 'omejitev domene',
+    'reg_confirm_restrict_domain_desc' => 'Vnesite seznam domen, ločenih z vejico, na katere želite omejiti registracijo. Uporabnik bo prejel e-pošto za potrditev naslova, preden bo omogočena interakcija z aplikacijo. <br> Upoštevajte, da uporabnik po uspešni registrciji lahko spremeni svoj e-poštni naslov.',
+    'reg_confirm_restrict_domain_placeholder' => 'Brez omejitev',
+
+    // Maintenance settings
+    'maint' => 'Vzdrževanje',
+    'maint_image_cleanup' => 'Odstrani /počisti slike',
+    'maint_image_cleanup_desc' => "Pregleda vsebino strani in revizij ter ugotovi, katere slike in risbe so v uporabi in katere so odvečne. Preden to poženeš, naredi popolno varnostno kopijo podatkovne zbirke in slik.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_run' => 'Zaženi čiščenje',
+    'maint_image_cleanup_warning' => 'Najdenih je bilo :count verjetno neuporabljenih slik. Ali si prepričan, da želiš odstraniti izbrane slike?',
+    'maint_image_cleanup_success' => ':count verjetno neuporavljenih slik je bilo najdenih in izbrisanih!',
+    'maint_image_cleanup_nothing_found' => 'Ni bilo najdenih neuporabljenih slik, nič ni izbrisano!',
+    'maint_send_test_email' => 'Pošlji testno e-pismo',
+    'maint_send_test_email_desc' => 'To pošlje testno e-pošto na vaš e-poštni naslov, naveden v vašem profilu.',
+    'maint_send_test_email_run' => 'Pošlji testno sporočilo',
+    'maint_send_test_email_success' => 'e-pošta poslana na :naslov',
+    'maint_send_test_email_mail_subject' => 'Testno e-sporočilo',
+    'maint_send_test_email_mail_greeting' => 'Zdi se, da dostava e-pošte deluje!',
+    'maint_send_test_email_mail_text' => 'Čestitke! Če ste prejeli e-poštno obvestilo so bile vaše e-poštne nastavitve pravilno konfigurirane.',
+    'maint_recycle_bin_desc' => 'Izbrisane police, knjige, poglavja in strani se pošljejo v koš, da jih je mogoče obnoviti ali trajno izbrisati. Starejše predmete v košu lahko čez nekaj časa samodejno odstranite, odvisno od konfiguracije sistema.',
+    'maint_recycle_bin_open' => 'Odpri koš',
+
+    // Recycle Bin
+    'recycle_bin' => 'Koš',
+    'recycle_bin_desc' => 'Tu lahko obnovite predmete, ki so bili izbrisani, ali pa jih trajno odstranite s sistema. Ta seznam je nefiltriran, za razliko od podobnih seznamov dejavnosti v sistemu, kjer se uporabljajo filtri dovoljenj.',
+    'recycle_bin_deleted_item' => 'Izbrisan element',
+    'recycle_bin_deleted_by' => 'Izbrisal uporabnik',
+    'recycle_bin_deleted_at' => 'Čas izbrisa',
+    'recycle_bin_permanently_delete' => 'Trajno izbrišem?',
+    'recycle_bin_restore' => 'Obnovi',
+    'recycle_bin_contents_empty' => 'Koš je prazen',
+    'recycle_bin_empty' => 'Izprazni koš',
+    'recycle_bin_empty_confirm' => 'S tem boste trajno uničili vse predmete v košu, vključno z vsebino vsakega predmeta. Ali ste prepričani, da želite izprazniti koš?',
+    'recycle_bin_destroy_confirm' => 'S tem dejanjem boste ta element skupaj s spodaj navedenimi podrejenimi elementi trajno izbrisali iz sistema in te vsebine ne boste mogli obnoviti. Ali ste prepričani, da želite trajno izbrisati ta element?',
+    'recycle_bin_destroy_list' => 'Predmeti, ki naj bodo trajno izbrisani',
+    'recycle_bin_restore_list' => 'Predmeti, ki naj bodo obnovljeni',
+    'recycle_bin_restore_confirm' => 'S tem dejanjem boste izbrisani element, vključno z vsemi podrejenimi elementi, obnovili na prvotno mesto. Če je bilo prvotno mesto od takrat izbrisano in je zdaj v košu, bo treba obnoviti tudi nadrejeni element.',
+    'recycle_bin_restore_deleted_parent' => 'Nadrejeni element je bil prav tako izbrisan. Dokler se ne obnovi nadrejenega elementa, ni mogoče obnoviti njemu podrejenih elementov.',
+    'recycle_bin_destroy_notification' => 'Izbrisano :count skupno število elementov iz koša.',
+    'recycle_bin_restore_notification' => 'Obnovljeno :count skupno število elementov iz koša.',
+
+    // Audit Log
+    'audit' => 'Dnevnik dogodkov',
+    'audit_desc' => 'Ta dnevnik dogodkov prikazuje seznam dejavnosti, ki jim sledi sistem. Seznam je nefiltriran, za razliko od podobnih seznamov dejavnosti v sistemu, kjer se uporabljajo filtri dovoljenj.',
+    'audit_event_filter' => 'Filter dogodkov',
+    'audit_event_filter_no_filter' => 'Ni filtra',
+    'audit_deleted_item' => 'Izbrisan element',
+    'audit_deleted_item_name' => 'Naziv: :name',
+    'audit_table_user' => 'Uporabnik',
+    'audit_table_event' => 'Dogodek',
+    'audit_table_related' => 'Povezani predmet ali podrobnost',
+    'audit_table_date' => 'Datum zadnje dejavnosti',
+    'audit_date_from' => 'Časovno obdobje od',
+    'audit_date_to' => 'Časovno obdobje do',
+
+    // Role Settings
+    'roles' => 'Vloge',
+    'role_user_roles' => 'Vloge uporabnika',
+    'role_create' => 'Ustvari novo vlogo',
+    'role_create_success' => 'Vloga uspešno ustvarjena',
+    'role_delete' => 'Brisanje vloge',
+    'role_delete_confirm' => 'Izbrisana bo vloga z imenom \':roleName\'.',
+    'role_delete_users_assigned' => 'Ta vloga ima dodeljenih :userCount uporabnikov. V kolikor želite uporabnike preseliti iz te vloge, spodaj izberite novo vlogo.',
+    'role_delete_no_migration' => "Ne prenašaj uporabnikov",
+    'role_delete_sure' => 'Ali ste prepričani, da želite izbrisati to vlogo?',
+    'role_delete_success' => 'Vloga uspešno izbrisana',
+    'role_edit' => 'Uredi vlogo',
+    'role_details' => 'Podrobnosti vloge',
+    'role_name' => 'Naziv vloge',
+    'role_desc' => 'Kratki opis vloge',
+    'role_external_auth_id' => 'Zunanje dokazilo ID',
+    'role_system' => 'Sistemska dovoljenja',
+    'role_manage_users' => 'Upravljanje uporabnikov',
+    'role_manage_roles' => 'Upravljanje vlog in dovoljenja vlog',
+    'role_manage_entity_permissions' => 'Upravljanje dovoljenj vseh knjig, poglavij in strani',
+    'role_manage_own_entity_permissions' => 'Upravljanje dovoljenj za svojo knjigo, poglavje in strani',
+    'role_manage_page_templates' => 'Uredi predloge',
+    'role_access_api' => 'API za dostop do sistema',
+    'role_manage_settings' => 'Nastavitve za upravljanje',
+    'role_asset' => 'Sistemska dovoljenja',
+    'roles_system_warning' => 'Zavedajte se, da lahko dostop do kateregakoli od zgornjih treh dovoljenj uporabniku omogoči, da spremeni lastne privilegije ali privilegije drugih v sistemu. Vloge s temi dovoljenji dodelite samo zaupanja vrednim uporabnikom.',
+    'role_asset_desc' => 'Ta dovoljenja nadzorujejo privzeti dostop do sredstev v sistemu. Dovoljenja za knjige, poglavja in strani bodo razveljavila ta dovoljenja.',
+    'role_asset_admins' => 'Skrbniki samodejno pridobijo dostop do vseh vsebin, vendar lahko te možnosti prikažejo ali pa skrijejo možnosti uporabniškega vmesnika.',
+    'role_all' => 'Vse',
+    'role_own' => 'Lasten',
+    'role_controlled_by_asset' => 'Nadzira ga sredstvo, v katerega so naloženi',
+    'role_save' => 'Shrani vlogo',
+    'role_update_success' => 'Vloga uspešno posodobljena',
+    'role_users' => 'Uporabniki v tej vlogi',
+    'role_users_none' => 'Tej vlogi trenutno ni dodeljen noben uporabnik',
+
+    // Users
+    'users' => 'Uporabniki',
+    'user_profile' => 'Uporabniški profil',
+    'users_add_new' => 'Dodaj novega uporabnika',
+    'users_search' => 'Išči uporabnike',
+    'users_latest_activity' => 'Zadnja dejavnost',
+    'users_details' => 'Podatki o uporabniku',
+    'users_details_desc' => 'Nastavite prikazno ime in e-poštni naslov za tega uporabnika. E-poštni naslov bo uporabljen za prijavo v aplikacijo.',
+    'users_details_desc_no_email' => ' Nastavite prikazno ime za tega uporabnika, da ga bodo drugi lahko prepoznali.',
+    'users_role' => 'Vloge uporabnika',
+    'users_role_desc' => 'Izberi vloge, ki bodo dodeljene uporabniku. Če je uporabniku dodeljenih več vlog, se dovoljenja združijo in prejmenjo vsa dovoljenja dodeljenih vlog.',
+    'users_password' => 'Uporabniško geslo',
+    'users_password_desc' => 'Nastavite geslo, ki se uporablja za prijavo v aplikacijo. Dolgo mora biti vsaj 6 znakov.',
+    'users_send_invite_text' => 'Uporabniku lahko pošljete e-poštno sporočilo s povabilom, ki mu omogoča, da nastavi svoje geslo, ali pa ga nastavite kar sami.',
+    'users_send_invite_option' => 'Pošlji uporabniku e-poštno povabilo',
+    'users_external_auth_id' => 'Zunanje dokazilo ID',
+    'users_external_auth_id_desc' => 'To je ID, s katerim se ta uporabnik ujema pri komunikaciji z vašim zunanjim sistemom za preverjanje pristnosti.',
+    'users_password_warning' => 'Spodaj izpolni le, če želiš spremeniti geslo.',
+    'users_system_public' => 'Ta uporabnik predstavlja vse gostujoče uporabnike, ki obiščejo vašo wiki stran. Za prijavo je ni mogoče uporabiti, ampak je dodeljena samodejno.',
+    'users_delete' => 'Brisanje uporabnika',
+    'users_delete_named' => 'Brisanje uporabnika :userName',
+    'users_delete_warning' => 'Iz sistema se bo popolnoma  izbrisal uporabnik z imenom \':userName\'',
+    'users_delete_confirm' => 'Ste prepričani, da želite izbrisati tega uporabnika?',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
+    'users_edit' => 'Uredi uporabnika',
+    'users_edit_profile' => 'Uredi profil',
+    'users_edit_success' => 'Uporabnik uspešno posodobljen',
+    'users_avatar' => 'Uporabnikov avatar',
+    'users_avatar_desc' => 'Izberi sliko, ki predstavlja uporabnika. Velikost mora biti približno 256px.',
+    'users_preferred_language' => 'Izbrani jezik',
+    'users_preferred_language_desc' => 'Ta možnost bo spremenila jezik, ki se uporablja za uporabniški vmesnik aplikacije. To ne bo vplivalo na nobeno vsebino, ki jo ustvari uporabnik.',
+    'users_social_accounts' => 'Družbene ikone / računi',
+    'users_social_accounts_info' => 'Tu lahko za hitrejšo in lažjo prijavo povežete druge račune. Prekinitev povezave računa tukaj ne prekliče predhodno odobrenega dostopa. Prekličite dostop iz nastavitev profila v povezanem družabnem računu.',
+    'users_social_connect' => 'Povežite račun',
+    'users_social_disconnect' => 'Odklop računa',
+    'users_social_connected' => ':socialAccount račun je bil uspešno dodan vašemu profilu.',
+    'users_social_disconnected' => ':socialAccount račun je bil uspešno odstranjen iz vašega profila.',
+    'users_api_tokens' => 'API žeton',
+    'users_api_tokens_none' => 'Nič API žetonov ni bilo ustvarjenih za uporabnika',
+    'users_api_tokens_create' => 'Ustvari žeton',
+    'users_api_tokens_expires' => 'Poteče',
+    'users_api_tokens_docs' => 'API dokumentacija',
+
+    // API Tokens
+    'user_api_token_create' => 'Ustvari žeton',
+    'user_api_token_name' => 'Ime',
+    'user_api_token_name_desc' => 'Dajte žetonu berljivo ime kot prihodnji opomnik o predvidenem namenu.',
+    'user_api_token_expiry' => 'Datum poteka',
+    'user_api_token_expiry_desc' => 'Določi datum izteka uporabnosti žetona. Po tem datumu, zahteve poslane s tem žetonom, ne bodo več delovale. 
+Če pustite to polje prazno, bo iztek uporabnosti 100.let .',
+    'user_api_token_create_secret_message' => 'Takoj po ustvarjanju tega žetona se ustvari in prikaže "Token ID" "in" Token Secret ". Skrivnost bo prikazana samo enkrat, zato se pred nadaljevanjem prepričajte o varnosti kopirnega mesta.',
+    'user_api_token_create_success' => 'API žeton uspešno ustvarjen',
+    'user_api_token_update_success' => 'API žeton uspešno posodobljen',
+    'user_api_token' => 'API žeton',
+    'user_api_token_id' => 'Žeton ID',
+    'user_api_token_id_desc' => 'To je sistemski identifikator, ki ga ni mogoče urejati za ta žeton in ga je treba navesti v zahtevah za API.',
+    'user_api_token_secret' => 'Skrivnost žetona',
+    'user_api_token_secret_desc' => 'To je sistemsko ustvarjena skrivnost za ta žeton, ki jo bo treba navesti v zahtevah za API. To bo prikazano samo enkrat, zato kopirajte to vrednost na varno mesto.',
+    'user_api_token_created' => 'Žeton ustvarjen :timeAgo',
+    'user_api_token_updated' => 'Žeton posodobljen :timeAgo',
+    'user_api_token_delete' => 'Briši žeton',
+    'user_api_token_delete_warning' => 'Iz sistema se bo popolnoma  izbrisal API žeton z imenom \':tokenName\' ',
+    'user_api_token_delete_confirm' => 'Ali ste prepričani, da želite izbrisati ta API žeton?',
+    'user_api_token_delete_success' => 'API žeton uspešno izbrisan',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'danščina',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/sl/validation.php b/resources/lang/sl/validation.php
new file mode 100644 (file)
index 0000000..bc7242b
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => ':attribute mora biti potrjen.',
+    'active_url'           => ':attribute ni veljaven URL.',
+    'after'                => ':attribute mora biti datum po :date.',
+    'alpha'                => ':attribute lahko vsebuje samo črke.',
+    'alpha_dash'           => ':attribute lahko vsebuje samo ?rke, ?tevilke in ?rtice.',
+    'alpha_num'            => ':attribute lahko vsebuje samo črke in številke.',
+    'array'                => ':attribute mora biti niz.',
+    'before'               => ':attribute mora biti datum pred :date.',
+    'between'              => [
+        'numeric' => ':attribute mora biti med :min in :max.',
+        'file'    => ':attribute mora biti med :min in :max kilobajti.',
+        'string'  => ':attribute mora biti med :min in :max znaki.',
+        'array'   => ':attribute mora imeti med :min in :max elementov.',
+    ],
+    'boolean'              => ':attribute polje mora biti pravilno ali napačno.',
+    'confirmed'            => ':attribute potrditev se ne ujema.',
+    'date'                 => ':attribute ni veljaven datum.',
+    'date_format'          => ':attribute se ne ujema z obliko :format.',
+    'different'            => ':attribute in :other morata biti različna.',
+    'digits'               => 'Atribut mora biti: števnik.',
+    'digits_between'       => ':attribute mora biti med :min in :max števkami.',
+    'email'                => ':attribute mora biti veljaven e-naslov.',
+    'ends_with' => 'The :attribute se mora končati z eno od določenih: :vrednost/values',
+    'filled'               => 'Polje ne sme biti prazno.',
+    'gt'                   => [
+        'numeric' => ':attribute mora biti večji kot :vrednost.',
+        'file'    => ':attribute mora biti večji kot :vrednost kilobytes',
+        'string'  => ':attribute mora biti večji kot :vrednost znakov',
+        'array'   => ':attribute mora biti večji kot :vrednost znakov',
+    ],
+    'gte'                  => [
+        'numeric' => ':attribute mora biti večji kot ali enak :vrednost.',
+        'file'    => ':attribute mora biti večji kot ali enak :vrednost kilobytes',
+        'string'  => ':attribute mora biti večji kot ali enak :vrednost znakov',
+        'array'   => ':attribute mora imeti :vrednost znakov ali več',
+    ],
+    'exists'               => 'Izbrani atribut je neveljaven.',
+    'image'                => ':attribute mora biti slika.',
+    'image_extension'      => ':attribute mora imeti veljavno & podprto slikovno pripono',
+    'in'                   => 'izbran :attribute je neveljaven.',
+    'integer'              => ':attribute mora biti celo število.',
+    'ip'                   => ':attribute mora biti veljaven IP naslov.',
+    'ipv4'                 => ':attribute mora biti veljaven IPv4 naslov.',
+    'ipv6'                 => ':attribute mora biti veljaven IPv6 naslov.',
+    'json'                 => ':attribute mora biti veljavna JSON povezava.',
+    'lt'                   => [
+        'numeric' => ':attribute mora biti manj kot :vrednost.',
+        'file'    => ':attribute mora biti manj kot :vrednost kilobytes',
+        'string'  => ':attribute mora biti manj kot :vrednost znakov',
+        'array'   => ':attribute mora imeti manj kot :vrednost znakov',
+    ],
+    'lte'                  => [
+        'numeric' => ':attribute mora biti manj kot ali enak :vrednost.',
+        'file'    => ':attribute mora biti manj kot ali enak :vrednost kilobytes',
+        'string'  => ':attribute mora biti manj kot ali enak :vrednost znakov',
+        'array'   => ':attribute ne sme imeti več kot :vrednost elementov',
+    ],
+    'max'                  => [
+        'numeric' => ':attribute ne sme biti večja od :max.',
+        'file'    => ':attribute ne sme biti večja od :max kilobytes.',
+        'string'  => 'Atribut naj ne bo večji od: max znakov.',
+        'array'   => ':attribute ne sme imeti več kot :max elementov.',
+    ],
+    'mimes'                => 'Atribut mora biti datoteka vrste:: vrednost.',
+    'min'                  => [
+        'numeric' => ':attribute mora biti najmanj :min.',
+        'file'    => ':attribute mora biti najmanj :min KB.',
+        'string'  => ':attribute mora biti najmanj :min znakov.',
+        'array'   => ':attribute mora imeti vsaj :min elementov.',
+    ],
+    'no_double_extension'  => ':attribute mora imeti samo eno razširitveno datoteko',
+    'not_in'               => 'Izbrani atribut je neveljaven.',
+    'not_regex'            => ':attribute oblika ni veljavna.',
+    'numeric'              => 'Atribut mora biti število.',
+    'regex'                => ':attribute oblika ni veljavna.',
+    'required'             => 'Polje :attribute je obvezno.',
+    'required_if'          => 'Polje atributa je obvezno, če: drugo je: vrednost.',
+    'required_with'        => 'Polje atributa je obvezno, ko: so prisotne vrednosti.',
+    'required_with_all'    => 'Polje atributa je obvezno, ko: so prisotne vrednosti.',
+    'required_without'     => 'Polje atributa je obvezno, če: vrednosti niso prisotne.',
+    'required_without_all' => 'Polje atributa je obvezno, če nobena od: vrednosti ni prisotna.',
+    'same'                 => 'Atribut in: drugi se morajo ujemati.',
+    'safe_url'             => 'Podana povezava morda ni varna.',
+    'size'                 => [
+        'numeric' => ':attribute mora biti :velikost.',
+        'file'    => ':attribute mora biti :velikost KB.',
+        'string'  => 'Atribut mora biti: velikost znakov.',
+        'array'   => ':attribute mora vsebovati :velikost elementov.',
+    ],
+    'string'               => ':attribute mora biti niz.',
+    'timezone'             => ':attribute mora biti veljavna cona.',
+    'unique'               => ':attribute je že zaseden.',
+    'url'                  => ':attribute oblika ni veljavna.',
+    'uploaded'             => 'Datoteke ni bilo mogoče naložiti. Strežnik morda ne sprejema datotek te velikosti.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Potrditev gesla',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 1cb8051b93920e18599396129598c7c0b3ef154c..e3fa051555f92bd5f24edf72342f110d0b385f84 100644 (file)
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'kommenterade',
+    'permissions_update'          => 'uppdaterade behörigheter',
 ];
index 96feee117011cab43499e45872f58f0fb3d700ae..8669055351722eb127ac674a2a57978f66777ac5 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Återställ lösenord',
     'reset_password_send_instructions' => 'Ange din e-postadress nedan så skickar vi ett mail med en länk för att återställa ditt lösenord.',
     'reset_password_send_button' => 'Skicka återställningslänk',
-    'reset_password_sent_success' => 'En länk för att återställa lösenordet har skickats till :email.',
+    'reset_password_sent' => 'En länk för återställning av lösenord kommer att skickas till :email om den e-postadressen finns i systemet.',
     'reset_password_success' => 'Ditt lösenord har återställts.',
     'email_reset_subject' => 'Återställ ditt lösenord till :appName',
     'email_reset_text' => 'Du får detta mail eftersom vi fått en begäran om att återställa lösenordet till ditt konto.',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => 'Skicka bekräftelse på nytt',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Du har blivit inbjuden att gå med i :appName!',
+    'user_invite_email_greeting' => 'Ett konto har skapats för dig i :appName.',
+    'user_invite_email_text' => 'Klicka på knappen nedan för att ange ett lösenord och få tillgång:',
+    'user_invite_email_action' => 'Ange kontolösenord',
+    'user_invite_page_welcome' => 'Välkommen till :appName!',
+    'user_invite_page_text' => 'För att slutföra ditt konto och få åtkomst måste du ange ett lösenord som kommer att användas för att logga in på :appName vid framtida besök.',
+    'user_invite_page_confirm_button' => 'Bekräfta lösenord',
+    'user_invite_success' => 'Lösenord satt, du har nu tillgång till :appName!'
 ];
\ No newline at end of file
index 4133ee642b4fc9e74dce8a46835acf1e227e29f1..7ad2712ccf40bbd00a18fb79fd1e764e64a8d7f1 100644 (file)
@@ -33,17 +33,19 @@ return [
     'copy' => 'Kopiera',
     'reply' => 'Svara',
     'delete' => 'Ta bort',
+    'delete_confirm' => 'Bekräfta radering',
     'search' => 'Sök',
     'search_clear' => 'Rensa sökning',
     'reset' => 'Återställ',
     'remove' => 'Radera',
     'add' => 'Lägg till',
+    'fullscreen' => 'Helskärm',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
+    'sort_options' => 'Sorteringsalternativ',
+    'sort_direction_toggle' => 'Växla sorteringsriktning',
+    'sort_ascending' => 'Sortera stigande',
+    'sort_descending' => 'Sortera fallande',
     'sort_name' => 'Namn',
     'sort_created_at' => 'Skapad',
     'sort_updated_at' => 'Uppdaterad',
@@ -59,12 +61,14 @@ return [
     'grid_view' => 'Rutnätsvy',
     'list_view' => 'Listvy',
     'default' => 'Förvald',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Brödsmula',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Profilmeny',
     'view_profile' => 'Visa profil',
     'edit_profile' => 'Redigera profil',
+    'dark_mode' => 'Mörkt läge',
+    'light_mode' => 'Ljust Läge',
 
     // Layout tabs
     'tab_info' => 'Information',
index 5e4085dec792eac2d657d2f016418e76eefa3892..68b141945e1d30d130762981d1aa2789941d152a 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Ladda fler',
     'image_image_name' => 'Bildnamn',
     'image_delete_used' => 'Den här bilden används på nedanstående sidor.',
-    'image_delete_confirm' => 'Klicka på "ta bort" en gång till för att bekräfta att du vill ta bort bilden.',
+    'image_delete_confirm_text' => 'Är du säker på att du vill radera denna bild?',
     'image_select_image' => 'Välj bild',
     'image_dropzone' => 'Släpp bilder här eller klicka för att ladda upp',
     'images_deleted' => 'Bilder borttagna',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Redigera kod',
     'code_language' => 'Språk',
     'code_content' => 'Kod',
+    'code_session_history' => 'Sessionshistorik',
     'code_save' => 'Spara',
 ];
index bc4b3a4afa55ca808bb9e546c531f4ff241f974a..d31bef6f00dc593bed331c3326270cbc140b58ef 100644 (file)
@@ -17,12 +17,13 @@ return [
     'recent_activity' => 'Aktivitet',
     'create_now' => 'Skapa en nu',
     'revisions' => 'Revisioner',
-    'meta_revision' => 'Revision #:revisionCount',
+    'meta_revision' => 'Revisions #:revisionCount',
     'meta_created' => 'Skapad :timeLength',
     'meta_created_name' => 'Skapad :timeLength av :user',
     'meta_updated' => 'Uppdaterad :timeLength',
     'meta_updated_name' => 'Uppdaterad :timeLength av :user',
-    'entity_select' => 'Entity Select',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'Välj enhet',
     'images' => 'Bilder',
     'my_recent_drafts' => 'Mina nyaste utkast',
     'my_recently_viewed' => 'Mina senast visade sidor',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Dessa rättigheter kommer att överskrida eventuella rollbaserade rättigheter.',
     'permissions_enable' => 'Aktivera anpassade rättigheter',
     'permissions_save' => 'Spara rättigheter',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Sökresultat',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Inga sidor matchade sökningen',
     'search_for_term' => 'Sök efter :term',
     'search_more' => 'Fler resultat',
-    'search_filters' => 'Sökfilter',
+    'search_advanced' => 'Avancerad sök',
+    'search_terms' => 'Söktermer',
     'search_content_type' => 'Innehållstyp',
     'search_exact_matches' => 'Exakta matchningar',
     'search_tags' => 'Taggar',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Skapa nytt kapitel',
     'chapters_delete' => 'Radera kapitel',
     'chapters_delete_named' => 'Radera kapitlet :chapterName',
-    'chapters_delete_explain' => 'Du håller på att ta bort kapitlet \':chapterName\'. Alla sidor kommer att flyttas direkt in i den aktuella boken istället.',
+    'chapters_delete_explain' => 'Detta kommer att ta bort kapitlet med namnet \':chapterName\'. Alla sidor som finns inom detta kapitel kommer också att raderas.',
     'chapters_delete_confirm' => 'Är du säker på att du vill ta bort det här kapitlet?',
     'chapters_edit' => 'Redigera kapitel',
     'chapters_edit_named' => 'Redigera kapitel :chapterName',
@@ -176,7 +179,7 @@ return [
     'pages_delete_confirm' => 'Är du säker på att du vill ta bort den här sidan?',
     'pages_delete_draft_confirm' => 'Är du säker på att du vill ta bort det här utkastet?',
     'pages_editing_named' => 'Redigerar sida :pageName',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Inställningar för utkast',
     'pages_edit_save_draft' => 'Spara utkast',
     'pages_edit_draft' => 'Redigera utkast',
     'pages_editing_draft' => 'Redigerar utkast',
@@ -203,14 +206,15 @@ return [
     'pages_copy_success' => 'Sidan har kopierats',
     'pages_permissions' => 'Rättigheter för sida',
     'pages_permissions_success' => 'Rättigheterna för sidan har uppdaterats',
-    'pages_revision' => 'Revision',
+    'pages_revision' => 'Revidering',
     'pages_revisions' => 'Sidrevisioner',
     'pages_revisions_named' => 'Sidrevisioner för :pageName',
     'pages_revision_named' => 'Sidrevision för :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Skapad av',
     'pages_revisions_date' => 'Revisionsdatum',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
+    'pages_revisions_numbered' => 'Revisions #:id',
     'pages_revisions_numbered_changes' => 'Revision #:id ändringar',
     'pages_revisions_changelog' => 'Ändringslogg',
     'pages_revisions_changes' => 'Ändringar',
@@ -234,7 +238,7 @@ return [
     ],
     'pages_draft_discarded' => 'Utkastet har tagits bort. Redigeringsverktyget har uppdaterats med aktuellt innehåll.',
     'pages_specific' => 'Specifik sida',
-    'pages_is_template' => 'Page Template',
+    'pages_is_template' => 'Sidmall',
 
     // Editor Sidebar
     'page_tags' => 'Sidtaggar',
@@ -243,11 +247,11 @@ return [
     'shelf_tags' => 'Hylltaggar',
     'tag' => 'Tagg',
     'tags' =>  'Taggar',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'Etikettnamn',
     'tag_value' => 'Taggvärde (Frivilligt)',
     'tags_explain' => "Lägg till taggar för att kategorisera ditt innehåll bättre. \n Du kan tilldela ett värde till en tagg för ännu bättre organisering.",
     'tags_add' => 'Lägg till ännu en tagg',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Ta bort denna etikett',
     'attachments' => 'Bilagor',
     'attachments_explain' => 'Ladda upp filer eller bifoga länkar till ditt innehåll. Dessa visas i sidokolumnen.',
     'attachments_explain_instant_save' => 'Ändringar här sparas omgående.',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Ladda upp fil',
     'attachments_link' => 'Bifoga länk',
     'attachments_set_link' => 'Ange länk',
-    'attachments_delete_confirm' => 'Klicka på "ta bort" igen för att bekräfta att du vill ta bort bilagan.',
+    'attachments_delete' => 'Är du säker på att du vill ta bort bilagan?',
     'attachments_dropzone' => 'Släpp filer här eller klicka för att ladda upp',
     'attachments_no_files' => 'Inga filer har laddats upp',
     'attachments_explain_link' => 'Du kan bifoga en länk om du inte vill ladda upp en fil. Detta kan vara en länk till en annan sida eller till en fil i molnet.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Länk till fil',
     'attachments_link_url_hint' => 'URL till sida eller fil',
     'attach' => 'Bifoga',
+    'attachments_insert_link' => 'Lägg till bilagelänk till sida',
     'attachments_edit_file' => 'Redigera fil',
     'attachments_edit_file_name' => 'Filnamn',
     'attachments_edit_drop_upload' => 'Släpp filer här eller klicka för att ladda upp och skriva över',
@@ -273,12 +278,12 @@ return [
     'attachments_file_uploaded' => 'Filen har laddats upp',
     'attachments_file_updated' => 'Filen har uppdaterats',
     'attachments_link_attached' => 'Länken har bifogats till sidan',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Mallar',
+    'templates_set_as_template' => 'Sidan är en mall',
+    'templates_explain_set_as_template' => 'Du kan använda denna sida som en mall så att dess innehåll kan användas när du skapar andra sidor. Andra användare kommer att kunna använda denna mall om de har visningsrättigheter för den här sidan.',
+    'templates_replace_content' => 'Ersätt sidinnehåll',
+    'templates_append_content' => 'Lägg till till sidans innehåll',
+    'templates_prepend_content' => 'Lägg till före sidans innehåll',
 
     // Profile View
     'profile_user_for_x' => 'Användare i :time',
index 64dbf4c1615100e780801bf278dc6c44b98c1b05..dbc3f37f237021350066bfe1fc36a29e4be4566e 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'E-posten har redan bekräftats, prova att logga in.',
     'email_confirmation_invalid' => 'Denna bekräftelsekod är inte giltig eller har redan använts. Vänligen prova att registera dig på nytt',
     'email_confirmation_expired' => 'Denna bekräftelsekod har gått ut. Vi har skickat dig en ny.',
+    'email_confirmation_awaiting' => 'E-postadressen för det konto som används måste bekräftas',
     'ldap_fail_anonymous' => 'LDAP-inloggning misslyckades med anonym bindning',
     'ldap_fail_authed' => 'LDAP-inloggning misslyckades med angivna dn- och lösenordsuppgifter',
     'ldap_extension_not_installed' => 'LDAP PHP-tillägg inte installerat',
     'ldap_cannot_connect' => 'Kan inte ansluta till ldap-servern. Anslutningen misslyckades',
+    'saml_already_logged_in' => 'Redan inloggad',
+    'saml_user_not_registered' => 'Användarnamnet är inte registrerat och automatisk registrering är inaktiverad',
+    'saml_no_email_address' => 'Kunde inte hitta en e-postadress för den här användaren i data som tillhandahålls av det externa autentiseringssystemet',
+    'saml_invalid_response_id' => 'En begäran från det externa autentiseringssystemet känns inte igen av en process som startats av denna applikation. Att navigera bakåt efter en inloggning kan orsaka detta problem.',
+    'saml_fail_authed' => 'Inloggning med :system misslyckades, systemet godkände inte auktoriseringen',
     'social_no_action_defined' => 'Ingen åtgärd definierad',
     'social_login_bad_response' => "Ett fel inträffade vid inloggning genom :socialAccount: \n:error",
     'social_account_in_use' => 'Detta konto från :socialAccount används redan. Testa att logga in med :socialAccount istället.',
@@ -27,7 +33,7 @@ return [
     'social_account_register_instructions' => 'Om du inte har något konto ännu kan du registerar dig genom att välja :socialAccount.',
     'social_driver_not_found' => 'Drivrutinen för den här tjänsten hittades inte',
     'social_driver_not_configured' => 'Dina inställningar för :socialAccount är inte korrekta.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Denna inbjudningslänk har löpt ut. Du kan istället försöka återställa ditt kontos lösenord.',
 
     // System
     'path_not_writable' => 'Kunde inte ladda upp till sökvägen :filePath. Kontrollera att webbservern har skrivåtkomst.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'Filuppladdningen har tagits ut.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Fel i sidmatchning vid uppdatering av bilaga',
     'attachment_not_found' => 'Bilagan hittades ej',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Sidan hittades inte',
     'sorry_page_not_found' => 'Tyvärr gick det inte att hitta sidan du söker.',
+    'sorry_page_not_found_permission_warning' => 'Om du förväntade dig att denna sida skulle existera, kanske du inte har behörighet att se den.',
     'return_home' => 'Återvänd till startsidan',
     'error_occurred' => 'Ett fel inträffade',
     'app_down' => ':appName är nere just nu',
     'back_soon' => 'Vi är snart tillbaka.',
 
+    // API errors
+    'api_no_authorization_found' => 'Ingen auktoriseringstoken hittades på denna begäran',
+    'api_bad_authorization_format' => 'En auktoriseringstoken hittades på denna begäran men formatet verkade felaktigt',
+    'api_user_token_not_found' => 'Ingen matchande API-token hittades för den angivna auktoriseringstoken',
+    'api_incorrect_token_secret' => 'Hemligheten för den angivna API-token är felaktig',
+    'api_user_no_api_permission' => 'Ägaren av den använda API-token har inte behörighet att göra API-anrop',
+    'api_user_token_expired' => 'Den använda auktoriseringstoken har löpt ut',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Ett fel uppstod när ett test mail skulle skickas:',
+
 ];
index 8924edc7a88881b464cfbc9e4c119f28c34051cd..640309b8848fe4396f3463079f4463887c59c05f 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Lösenord måste vara minst sex tecken långa och anges likadant två gånger.',
     'user' => "Det finns ingen användare med den e-postadressen.",
-    'token' => 'Återställningskoden är ogiltig.',
+    'token' => 'Lösenordsåterställningstoken är ogiltig för denna e-postadress.',
     'sent' => 'Vi har mailat dig en länk för att återställa ditt lösenord!',
     'reset' => 'Ditt lösenord har blivit återställt!',
 
index c848ac65148840b7ec9fcea22dde6a092c89da3b..8c6caaf4a9e6d1096e6423f30fa89382fb5533fc 100644 (file)
@@ -29,7 +29,7 @@ return [
     'app_editor_desc' => 'Välj vilket redigeringsverktyg som ska användas av alla användare för att redigera sidor.',
     'app_custom_html' => 'Egen HTML i <head>',
     'app_custom_html_desc' => 'Eventuellt innehåll i det här fältet placeras längst ner i <head>-sektionen på varje sida. Detta är användbart för att skriva över stilmaller eller lägga in spårningskoder.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'Anpassat innehåll i HTML-huvud är inaktiverat på denna inställningssida för att säkerställa att eventuella ändringar som påverkar funktionaliteten kan återställas.',
     'app_logo' => 'Applikationslogotyp',
     'app_logo_desc' => 'Bilden bör vara minst 43px hög. <br>Större bilder skalas ner.',
     'app_primary_color' => 'Primärfärg',
@@ -41,12 +41,22 @@ return [
     'app_disable_comments_toggle' => 'Inaktivera kommentarer',
     'app_disable_comments_desc' => 'Inaktivera kommentarer på alla sidor i applikationen. Befintliga kommentarer visas inte.',
 
+    // Color settings
+    'content_colors' => 'Innehållsfärger',
+    'content_colors_desc' => 'Ställer in färger för alla element i sidornas hierarki. Att välja färger med samma ljusstyrka som standardfärgerna rekommenderas för läsbarhet.',
+    'bookshelf_color' => 'Hyllfärg',
+    'book_color' => 'Bokens färg',
+    'chapter_color' => 'Kapitels färg',
+    'page_color' => 'Sidfärg',
+    'page_draft_color' => 'Färg på sidutkast',
+
     // Registration Settings
     'reg_settings' => 'Registreringsinställningar',
     'reg_enable' => 'Tillåt registrering',
     'reg_enable_toggle' => 'Tillåt registrering',
     'reg_enable_desc' => 'När registrering tillåts kan användaren logga in som en användare. Vid registreringen ges de en förvald användarroll.',
     'reg_default_role' => 'Standardroll efter registrering',
+    'reg_enable_external_warning' => 'Alternativet ovan ignoreras medan extern LDAP eller SAML-autentisering är aktiv. Användarkonton för icke-existerande medlemmar kommer att skapas automatiskt om autentisering mot det externa system som används lyckas.',
     'reg_email_confirmation' => 'E-postbekräftelse',
     'reg_email_confirmation_toggle' => 'Kräv e-postbekräftelse',
     'reg_confirm_email_desc' => 'Om registrering begränas till vissa domäner kommer e-postbekräftelse alltid att krävas och den här inställningen kommer att ignoreras.',
@@ -58,11 +68,53 @@ return [
     'maint' => 'Underhåll',
     'maint_image_cleanup' => 'Rensa bilder',
     'maint_image_cleanup_desc' => "Söker igenom innehåll i sidor & revisioner för att se vilka bilder och teckningar som är i bruk och vilka som är överflödiga. Se till att ta en komplett backup av databas och bilder innan du kör detta.",
-    'maint_image_cleanup_ignore_revisions' => 'Ignorera bilder i revisioner',
+    'maint_delete_images_only_in_revisions' => 'Ta också bort bilder som bara finns i gamla sidrevideringar',
     'maint_image_cleanup_run' => 'Kör rensning',
     'maint_image_cleanup_warning' => 'Hittade :count bilder som potentiellt inte används. Vill du verkligen ta bort dessa bilder?',
     'maint_image_cleanup_success' => 'Hittade och raderade :count bilder som potentiellt inte används!',
     'maint_image_cleanup_nothing_found' => 'Hittade inga oanvända bilder, så inget har raderats!',
+    'maint_send_test_email' => 'Skicka ett testmail',
+    'maint_send_test_email_desc' => 'Detta skickar ett testmeddelande till den e-postadress som anges i din profil.',
+    'maint_send_test_email_run' => 'Skicka testmail',
+    'maint_send_test_email_success' => 'E-post skickat till :address',
+    'maint_send_test_email_mail_subject' => 'Testmejl',
+    'maint_send_test_email_mail_greeting' => 'E-postleverans verkar fungera!',
+    'maint_send_test_email_mail_text' => 'Grattis! Eftersom du fick detta e-postmeddelande verkar dina e-postinställningar vara korrekt konfigurerade.',
+    'maint_recycle_bin_desc' => 'Borttagna hyllor, böcker, kapitel & sidor skickas till papperskorgen så att de kan återställas eller raderas permanent. Äldre objekt i papperskorgen kan automatiskt tas bort efter ett tag beroende på systemkonfiguration.',
+    'maint_recycle_bin_open' => 'Öppna papperskorgen',
+
+    // Recycle Bin
+    'recycle_bin' => 'Papperskorgen',
+    'recycle_bin_desc' => 'Här kan du återställa objekt som har tagits bort eller välja att permanent ta bort dem från systemet. Denna lista är ofiltrerad till skillnad från liknande aktivitetslistor i systemet där behörighetsfilter tillämpas.',
+    'recycle_bin_deleted_item' => 'Raderat objekt',
+    'recycle_bin_deleted_by' => 'Borttagen av',
+    'recycle_bin_deleted_at' => 'Tid för borttagning',
+    'recycle_bin_permanently_delete' => 'Radera permanent',
+    'recycle_bin_restore' => 'Återställ',
+    'recycle_bin_contents_empty' => 'Papperskorgen är för närvarande tom',
+    'recycle_bin_empty' => 'Töm papperskorgen',
+    'recycle_bin_empty_confirm' => 'Detta kommer permanent att förstöra alla objekt i papperskorgen inklusive innehåll som finns i varje objekt. Är du säker du vill tömma papperskorgen?',
+    'recycle_bin_destroy_confirm' => 'Denna åtgärd kommer att permanent ta bort detta objekt, tillsammans med alla underordnade element som anges nedan, från systemet och du kommer inte att kunna återställa detta innehåll. Är du säker på att du vill ta bort objektet permanent?',
+    'recycle_bin_destroy_list' => 'Objekt som ska förstöras',
+    'recycle_bin_restore_list' => 'Objekt som ska återställas',
+    'recycle_bin_restore_confirm' => 'Denna åtgärd kommer att återställa det raderade objektet, inklusive alla underordnade element, till deras ursprungliga plats. Om den ursprungliga platsen har tagits bort sedan dess, och är nu i papperskorgen, kommer det överordnade objektet också att behöva återställas.',
+    'recycle_bin_restore_deleted_parent' => 'Föräldern till det här objektet har också tagits bort. Dessa kommer att förbli raderade tills den förälder är återställd.',
+    'recycle_bin_destroy_notification' => 'Raderade :count totala objekt från papperskorgen.',
+    'recycle_bin_restore_notification' => 'Återställt :count totala objekt från papperskorgen.',
+
+    // Audit Log
+    'audit' => 'Auditlogg',
+    'audit_desc' => 'Denna granskningslogg visar en lista över aktiviteter som spåras i systemet. Denna lista är ofiltrerad till skillnad från liknande aktivitetslistor i systemet där behörighetsfilter tillämpas.',
+    'audit_event_filter' => 'Händelse Filter',
+    'audit_event_filter_no_filter' => 'Inget filter',
+    'audit_deleted_item' => 'Raderat objekt',
+    'audit_deleted_item_name' => 'Namn: :name',
+    'audit_table_user' => 'Användare',
+    'audit_table_event' => 'Händelse',
+    'audit_table_related' => 'Relaterat objekt eller detalj',
+    'audit_table_date' => 'Datum för senaste aktiviteten',
+    'audit_date_from' => 'Datumintervall från',
+    'audit_date_to' => 'Datumintervall till',
 
     // Role Settings
     'roles' => 'Roller',
@@ -85,9 +137,11 @@ return [
     'role_manage_roles' => 'Hantera roller & rättigheter',
     'role_manage_entity_permissions' => 'Hantera rättigheter för alla böcker, kapitel och sidor',
     'role_manage_own_entity_permissions' => 'Hantera rättigheter för egna böcker, kapitel och sidor',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => 'Hantera mallar',
+    'role_access_api' => 'Åtkomst till systemets API',
     'role_manage_settings' => 'Hantera appinställningar',
     'role_asset' => 'Tillgång till innehåll',
+    'roles_system_warning' => 'Var medveten om att åtkomst till någon av ovanstående tre behörigheter kan tillåta en användare att ändra sina egna rättigheter eller andras rättigheter i systemet. Tilldela endast roller med dessa behörigheter till betrodda användare.',
     'role_asset_desc' => 'Det här är standardinställningarna för allt innehåll i systemet. Eventuella anpassade rättigheter på böcker, kapitel och sidor skriver över dessa inställningar.',
     'role_asset_admins' => 'Administratörer har automatisk tillgång till allt innehåll men dessa alternativ kan visa och dölja vissa gränssnittselement',
     'role_all' => 'Alla',
@@ -103,6 +157,7 @@ return [
     'user_profile' => 'Användarprofil',
     'users_add_new' => 'Lägg till användare',
     'users_search' => 'Sök användare',
+    'users_latest_activity' => 'Senaste aktivitet',
     'users_details' => 'Användarinformation',
     'users_details_desc' => 'Ange ett visningsnamn och en e-postadress för den här användaren. E-postadressen kommer att användas vid inloggningen.',
     'users_details_desc_no_email' => 'Ange ett visningsnamn för den här användaren så att andra kan känna igen den.',
@@ -110,17 +165,20 @@ return [
     'users_role_desc' => 'Välj vilka roller den här användaren ska tilldelas. Om en användare har tilldelats flera roller kommer behörigheterna från dessa roller att staplas och de kommer att få alla rättigheter i de tilldelade rollerna.',
     'users_password' => 'Användarlösenord',
     'users_password_desc' => 'Ange ett lösenord som ska användas för att logga in på sidan. Lösenordet måste vara minst 5 tecken långt.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
+    'users_send_invite_text' => 'Du kan välja att skicka denna användare ett e-postmeddelande som tillåter dem att ställa in sitt eget lösenord, eller så kan du ställa in deras lösenord själv.',
+    'users_send_invite_option' => 'Skicka e-post med inbjudan',
     'users_external_auth_id' => 'Externt ID för autentisering',
-    'users_external_auth_id_desc' => 'Detta är det ID som används för att matcha användaren när den kommunicerar med ditt LDAP-system.',
+    'users_external_auth_id_desc' => 'Detta är det ID som används för att matcha denna användare när du kommunicerar med ditt externa autentiseringssystem.',
     'users_password_warning' => 'Fyll i nedanstående fält endast om du vill byta lösenord:',
     'users_system_public' => 'Den här användaren representerar eventuella gäster som använder systemet. Den kan inte användas för att logga in utan tilldeles automatiskt.',
     'users_delete' => 'Ta bort användare',
     'users_delete_named' => 'Ta bort användaren :userName',
     'users_delete_warning' => 'Detta kommer att ta bort användaren \':userName\' från systemet helt och hållet.',
     'users_delete_confirm' => 'Är du säker på att du vill ta bort användaren?',
-    'users_delete_success' => 'Användaren har tagits bort',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Redigera användare',
     'users_edit_profile' => 'Redigera profil',
     'users_edit_success' => 'Användaren har uppdaterats',
@@ -134,6 +192,32 @@ return [
     'users_social_disconnect' => 'Koppla från konto',
     'users_social_connected' => ':socialAccount har kopplats till ditt konto.',
     'users_social_disconnected' => ':socialAccount har kopplats bort från ditt konto.',
+    'users_api_tokens' => 'API-nyckel',
+    'users_api_tokens_none' => 'Inga API-tokens har skapats för den här användaren',
+    'users_api_tokens_create' => 'Skapa token',
+    'users_api_tokens_expires' => 'Förfaller',
+    'users_api_tokens_docs' => 'API-dokumentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Skapa API-nyckel',
+    'user_api_token_name' => 'Namn',
+    'user_api_token_name_desc' => 'Ge din token ett läsbart namn som en framtida påminnelse om dess avsedda syfte.',
+    'user_api_token_expiry' => 'Förfallodatum',
+    'user_api_token_expiry_desc' => 'Ange ett datum då denna token går ut. Efter detta datum kommer förfrågningar som görs med denna token inte längre att fungera. Lämnar du detta fält tomt kommer utgångsdatum att sättas 100 år in i framtiden.',
+    'user_api_token_create_secret_message' => 'Omedelbart efter att du skapat denna token kommer ett "Token ID" & "Token Secret" att genereras och visas. Token Secret kommer bara att visas en enda gång så se till att kopiera värdet till en säker plats innan du fortsätter.',
+    'user_api_token_create_success' => 'API-token har skapats',
+    'user_api_token_update_success' => 'API-token har uppdaterats',
+    'user_api_token' => 'API-nyckel',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'Detta är en icke-redigerbar systemgenererad identifierare för denna token som måste tillhandahållas i API-förfrågningar.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'Detta är en systemgenererad hemlighet för denna token som måste tillhandahållas i API-förfrågningar. Denna kommer bara att visas en gång så kopiera detta värde till en säker plats.',
+    'user_api_token_created' => 'Token skapad :timeAgo',
+    'user_api_token_updated' => 'Token Uppdaterad :timeAgo',
+    'user_api_token_delete' => 'Ta bort token',
+    'user_api_token_delete_warning' => 'Detta kommer att helt ta bort denna API-token med namnet \':tokenName\' från systemet.',
+    'user_api_token_delete_confirm' => 'Är du säker på att du vill ta bort denna API-token?',
+    'user_api_token_delete_success' => 'API-token har tagits bort',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Danska',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenska',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 490d1d85b2c7ead48198f9dfcb605772526aa2c2..3190318559d1642c960bc6b2c612a27b874fb757 100644 (file)
@@ -30,19 +30,19 @@ return [
     'digits'               => ':attribute måste vara :digits siffror.',
     'digits_between'       => ':attribute måste vara mellan :min och :max siffror.',
     'email'                => ':attribute måste vara en giltig e-postadress.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ':attribute måste sluta med något av följande: :values',
     'filled'               => ':attribute är obligatoriskt.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute måste vara större än :value.',
+        'file'    => ':attribute måste vara större än :value kilobytes.',
+        'string'  => ':attribute måste vara större än :value tecken.',
+        'array'   => ':attribute måste ha mer än :value objekt.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => ':attribute måste vara större än eller likamed :value.',
+        'file'    => ':attribute måste vara större än eller lika med :value kilobytes.',
+        'string'  => ':attribute måste vara större än eller lika med :value tecken.',
+        'array'   => ':attribute måste ha :value objekt eller mer.',
     ],
     'exists'               => 'Valt värde för :attribute är ogiltigt.',
     'image'                => ':attribute måste vara en bild.',
@@ -50,20 +50,20 @@ return [
     'in'                   => 'Vald :attribute är ogiltigt.',
     'integer'              => ':attribute måste vara en integer.',
     'ip'                   => ':attribute måste vara en giltig IP-adress.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ipv4'                 => ':attribute måste vara en giltig IPv4-adress.',
+    'ipv6'                 => ':attribute måste vara en giltig IPv6-adress.',
+    'json'                 => ':attribute måste vara en giltig JSON-sträng.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute måste vara mindre än :value.',
+        'file'    => ':attribute måste vara mindre än :value kilobytes.',
+        'string'  => ':attribute måste vara mindre än :value tecken.',
+        'array'   => ':attribute måste ha mindre än :value objekt.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute måste vara mindre än eller lika :value.',
+        'file'    => ':attribute måste vara mindre än eller lika med :value kilobytes.',
+        'string'  => ':attribute måste vara mindre än eller lika med :value tecken.',
+        'array'   => ':attribute får inte innehålla mer än :max objekt.',
     ],
     'max'                  => [
         'numeric' => ':attribute får inte vara större än :max.',
@@ -80,7 +80,7 @@ return [
     ],
     'no_double_extension'  => ':attribute får bara ha ett filtillägg.',
     'not_in'               => 'Vald :attribute är inte giltig',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => 'Formatet på :attribute är ogiltigt.',
     'numeric'              => ':attribute måste vara ett nummer.',
     'regex'                => ':attribute har ett ogiltigt format.',
     'required'             => ':attribute är obligatoriskt.',
@@ -90,6 +90,7 @@ return [
     'required_without'     => ':attribute är obligatoriskt när :values inte finns.',
     'required_without_all' => ':attribute är obligatirskt när ingen av :values finns.',
     'same'                 => ':attribute och :other måste stämma överens.',
+    'safe_url'             => 'Den angivna länken kanske inte är säker.',
     'size'                 => [
         'numeric' => ':attribute måste vara :size.',
         'file'    => ':attribute måste vara :size kilobyte.',
diff --git a/resources/lang/th/activities.php b/resources/lang/th/activities.php
new file mode 100644 (file)
index 0000000..4cac54b
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'created page',
+    'page_create_notification'    => 'Page Successfully Created',
+    'page_update'                 => 'updated page',
+    'page_update_notification'    => 'Page Successfully Updated',
+    'page_delete'                 => 'deleted page',
+    'page_delete_notification'    => 'Page Successfully Deleted',
+    'page_restore'                => 'restored page',
+    'page_restore_notification'   => 'Page Successfully Restored',
+    'page_move'                   => 'moved page',
+
+    // Chapters
+    'chapter_create'              => 'created chapter',
+    'chapter_create_notification' => 'Chapter Successfully Created',
+    'chapter_update'              => 'updated chapter',
+    'chapter_update_notification' => 'Chapter Successfully Updated',
+    'chapter_delete'              => 'deleted chapter',
+    'chapter_delete_notification' => 'Chapter Successfully Deleted',
+    'chapter_move'                => 'moved chapter',
+
+    // Books
+    'book_create'                 => 'created book',
+    'book_create_notification'    => 'Book Successfully Created',
+    'book_update'                 => 'updated book',
+    'book_update_notification'    => 'Book Successfully Updated',
+    'book_delete'                 => 'deleted book',
+    'book_delete_notification'    => 'Book Successfully Deleted',
+    'book_sort'                   => 'sorted book',
+    'book_sort_notification'      => 'Book Successfully Re-sorted',
+
+    // Bookshelves
+    'bookshelf_create'            => 'created Bookshelf',
+    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
+    'bookshelf_update'                 => 'updated bookshelf',
+    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
+    'bookshelf_delete'                 => 'deleted bookshelf',
+    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+
+    // Other
+    'commented_on'                => 'commented on',
+];
diff --git a/resources/lang/th/auth.php b/resources/lang/th/auth.php
new file mode 100644 (file)
index 0000000..d64fce9
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'These credentials do not match our records.',
+    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
+
+    // Login & Register
+    'sign_up' => 'Sign up',
+    'log_in' => 'Log in',
+    'log_in_with' => 'Login with :socialDriver',
+    'sign_up_with' => 'Sign up with :socialDriver',
+    'logout' => 'Logout',
+
+    'name' => 'Name',
+    'username' => 'Username',
+    'email' => 'Email',
+    'password' => 'Password',
+    'password_confirm' => 'Confirm Password',
+    'password_hint' => 'Must be over 7 characters',
+    'forgot_password' => 'Forgot Password?',
+    'remember_me' => 'Remember Me',
+    'ldap_email_hint' => 'Please enter an email to use for this account.',
+    'create_account' => 'Create Account',
+    'already_have_account' => 'Already have an account?',
+    'dont_have_account' => 'Don\'t have an account?',
+    'social_login' => 'Social Login',
+    'social_registration' => 'Social Registration',
+    'social_registration_text' => 'Register and sign in using another service.',
+
+    'register_thanks' => 'Thanks for registering!',
+    'register_confirm' => 'Please check your email and click the confirmation button to access :appName.',
+    'registrations_disabled' => 'Registrations are currently disabled',
+    'registration_email_domain_invalid' => 'That email domain does not have access to this application',
+    'register_success' => 'Thanks for signing up! You are now registered and signed in.',
+
+
+    // Password Reset
+    'reset_password' => 'Reset Password',
+    'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.',
+    'reset_password_send_button' => 'Send Reset Link',
+    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_success' => 'Your password has been successfully reset.',
+    'email_reset_subject' => 'Reset your :appName password',
+    'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.',
+    'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Confirm your email on :appName',
+    'email_confirm_greeting' => 'Thanks for joining :appName!',
+    'email_confirm_text' => 'Please confirm your email address by clicking the button below:',
+    'email_confirm_action' => 'Confirm Email',
+    'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.',
+    'email_confirm_success' => 'Your email has been confirmed!',
+    'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.',
+
+    'email_not_confirmed' => 'Email Address Not Confirmed',
+    'email_not_confirmed_text' => 'Your email address has not yet been confirmed.',
+    'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.',
+    'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.',
+    'email_not_confirmed_resend_button' => 'Resend Confirmation Email',
+
+    // User Invite
+    'user_invite_email_subject' => 'You have been invited to join :appName!',
+    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
+    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
+    'user_invite_email_action' => 'Set Account Password',
+    'user_invite_page_welcome' => 'Welcome to :appName!',
+    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
+    'user_invite_page_confirm_button' => 'Confirm Password',
+    'user_invite_success' => 'Password set, you now have access to :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/th/common.php b/resources/lang/th/common.php
new file mode 100644 (file)
index 0000000..e87bd11
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Cancel',
+    'confirm' => 'Confirm',
+    'back' => 'Back',
+    'save' => 'Save',
+    'continue' => 'Continue',
+    'select' => 'Select',
+    'toggle_all' => 'Toggle All',
+    'more' => 'More',
+
+    // Form Labels
+    'name' => 'Name',
+    'description' => 'Description',
+    'role' => 'Role',
+    'cover_image' => 'Cover image',
+    'cover_image_description' => 'This image should be approx 440x250px.',
+    
+    // Actions
+    'actions' => 'Actions',
+    'view' => 'View',
+    'view_all' => 'View All',
+    'create' => 'Create',
+    'update' => 'Update',
+    'edit' => 'Edit',
+    'sort' => 'Sort',
+    'move' => 'Move',
+    'copy' => 'Copy',
+    'reply' => 'Reply',
+    'delete' => 'Delete',
+    'delete_confirm' => 'Confirm Deletion',
+    'search' => 'Search',
+    'search_clear' => 'Clear Search',
+    'reset' => 'Reset',
+    'remove' => 'Remove',
+    'add' => 'Add',
+    'fullscreen' => 'Fullscreen',
+
+    // Sort Options
+    'sort_options' => 'Sort Options',
+    'sort_direction_toggle' => 'Sort Direction Toggle',
+    'sort_ascending' => 'Sort Ascending',
+    'sort_descending' => 'Sort Descending',
+    'sort_name' => 'Name',
+    'sort_created_at' => 'Created Date',
+    'sort_updated_at' => 'Updated Date',
+
+    // Misc
+    'deleted_user' => 'Deleted User',
+    'no_activity' => 'No activity to show',
+    'no_items' => 'No items available',
+    'back_to_top' => 'Back to top',
+    'toggle_details' => 'Toggle Details',
+    'toggle_thumbnails' => 'Toggle Thumbnails',
+    'details' => 'Details',
+    'grid_view' => 'Grid View',
+    'list_view' => 'List View',
+    'default' => 'Default',
+    'breadcrumb' => 'Breadcrumb',
+
+    // Header
+    'profile_menu' => 'Profile Menu',
+    'view_profile' => 'View Profile',
+    'edit_profile' => 'Edit Profile',
+    'dark_mode' => 'Dark Mode',
+    'light_mode' => 'Light Mode',
+
+    // Layout tabs
+    'tab_info' => 'Info',
+    'tab_content' => 'Content',
+
+    // Email Content
+    'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
+    'email_rights' => 'All rights reserved',
+];
diff --git a/resources/lang/th/components.php b/resources/lang/th/components.php
new file mode 100644 (file)
index 0000000..48a0a32
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Image Select',
+    'image_all' => 'All',
+    'image_all_title' => 'View all images',
+    'image_book_title' => 'View images uploaded to this book',
+    'image_page_title' => 'View images uploaded to this page',
+    'image_search_hint' => 'Search by image name',
+    'image_uploaded' => 'Uploaded :uploadedDate',
+    'image_load_more' => 'Load More',
+    'image_image_name' => 'Image Name',
+    'image_delete_used' => 'This image is used in the pages below.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_select_image' => 'Select Image',
+    'image_dropzone' => 'Drop images or click here to upload',
+    'images_deleted' => 'Images Deleted',
+    'image_preview' => 'Image Preview',
+    'image_upload_success' => 'Image uploaded successfully',
+    'image_update_success' => 'Image details successfully updated',
+    'image_delete_success' => 'Image successfully deleted',
+    'image_upload_remove' => 'Remove',
+
+    // Code Editor
+    'code_editor' => 'Edit Code',
+    'code_language' => 'Code Language',
+    'code_content' => 'Code Content',
+    'code_session_history' => 'Session History',
+    'code_save' => 'Save Code',
+];
diff --git a/resources/lang/th/entities.php b/resources/lang/th/entities.php
new file mode 100644 (file)
index 0000000..f64867a
--- /dev/null
@@ -0,0 +1,316 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Recently Created',
+    'recently_created_pages' => 'Recently Created Pages',
+    'recently_updated_pages' => 'Recently Updated Pages',
+    'recently_created_chapters' => 'Recently Created Chapters',
+    'recently_created_books' => 'Recently Created Books',
+    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_update' => 'Recently Updated',
+    'recently_viewed' => 'Recently Viewed',
+    'recent_activity' => 'Recent Activity',
+    'create_now' => 'Create one now',
+    'revisions' => 'Revisions',
+    'meta_revision' => 'Revision #:revisionCount',
+    'meta_created' => 'Created :timeLength',
+    'meta_created_name' => 'Created :timeLength by :user',
+    'meta_updated' => 'Updated :timeLength',
+    'meta_updated_name' => 'Updated :timeLength by :user',
+    'entity_select' => 'Entity Select',
+    'images' => 'Images',
+    'my_recent_drafts' => 'My Recent Drafts',
+    'my_recently_viewed' => 'My Recently Viewed',
+    'no_pages_viewed' => 'You have not viewed any pages',
+    'no_pages_recently_created' => 'No pages have been recently created',
+    'no_pages_recently_updated' => 'No pages have been recently updated',
+    'export' => 'Export',
+    'export_html' => 'Contained Web File',
+    'export_pdf' => 'PDF File',
+    'export_text' => 'Plain Text File',
+
+    // Permissions and restrictions
+    'permissions' => 'Permissions',
+    'permissions_intro' => 'Once enabled, These permissions will take priority over any set role permissions.',
+    'permissions_enable' => 'Enable Custom Permissions',
+    'permissions_save' => 'Save Permissions',
+
+    // Search
+    'search_results' => 'Search Results',
+    'search_total_results_found' => ':count result found|:count total results found',
+    'search_clear' => 'Clear Search',
+    'search_no_pages' => 'No pages matched this search',
+    'search_for_term' => 'Search for :term',
+    'search_more' => 'More Results',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
+    'search_content_type' => 'Content Type',
+    'search_exact_matches' => 'Exact Matches',
+    'search_tags' => 'Tag Searches',
+    'search_options' => 'Options',
+    'search_viewed_by_me' => 'Viewed by me',
+    'search_not_viewed_by_me' => 'Not viewed by me',
+    'search_permissions_set' => 'Permissions set',
+    'search_created_by_me' => 'Created by me',
+    'search_updated_by_me' => 'Updated by me',
+    'search_date_options' => 'Date Options',
+    'search_updated_before' => 'Updated before',
+    'search_updated_after' => 'Updated after',
+    'search_created_before' => 'Created before',
+    'search_created_after' => 'Created after',
+    'search_set_date' => 'Set Date',
+    'search_update' => 'Update Search',
+
+    // Shelves
+    'shelf' => 'Shelf',
+    'shelves' => 'Shelves',
+    'x_shelves' => ':count Shelf|:count Shelves',
+    'shelves_long' => 'Bookshelves',
+    'shelves_empty' => 'No shelves have been created',
+    'shelves_create' => 'Create New Shelf',
+    'shelves_popular' => 'Popular Shelves',
+    'shelves_new' => 'New Shelves',
+    'shelves_new_action' => 'New Shelf',
+    'shelves_popular_empty' => 'The most popular shelves will appear here.',
+    'shelves_new_empty' => 'The most recently created shelves will appear here.',
+    'shelves_save' => 'Save Shelf',
+    'shelves_books' => 'Books on this shelf',
+    'shelves_add_books' => 'Add books to this shelf',
+    'shelves_drag_books' => 'Drag books here to add them to this shelf',
+    'shelves_empty_contents' => 'This shelf has no books assigned to it',
+    'shelves_edit_and_assign' => 'Edit shelf to assign books',
+    'shelves_edit_named' => 'Edit Bookshelf :name',
+    'shelves_edit' => 'Edit Bookshelf',
+    'shelves_delete' => 'Delete Bookshelf',
+    'shelves_delete_named' => 'Delete Bookshelf :name',
+    'shelves_delete_explain' => "This will delete the bookshelf with the name ':name'. Contained books will not be deleted.",
+    'shelves_delete_confirmation' => 'Are you sure you want to delete this bookshelf?',
+    'shelves_permissions' => 'Bookshelf Permissions',
+    'shelves_permissions_updated' => 'Bookshelf Permissions Updated',
+    'shelves_permissions_active' => 'Bookshelf Permissions Active',
+    'shelves_copy_permissions_to_books' => 'Copy Permissions to Books',
+    'shelves_copy_permissions' => 'Copy Permissions',
+    'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this bookshelf to all books contained within. Before activating, ensure any changes to the permissions of this bookshelf have been saved.',
+    'shelves_copy_permission_success' => 'Bookshelf permissions copied to :count books',
+
+    // Books
+    'book' => 'Book',
+    'books' => 'Books',
+    'x_books' => ':count Book|:count Books',
+    'books_empty' => 'No books have been created',
+    'books_popular' => 'Popular Books',
+    'books_recent' => 'Recent Books',
+    'books_new' => 'New Books',
+    'books_new_action' => 'New Book',
+    'books_popular_empty' => 'The most popular books will appear here.',
+    'books_new_empty' => 'The most recently created books will appear here.',
+    'books_create' => 'Create New Book',
+    'books_delete' => 'Delete Book',
+    'books_delete_named' => 'Delete Book :bookName',
+    'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.',
+    'books_delete_confirmation' => 'Are you sure you want to delete this book?',
+    'books_edit' => 'Edit Book',
+    'books_edit_named' => 'Edit Book :bookName',
+    'books_form_book_name' => 'Book Name',
+    'books_save' => 'Save Book',
+    'books_permissions' => 'Book Permissions',
+    'books_permissions_updated' => 'Book Permissions Updated',
+    'books_empty_contents' => 'No pages or chapters have been created for this book.',
+    'books_empty_create_page' => 'Create a new page',
+    'books_empty_sort_current_book' => 'Sort the current book',
+    'books_empty_add_chapter' => 'Add a chapter',
+    'books_permissions_active' => 'Book Permissions Active',
+    'books_search_this' => 'Search this book',
+    'books_navigation' => 'Book Navigation',
+    'books_sort' => 'Sort Book Contents',
+    'books_sort_named' => 'Sort Book :bookName',
+    'books_sort_name' => 'Sort by Name',
+    'books_sort_created' => 'Sort by Created Date',
+    'books_sort_updated' => 'Sort by Updated Date',
+    'books_sort_chapters_first' => 'Chapters First',
+    'books_sort_chapters_last' => 'Chapters Last',
+    'books_sort_show_other' => 'Show Other Books',
+    'books_sort_save' => 'Save New Order',
+
+    // Chapters
+    'chapter' => 'Chapter',
+    'chapters' => 'Chapters',
+    'x_chapters' => ':count Chapter|:count Chapters',
+    'chapters_popular' => 'Popular Chapters',
+    'chapters_new' => 'New Chapter',
+    'chapters_create' => 'Create New Chapter',
+    'chapters_delete' => 'Delete Chapter',
+    'chapters_delete_named' => 'Delete Chapter :chapterName',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages will be removed and added directly to the parent book.',
+    'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?',
+    'chapters_edit' => 'Edit Chapter',
+    'chapters_edit_named' => 'Edit Chapter :chapterName',
+    'chapters_save' => 'Save Chapter',
+    'chapters_move' => 'Move Chapter',
+    'chapters_move_named' => 'Move Chapter :chapterName',
+    'chapter_move_success' => 'Chapter moved to :bookName',
+    'chapters_permissions' => 'Chapter Permissions',
+    'chapters_empty' => 'No pages are currently in this chapter.',
+    'chapters_permissions_active' => 'Chapter Permissions Active',
+    'chapters_permissions_success' => 'Chapter Permissions Updated',
+    'chapters_search_this' => 'Search this chapter',
+
+    // Pages
+    'page' => 'Page',
+    'pages' => 'Pages',
+    'x_pages' => ':count Page|:count Pages',
+    'pages_popular' => 'Popular Pages',
+    'pages_new' => 'New Page',
+    'pages_attachments' => 'Attachments',
+    'pages_navigation' => 'Page Navigation',
+    'pages_delete' => 'Delete Page',
+    'pages_delete_named' => 'Delete Page :pageName',
+    'pages_delete_draft_named' => 'Delete Draft Page :pageName',
+    'pages_delete_draft' => 'Delete Draft Page',
+    'pages_delete_success' => 'Page deleted',
+    'pages_delete_draft_success' => 'Draft page deleted',
+    'pages_delete_confirm' => 'Are you sure you want to delete this page?',
+    'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?',
+    'pages_editing_named' => 'Editing Page :pageName',
+    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_save_draft' => 'Save Draft',
+    'pages_edit_draft' => 'Edit Page Draft',
+    'pages_editing_draft' => 'Editing Draft',
+    'pages_editing_page' => 'Editing Page',
+    'pages_edit_draft_save_at' => 'Draft saved at ',
+    'pages_edit_delete_draft' => 'Delete Draft',
+    'pages_edit_discard_draft' => 'Discard Draft',
+    'pages_edit_set_changelog' => 'Set Changelog',
+    'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made',
+    'pages_edit_enter_changelog' => 'Enter Changelog',
+    'pages_save' => 'Save Page',
+    'pages_title' => 'Page Title',
+    'pages_name' => 'Page Name',
+    'pages_md_editor' => 'Editor',
+    'pages_md_preview' => 'Preview',
+    'pages_md_insert_image' => 'Insert Image',
+    'pages_md_insert_link' => 'Insert Entity Link',
+    'pages_md_insert_drawing' => 'Insert Drawing',
+    'pages_not_in_chapter' => 'Page is not in a chapter',
+    'pages_move' => 'Move Page',
+    'pages_move_success' => 'Page moved to ":parentName"',
+    'pages_copy' => 'Copy Page',
+    'pages_copy_desination' => 'Copy Destination',
+    'pages_copy_success' => 'Page successfully copied',
+    'pages_permissions' => 'Page Permissions',
+    'pages_permissions_success' => 'Page permissions updated',
+    'pages_revision' => 'Revision',
+    'pages_revisions' => 'Page Revisions',
+    'pages_revisions_named' => 'Page Revisions for :pageName',
+    'pages_revision_named' => 'Page Revision for :pageName',
+    'pages_revisions_created_by' => 'Created By',
+    'pages_revisions_date' => 'Revision Date',
+    'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'Revision #:id',
+    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_changelog' => 'Changelog',
+    'pages_revisions_changes' => 'Changes',
+    'pages_revisions_current' => 'Current Version',
+    'pages_revisions_preview' => 'Preview',
+    'pages_revisions_restore' => 'Restore',
+    'pages_revisions_none' => 'This page has no revisions',
+    'pages_copy_link' => 'Copy Link',
+    'pages_edit_content_link' => 'Edit Content',
+    'pages_permissions_active' => 'Page Permissions Active',
+    'pages_initial_revision' => 'Initial publish',
+    'pages_initial_name' => 'New Page',
+    'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.',
+    'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count users have started editing this page',
+        'start_b' => ':userName has started editing this page',
+        'time_a' => 'since the page was last updated',
+        'time_b' => 'in the last :minCount minutes',
+        'message' => ':start :time. Take care not to overwrite each other\'s updates!',
+    ],
+    'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content',
+    'pages_specific' => 'Specific Page',
+    'pages_is_template' => 'Page Template',
+
+    // Editor Sidebar
+    'page_tags' => 'Page Tags',
+    'chapter_tags' => 'Chapter Tags',
+    'book_tags' => 'Book Tags',
+    'shelf_tags' => 'Shelf Tags',
+    'tag' => 'Tag',
+    'tags' =>  'Tags',
+    'tag_name' =>  'Tag Name',
+    'tag_value' => 'Tag Value (Optional)',
+    'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.",
+    'tags_add' => 'Add another tag',
+    'tags_remove' => 'Remove this tag',
+    'attachments' => 'Attachments',
+    'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.',
+    'attachments_explain_instant_save' => 'Changes here are saved instantly.',
+    'attachments_items' => 'Attached Items',
+    'attachments_upload' => 'Upload File',
+    'attachments_link' => 'Attach Link',
+    'attachments_set_link' => 'Set Link',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
+    'attachments_dropzone' => 'Drop files or click here to attach a file',
+    'attachments_no_files' => 'No files have been uploaded',
+    'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
+    'attachments_link_name' => 'Link Name',
+    'attachment_link' => 'Attachment link',
+    'attachments_link_url' => 'Link to file',
+    'attachments_link_url_hint' => 'Url of site or file',
+    'attach' => 'Attach',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
+    'attachments_edit_file' => 'Edit File',
+    'attachments_edit_file_name' => 'File Name',
+    'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
+    'attachments_order_updated' => 'Attachment order updated',
+    'attachments_updated_success' => 'Attachment details updated',
+    'attachments_deleted' => 'Attachment deleted',
+    'attachments_file_uploaded' => 'File successfully uploaded',
+    'attachments_file_updated' => 'File successfully updated',
+    'attachments_link_attached' => 'Link successfully attached to page',
+    'templates' => 'Templates',
+    'templates_set_as_template' => 'Page is a template',
+    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
+    'templates_replace_content' => 'Replace page content',
+    'templates_append_content' => 'Append to page content',
+    'templates_prepend_content' => 'Prepend to page content',
+
+    // Profile View
+    'profile_user_for_x' => 'User for :time',
+    'profile_created_content' => 'Created Content',
+    'profile_not_created_pages' => ':userName has not created any pages',
+    'profile_not_created_chapters' => ':userName has not created any chapters',
+    'profile_not_created_books' => ':userName has not created any books',
+    'profile_not_created_shelves' => ':userName has not created any shelves',
+
+    // Comments
+    'comment' => 'Comment',
+    'comments' => 'Comments',
+    'comment_add' => 'Add Comment',
+    'comment_placeholder' => 'Leave a comment here',
+    'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments',
+    'comment_save' => 'Save Comment',
+    'comment_saving' => 'Saving comment...',
+    'comment_deleting' => 'Deleting comment...',
+    'comment_new' => 'New Comment',
+    'comment_created' => 'commented :createDiff',
+    'comment_updated' => 'Updated :updateDiff by :username',
+    'comment_deleted_success' => 'Comment deleted',
+    'comment_created_success' => 'Comment added',
+    'comment_updated_success' => 'Comment updated',
+    'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
+    'comment_in_reply_to' => 'In reply to :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Are you sure you want to delete this revision?',
+    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_delete_success' => 'Revision deleted',
+    'revision_cannot_delete_latest' => 'Cannot delete the latest revision.'
+];
\ No newline at end of file
diff --git a/resources/lang/th/errors.php b/resources/lang/th/errors.php
new file mode 100644 (file)
index 0000000..79024e4
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'You do not have permission to access the requested page.',
+    'permissionJson' => 'You do not have permission to perform the requested action.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.',
+    'email_already_confirmed' => 'Email has already been confirmed, Try logging in.',
+    'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.',
+    'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.',
+    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+    'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
+    'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
+    'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
+    'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
+    'saml_already_logged_in' => 'Already logged in',
+    'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
+    'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
+    'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
+    'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
+    'social_no_action_defined' => 'No action defined',
+    'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
+    'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
+    'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.',
+    'social_account_existing' => 'This :socialAccount is already attached to your profile.',
+    'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
+    'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ',
+    'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.',
+    'social_driver_not_found' => 'Social driver not found',
+    'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
+    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+
+    // System
+    'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
+    'cannot_get_image_from_url' => 'Cannot get image from :url',
+    'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
+    'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'uploaded'  => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'image_upload_error' => 'An error occurred uploading the image',
+    'image_upload_type_error' => 'The image type being uploaded is invalid',
+    'file_upload_timeout' => 'The file upload has timed out.',
+
+    // Attachments
+    'attachment_not_found' => 'Attachment not found',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
+    'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage',
+
+    // Entities
+    'entity_not_found' => 'Entity not found',
+    'bookshelf_not_found' => 'Bookshelf not found',
+    'book_not_found' => 'Book not found',
+    'page_not_found' => 'Page not found',
+    'chapter_not_found' => 'Chapter not found',
+    'selected_book_not_found' => 'The selected book was not found',
+    'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
+    'guests_cannot_save_drafts' => 'Guests cannot save drafts',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
+    'users_cannot_delete_guest' => 'You cannot delete the guest user',
+
+    // Roles
+    'role_cannot_be_edited' => 'This role cannot be edited',
+    'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
+    'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
+    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+
+    // Comments
+    'comment_list' => 'An error occurred while fetching the comments.',
+    'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.',
+    'comment_add' => 'An error occurred while adding / updating the comment.',
+    'comment_delete' => 'An error occurred while deleting the comment.',
+    'empty_comment' => 'Cannot add an empty comment.',
+
+    // Error pages
+    '404_page_not_found' => 'Page Not Found',
+    'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
+    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'return_home' => 'Return to home',
+    'error_occurred' => 'An Error Occurred',
+    'app_down' => ':appName is down right now',
+    'back_soon' => 'It will be back up soon.',
+
+    // API errors
+    'api_no_authorization_found' => 'No authorization token found on the request',
+    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
+    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
+    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
+    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
+    'api_user_token_expired' => 'The authorization token used has expired',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+
+];
diff --git a/resources/lang/th/pagination.php b/resources/lang/th/pagination.php
new file mode 100644 (file)
index 0000000..85bd12f
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; Previous',
+    'next'     => 'Next &raquo;',
+
+];
diff --git a/resources/lang/th/passwords.php b/resources/lang/th/passwords.php
new file mode 100644 (file)
index 0000000..b408f3c
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+    'password' => 'Passwords must be at least eight characters and match the confirmation.',
+    'user' => "We can't find a user with that e-mail address.",
+    'token' => 'The password reset token is invalid for this email address.',
+    'sent' => 'We have e-mailed your password reset link!',
+    'reset' => 'Your password has been reset!',
+
+];
diff --git a/resources/lang/th/settings.php b/resources/lang/th/settings.php
new file mode 100644 (file)
index 0000000..2bd314c
--- /dev/null
@@ -0,0 +1,229 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Settings',
+    'settings_save' => 'Save Settings',
+    'settings_save_success' => 'Settings saved',
+
+    // App Settings
+    'app_customization' => 'Customization',
+    'app_features_security' => 'Features & Security',
+    'app_name' => 'Application Name',
+    'app_name_desc' => 'This name is shown in the header and in any system-sent emails.',
+    'app_name_header' => 'Show name in header',
+    'app_public_access' => 'Public Access',
+    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
+    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
+    'app_public_access_toggle' => 'Allow public access',
+    'app_public_viewing' => 'Allow public viewing?',
+    'app_secure_images' => 'Higher Security Image Uploads',
+    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.',
+    'app_editor' => 'Page Editor',
+    'app_editor_desc' => 'Select which editor will be used by all users to edit pages.',
+    'app_custom_html' => 'Custom HTML Head Content',
+    'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the <head> section of every page. This is handy for overriding styles or adding analytics code.',
+    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_logo' => 'Application Logo',
+    'app_logo_desc' => 'This image should be 43px in height. <br>Large images will be scaled down.',
+    'app_primary_color' => 'Application Primary Color',
+    'app_primary_color_desc' => 'Sets the primary color for the application including the banner, buttons, and links.',
+    'app_homepage' => 'Application Homepage',
+    'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
+    'app_homepage_select' => 'Select a page',
+    'app_disable_comments' => 'Disable Comments',
+    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
+
+    // Color settings
+    'content_colors' => 'Content Colors',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
+    // Registration Settings
+    'reg_settings' => 'Registration',
+    'reg_enable' => 'Enable Registration',
+    'reg_enable_toggle' => 'Enable registration',
+    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_default_role' => 'Default user role after registration',
+    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    'reg_email_confirmation' => 'Email Confirmation',
+    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.',
+    'reg_confirm_restrict_domain' => 'Domain Restriction',
+    'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
+    'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
+
+    // Maintenance settings
+    'maint' => 'Maintenance',
+    'maint_image_cleanup' => 'Cleanup Images',
+    'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
+    'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+    'maint_image_cleanup_run' => 'Run Cleanup',
+    'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
+    'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
+    'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
+    'maint_send_test_email' => 'Send a Test Email',
+    'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
+    'maint_send_test_email_run' => 'Send test email',
+    'maint_send_test_email_success' => 'Email sent to :address',
+    'maint_send_test_email_mail_subject' => 'Test Email',
+    'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
+    'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
+    // Role Settings
+    'roles' => 'Roles',
+    'role_user_roles' => 'User Roles',
+    'role_create' => 'Create New Role',
+    'role_create_success' => 'Role successfully created',
+    'role_delete' => 'Delete Role',
+    'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
+    'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
+    'role_delete_no_migration' => "Don't migrate users",
+    'role_delete_sure' => 'Are you sure you want to delete this role?',
+    'role_delete_success' => 'Role successfully deleted',
+    'role_edit' => 'Edit Role',
+    'role_details' => 'Role Details',
+    'role_name' => 'Role Name',
+    'role_desc' => 'Short Description of Role',
+    'role_external_auth_id' => 'External Authentication IDs',
+    'role_system' => 'System Permissions',
+    'role_manage_users' => 'Manage users',
+    'role_manage_roles' => 'Manage roles & role permissions',
+    'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions',
+    'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
+    'role_manage_page_templates' => 'Manage page templates',
+    'role_access_api' => 'Access system API',
+    'role_manage_settings' => 'Manage app settings',
+    'role_asset' => 'Asset Permissions',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
+    'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
+    'role_all' => 'All',
+    'role_own' => 'Own',
+    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_save' => 'Save Role',
+    'role_update_success' => 'Role successfully updated',
+    'role_users' => 'Users in this role',
+    'role_users_none' => 'No users are currently assigned to this role',
+
+    // Users
+    'users' => 'Users',
+    'user_profile' => 'User Profile',
+    'users_add_new' => 'Add New User',
+    'users_search' => 'Search Users',
+    'users_details' => 'User Details',
+    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
+    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_role' => 'User Roles',
+    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
+    'users_password' => 'User Password',
+    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
+    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
+    'users_send_invite_option' => 'Send user invite email',
+    'users_external_auth_id' => 'External Authentication ID',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+    'users_password_warning' => 'Only fill the below if you would like to change your password.',
+    'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
+    'users_delete' => 'Delete User',
+    'users_delete_named' => 'Delete user :userName',
+    'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
+    'users_delete_confirm' => 'Are you sure you want to delete this user?',
+    'users_delete_success' => 'Users successfully removed',
+    'users_edit' => 'Edit User',
+    'users_edit_profile' => 'Edit Profile',
+    'users_edit_success' => 'User successfully updated',
+    'users_avatar' => 'User Avatar',
+    'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.',
+    'users_preferred_language' => 'Preferred Language',
+    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_social_accounts' => 'Social Accounts',
+    'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.',
+    'users_social_connect' => 'Connect Account',
+    'users_social_disconnect' => 'Disconnect Account',
+    'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
+    'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token Created :timeAgo',
+    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'Slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/th/validation.php b/resources/lang/th/validation.php
new file mode 100644 (file)
index 0000000..76b57a2
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => 'The :attribute must be accepted.',
+    'active_url'           => 'The :attribute is not a valid URL.',
+    'after'                => 'The :attribute must be a date after :date.',
+    'alpha'                => 'The :attribute may only contain letters.',
+    'alpha_dash'           => 'The :attribute may only contain letters, numbers, dashes and underscores.',
+    'alpha_num'            => 'The :attribute may only contain letters and numbers.',
+    'array'                => 'The :attribute must be an array.',
+    'before'               => 'The :attribute must be a date before :date.',
+    'between'              => [
+        'numeric' => 'The :attribute must be between :min and :max.',
+        'file'    => 'The :attribute must be between :min and :max kilobytes.',
+        'string'  => 'The :attribute must be between :min and :max characters.',
+        'array'   => 'The :attribute must have between :min and :max items.',
+    ],
+    'boolean'              => 'The :attribute field must be true or false.',
+    'confirmed'            => 'The :attribute confirmation does not match.',
+    'date'                 => 'The :attribute is not a valid date.',
+    'date_format'          => 'The :attribute does not match the format :format.',
+    'different'            => 'The :attribute and :other must be different.',
+    'digits'               => 'The :attribute must be :digits digits.',
+    'digits_between'       => 'The :attribute must be between :min and :max digits.',
+    'email'                => 'The :attribute must be a valid email address.',
+    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'filled'               => 'The :attribute field is required.',
+    'gt'                   => [
+        'numeric' => 'The :attribute must be greater than :value.',
+        'file'    => 'The :attribute must be greater than :value kilobytes.',
+        'string'  => 'The :attribute must be greater than :value characters.',
+        'array'   => 'The :attribute must have more than :value items.',
+    ],
+    'gte'                  => [
+        'numeric' => 'The :attribute must be greater than or equal :value.',
+        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be greater than or equal :value characters.',
+        'array'   => 'The :attribute must have :value items or more.',
+    ],
+    'exists'               => 'The selected :attribute is invalid.',
+    'image'                => 'The :attribute must be an image.',
+    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'in'                   => 'The selected :attribute is invalid.',
+    'integer'              => 'The :attribute must be an integer.',
+    'ip'                   => 'The :attribute must be a valid IP address.',
+    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
+    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
+    'json'                 => 'The :attribute must be a valid JSON string.',
+    'lt'                   => [
+        'numeric' => 'The :attribute must be less than :value.',
+        'file'    => 'The :attribute must be less than :value kilobytes.',
+        'string'  => 'The :attribute must be less than :value characters.',
+        'array'   => 'The :attribute must have less than :value items.',
+    ],
+    'lte'                  => [
+        'numeric' => 'The :attribute must be less than or equal :value.',
+        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be less than or equal :value characters.',
+        'array'   => 'The :attribute must not have more than :value items.',
+    ],
+    'max'                  => [
+        'numeric' => 'The :attribute may not be greater than :max.',
+        'file'    => 'The :attribute may not be greater than :max kilobytes.',
+        'string'  => 'The :attribute may not be greater than :max characters.',
+        'array'   => 'The :attribute may not have more than :max items.',
+    ],
+    'mimes'                => 'The :attribute must be a file of type: :values.',
+    'min'                  => [
+        'numeric' => 'The :attribute must be at least :min.',
+        'file'    => 'The :attribute must be at least :min kilobytes.',
+        'string'  => 'The :attribute must be at least :min characters.',
+        'array'   => 'The :attribute must have at least :min items.',
+    ],
+    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'not_in'               => 'The selected :attribute is invalid.',
+    'not_regex'            => 'The :attribute format is invalid.',
+    'numeric'              => 'The :attribute must be a number.',
+    'regex'                => 'The :attribute format is invalid.',
+    'required'             => 'The :attribute field is required.',
+    'required_if'          => 'The :attribute field is required when :other is :value.',
+    'required_with'        => 'The :attribute field is required when :values is present.',
+    'required_with_all'    => 'The :attribute field is required when :values is present.',
+    'required_without'     => 'The :attribute field is required when :values is not present.',
+    'required_without_all' => 'The :attribute field is required when none of :values are present.',
+    'same'                 => 'The :attribute and :other must match.',
+    'size'                 => [
+        'numeric' => 'The :attribute must be :size.',
+        'file'    => 'The :attribute must be :size kilobytes.',
+        'string'  => 'The :attribute must be :size characters.',
+        'array'   => 'The :attribute must contain :size items.',
+    ],
+    'string'               => 'The :attribute must be a string.',
+    'timezone'             => 'The :attribute must be a valid zone.',
+    'unique'               => 'The :attribute has already been taken.',
+    'url'                  => 'The :attribute format is invalid.',
+    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Password confirmation required',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index ccc55e1725f0cb1bbf82f404a07ea4d3c5772678..67f9653f432c1975a7164bf509cce8d6b1611f0f 100644 (file)
@@ -6,43 +6,44 @@
 return [
 
     // Pages
-    'page_create'                 => 'saya oluşturuldu',
+    'page_create'                 => 'sayfa oluşturdu',
     'page_create_notification'    => 'Sayfa Başarıyla Oluşturuldu',
-    'page_update'                 => 'sayfa güncellendi',
+    'page_update'                 => 'sayfayı güncelledi',
     'page_update_notification'    => 'Sayfa Başarıyla Güncellendi',
-    'page_delete'                 => 'sayfa silindi',
+    'page_delete'                 => 'sayfayı sildi',
     'page_delete_notification'    => 'Sayfa Başarıyla Silindi',
-    'page_restore'                => 'sayfa kurtarıldı',
-    'page_restore_notification'   => 'Sayfa Başarıyla Kurtarıldı',
-    'page_move'                   => 'sayfa taşındı',
+    'page_restore'                => 'sayfayı eski haline getirdi',
+    'page_restore_notification'   => 'Sayfa Başarıyla Eski Haline Getirildi',
+    'page_move'                   => 'sayfayı taşıdı',
 
     // Chapters
-    'chapter_create'              => 'bölüm oluşturuldu',
+    'chapter_create'              => 'bölüm oluşturdu',
     'chapter_create_notification' => 'Bölüm Başarıyla Oluşturuldu',
-    'chapter_update'              => 'bölüm güncellendi',
+    'chapter_update'              => 'bölümü güncelledi',
     'chapter_update_notification' => 'Bölüm Başarıyla Güncellendi',
-    'chapter_delete'              => 'bölüm silindi',
+    'chapter_delete'              => 'bölümü sildi',
     'chapter_delete_notification' => 'Bölüm Başarıyla Silindi',
-    'chapter_move'                => 'bölüm taşındı',
+    'chapter_move'                => 'bölümü taşıdı',
 
     // Books
-    'book_create'                 => 'kitap oluşturuldu',
+    'book_create'                 => 'kitap oluşturdu',
     'book_create_notification'    => 'Kitap Başarıyla Oluşturuldu',
-    'book_update'                 => 'kitap güncellendi',
+    'book_update'                 => 'kitabı güncelledi',
     'book_update_notification'    => 'Kitap Başarıyla Güncellendi',
-    'book_delete'                 => 'kitap silindi',
+    'book_delete'                 => 'kitabı sildi',
     'book_delete_notification'    => 'Kitap Başarıyla Silindi',
-    'book_sort'                   => 'kitap düzenlendi',
+    'book_sort'                   => 'kitabı sıraladı',
     'book_sort_notification'      => 'Kitap Başarıyla Yeniden Sıralandı',
 
     // Bookshelves
-    'bookshelf_create'            => 'kitaplık oluşturuldu',
+    'bookshelf_create'            => 'kitaplık oluşturdu',
     'bookshelf_create_notification'    => 'Kitaplık Başarıyla Oluşturuldu',
-    'bookshelf_update'                 => 'kitaplık güncellendi',
+    'bookshelf_update'                 => 'kitaplığı güncelledi',
     'bookshelf_update_notification'    => 'Kitaplık Başarıyla Güncellendi',
-    'bookshelf_delete'                 => 'kitaplık silindi',
+    'bookshelf_delete'                 => 'kitaplığı sildi',
     'bookshelf_delete_notification'    => 'Kitaplık Başarıyla Silindi',
 
     // Other
     'commented_on'                => 'yorum yaptı',
+    'permissions_update'          => 'güncellenmiş izinler',
 ];
index 7019c51c50d7fe96bc3b6f1fcbaaf1fdca643d18..6a0e2c1b5ea6bce020997932adb93ebb4699f395 100644 (file)
@@ -6,72 +6,72 @@
  */
 return [
 
-    'failed' => 'Girilen bilgiler bizdeki kayıtlarla uyuşmuyor.',
+    'failed' => 'Girdiğiniz bilgiler kayıtlarımızla uyuşmuyor.',
     'throttle' => 'Çok fazla giriş yapmaya çalıştınız. Lütfen :seconds saniye içinde tekrar deneyin.',
 
     // Login & Register
-    'sign_up' => 'Kayıt Ol',
+    'sign_up' => 'Kaydol',
     'log_in' => 'Giriş Yap',
     'log_in_with' => ':socialDriver ile giriş yap',
-    'sign_up_with' => ':socialDriver ile kayıt ol',
+    'sign_up_with' => ':socialDriver ile kaydol',
     'logout' => 'Çıkış Yap',
 
     'name' => 'İsim',
     'username' => 'Kullanıcı Adı',
-    'email' => 'Email',
+    'email' => 'E-posta',
     'password' => 'Şifre',
-    'password_confirm' => 'Şifreyi onayla',
-    'password_hint' => 'En az 5 karakter olmalı',
+    'password_confirm' => 'Şifreyi Onaylayın',
+    'password_hint' => 'En az 8 karakter olmalı',
     'forgot_password' => 'Şifrenizi mi unuttunuz?',
     'remember_me' => 'Beni Hatırla',
-    'ldap_email_hint' => 'Hesabı kullanmak istediğiniz e-mail adresinizi giriniz.',
+    'ldap_email_hint' => 'Bu hesap için kullanmak istediğiniz e-posta adresini giriniz.',
     'create_account' => 'Hesap Oluştur',
     'already_have_account' => 'Zaten bir hesabınız var mı?',
     'dont_have_account' => 'Hesabınız yok mu?',
-    'social_login' => 'Diğer Servisler ile Giriş Yap',
-    'social_registration' => 'Diğer Servisler ile Kayıt Ol',
-    'social_registration_text' => 'Diğer servisler ile kayıt ol ve giriş yap.',
+    'social_login' => 'Diğer Servisler ile Giriş Yapın',
+    'social_registration' => 'Diğer Servisler ile Kaydolun',
+    'social_registration_text' => 'Başka bir servis aracılığıyla kaydolun ve giriş yapın.',
 
-    'register_thanks' => 'Kayıt olduğunuz için teşekkürler!',
-    'register_confirm' => 'Lütfen e-posta adresinizi kontrol edin ve gelen doğrulama bağlantısına tıklayınız. :appName.',
-    'registrations_disabled' => 'Kayıt olma özelliği geçici olarak kısıtlanmıştır',
-    'registration_email_domain_invalid' => 'Bu e-mail sağlayıcısının bu uygulamaya erişim izni yoktur.',
-    'register_success' => 'Artık kayıtlı bir kullanıcı olarak giriş yaptınız.',
+    'register_thanks' => 'Kaydolduğunuz için teşekkürler!',
+    'register_confirm' => ':appName erişimi için lütfen e-posta adresinizi kontrol edin ve size gönderilen doğrulama bağlantısına tıklayın.',
+    'registrations_disabled' => 'Kayıtlar devre dışı bırakılmıştır',
+    'registration_email_domain_invalid' => 'Bu e-posta sağlayıcısının uygulamaya erişim izni bulunmuyor',
+    'register_success' => 'Kaydolduğunuz için teşekkürler! Artık kayıtlı bir kullanıcı olarak giriş yaptınız.',
 
 
     // Password Reset
-    'reset_password' => 'Parolayı Sıfırla',
-    'reset_password_send_instructions' => 'Aşağıya e-mail adresinizi girdiğinizde parola yenileme bağlantısı mail adresinize gönderilecektir.',
-    'reset_password_send_button' => '>Sıfırlama Bağlantısını Gönder',
-    'reset_password_sent_success' => 'Sıfırlama bağlantısı :email adresinize gönderildi.',
-    'reset_password_success' => 'Parolanız başarıyla sıfırlandı.',
-    'email_reset_subject' => ':appName şifrenizi sıfırlayın.',
-    'email_reset_text' => ' Parola sıfırlama isteğinde bulunduğunuz için bu maili görüntülüyorsunuz.',
-    'email_reset_not_requested' => 'Eğer bu parola sıfırlama isteğinde bulunmadıysanız herhangi bir işlem yapmanıza gerek yoktur.',
+    'reset_password' => 'Şifreyi Sıfırla',
+    'reset_password_send_instructions' => 'Aşağıya gireceğiniz e-posta adresine şifre sıfırlama bağlantısı gönderilecektir.',
+    'reset_password_send_button' => 'Sıfırlama Bağlantısını Gönder',
+    'reset_password_sent' => 'Şifre sıfırlama bağlantısı, :email adresinin sistemde bulunması durumunda e-posta olarak gönderilecektir.',
+    'reset_password_success' => 'Şifreniz başarıyla sıfırlandı.',
+    'email_reset_subject' => ':appName şifrenizi sıfırlayın',
+    'email_reset_text' => 'Hesap şifrenizi sıfırlama isteğinde bulunduğunuz için bu e-postayı aldınız.',
+    'email_reset_not_requested' => 'Şifre sıfırlama isteğinde bulunmadıysanız herhangi bir işlem yapmanıza gerek yoktur.',
 
 
     // Email Confirmation
-    'email_confirm_subject' => ':appName için girdiğiniz mail adresiniz onaylayınız',
-    'email_confirm_greeting' => ':appName\'e katıldığınız için teşekkürler!',
-    'email_confirm_text' => 'Lütfen e-mail adresinizi aşağıda bulunan butona tıklayarak onaylayınız:',
-    'email_confirm_action' => 'E-Maili Onayla',
-    'email_confirm_send_error' => 'e-mail onayı gerekli fakat sistem mail göndermeyi başaramadı. Yöneticiniz ile görüşüp kurulumlarda bir sorun olmadığını doğrulayın.',
-    'email_confirm_success' => 'e-mail adresiniz onaylandı!',
-    'email_confirm_resent' => 'Doğrulama maili gönderildi, lütfen gelen kutunuzu kontrol ediniz...',
+    'email_confirm_subject' => ':appName için girdiğiniz e-posta adresini doğrulayın',
+    'email_confirm_greeting' => ':appName uygulamasına katıldığınız için teşekkürler!',
+    'email_confirm_text' => 'Lütfen aşağıdaki butona tıklayarak e-posta adresinizi doğrulayın:',
+    'email_confirm_action' => 'E-posta Adresini Doğrula',
+    'email_confirm_send_error' => 'E-posta adresinin doğrulanması gerekiyor fakat sistem, doğrulama bağlantısını göndermeyi başaramadı. E-posta adresinin doğru bir şekilde ayarlığından emin olmak için yöneticiyle iletişime geçin.',
+    'email_confirm_success' => 'E-posta adresiniz doğrulandı!',
+    'email_confirm_resent' => 'Doğrulama e-postası tekrar gönderildi, lütfen gelen kutunuzu kontrol ediniz.',
 
-    'email_not_confirmed' => 'E-mail Adresi Doğrulanmadı',
-    'email_not_confirmed_text' => 'Sağlamış olduğunuz e-mail adresi henüz doğrulanmadı.',
-    'email_not_confirmed_click_link' => 'Lütfen kayıt olduktan kısa süre sonra size gönderilen maildeki bağlantıya tıklayın ve mail adresinizi onaylayın.',
-    'email_not_confirmed_resend' => 'Eğer gelen maili bulamadıysanız aşağıdaki formu tekrar doldurarak onay mailini kendinize tekrar gönderebilirsiniz.',
-    'email_not_confirmed_resend_button' => 'Doğrulama Mailini Yeniden Yolla',
+    'email_not_confirmed' => 'E-posta Adresi Doğrulanmadı',
+    'email_not_confirmed_text' => 'E-posta adresiniz henüz doğrulanmadı.',
+    'email_not_confirmed_click_link' => 'Lütfen kaydolduktan hemen sonra size gönderilen e-postadaki bağlantıya tıklayın.',
+    'email_not_confirmed_resend' => 'Eğer e-postayı bulamıyorsanız, aşağıdaki formu doldurarak doğrulama e-postasının tekrar gönderilmesini sağlayabilirsiniz.',
+    'email_not_confirmed_resend_button' => 'Doğrulama E-postasını Tekrar Gönder',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => ':appName uygulamasına davet edildiniz!',
+    'user_invite_email_greeting' => ':appName üzerinde sizin için bir hesap oluşturuldu.',
+    'user_invite_email_text' => 'Hesap şifrenizi belirlemek ve hesabınıza erişim sağlayabilmek için aşağıdaki butona tıklayın:',
+    'user_invite_email_action' => 'Hesap Şifresini Belirleyin',
+    'user_invite_page_welcome' => ':appName uygulamasına hoş geldiniz!',
+    'user_invite_page_text' => 'Hesap kurulumunuzu tamamlamak ve gelecekteki :appName ziyaretlerinizde hesabınıza erişim sağlayabilmeniz için bir şifre belirlemeniz gerekiyor.',
+    'user_invite_page_confirm_button' => 'Şifreyi Onayla',
+    'user_invite_success' => 'Şifreniz ayarlandı, artık :appName uygulamasına giriş yapabilirsiniz!'
 ];
\ No newline at end of file
index a87a0d5dbfa81d54f15cf2f55dcf017785fa1bbc..a252ac7703d6e9b65f8eb0f010e8e3fa75ebb972 100644 (file)
@@ -11,7 +11,7 @@ return [
     'save' => 'Kaydet',
     'continue' => 'Devam',
     'select' => 'Seç',
-    'toggle_all' => 'Hepsini Değiştir',
+    'toggle_all' => 'Hepsini Aç/Kapat',
     'more' => 'Daha Fazla',
 
     // Form Labels
@@ -19,12 +19,12 @@ return [
     'description' => 'Açıklama',
     'role' => 'Rol',
     'cover_image' => 'Kapak resmi',
-    'cover_image_description' => 'Bu resim yaklaşık 440x250px boyutlarında olmalıdır.',
+    'cover_image_description' => 'Bu görsel yaklaşık 440x250px boyutlarında olmalıdır.',
     
     // Actions
-    'actions' => 'Aksiyonlar',
+    'actions' => 'İşlemler',
     'view' => 'Görüntüle',
-    'view_all' => 'Hepsini Görüntüle',
+    'view_all' => 'Hepsini Göster',
     'create' => 'Oluştur',
     'update' => 'Güncelle',
     'edit' => 'Düzenle',
@@ -33,44 +33,48 @@ return [
     'copy' => 'Kopyala',
     'reply' => 'Yanıtla',
     'delete' => 'Sil',
+    'delete_confirm' => 'Silmeyi Onayla',
     'search' => 'Ara',
     'search_clear' => 'Aramayı Temizle',
     'reset' => 'Sıfırla',
     'remove' => 'Kaldır',
     'add' => 'Ekle',
+    'fullscreen' => 'Tam Ekran',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
+    'sort_options' => 'Sıralama Seçenekleri',
+    'sort_direction_toggle' => 'Sıralama Yönünü Değiştir',
+    'sort_ascending' => 'Artan Sıralama',
+    'sort_descending' => 'Azalan Sıralama',
     'sort_name' => 'İsim',
     'sort_created_at' => 'Oluşturulma Tarihi',
-    'sort_updated_at' => 'Güncellenme Tarihi',
+    'sort_updated_at' => 'Güncelleme Tarihi',
 
     // Misc
     'deleted_user' => 'Silinmiş Kullanıcı',
-    'no_activity' => 'Gösterilecek aktivite yok',
-    'no_items' => 'Kullanılabilir öge yok',
+    'no_activity' => 'Gösterilecek eylem bulunamadı',
+    'no_items' => 'Herhangi bir öge bulunamadı',
     'back_to_top' => 'Başa dön',
-    'toggle_details' => 'Detayları değiştir',
-    'toggle_thumbnails' => 'Küçük resimleri değiştir',
+    'toggle_details' => 'Detayları Göster/Gizle',
+    'toggle_thumbnails' => 'Ön İzleme Görsellerini Göster/Gizle',
     'details' => 'Detaylar',
-    'grid_view' => 'Grid görünümü',
-    'list_view' => 'Liste görünümü',
+    'grid_view' => 'Izgara Görünümü',
+    'list_view' => 'Liste Görünümü',
     'default' => 'Varsayılan',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Gezinti Menüsü',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Profil Menüsü',
     'view_profile' => 'Profili Görüntüle',
     'edit_profile' => 'Profili Düzenle',
+    'dark_mode' => 'Gece Modu',
+    'light_mode' => 'Aydınlık Modu',
 
     // Layout tabs
     'tab_info' => 'Bilgi',
     'tab_content' => 'İçerik',
 
     // Email Content
-    'email_action_help' => 'Eğer ":actionText" butonuna tıklamakta zorluk çekiyorsanız, aşağıda bulunan linki kopyalayıp tarayıcınıza yapıştırabilirsiniz.',
-    'email_rights' => 'Bütün hakları saklıdır',
+    'email_action_help' => '":actionText" butonuna tıklamada sorun yaşıyorsanız, aşağıda bulunan bağlantıyı kopyalayıp tarayıcınıza yapıştırın:',
+    'email_rights' => 'Tüm hakları saklıdır',
 ];
index d52f2d0061bb908e7042f77fb4080c020afa5fb4..009c48f23bb8f1bd495acc4ad6fcd73859056455 100644 (file)
@@ -6,28 +6,29 @@ return [
 
     // Image Manager
     'image_select' => 'Görsel Seç',
-    'image_all' => 'Tümü',
-    'image_all_title' => 'Tüm görselleri temizle',
+    'image_all' => 'Hepsi',
+    'image_all_title' => 'Bütün görselleri görüntüle',
     'image_book_title' => 'Bu kitaba ait görselleri görüntüle',
     'image_page_title' => 'Bu sayfaya ait görselleri görüntüle',
-    'image_search_hint' => 'Görsel adı ile ara',
+    'image_search_hint' => 'Görsel adıyla ara',
     'image_uploaded' => ':uploadedDate tarihinde yüklendi',
-    'image_load_more' => 'Daha Fazla ',
+    'image_load_more' => 'Devamını Göster',
     'image_image_name' => 'Görsel Adı',
-    'image_delete_used' => 'Bu görsel aşağıda bulunan görsellerde kullanılmış.',
-    'image_delete_confirm' => 'Gerçekten bu görseli silmek istiyorsanız sil tuşuna basınız.',
+    'image_delete_used' => 'Bu görsel aşağıda bulunan sayfalarda kullanılmış.',
+    'image_delete_confirm_text' => 'Bu resmi silmek istediğinizden emin misiniz?',
     'image_select_image' => 'Görsel Seç',
-    'image_dropzone' => 'Görselleri buraya sürükle veya seçmek için buraya tıkla',
+    'image_dropzone' => 'Görselleri sürükleyin ya da seçin',
     'images_deleted' => 'Görseller Silindi',
-    'image_preview' => 'Görsel Önizleme',
+    'image_preview' => 'Görsel Ön İzlemesi',
     'image_upload_success' => 'Görsel başarıyla yüklendi',
-    'image_update_success' => 'Görsel başarıyla güncellendi',
+    'image_update_success' => 'Görsel detayları başarıyla güncellendi',
     'image_delete_success' => 'Görsel başarıyla silindi',
     'image_upload_remove' => 'Kaldır',
 
     // Code Editor
-    'code_editor' => 'Kodu Güncelle',
-    'code_language' => 'Kod Dil',
+    'code_editor' => 'Kodu Düzenle',
+    'code_language' => 'Kod Dili',
     'code_content' => 'Kod İçeriği',
+    'code_session_history' => 'Oturum Geçmişi',
     'code_save' => 'Kodu Kaydet',
 ];
index f9eb54fb078fe8e6659018bb792278f032e6df0c..2f447e7953a51a7a6cb3bfb257c57904eac855cb 100644 (file)
@@ -7,70 +7,73 @@ return [
 
     // Shared
     'recently_created' => 'Yakın Zamanda Oluşturuldu',
-    'recently_created_pages' => 'Yakın Zamanda Oluşturulmuş Sayfalar',
-    'recently_updated_pages' => 'Yakın Zamanda Güncellenmiş Sayfalar',
-    'recently_created_chapters' => 'Yakın Zamanda Oluşturulmuş Bölümler',
-    'recently_created_books' => 'Yakın Zamanda Olşturulmuş Kitaplar',
-    'recently_created_shelves' => 'Yakın Zamanda Oluşturulmuş Kitaplıklar',
+    'recently_created_pages' => 'Yakın Zamanda Oluşturulan Sayfalar',
+    'recently_updated_pages' => 'Yakın Zamanda Güncellenen Sayfalar',
+    'recently_created_chapters' => 'Yakın Zamanda Oluşturulan Bölümler',
+    'recently_created_books' => 'Yakın Zamanda Oluşturulan Kitaplar',
+    'recently_created_shelves' => 'Yakın Zamanda Oluşturulan Kitaplıklar',
     'recently_update' => 'Yakın Zamanda Güncellenmiş',
     'recently_viewed' => 'Yakın Zamanda Görüntülenmiş',
     'recent_activity' => 'Son Hareketler',
     'create_now' => 'Hemen bir tane oluştur',
     'revisions' => 'Revizyonlar',
     'meta_revision' => 'Revizyon #:revisionCount',
-    'meta_created' => 'Oluşturuldu :timeLength',
-    'meta_created_name' => ':user tarafından :timeLength tarihinde oluşturuldu',
-    'meta_updated' => 'Güncellendi :timeLength',
-    'meta_updated_name' => ':user tarafından :timeLength tarihinde güncellendi',
-    'entity_select' => 'Öğe Seçme',
+    'meta_created' => ':timeLength Oluşturuldu',
+    'meta_created_name' => ':user tarafından :timeLength oluşturuldu',
+    'meta_updated' => ':timeLength güncellendi',
+    'meta_updated_name' => ':user tarafından :timeLength güncellendi',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'Öge Seçimi',
     'images' => 'Görseller',
     'my_recent_drafts' => 'Son Taslaklarım',
-    'my_recently_viewed' => 'Son Görüntülemelerim',
+    'my_recently_viewed' => 'Son Görüntülediklerim',
     'no_pages_viewed' => 'Herhangi bir sayfa görüntülemediniz',
     'no_pages_recently_created' => 'Yakın zamanda bir sayfa oluşturulmadı',
     'no_pages_recently_updated' => 'Yakın zamanda bir sayfa güncellenmedi',
     'export' => 'Dışa Aktar',
-    'export_html' => 'Contained Web Dosyası',
+    'export_html' => 'Web Dosyası',
     'export_pdf' => 'PDF Dosyası',
     'export_text' => 'Düz Metin Dosyası',
 
     // Permissions and restrictions
     'permissions' => 'İzinler',
-    'permissions_intro' => 'Etkinleştirildikten sonra bu izinler diğer diğer bütün izinlerden öncelikli olacaktır.',
+    'permissions_intro' => 'Etkinleştirildikten sonra bu izinler, diğer bütün izinlerden öncelikli olacaktır.',
     'permissions_enable' => 'Özelleştirilmiş Yetkileri Etkinleştir',
     'permissions_save' => 'İzinleri Kaydet',
+    'permissions_owner' => 'Sahip',
 
     // Search
     'search_results' => 'Arama Sonuçları',
     'search_total_results_found' => ':count sonuç bulundu |:count toplam sonuç bulundu',
-    'search_clear' => 'Aramaları Temizle',
-    'search_no_pages' => 'Bu aramayla herhangi bir sonuç eşleşmedi',
-    'search_for_term' => ':term için ara',
+    'search_clear' => 'Aramayı Temizle',
+    'search_no_pages' => 'Bu aramayla ilgili herhangi bir sayfa bulunamadı',
+    'search_for_term' => ':term için Ara',
     'search_more' => 'Daha Fazla Sonuç',
-    'search_filters' => 'Arama Filtreleri',
+    'search_advanced' => 'Gelişmiş Arama',
+    'search_terms' => 'Terimleri Ara',
     'search_content_type' => 'İçerik Türü',
     'search_exact_matches' => 'Tam Eşleşmeler',
     'search_tags' => 'Etiket Aramaları',
     'search_options' => 'Ayarlar',
-    'search_viewed_by_me' => 'Benim tarafımdan görüntülendi',
-    'search_not_viewed_by_me' => 'Benim tarafımdan görüntülenmedi',
-    'search_permissions_set' => 'İzinler ayarlan',
-    'search_created_by_me' => 'Benim tarafımdan oluşturuldu',
-    'search_updated_by_me' => 'Benim tarafımdan güncellendi',
+    'search_viewed_by_me' => 'Görüntülediklerim',
+    'search_not_viewed_by_me' => 'Görüntülemediklerim',
+    'search_permissions_set' => 'İzinler ayarlanmış',
+    'search_created_by_me' => 'Oluşturduklarım',
+    'search_updated_by_me' => 'Güncellediklerim',
     'search_date_options' => 'Tarih Seçenekleri',
     'search_updated_before' => 'Önce güncellendi',
     'search_updated_after' => 'Sonra güncellendi',
     'search_created_before' => 'Önce oluşturuldu',
     'search_created_after' => 'Sonra oluşturuldu',
-    'search_set_date' => 'Tarih Ayarla',
+    'search_set_date' => 'Tarihi Ayarla',
     'search_update' => 'Aramayı Güncelle',
 
     // Shelves
     'shelf' => 'Kitaplık',
     'shelves' => 'Kitaplıklar',
-    'x_shelves' => ':count Kitaplık|:count Kitaplıklar',
+    'x_shelves' => ':count Kitaplık|:count Kitaplık',
     'shelves_long' => 'Kitaplıklar',
-    'shelves_empty' => 'Hiç kitaplık oluşturulma',
+    'shelves_empty' => 'Hiç kitaplık oluşturulmamış',
     'shelves_create' => 'Yeni Kitaplık Oluştur',
     'shelves_popular' => 'Popüler Kitaplıklar',
     'shelves_new' => 'Yeni Kitaplıklar',
@@ -80,28 +83,28 @@ return [
     'shelves_save' => 'Kitaplığı Kaydet',
     'shelves_books' => 'Bu kitaplıktaki kitaplar',
     'shelves_add_books' => 'Bu kitaplığa kitap ekle',
-    'shelves_drag_books' => 'Bu kitaplığa kitap eklemek için kitapları buraya sürükle',
-    'shelves_empty_contents' => 'Bu kitaplığa henüz hiç bir kitap atanmamış',
-    'shelves_edit_and_assign' => 'Kitaplığa kitap eklemek için güncelle',
-    'shelves_edit_named' => ':name Kitaplığını Güncelle',
-    'shelves_edit' => 'Kitaplığı Güncelle',
+    'shelves_drag_books' => 'Bu kitaplığa eklemek istediğin kitapları buraya sürükle',
+    'shelves_empty_contents' => 'Bu kitaplıkta hiç kitap bulunamadı',
+    'shelves_edit_and_assign' => 'Kitap eklemek için kitaplığı düzenleyin',
+    'shelves_edit_named' => ':name Kitaplığını Düzenle',
+    'shelves_edit' => 'Kitaplığı Düzenle',
     'shelves_delete' => 'Kitaplığı Sil',
     'shelves_delete_named' => ':name Kitaplığını Sil',
-    'shelves_delete_explain' => "Bu işlem :name kitaplığını silecektir. İçerdiği kitaplar silinmeyecektir.",
-    'shelves_delete_confirmation' => 'Bu kitaplığı silmek istediğinizden emin misiniz?',
+    'shelves_delete_explain' => "Bu işlem ':name' kitaplığını silecektir ancak içerdiği kitaplara dokunulmayacaktır.",
+    'shelves_delete_confirmation' => 'Bu kitaplığı silmek istediğinize emin misiniz?',
     'shelves_permissions' => 'Kitaplık İzinleri',
     'shelves_permissions_updated' => 'Kitaplık İzinleri Güncellendi',
     'shelves_permissions_active' => 'Kitaplık İzinleri Aktif',
     'shelves_copy_permissions_to_books' => 'İzinleri Kitaplara Kopyala',
     'shelves_copy_permissions' => 'İzinleri Kopyala',
-    'shelves_copy_permissions_explain' => 'Bu işlem sonucunda kitaplığınızın izinleri içerdiği kitaplara da aynen uygulanır. Aktifleştirmeden bu kitaplığa ait izinleri kaydettiğinizden emin olun.',
+    'shelves_copy_permissions_explain' => 'Bu işlem sonucunda kitaplığınızın izinleri, içerdiği kitaplara da aynen uygulanır. Aktifleştirmeden önce bu kitaplığa ait izinleri kaydettiğinizden emin olun.',
     'shelves_copy_permission_success' => 'Kitaplık izinleri :count adet kitaba kopyalandı',
 
     // Books
     'book' => 'Kitap',
     'books' => 'Kitaplar',
-    'x_books' => ':count Kitap|:count Kitaplar',
-    'books_empty' => 'Hiç kitap oluşturulma',
+    'x_books' => ':count Kitap|:count Kitap',
+    'books_empty' => 'Hiç kitap oluşturulmamış',
     'books_popular' => 'Popüler Kitaplar',
     'books_recent' => 'En Son Kitaplar',
     'books_new' => 'Yeni Kitaplar',
@@ -110,19 +113,19 @@ return [
     'books_new_empty' => 'En yeni kitaplar burada görüntülenecek.',
     'books_create' => 'Yeni Kitap Oluştur',
     'books_delete' => 'Kitabı Sil',
-    'books_delete_named' => ':bookName kitabını sil',
-    'books_delete_explain' => 'Bu işlem \':bookName\' kitabını silecek. Bütün sayfa ve bölümler silinecektir.',
-    'books_delete_confirmation' => 'Bu kitabı silmek istediğinizden emin misiniz?',
-    'books_edit' => 'Kitabı Güncelle',
-    'books_edit_named' => ':bookName Kitabını Güncelle',
+    'books_delete_named' => ':bookName Kitabını Sil',
+    'books_delete_explain' => 'Bu işlem \':bookName\' kitabını silecektir. Ayrıca kitaba ait bütün sayfalar ve bölümler de silinecektir.',
+    'books_delete_confirmation' => 'Bu kitabı silmek istediğinize emin misiniz?',
+    'books_edit' => 'Kitabı Düzenle',
+    'books_edit_named' => ':bookName Kitabını Düzenle',
     'books_form_book_name' => 'Kitap Adı',
     'books_save' => 'Kitabı Kaydet',
     'books_permissions' => 'Kitap İzinleri',
     'books_permissions_updated' => 'Kitap İzinleri Güncellendi',
-    'books_empty_contents' => 'Bu kitaba ait sayfa veya bölüm oluşturulma.',
-    'books_empty_create_page' => 'Yeni sayfa oluştur',
+    'books_empty_contents' => 'Bu kitaba ait sayfa veya bölüm oluşturulmamış.',
+    'books_empty_create_page' => 'Yeni bir sayfa oluştur',
     'books_empty_sort_current_book' => 'Mevcut kitabı sırala',
-    'books_empty_add_chapter' => 'Yeni bölüm ekle',
+    'books_empty_add_chapter' => 'Yeni bir bölüm ekle',
     'books_permissions_active' => 'Kitap İzinleri Aktif',
     'books_search_this' => 'Bu kitapta ara',
     'books_navigation' => 'Kitap Navigasyonu',
@@ -130,7 +133,7 @@ return [
     'books_sort_named' => ':bookName Kitabını Sırala',
     'books_sort_name' => 'İsme Göre Sırala',
     'books_sort_created' => 'Oluşturulma Tarihine Göre Sırala',
-    'books_sort_updated' => 'Güncellenme Tarihine Göre Sırala',
+    'books_sort_updated' => 'Güncelleme Tarihine Göre Sırala',
     'books_sort_chapters_first' => 'Önce Bölümler',
     'books_sort_chapters_last' => 'En Son Bölümler',
     'books_sort_show_other' => 'Diğer Kitapları Göster',
@@ -139,67 +142,67 @@ return [
     // Chapters
     'chapter' => 'Bölüm',
     'chapters' => 'Bölümler',
-    'x_chapters' => ':count Bölüm|:count Bölümler',
+    'x_chapters' => ':count Bölüm|:count Bölüm',
     'chapters_popular' => 'Popüler Bölümler',
     'chapters_new' => 'Yeni Bölüm',
     'chapters_create' => 'Yeni Bölüm Oluştur',
     'chapters_delete' => 'Bölümü Sil',
     'chapters_delete_named' => ':chapterName Bölümünü Sil',
-    'chapters_delete_explain' => 'Bu işlem \':chapterName\' kitabını silecek. Bütün sayfalar silinecek ve direkt olarak ana kitab eklenecektir.',
-    'chapters_delete_confirm' => 'Bölümü silmek istediğinizden emin misiniz?',
-    'chapters_edit' => 'Bölümü Güncelle',
-    'chapters_edit_named' => ':chapterName Bölümünü Güncelle',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_confirm' => 'Bölümü silmek istediğinize emin misiniz?',
+    'chapters_edit' => 'Bölümü Düzenle',
+    'chapters_edit_named' => ':chapterName Bölümünü Düzenle',
     'chapters_save' => 'Bölümü Kaydet',
     'chapters_move' => 'Bölümü Taşı',
     'chapters_move_named' => ':chapterName Bölümünü Taşı',
-    'chapter_move_success' => 'Bölüm :bookName Kitabına Taşındı',
+    'chapter_move_success' => 'Bölüm, :bookName kitabına taşındı',
     'chapters_permissions' => 'Bölüm İzinleri',
-    'chapters_empty' => 'Bu bölümde henüz bir sayfa yok.',
+    'chapters_empty' => 'Bu bölümde henüz bir sayfa bulunmuyor.',
     'chapters_permissions_active' => 'Bölüm İzinleri Aktif',
     'chapters_permissions_success' => 'Bölüm İzinleri Güncellendi',
-    'chapters_search_this' => 'Bu bölümü ara',
+    'chapters_search_this' => 'Bu bölümde ara',
 
     // Pages
     'page' => 'Sayfa',
     'pages' => 'Sayfalar',
-    'x_pages' => ':count Sayfa|:count Sayfalar',
+    'x_pages' => ':count Sayfa|:count Sayfa',
     'pages_popular' => 'Popüler Sayfalar',
     'pages_new' => 'Yeni Sayfa',
     'pages_attachments' => 'Ekler',
     'pages_navigation' => 'Sayfa Navigasyonu',
     'pages_delete' => 'Sayfayı Sil',
     'pages_delete_named' => ':pageName Sayfasını Sil',
-    'pages_delete_draft_named' => ':pageName Taslak Sayfasını Sil',
-    'pages_delete_draft' => 'Taslak Sayfayı Sil',
+    'pages_delete_draft_named' => ':pageName Sayfa Taslağını Sil',
+    'pages_delete_draft' => 'Sayfa Taslağını Sil',
     'pages_delete_success' => 'Sayfa silindi',
-    'pages_delete_draft_success' => 'Taslak sayfa silindi',
-    'pages_delete_confirm' => 'Bu sayfayı silmek istediğinizden emin misiniz?',
-    'pages_delete_draft_confirm' => 'Bu taslak sayfayı silmek istediğinizden emin misiniz?',
+    'pages_delete_draft_success' => 'Sayfa taslağı silindi',
+    'pages_delete_confirm' => 'Bu sayfayı silmek istediğinize emin misiniz?',
+    'pages_delete_draft_confirm' => 'Bu sayfa taslağını silmek istediğinize emin misiniz?',
     'pages_editing_named' => ':pageName Sayfası Düzenleniyor',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Taslak Seçenekleri',
     'pages_edit_save_draft' => 'Taslağı Kaydet',
-    'pages_edit_draft' => 'Taslak Sayfasını Düzenle',
+    'pages_edit_draft' => 'Sayfa Taslağını Düzenle',
     'pages_editing_draft' => 'Taslak Düzenleniyor',
     'pages_editing_page' => 'Sayfa Düzenleniyor',
     'pages_edit_draft_save_at' => 'Taslak kaydedildi ',
-    'pages_edit_delete_draft' => 'Taslağı Sl',
+    'pages_edit_delete_draft' => 'Taslağı Sil',
     'pages_edit_discard_draft' => 'Taslağı Yoksay',
-    'pages_edit_set_changelog' => 'Değişiklik Logunu Kaydet',
-    'pages_edit_enter_changelog_desc' => 'Yaptığınız değişiklikler hakkında kısa bir bilgilendirme ekleyin',
-    'pages_edit_enter_changelog' => 'Değişim Günlüğü Ekleyin',
+    'pages_edit_set_changelog' => 'Değişim Günlüğünü Ayarla',
+    'pages_edit_enter_changelog_desc' => 'Yaptığınız değişiklikler hakkında kısa bir açıklama girin',
+    'pages_edit_enter_changelog' => 'Değişim Günlüğünü Yazın',
     'pages_save' => 'Sayfayı Kaydet',
     'pages_title' => 'Sayfa Başlığı',
     'pages_name' => 'Sayfa İsmi',
-    'pages_md_editor' => 'Editör',
-    'pages_md_preview' => 'Önizleme',
+    'pages_md_editor' => 'Düzenleyici',
+    'pages_md_preview' => 'Ön İzleme',
     'pages_md_insert_image' => 'Görsel Ekle',
-    'pages_md_insert_link' => 'Öge Linki Ekle',
+    'pages_md_insert_link' => 'Öge Bağlantısı Ekle',
     'pages_md_insert_drawing' => 'Çizim Ekle',
-    'pages_not_in_chapter' => 'Sayfa Bu Bölümde Değil',
+    'pages_not_in_chapter' => 'Bu sayfa, bir bölüme ait değil',
     'pages_move' => 'Sayfayı Taşı',
-    'pages_move_success' => 'Sayfa ":parentName"\'a taşındı',
+    'pages_move_success' => 'Sayfa şuraya taşındı: :parentName',
     'pages_copy' => 'Sayfayı Kopyala',
-    'pages_copy_desination' => 'Kopyalanacak Hedef',
+    'pages_copy_desination' => 'Kopyalama Hedefi',
     'pages_copy_success' => 'Sayfa başarıyla kopyalandı',
     'pages_permissions' => 'Sayfa İzinleri',
     'pages_permissions_success' => 'Sayfa izinleri güncellendi',
@@ -207,34 +210,35 @@ return [
     'pages_revisions' => 'Sayfa Revizyonları',
     'pages_revisions_named' => ':pageName için Sayfa Revizyonları',
     'pages_revision_named' => ':pageName için Sayfa Revizyonu',
-    'pages_revisions_created_by' => 'Oluşturan',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions_created_by' => 'Revize Eden',
     'pages_revisions_date' => 'Revizyon Tarihi',
     'pages_revisions_number' => '#',
     'pages_revisions_numbered' => 'Revizyon #:id',
     'pages_revisions_numbered_changes' => 'Revizyon #:id Değişiklikleri',
     'pages_revisions_changelog' => 'Değişim Günlüğü',
     'pages_revisions_changes' => 'Değişiklikler',
-    'pages_revisions_current' => 'Mevcut Versiyon',
-    'pages_revisions_preview' => 'Önizleme',
-    'pages_revisions_restore' => 'Kurtar',
-    'pages_revisions_none' => 'Bu sayfaya ait revizyon yok',
-    'pages_copy_link' => 'Linki kopyala',
+    'pages_revisions_current' => 'Şimdiki Sürüm',
+    'pages_revisions_preview' => 'Ön İzleme',
+    'pages_revisions_restore' => 'Geri Dön',
+    'pages_revisions_none' => 'Bu sayfaya ait herhangi bir revizyon bulunamadı',
+    'pages_copy_link' => 'Bağlantıyı kopyala',
     'pages_edit_content_link' => 'İçeriği Düzenle',
     'pages_permissions_active' => 'Sayfa İzinleri Aktif',
-    'pages_initial_revision' => 'İlk Yayın',
+    'pages_initial_revision' => 'İlk yayın',
     'pages_initial_name' => 'Yeni Sayfa',
-    'pages_editing_draft_notification' => 'Şu anda :timeDiff tarhinde kaydedilmiş olan taslağı düzenlemektesiniz.',
-    'pages_draft_edited_notification' => 'Bu sayfa son girişinizden bu yana güncellendi. Değişiklikleri yoksayıp, kaydetmeden çıkmanız önerilir.',
+    'pages_editing_draft_notification' => 'Şu anda en son :timeDiff tarihinde kaydedilmiş olan taslağı düzenliyorsunuz.',
+    'pages_draft_edited_notification' => 'Bu sayfa o zamandan bu zamana güncellenmiş, bu nedenle bu taslağı yok saymanız önerilir.',
     'pages_draft_edit_active' => [
-        'start_a' => ':count kullanıcı bu sayfayı düzenlemeye başladı',
-        'start_b' => ':userName kullanıcısı bu sayfayı düzenlemeye başladı',
+        'start_a' => ':count kullanıcı, bu sayfayı düzenlemeye başladı',
+        'start_b' => ':userName, bu sayfayı düzenlemeye başladı',
         'time_a' => 'sayfa son güncellendiğinden beri',
         'time_b' => 'son :minCount dakikada',
-        'message' => ':start :time. Birbirinizin düzenlemelerinin çakışmamasına dikkat edin!',
+        'message' => ':start :time. Düzenlemelerinizin çakışmamasına dikkat edin!',
     ],
-    'pages_draft_discarded' => 'Taslak yok sayıldı, editör mevcut sayfa içeriği ile güncellendi',
-    'pages_specific' => 'Özel Sayfa',
-    'pages_is_template' => 'Page Template',
+    'pages_draft_discarded' => 'Taslak yok sayıldı. Düzenleyici, mevcut sayfa içeriği kullanılarak güncellendi',
+    'pages_specific' => 'Spesifik Sayfa',
+    'pages_is_template' => 'Sayfa Şablonu',
 
     // Editor Sidebar
     'page_tags' => 'Sayfa Etiketleri',
@@ -243,72 +247,73 @@ return [
     'shelf_tags' => 'Kitaplık Etiketleri',
     'tag' => 'Etiket',
     'tags' =>  'Etiketler',
-    'tag_name' =>  'Tag Name',
-    'tag_value' => 'Etiket İçeriği (Opsiyonel)',
-    'tags_explain' => "İçeriğini daha iyi kategorize etmek için bazı etiketler ekle. Etiketlere değer atayarak daha derin bir organizasyon yapısına sahip olabilirsin.",
+    'tag_name' =>  'Etiket İsmi',
+    'tag_value' => 'Etiket Değeri (Opsiyonel)',
+    'tags_explain' => "İçeriğinizi daha iyi kategorize etmek için etiket ekleyin. Etiketlere değer atayarak daha derinlemesine bir düzen elde edebilirsiniz.",
     'tags_add' => 'Başka etiket ekle',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Bu etiketi sil',
     'attachments' => 'Ekler',
-    'attachments_explain' => 'Sayfanızda göstermek için bazı dosyalar yükleyin veya bazı bağlantılar ekleyin. Bunlar sayfanın sidebarında görülebilir.',
+    'attachments_explain' => 'Sayfanızda göstermek için dosyalar yükleyin veya bağlantılar ekleyin. Bunlar, sayfaya ait yan menüde gösterilecektir.',
     'attachments_explain_instant_save' => 'Burada yapılan değişiklikler anında kaydedilir.',
     'attachments_items' => 'Eklenmiş Ögeler',
     'attachments_upload' => 'Dosya Yükle',
     'attachments_link' => 'Link Ekle',
-    'attachments_set_link' => 'Link Düzenle',
-    'attachments_delete_confirm' => 'Eki gerçekten silmek istiyor musunuz?',
-    'attachments_dropzone' => 'Dosyaları buraya sürükle veya  eklemek için buraya tıkla',
-    'attachments_no_files' => 'Hiç bir dosya yüklenmedi',
-    'attachments_explain_link' => 'Eğer dosya yüklememeyi tercih ederseniz link ekleyebilirsiniz. Bu başka bir sayfaya veya buluttaki bir dosyanın linki olabilir.',
+    'attachments_set_link' => 'Bağlantıyı Ata',
+    'attachments_delete' => 'Bu eki silmek istediğinize emin misiniz?',
+    'attachments_dropzone' => 'Dosyaları sürükleyin veya seçin',
+    'attachments_no_files' => 'Hiçbir dosya yüklenmedi',
+    'attachments_explain_link' => 'Eğer dosya yüklememeyi tercih ederseniz bağlantı ekleyebilirsiniz. Bu bağlantı başka bir sayfanın veya bulut depolamadaki bir dosyanın bağlantısı olabilir.',
     'attachments_link_name' => 'Bağlantı Adı',
-    'attachment_link' => 'Ek linki',
-    'attachments_link_url' => 'Dosya linki',
-    'attachments_link_url_hint' => 'Dosyanın veya sitenin url adres',
+    'attachment_link' => 'Ek bağlantısı',
+    'attachments_link_url' => 'Dosya bağlantısı',
+    'attachments_link_url_hint' => 'Dosyanın veya sitenin url adresi',
     'attach' => 'Ekle',
+    'attachments_insert_link' => 'Sayfaya Bağlantı Ekle',
     'attachments_edit_file' => 'Dosyayı Düzenle',
     'attachments_edit_file_name' => 'Dosya Adı',
-    'attachments_edit_drop_upload' => 'Dosyaları sürükle veya yüklemek için buraya tıkla',
-    'attachments_order_updated' => 'Ek sırası güncellendi',
+    'attachments_edit_drop_upload' => 'Üzerine yazılacak dosyaları sürükleyin veya seçin',
+    'attachments_order_updated' => 'Ek sıralaması güncellendi',
     'attachments_updated_success' => 'Ek detayları güncellendi',
     'attachments_deleted' => 'Ek silindi',
     'attachments_file_uploaded' => 'Dosya başarıyla yüklendi',
     'attachments_file_updated' => 'Dosya başarıyla güncellendi',
-    'attachments_link_attached' => 'Link sayfaya başarıyla eklendi',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'attachments_link_attached' => 'Bağlantı, sayfaya başarıyla eklendi',
+    'templates' => 'Şablonlar',
+    'templates_set_as_template' => 'Bu sayfa, bir şablondur',
+    'templates_explain_set_as_template' => 'Başka sayfalar oluştururken bu sayfanın içeriğini kullanabilmek için bu sayfayı bir şablon olarak ayarlayabilirsiniz. Bu sayfayı görüntüleme yetkisi olan kullanıcılar da bu şablonu kullanabileceklerdir.',
+    'templates_replace_content' => 'Sayfa içeriğini değiştir',
+    'templates_append_content' => 'Sayfa içeriğine ekle',
+    'templates_prepend_content' => 'Sayfa içeriğinin başına ekle',
 
     // Profile View
-    'profile_user_for_x' => 'Kullanıcı :time',
+    'profile_user_for_x' => 'Üyelik süresi: :time',
     'profile_created_content' => 'Oluşturulan İçerik',
-    'profile_not_created_pages' => ':userName herhangi bir sayfa oluşturma',
-    'profile_not_created_chapters' => ':userName herhangi bir bölüm oluşturma',
-    'profile_not_created_books' => ':userName herhangi bir kitap oluşturma',
-    'profile_not_created_shelves' => ':userName herhangi bir kitaplık oluşturma',
+    'profile_not_created_pages' => ':userName herhangi bir sayfa oluşturmamış',
+    'profile_not_created_chapters' => ':userName herhangi bir bölüm oluşturmamış',
+    'profile_not_created_books' => ':userName herhangi bir kitap oluşturmamış',
+    'profile_not_created_shelves' => ':userName herhangi bir kitaplık oluşturmamış',
 
     // Comments
     'comment' => 'Yorum',
     'comments' => 'Yorumlar',
     'comment_add' => 'Yorum Ekle',
-    'comment_placeholder' => 'Buraya yorum ekle',
-    'comment_count' => '{0} Yorum Yok|{1} 1 Yorum|[2,*] :count Yorun',
-    'comment_save' => 'Yorum Kaydet',
-    'comment_saving' => 'Yorum kaydediliyor...',
+    'comment_placeholder' => 'Buraya bir yorum yazın',
+    'comment_count' => '{0} Yorum Yapılmamış|{1} 1 Yorum|[2,*] :count Yorum',
+    'comment_save' => 'Yorumu Gönder',
+    'comment_saving' => 'Yorum gönderiliyor...',
     'comment_deleting' => 'Yorum siliniyor...',
     'comment_new' => 'Yeni Yorum',
-    'comment_created' => 'yorum yaptı :createDiff',
-    'comment_updated' => ':username tarafından :updateDiff önce güncellendi',
+    'comment_created' => ':createDiff yorum yaptı',
+    'comment_updated' => ':username tarafından :updateDiff güncellendi',
     'comment_deleted_success' => 'Yorum silindi',
-    'comment_created_success' => 'Yorum eklendi',
+    'comment_created_success' => 'Yorum gönderildi',
     'comment_updated_success' => 'Yorum güncellendi',
-    'comment_delete_confirm' => 'Bu yorumu silmek istediğinizden emin misiniz?',
+    'comment_delete_confirm' => 'Bu yorumu silmek istediğinize emin misiniz?',
     'comment_in_reply_to' => ':commentId yorumuna yanıt olarak',
 
     // Revision
-    'revision_delete_confirm' => 'Bu revizyonu silmek istediğinizden emin misiniz?',
-    'revision_restore_confirm' => 'Bu revizyonu yeniden yüklemek istediğinizden emin misiniz? Mevcut sayfa içeriği değiştirilecektir.',
+    'revision_delete_confirm' => 'Bu revizyonu silmek istediğinize emin misiniz?',
+    'revision_restore_confirm' => 'Bu revizyonu yeniden yüklemek istediğinize emin misiniz? Sayfanın şu anki içeriği değiştirilecektir.',
     'revision_delete_success' => 'Revizyon silindi',
-    'revision_cannot_delete_latest' => 'Son revizyon silinemez.'
+    'revision_cannot_delete_latest' => 'Son revizyonu silemezsiniz.'
 ];
\ No newline at end of file
index 8a897b4883ecf82503e2353b90bc23371f817b09..d3a24622c2c8eb73bba91c3c5a4a97155aa27154 100644 (file)
@@ -5,50 +5,55 @@
 return [
 
     // Permissions
-    'permission' => 'Bu sayfaya erişme yetkiniz yok.',
-    'permissionJson' => 'Bu işlemi yapmak için yetkiniz yo.',
+    'permission' => 'Bu sayfaya erişim izniniz bulunmuyor.',
+    'permissionJson' => 'Bu işlemi yapmaya yetkiniz bulunmuyor.',
 
     // Auth
-    'error_user_exists_different_creds' => ':email adresi farklı kullanıcı bilgileri ile zaten kullanımda.',
-    'email_already_confirmed' => 'E-mail halihazırda onaylanmış, giriş yapmayı dene.',
-    'email_confirmation_invalid' => 'Bu doğrulama tokenı daha önce kullanılmış veya geçerli değil, lütfen tekrar kayıt olmayı deneyin.',
-    'email_confirmation_expired' => 'Doğrulama token\'ının süresi geçmiş, yeni bir mail gönderildi.',
-    'ldap_fail_anonymous' => 'Anonim LDAP girişi başarısız oldu',
-    'ldap_fail_authed' => 'Verdiğiniz bilgiler ile LDAP girişi başarısız oldu.',
-    'ldap_extension_not_installed' => 'LDAP PHP eklentisi yüklenmedi',
+    'error_user_exists_different_creds' => ':email e-posta adresine sahip bir kullanıcı zaten var.',
+    'email_already_confirmed' => 'E-posta adresi zaten doğrulanmış, giriş yapmayı deneyin.',
+    'email_confirmation_invalid' => 'Bu doğrulama kodu ya geçersiz ya da daha önce kullanılmış, lütfen tekrar kaydolmayı deneyin.',
+    'email_confirmation_expired' => 'Doğrulama kodunun süresi doldu, yeni bir doğrulama kodu e-posta adresine gönderildi.',
+    'email_confirmation_awaiting' => 'Bu hesaba ait e-posta adresinin doğrulanması gerekiyor',
+    'ldap_fail_anonymous' => 'Anonim olarak gerçekleştirilmeye çalışılan LDAP erişimi başarısız oldu',
+    'ldap_fail_authed' => 'Verilen bilgiler kullanılarak gerçekleştirilmeye çalışılan LDAP erişimi başarısız oldu',
+    'ldap_extension_not_installed' => 'LDAP PHP eklentisi kurulu değil',
     'ldap_cannot_connect' => 'LDAP sunucusuna bağlanılamadı, ilk bağlantı başarısız oldu',
-    'social_no_action_defined' => 'Bir aksiyon tanımlanmadı',
-    'social_login_bad_response' => ":socialAccount girişi sırasında hata oluştu: \n:error",
+    'saml_already_logged_in' => 'Zaten giriş yapılmış',
+    'saml_user_not_registered' => ':name adlı kullanıcı kayıtlı değil ve otomatik kaydolma devre dışı bırakılmış',
+    'saml_no_email_address' => 'Harici kimlik doğrulama sisteminden gelen veriler, bu kullanıcının e-posta adresini içermiyor',
+    'saml_invalid_response_id' => 'Harici doğrulama sistemi tarafından sağlanan bir veri talebi, bu uygulama tarafından başlatılan bir işlem tarafından tanınamadı. Giriş yaptıktan sonra geri dönmek bu soruna yol açmış olabilir.',
+    'saml_fail_authed' => ':system kullanarak giriş yapma başarısız oldu; sistem, başarılı bir kimlik doğrulama sağlayamadı',
+    'social_no_action_defined' => 'Herhangi bir eylem tanımlanmamış',
+    'social_login_bad_response' => ":socialAccount girişi sırasında bir hata meydana geldi: \n:error",
     'social_account_in_use' => 'Bu :socialAccount zaten kullanımda, :socialAccount hesabıyla giriş yapmayı deneyin.',
-    'social_account_email_in_use' => ':email adresi zaten kullanımda. Eğer zaten bir hesabınız varsa :socialAccount hesabınızı profil ayarları kısmından bağlayabilirsiniz.',
-    'social_account_existing' => 'Bu :socialAccount zaten profilinize eklenmiş.',
-    'social_account_already_used_existing' => 'Bu :socialAccount başka bir kullanıcı tarafından kullanılıyor.',
-    'social_account_not_used' => 'Bu :socialAccount hesabı hiç bir kullanıcıya bağlı değil. Lütfen profil ayarlarına gidiniz ve bağlayınız. ',
-    'social_account_register_instructions' => 'Hala bir hesabınız yoksa :socialAccount ile kayıt olabilirsiniz.',
+    'social_account_email_in_use' => ':email e-posta adresi zaten kullanılıyor. Zaten bir hesabınız varsa, :socialAccount hesabınızı profil ayarlarınızdan mevcut hesabınıza bağlayabilirsiniz.',
+    'social_account_existing' => 'Bu :socialAccount zaten hesabınıza bağlanmış.',
+    'social_account_already_used_existing' => 'Bu :socialAccount, başka bir kullanıcı tarafından kullanılıyor.',
+    'social_account_not_used' => 'Bu :socialAccount hesabı hiç bir kullanıcıya bağlanmamış. Lütfen profil ayarlarınızdan mevcut hesabınıza bağlayınız. ',
+    'social_account_register_instructions' => 'Hâlâ bir hesabınız yoksa, :socialAccount aracılığıyla kaydolabilirsiniz.',
     'social_driver_not_found' => 'Social driver bulunamadı',
     'social_driver_not_configured' => ':socialAccount ayarlarınız doğru bir şekilde ayarlanmadı.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Davetiye bağlantısının süresi doldu. Bunun yerine parolanızı sıfırlamayı deneyebilirsiniz.',
 
     // System
     'path_not_writable' => ':filePath dosya yolu yüklenemedi. Sunucuya yazılabilir olduğundan emin olun.',
-    'cannot_get_image_from_url' => ':url\'den görsel alınamadı',
-    'cannot_create_thumbs' => 'Sunucu küçük resimleri oluşturamadı. Lütfen GD PHP eklentisinin yüklü olduğundan emin olun.',
-    'server_upload_limit' => 'Sunucu bu boyutta dosya yüklemenize izin vermiyor. Lütfen daha küçük boyutta dosya yüklemeyi deneyiniz.',
-    'uploaded'  => 'Sunucu bu boyutta dosya yüklemenize izin vermiyor. Lütfen daha küçük boyutta dosya yüklemeyi deneyiniz.',
-    'image_upload_error' => 'Görsel yüklenirken bir hata oluştu',
-    'image_upload_type_error' => 'Yüklemeye çalıştığınız dosya türü geçerli değildir',
+    'cannot_get_image_from_url' => ':url adresindeki görsel alınamadı',
+    'cannot_create_thumbs' => 'Sunucu, görsel ön izlemelerini oluşturamadı. Lütfen GD PHP eklentisinin kurulu olduğundan emin olun.',
+    'server_upload_limit' => 'Sunucu bu boyutta dosya yüklemenize izin vermiyor. Lütfen daha küçük bir dosya deneyin.',
+    'uploaded'  => 'Sunucu bu boyutta dosya yüklemenize izin vermiyor. Lütfen daha küçük bir dosya deneyin.',
+    'image_upload_error' => 'Görsel yüklenirken bir hata meydana geldi',
+    'image_upload_type_error' => 'Yüklemeye çalıştığınız dosya türü geçersizdir',
     'file_upload_timeout' => 'Dosya yüklemesi zaman aşımına uğradı',
 
     // Attachments
-    'attachment_page_mismatch' => 'Ek güncellemesi sırasında sayfa uyuşmazlığı yaşandı',
     'attachment_not_found' => 'Ek bulunamadı',
 
     // Pages
-    'page_draft_autosave_fail' => 'Taslak kaydetme başarısız. Sayfanızı kaydetmeden önce internet bağlantınız olduğundan emin olun',
-    'page_custom_home_deletion' => 'Bu sayfa anasayfa olarak ayarlandığı için silinemez',
+    'page_draft_autosave_fail' => 'Taslak kaydetme başarısız oldu. Bu sayfayı kaydetmeden önce internet bağlantınız olduğundan emin olun',
+    'page_custom_home_deletion' => 'Bu sayfa, "Ana Sayfa" olarak ayarlandığı için silinemez',
 
     // Entities
-    'entity_not_found' => 'Eleman bulunamadı',
+    'entity_not_found' => 'Öge bulunamadı',
     'bookshelf_not_found' => 'Kitaplık bulunamadı',
     'book_not_found' => 'Kitap bulunamadı',
     'page_not_found' => 'Sayfa bulunamadı',
@@ -63,23 +68,35 @@ return [
 
     // Roles
     'role_cannot_be_edited' => 'Bu rol düzenlenemez',
-    'role_system_cannot_be_deleted' => 'Bu bir yönetici rolüdür ve silinemez',
-    'role_registration_default_cannot_delete' => 'Bu rol varsayılan yönetici rolü olarak atandığı için silinemez ',
-    'role_cannot_remove_only_admin' => 'Bu kullanıcı yönetici rolü olan tek kullanıcı olduğu için silinemez. Bu kullanıcıyı silmek için önce başka bir kullanıcıya yönetici rolü atayın.',
+    'role_system_cannot_be_deleted' => 'Bu rol, bir sistem rolüdür ve silinemez',
+    'role_registration_default_cannot_delete' => 'Bu rol, kaydolan üyelere varsayılan olarak atandığı için silinemez',
+    'role_cannot_remove_only_admin' => 'Bu kullanıcı, yönetici rolü olan tek kullanıcı olduğu için silinemez. Bu kullanıcıyı silmek için önce başka bir kullanıcıya yönetici rolü atayın.',
 
     // Comments
     'comment_list' => 'Yorumlar yüklenirken bir hata oluştu.',
     'cannot_add_comment_to_draft' => 'Taslaklara yorum ekleyemezsiniz.',
     'comment_add' => 'Yorum eklerken/güncellerken bir hata olıuştu.',
     'comment_delete' => 'Yorum silinirken bir hata oluştu.',
-    'empty_comment' => 'Boş bir yorum eklenemez.',
+    'empty_comment' => 'Boş bir yorum ekleyemezsiniz.',
 
     // Error pages
     '404_page_not_found' => 'Sayfa Bulunamadı',
     'sorry_page_not_found' => 'Üzgünüz, aradığınız sayfa bulunamıyor.',
-    'return_home' => 'Anasayfaya dön',
+    'sorry_page_not_found_permission_warning' => 'Bu sayfanın var olduğunu düşünüyorsanız, görüntüleme iznine sahip olmayabilirsiniz.',
+    'return_home' => 'Ana sayfaya dön',
     'error_occurred' => 'Bir Hata Oluştu',
-    'app_down' => ':appName şu anda inaktif',
-    'back_soon' => 'En kısa zamanda aktif hale gelecek.',
+    'app_down' => ':appName şu anda erişilemez durumda',
+    'back_soon' => 'En kısa sürede tekrar erişilebilir duruma gelecektir.',
+
+    // API errors
+    'api_no_authorization_found' => 'Yapılan istekte, yetkilendirme anahtarı bulunamadı',
+    'api_bad_authorization_format' => 'Yapılan istekte bir yetkilendirme anahtarı bulundu fakat doğru görünmüyor',
+    'api_user_token_not_found' => 'Sağlanan yetkilendirme anahtarı ile eşleşen bir API anahtarı bulunamadı',
+    'api_incorrect_token_secret' => 'Kullanılan API için sağlanan gizli anahtar doğru değil',
+    'api_user_no_api_permission' => 'Kullanılan API anahtarının sahibi API çağrısı yapmak için izne sahip değil',
+    'api_user_token_expired' => 'Kullanılan yetkilendirme anahtarının süresi doldu',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Test e-postası gönderilirken bir hata meydana geldi:',
 
 ];
index 42e9ef2c72533663ef941fd7798eff61efacea6a..ba1fd913f45674e396f5589496965442c834508e 100644 (file)
@@ -6,10 +6,10 @@
  */
 return [
 
-    'password' => 'Parolanız en az 6 karakterden oluşmalı ve doğrulama parolası ile eşleşmelidir. ',
-    'user' => "Bu e-mail adresi ile ilişkilendirilmiş bir kullanıcı bulamadık.",
-    'token' => 'Parola yenileme tokeni geçerli değil.',
-    'sent' => 'Parola sıfırlanma bağlantısını e-mail adresinize gönderdik!',
-    'reset' => 'Parolanız sıfırlandı!',
+    'password' => 'Şifreniz en az 6 karakterden oluşmalı ve her iki şifre de birbiri ile eşleşmelidir.',
+    'user' => "Bu e-posta adresine sahip bir kullanıcı bulamadık.",
+    'token' => 'Şifre sıfırlama anahtarı, bu e-posta adresi için geçersizdir.',
+    'sent' => 'Şifre sıfırlama bağlantısını e-posta adresinize gönderdik!',
+    'reset' => 'Şifreniz sıfırlandı!',
 
 ];
index bb35fe3237eea8166921b07c9a3da0a7e3457770..fd861c17011c23573a1de8f2e60444f6dc640bc9 100755 (executable)
@@ -9,90 +9,144 @@ return [
     // Common Messages
     'settings' => 'Ayarlar',
     'settings_save' => 'Ayarları Kaydet',
-    'settings_save_success' => 'Ayarlar Kaydedildi',
+    'settings_save_success' => 'Ayarlar kaydedildi',
 
     // App Settings
     'app_customization' => 'Özelleştirme',
     'app_features_security' => 'Özellikler & Güvenlik',
     'app_name' => 'Uygulama Adı',
-    'app_name_desc' => 'Bu isim başlıkta ve sistem tarafında gönderilen tüm mesajlarda gösterilecektir.',
+    'app_name_desc' => 'Bu isim, başlıkta ve sistem tarafından gönderilen bütün e-postalarda gösterilecektir.',
     'app_name_header' => 'İsmi başlıkta göster',
     'app_public_access' => 'Açık Erişim',
-    'app_public_access_desc' => 'Bu özelliği aktif etmek giriş yapmamış misafir kullanıcıların sizin BookStack uygulamanıza erişmesini sağlar',
-    'app_public_access_desc_guest' => 'Kayıtlı olmayan kullanıcılar için erişim yetkisi "Guest" kullanıcısı üzerinden düzenlenebilir.',
+    'app_public_access_desc' => 'Bu özelliği aktifleştirmek, giriş yapmamış misafir kullanıcıların BookStack uygulamanıza erişimini sağlar.',
+    'app_public_access_desc_guest' => 'Kayıtlı olmayan kullanıcılar için erişim yetkileri, "Guest" kullanıcısı üzerinden kontrol edilebilir.',
     'app_public_access_toggle' => 'Açık erişime izin ver',
-    'app_public_viewing' => 'Herkese açık görüntülenmeye izin verilsin mi?',
-    'app_secure_images' => 'Daha Yüksek Güvenlikli Görsel Yüklemeleri',
-    'app_secure_images_toggle' => 'Daha yüksek güveblikli görsel yüklemelerine izin ver',
-    'app_secure_images_desc' => 'Performans sebepleri nedeniyle bütün görseller halka açık. Bu opsiyon rastgele ve tahmin edilmesi zor dizileri görsel linklerinin önüne ekler. Dizin indexlerinin kapalı olduğundan emin olun.',
-    'app_editor' => 'Sayfa Editörü',
-    'app_editor_desc' => 'Sayfa düzenlemesi yapılırken hangi editörün kullanılacağını seçin.',
-    'app_custom_html' => 'Özel HTML Head İçeriği',
-    'app_custom_html_desc' => 'Buraya eklenecek olan içerik <head> taginin en sonuna eklenecektir. Bu stilleri override ederken veya analytics eklerken faydalı bir kullanım şeklidir.',
-    'app_custom_html_disabled_notice' => 'Yapılan hatalı değişikliklerin geriye alınabilmesi için bu sayfada özel HTML head içeriği kapalı.',
+    'app_public_viewing' => 'Herkese açık görüntülemeye izin verilsin mi?',
+    'app_secure_images' => 'Daha Güvenli Görsel Yüklemeleri',
+    'app_secure_images_toggle' => 'Daha güvenli görsel yüklemelerini aktifleştir',
+    'app_secure_images_desc' => 'Bütün görseller, performans sebepleri nedeniyle herkes tarafından görüntülenebilir durumdadır. Bu seçeneği aktif ederseniz; görsel bağlantılarının önüne rastgele, tahmini zor karakterler eklenmesini sağlarsınız. Kolay erişimin önlenmesi için dizin indekslerinin kapalı olduğundan emin olun.',
+    'app_editor' => 'Sayfa Düzenleyicisi',
+    'app_editor_desc' => 'Sayfa düzenlemesi yapılırken, bütün kullanıcılar tarafından hangi düzenleyicinin kullanılacağını seçin.',
+    'app_custom_html' => 'Özel HTML "Head" İçeriği',
+    'app_custom_html_desc' => 'Buraya yazacağınız içerik, <head> etiketinin içine ve en sonuna eklenecektir. Bu işlem, stil değişikliklerinin uygulanmasında ya da analytics kodlarının eklenmesinde yararlı olmaktadır.',
+    'app_custom_html_disabled_notice' => 'Olası hatalı değişikliklerin geriye alınabilmesi için bu sayfanın özelleştirilmiş HTML "head" içeriği devre dışı bırakıldı.',
     'app_logo' => 'Uygulama Logosu',
-    'app_logo_desc' => 'Bu görsel 43px yüksekliğinde olmalı. <br>Büyük görseller ölçeklenecektir.',
+    'app_logo_desc' => 'Bu görsel 43px yüksekliğinde olmalıdır. <br>Daha büyük görseller otomatik olarak küçültülecektir.',
     'app_primary_color' => 'Uygulamanın Birincil Rengi',
-    'app_primary_color_desc' => 'Bu bir hex değeri olmalıdır. <br>Varsayılan rengi seçmek için boş bırakın.',
-    'app_homepage' => 'Uygulama Anasayfası',
-    'app_homepage_desc' => 'Anasayfada görünmesi için bir view seçin. Sayfa izinleri seçili sayfalar için yok sayılacaktır.',
-    'app_homepage_select' => 'Sayfa seçiniz',
-    'app_disable_comments' => 'Yorumları Engelle',
-    'app_disable_comments_toggle' => 'Yorumları engelle',
-    'app_disable_comments_desc' => 'Yorumları uygulamadaki bütün sayfalar için engelle. <br> Mevcut yorumlar gösterilmeyecektir.',
+    'app_primary_color_desc' => 'Başlık, butonlar ve bağlantılar da dahil olmak üzere uygulama için ana rengi ayarlar.',
+    'app_homepage' => 'Ana Sayfa',
+    'app_homepage_desc' => 'Varsayılan görünüm yerine ana sayfada görünmesi için bir görünüm seçin. Sayfa izinleri, burada seçeceğiniz sayfalar için yok sayılacaktır.',
+    'app_homepage_select' => 'Bir sayfa seçin',
+    'app_disable_comments' => 'Yorumları Devre Dışı Bırak',
+    'app_disable_comments_toggle' => 'Yorumları devre dışı bırak',
+    'app_disable_comments_desc' => 'Bütün sayfalar için yorumları devre dışı bırakır. <br> Mevcut yorumlar gösterilmeyecektir.',
+
+    // Color settings
+    'content_colors' => 'İçerik Renkleri',
+    'content_colors_desc' => 'Sayfa hiyerarşisinde bulunan bütün elemanlar için renkleri ayarlar. Bu renkleri varsayılan renklerin parlaklığına yakın bir parlaklıkta seçmeniz, okunurluğun iyileştirilmesi açısından önerilir.',
+    'bookshelf_color' => 'Raf Rengi',
+    'book_color' => 'Kitap Rengi',
+    'chapter_color' => 'Bölüm Rengi',
+    'page_color' => 'Sayfa Rengi',
+    'page_draft_color' => 'Sayfa Taslağı Rengi',
 
     // Registration Settings
-    'reg_settings' => 'Kayıt',
-    'reg_enable' => 'Kaydolmaya İzin Ver',
-    'reg_enable_toggle' => 'Kaydolmaya izin ver',
-    'reg_enable_desc' => 'Kayıt olmaya izin verdiğinizde kullanıcılar kendilerini uygulamaya kaydedebilecekler. Kayıt olduktan sonra kendilerine varsayılan kullanıcı rolü atanacaktır.',
-    'reg_default_role' => 'Kayıt olduktan sonra varsayılan kullanıcı rolü',
-    'reg_email_confirmation' => 'Email Doğrulama',
-    'reg_email_confirmation_toggle' => 'E-mail onayı gerektir',
-    'reg_confirm_email_desc' => 'Eğer domain kısıtlaması kullanılıyorsa o zaman email doğrulaması gereklidir ve bu seçenek yok sayılacaktır.',
-    'reg_confirm_restrict_domain' => 'Domain Kısıtlaması',
-    'reg_confirm_restrict_domain_desc' => 'Kısıtlamak istediğiniz email domainlerini vigül ile ayırarak yazınız. Kullanıcılara uygulamaya erişmeden önce adreslerini doğrulamak için bir mail gönderilecektir. <br> Kullanıcılar başarıyla kaydolduktan sonra email adreslerini değiştiremeyeceklerdir.',
+    'reg_settings' => 'Kayıt İşlemleri',
+    'reg_enable' => 'Kayıtları Aktifleştir',
+    'reg_enable_toggle' => 'Kayıtları aktifleştir',
+    'reg_enable_desc' => 'Kullanıcılar kaydolduktan sonra sizin belirleyeceğiniz bir role otomatik olarak atanabilirler.',
+    'reg_default_role' => 'Kayıttan sonraki varsayılan kullanıcı rolü',
+    'reg_enable_external_warning' => 'Harici LDAP veya SAML kimlik doğrulaması etkinken yukarıdaki seçenek yok sayılır. Mevcut harici üyelere yönelik kimlik doğrulama başarılı olursa, mevcut olmayan üyelerin kullanıcı hesapları otomatik olarak oluşturulur.',
+    'reg_email_confirmation' => 'E-posta Doğrulaması',
+    'reg_email_confirmation_toggle' => 'E-posta doğrulamasını zorunlu kıl',
+    'reg_confirm_email_desc' => 'Eğer alan adı kısıtlaması kullanılıyorsa, bu seçenek yok sayılarak e-posta doğrulaması zorunlu kılınacaktır.',
+    'reg_confirm_restrict_domain' => 'Alan Adı Kısıtlaması',
+    'reg_confirm_restrict_domain_desc' => 'Kısıtlamak istediğiniz e-posta alan adlarını vigül ile ayırarak yazınız. Kullanıcılara, uygulamaya erişmeden önce adreslerini doğrulamaları için bir e-posta gönderilecektir. <br> Başarılı bir kayıt işleminden sonra kullanıcıların e-posta adreslerini değiştirebileceklerini unutmayın.',
     'reg_confirm_restrict_domain_placeholder' => 'Hiçbir kısıtlama tanımlanmamış',
 
     // Maintenance settings
     'maint' => 'Bakım',
-    'maint_image_cleanup' => 'Görsel Temizliği',
-    'maint_image_cleanup_desc' => "Sayfaları ve revizyon içeriklerini tarayarak hangi gösel ve çizimlerin kullanımda olduğunu ve hangilerinin gereksiz olduğunu tespit eder. Bunu başlatmadan veritabanı ve görsellerin tam bir yedeğinin alındığından emin olun.",
-    'maint_image_cleanup_ignore_revisions' => 'Revizyonlardaki görselleri yoksay',
+    'maint_image_cleanup' => 'Görselleri Temizle',
+    'maint_image_cleanup_desc' => "Sayfaları ve revizyon içeriklerini tarayarak hangi görsellerin ve çizimlerin kullanımda olduğunu ve hangilerinin gereksiz olduğunu tespit eder. Bunu başlatmadan önce veritabanının ve görsellerin tam bir yedeğinin alındığından emin olun.",
+    'maint_delete_images_only_in_revisions' => 'Eski sayfa revizyonlarındaki görselleri de sil',
     'maint_image_cleanup_run' => 'Temizliği Başlat',
-    'maint_image_cleanup_warning' => ':count potansiyel kullanılmayan görsel bulundu. Bu görselleri silmek istediğinizden emin misiniz?',
-    'maint_image_cleanup_success' => ':count potanisyel kullanılmayan görsel bulundu ve silindi!',
-    'maint_image_cleanup_nothing_found' => 'Kullanılmayan görsel bulunamadı ve birşey silinmedi!',
+    'maint_image_cleanup_warning' => 'Muhtemelen kullanılmayan :count adet görsel bulundu. Bu görselleri silmek istediğinize emin misiniz?',
+    'maint_image_cleanup_success' => 'Muhtemelen kullanılmayan :count adet görsel bulundu ve silindi!',
+    'maint_image_cleanup_nothing_found' => 'Kullanılmayan görsel bulunamadığından hiçbir şey silinmedi!',
+    'maint_send_test_email' => 'Deneme E-postası Gönder',
+    'maint_send_test_email_desc' => 'Bu işlem, profilinize tanımladığınız e-posta adresine bir deneme e-postası gönderir.',
+    'maint_send_test_email_run' => 'Deneme e-postasını gönder',
+    'maint_send_test_email_success' => 'E-posta, :address adresine gönderildi',
+    'maint_send_test_email_mail_subject' => 'Deneme E-postası',
+    'maint_send_test_email_mail_greeting' => 'E-posta iletimi çalışıyor gibi görünüyor!',
+    'maint_send_test_email_mail_text' => 'Tebrikler! Eğer bu e-posta bildirimini alıyorsanız, e-posta ayarlarınız doğru bir şekilde ayarlanmış demektir.',
+    'maint_recycle_bin_desc' => 'Silinen raflar, kitaplar, bölümler ve sayfalar geri dönüşüm kutusuna gönderilir, böylece geri yüklenebilir veya kalıcı olarak silinebilir. Geri dönüşüm kutusundaki daha eski öğeler, sistem yapılandırmasına bağlı olarak bir süre sonra otomatik olarak kaldırılabilir.',
+    'maint_recycle_bin_open' => 'Geri Dönüşüm Kutusunu Aç',
+
+    // Recycle Bin
+    'recycle_bin' => 'Geri Dönüşüm Kutusu',
+    'recycle_bin_desc' => 'Burada silinen öğeleri geri yükleyebilir veya bunları sistemden kalıcı olarak kaldırmayı seçebilirsiniz. Bu liste, izin filtrelerinin uygulandığı sistemdeki benzer etkinlik listelerinden farklı olarak filtrelenmez.',
+    'recycle_bin_deleted_item' => 'Silinen öge',
+    'recycle_bin_deleted_by' => 'Tarafından silindi',
+    'recycle_bin_deleted_at' => 'Silinme Zamanı',
+    'recycle_bin_permanently_delete' => 'Kalıcı Olarak Sil',
+    'recycle_bin_restore' => 'Geri Yükle',
+    'recycle_bin_contents_empty' => 'Geri dönüşüm kutusu boş',
+    'recycle_bin_empty' => 'Geri Dönüşüm Kutusunu Boşalt',
+    'recycle_bin_empty_confirm' => 'Bu işlem, her bir öğenin içinde bulunan içerik de dahil olmak üzere geri dönüşüm kutusundaki tüm öğeleri kalıcı olarak imha edecektir. Geri dönüşüm kutusunu boşaltmak istediğinizden emin misiniz?',
+    'recycle_bin_destroy_confirm' => 'Bu işlem, bu öğeyi kalıcı olarak ve aşağıda listelenen alt öğelerle birlikte sistemden silecek ve bu içeriği geri yükleyemeyeceksiniz. Bu öğeyi kalıcı olarak silmek istediğinizden emin misiniz?',
+    'recycle_bin_destroy_list' => 'Kalıcı Olarak Silinecek Öğeler',
+    'recycle_bin_restore_list' => 'Geri Yüklenecek Öğeler',
+    'recycle_bin_restore_confirm' => 'Bu eylem, tüm alt öğeler dahil olmak üzere silinen öğeyi orijinal konumlarına geri yükleyecektir. Orijinal konum o zamandan beri silinmişse ve şimdi geri dönüşüm kutusunda bulunuyorsa, üst öğenin de geri yüklenmesi gerekecektir.',
+    'recycle_bin_restore_deleted_parent' => 'Bu öğenin üst öğesi de silindi. Bunlar, üst öğe de geri yüklenene kadar silinmiş olarak kalacaktır.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Denetim Kaydı',
+    'audit_desc' => 'Bu denetim günlüğü, sistemde izlenen etkinliklerin bir listesini görüntüler. Bu liste, izin filtrelerinin uygulandığı sistemdeki benzer etkinlik listelerinden farklı olarak filtrelenmez.',
+    'audit_event_filter' => 'Etkinlik Filtresi',
+    'audit_event_filter_no_filter' => 'Filtre Yok',
+    'audit_deleted_item' => 'Silinen Öge',
+    'audit_deleted_item_name' => 'Isim: :name',
+    'audit_table_user' => 'Kullanıcı',
+    'audit_table_event' => 'Etkinlik',
+    'audit_table_related' => 'İlgili Öğe veya Detay',
+    'audit_table_date' => 'Aktivite Tarihi',
+    'audit_date_from' => 'Tarih Aralığından',
+    'audit_date_to' => 'Tarih Aralığına',
 
     // Role Settings
     'roles' => 'Roller',
     'role_user_roles' => 'Kullanıcı Rolleri',
     'role_create' => 'Yeni Rol Oluştur',
-    'role_create_success' => 'Rol Başarıyla Oluşturuldu',
+    'role_create_success' => 'Rol, başarıyla oluşturuldu',
     'role_delete' => 'Rolü Sil',
-    'role_delete_confirm' => 'Bu işlem \':roleName\' rolünü silecektir.',
-    'role_delete_users_assigned' => 'Bu role atanmış :userCount adet kullanıcı var. Eğer bu kullanıcıların rollerini değiştirmek istiyorsanız aşağıdan yeni bir rol seçin.',
+    'role_delete_confirm' => 'Bu işlem, \':roleName\' adlı rolü silecektir.',
+    'role_delete_users_assigned' => 'Bu role atanmış :userCount adet kullanıcı var. Eğer bu kullanıcıların rollerini değiştirmek istiyorsanız, aşağıdan yeni bir rol seçin.',
     'role_delete_no_migration' => "Kullanıcıları taşıma",
-    'role_delete_sure' => 'Bu rolü silmek istediğinizden emin misiniz?',
+    'role_delete_sure' => 'Bu rolü silmek istediğinize emin misiniz?',
     'role_delete_success' => 'Rol başarıyla silindi',
     'role_edit' => 'Rolü Düzenle',
     'role_details' => 'Rol Detayları',
     'role_name' => 'Rol Adı',
     'role_desc' => 'Rolün Kısa Tanımı',
-    'role_external_auth_id' => 'Harici Authentication ID\'leri',
+    'role_external_auth_id' => 'Harici Doğrulama Kimlikleri',
     'role_system' => 'Sistem Yetkileri',
     'role_manage_users' => 'Kullanıcıları yönet',
     'role_manage_roles' => 'Rolleri ve rol izinlerini yönet',
     'role_manage_entity_permissions' => 'Bütün kitap, bölüm ve sayfa izinlerini yönet',
-    'role_manage_own_entity_permissions' => 'Sahip olunan kitap, bölüm ve sayfaların izinlerini yönet',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_own_entity_permissions' => 'Kendine ait kitabın, bölümün ve sayfaların izinlerini yönet',
+    'role_manage_page_templates' => 'Sayfa şablonlarını yönet',
+    'role_access_api' => 'Sistem programlama arayüzüne (API) eriş',
     'role_manage_settings' => 'Uygulama ayarlarını yönet',
-    'role_asset' => 'Asset Yetkileri',
-    'role_asset_desc' => 'Bu izinleri assetlere sistem içinden varsayılan erişimi kontrol eder. Kitaplar, bölümler ve sayfaların izinleri bu izinleri override eder.',
-    'role_asset_admins' => 'Yöneticilere otomatik olarak bütün içeriğe erişim yetkisi verilir fakat bu opsiyonlar UI özelliklerini gösterir veya gizler.',
+    'role_asset' => 'Varlık Yetkileri',
+    'roles_system_warning' => 'Yukarıdaki üç izinden herhangi birine erişimin, kullanıcının kendi ayrıcalıklarını veya sistemdeki diğerlerinin ayrıcalıklarını değiştirmesine izin verebileceğini unutmayın. Yalnızca bu izinlere sahip rolleri güvenilir kullanıcılara atayın.',
+    'role_asset_desc' => 'Bu izinler, sistem içindeki varlıklara varsayılan erişim izinlerini ayarlar. Kitaplar, bölümler ve sayfalar üzerindeki izinler, buradaki izinleri geçersiz kılar.',
+    'role_asset_admins' => 'Yöneticilere otomatik olarak bütün içeriğe erişim yetkisi verilir ancak bu seçenekler, kullanıcı arayüzündeki bazı seçeneklerin gösterilmesine veya gizlenmesine neden olabilir.',
     'role_all' => 'Hepsi',
-    'role_own' => 'Sahip Olunan',
-    'role_controlled_by_asset' => 'Yükledikleri asset tarafından kontrol ediliyor',
+    'role_own' => 'Kendine Ait',
+    'role_controlled_by_asset' => 'Yüklendikleri varlık tarafından kontrol ediliyor',
     'role_save' => 'Rolü Kaydet',
     'role_update_success' => 'Rol başarıyla güncellendi',
     'role_users' => 'Bu roldeki kullanıcılar',
@@ -102,38 +156,68 @@ return [
     'users' => 'Kullanıcılar',
     'user_profile' => 'Kullanıcı Profili',
     'users_add_new' => 'Yeni Kullanıcı Ekle',
-    'users_search' => 'Kullanıcıları Ara',
+    'users_search' => 'Kullanıcı Ara',
+    'users_latest_activity' => 'Son Etkinlik',
     'users_details' => 'Kullanıcı Detayları',
-    'users_details_desc' => 'Bu kullanıcı için gösterilecek bir isim ve mail adresi belirleyin. Bu e-mail adresi kullanıcı tarafından giriş yaparken kullanılacak.',
+    'users_details_desc' => 'Bu kullanıcı için gösterilecek bir isim ve e-posta adresi belirleyin. Buraya yazacağınız e-posta adresi, uygulamaya giriş yaparken kullanılacaktır.',
     'users_details_desc_no_email' => 'Diğer kullanıcılar tarafından tanınabilmesi için bir isim belirleyin.',
     'users_role' => 'Kullanıcı Rolleri',
-    'users_role_desc' => 'Bu kullanıcının hangi rollere atanabileceğini belirleyin. Eğer bir kullanıcıya birden fazla rol atanırsa, kullanıcı bütün rollerin özelliklerini kullanabilir.',
-    'users_password' => 'Kullanıcı Parolası',
-    'users_password_desc' => 'Kullanıcının giriş yaparken kullanacağı bir parola belirleyin. Parola en az 5 karakter olmalıdır.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
-    'users_external_auth_id' => 'Harici Authentication ID\'si',
-    'users_external_auth_id_desc' => 'Bu ID kullanıcı LDAP sunucu ile bağlantı kurarken kullanılır.',
-    'users_password_warning' => 'Sadece parolanızı değiştirmek istiyorsanız aşağıyı doldurunuz.',
-    'users_system_public' => 'Bu kullanıcı sizin uygulamanızı ziyaret eden bütün misafir kullanıcıları temsil eder. Giriş yapmak için kullanılamaz, otomatik olarak atanır.',
-    'users_delete' => 'Kullanıcı Sil',
+    'users_role_desc' => 'Bu kullanıcının hangi rollere atanacağını belirleyin. Birden fazla role sahip kullanıcılar, atandığı bütün rollerin yetkilerine sahip olurlar.',
+    'users_password' => 'Kullanıcı Şifresi',
+    'users_password_desc' => 'Kullanıcının giriş yaparken kullanacağı bir şifre belirleyin. Şifre en az 6 karakterden oluşmalıdır.',
+    'users_send_invite_text' => 'Bu kullanıcıya kendi şifresini belirleyebilmesi için bir davetiye e-postası gönderebilir ya da kullanıcının şifresini kendiniz belirleyebilirsiniz.',
+    'users_send_invite_option' => 'Kullanıcıya davetiye e-postası gönder',
+    'users_external_auth_id' => 'Harici Doğrulama Kimliği',
+    'users_external_auth_id_desc' => 'Bu kimlik numarası (ID), harici kimlik doğrulama sisteminizle iletişim kurarken bu kullanıcıyla eşleştirmek için kullanılır.',
+    'users_password_warning' => 'Şifrenizi değiştirmek istiyorsanız, aşağıdaki formu doldurunuz.',
+    'users_system_public' => 'Bu kullanıcı, uygulamanızı ziyaret eden bütün misafir kullanıcıları temsil eder. Giriş yapmak için kullanılamaz ancak otomatik olarak atanır.',
+    'users_delete' => 'Kullanıcı Sil',
     'users_delete_named' => ':userName kullanıcısını sil ',
     'users_delete_warning' => 'Bu işlem \':userName\' kullanıcısını sistemden tamamen silecektir.',
     'users_delete_confirm' => 'Bu kullanıcıyı tamamen silmek istediğinize emin misiniz?',
-    'users_delete_success' => 'Kullanıcılar başarıyla silindi.',
-    'users_edit' => 'Kullanıcıyı Güncelle',
+    'users_migrate_ownership' => 'Sahipliği Taşıyın',
+    'users_migrate_ownership_desc' => 'Başka bir kullanıcının şu anda bu kullanıcıya ait olan tüm öğelerin sahibi olmasını istiyorsanız buradan bir kullanıcı seçin.',
+    'users_none_selected' => 'Hiçbir kullanıcı seçilmedi',
+    'users_delete_success' => 'Kullanıcı başarıyla kaldırıldı',
+    'users_edit' => 'Kullanıcıyı Düzenle',
     'users_edit_profile' => 'Profili Düzenle',
     'users_edit_success' => 'Kullanıcı başarıyla güncellendi',
-    'users_avatar' => 'Kullanıcı Avatarı',
-    'users_avatar_desc' => 'Bu kullanıcıyı temsil eden bir görsel seçin. Yaklaşık 256px kare olmalıdır.',
+    'users_avatar' => 'Avatar',
+    'users_avatar_desc' => 'Bu kullanıcıyı temsil eden bir görsel seçin. Bu görsel yaklaşık 256px boyutunda bir kare olmalıdır.',
     'users_preferred_language' => 'Tercih Edilen Dil',
-    'users_preferred_language_desc' => 'Bu seçenek kullanıcı arayüzünün dilini değiştirecektir. Herhangi bir kullanıcı içeriğini etkilemeyecektir.',
+    'users_preferred_language_desc' => 'Bu seçenek, kullanıcı arayüzünün dilini değiştirmek için kullanılır. Burada yapılan değişiklik herhangi bir kullanıcı tarafından oluşturulmuş içeriği etkilemeyecektir.',
     'users_social_accounts' => 'Sosyal Hesaplar',
-    'users_social_accounts_info' => 'Burada diğer hesaplarınızı ekleyerek daha hızlı ve kolay giriş sağlayabilirsiniz. Bir hesabın bağlantısını kesmek daha önce edilnilen erişiminizi kaldırmaz. Profil ayarlarınızdan bağlı sosyal hesabınızın erişimini kaldırınız.',
-    'users_social_connect' => 'Hesap Bağla',
+    'users_social_accounts_info' => 'Buraya diğer hesaplarınızı ekleyerek, uygulamaya daha hızlı ve kolay bir giriş sağlayabilirsiniz. Bir hesabın bağlantısını kesmek daha önce sahip olduğunuz erişimi kaldırmaz. Bağlı sosyal hesabınızın erişimini, profil ayarlarınızdan kaldırabilirsiniz.',
+    'users_social_connect' => 'Hesa Bağla',
     'users_social_disconnect' => 'Hesabın Bağlantısını Kes',
-    'users_social_connected' => ':socialAccount hesabı profilinize başarıyla bağlandı.',
+    'users_social_connected' => ':socialAccount hesabı, profilinize başarıyla bağlandı.',
     'users_social_disconnected' => ':socialAccount hesabınızın profilinizle ilişiği başarıyla kesildi.',
+    'users_api_tokens' => 'API Anahtarları',
+    'users_api_tokens_none' => 'Bu kullanıcı için oluşturulmuş herhangi bir API anahtarı bulunamadı',
+    'users_api_tokens_create' => 'Anahtar Oluştur',
+    'users_api_tokens_expires' => 'Bitiş süresi',
+    'users_api_tokens_docs' => 'API Dokümantasyonu',
+
+    // API Tokens
+    'user_api_token_create' => 'API Anahtarı Oluştur',
+    'user_api_token_name' => 'İsim',
+    'user_api_token_name_desc' => 'Anahtarınıza gelecekte ne amaçla kullanıldığını hatırlatması açısından anlamlı bir isim veriniz.',
+    'user_api_token_expiry' => 'Bitiş Tarihi',
+    'user_api_token_expiry_desc' => 'Bu anahtarın süresinin dolduğu bir tarih belirleyin. Bu tarihten sonra, bu anahtar kullanılarak yapılan istekler artık çalışmaz. Bu alanı boş bırakmak, bitiş tarihini 100 yıl sonrası yapar.',
+    'user_api_token_create_secret_message' => 'Bu anahtar oluşturulduktan hemen sonra bir "ID Anahtarı" ve "Gizli Anahtar" üretilip görüntülenecektir. Gizli anahtar sadece bir defa gösterilecektir, bu yüzden devam etmeden önce bu değeri güvenli bir yere kopyaladığınızdan emin olun.',
+    'user_api_token_create_success' => 'API anahtarı başarıyla oluşturuldu',
+    'user_api_token_update_success' => 'API anahtarı başarıyla güncellendi',
+    'user_api_token' => 'API Erişim Anahtarı',
+    'user_api_token_id' => 'Anahtar ID',
+    'user_api_token_id_desc' => 'Bu, API isteklerini karşılamak için sistem tarafından oluşturulmuş ve sonradan düzenlenemeyen bir tanımlayıcıdır.',
+    'user_api_token_secret' => 'Gizli Anahtar',
+    'user_api_token_secret_desc' => 'Bu, API isteklerinde sağlanması gereken anahtar için sistem tarafından oluşturulan bir gizli anahtardır. Bu anahtar sadece bir defa görüntülenecektir, bu nedenle bu değeri güvenli bir yere kopyalayın.',
+    'user_api_token_created' => 'Anahtar :timeAgo Oluşturuldu',
+    'user_api_token_updated' => 'Anahtar :timeAgo Güncellendi',
+    'user_api_token_delete' => 'Anahtarı Sil',
+    'user_api_token_delete_warning' => 'Bu işlem \':tokenName\' adındaki API anahtarını sistemden tamamen silecektir.',
+    'user_api_token_delete_confirm' => 'Bu API anahtarını silmek istediğinize emin misiniz?',
+    'user_api_token_delete_success' => 'API anahtarı başarıyla silindi',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Danca',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'İbranice',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovence',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index f0a12bc80689b860e2deac279dd4107df92b1671..45b7189d7dca89a2d3cc945f8be4d87f9b1052c8 100644 (file)
@@ -9,103 +9,104 @@ return [
 
     // Standard laravel validation lines
     'accepted'             => ':attribute kabul edilmelidir.',
-    'active_url'           => ':attribute geçerli bir URL adresi değildir.',
-    'after'                => ':attribute :date tarihinden sonra bir tarih olmalıdır.',
+    'active_url'           => ':attribute, geçerli bir URL adresi değildir.',
+    'after'                => ':attribute tarihi, :date tarihinden sonraki bir tarih olmalıdır.',
     'alpha'                => ':attribute sadece harflerden oluşabilir.',
     'alpha_dash'           => ':attribute sadece harf, rakam ve tirelerden oluşabilir.',
-    'alpha_num'            => ':attribute sadece harf ve rakam oluşabilir.',
-    'array'                => ':attribute array olmalıdır..',
-    'before'               => ':attribute :date tarihinden önce bir tarih olmalıdır.',
+    'alpha_num'            => ':attribute sadece harflerden ve rakamlardan oluşabilir.',
+    'array'                => ':attribute bir dizi olmalıdır.',
+    'before'               => ':attribute tarihi, :date tarihinden önceki bir tarih olmalıdır.',
     'between'              => [
-        'numeric' => ':attribute, :min ve :max değerleri arasında olmalıdır.',
+        'numeric' => ':attribute değeri, :min ve :max değerleri arasında olmalıdır.',
         'file'    => ':attribute, :min ve :max kilobyte boyutları arasında olmalıdır.',
         'string'  => ':attribute, :min ve :max karakter arasında olmalıdır.',
-        'array'   => ':attribute :min ve :max öge arasında olmalıdır.',
+        'array'   => ':attribute, :min ve :max öge arasında olmalıdır.',
     ],
-    'boolean'              => ':attribute true veya false olmalıdır.',
+    'boolean'              => ':attribute değeri true veya false olmalıdır.',
     'confirmed'            => ':attribute doğrulaması eşleşmiyor.',
     'date'                 => ':attribute geçerli bir tarih değil.',
-    'date_format'          => ':attribute formatı :format\'ına uymuyor.',
-    'different'            => ':attribute be :other birbirinden farklı olmalıdır.',
-    'digits'               => ':attribute :digits basamaklı olmalıdır.',
-    'digits_between'       => ':attribute :min ve :max basamaklı olmalıdır.',
-    'email'                => ':attribute geçerli bir e-mail adresi olmalıdır.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
-    'filled'               => ':attribute gerekli bir alandır.',
+    'date_format'          => ':attribute formatı, :format formatına uymuyor.',
+    'different'            => ':attribute ve :other birbirinden farklı olmalıdır.',
+    'digits'               => ':attribute, :digits basamaklı olmalıdır.',
+    'digits_between'       => ':attribute, en az :min ve en fazla :max basamaklı olmalıdır.',
+    'email'                => ':attribute, geçerli bir e-posta adresi olmalıdır.',
+    'ends_with' => ':attribute, şunlardan birisiyle bitmelidir: :values',
+    'filled'               => ':attribute alanı zorunludur.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute, :max değerinden büyük olmalıdır.',
+        'file'    => ':attribute, :value kilobayttan büyük olmalıdır.',
+        'string'  => ':attribute, :value karakterden fazla olmalıdır.',
+        'array'   => ':attribute, :value ögeden daha fazla öge içermelidir.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => ':attribute, :value değerinden büyük veya bu değere eşit olmalıdır.',
+        'file'    => ':attribute, en az :value kilobayt olmalıdır.',
+        'string'  => ':attribute, en az :value karakter içermelidir.',
+        'array'   => ':attribute, en az :value öge içermelidir.',
     ],
-    'exists'               => 'Seçilen :attribute geçerli bir alan değildir.',
-    'image'                => ':attribute bir görsel olmalıdır.',
-    'image_extension'      => ':attribute geçerli ve desteklenen bir görsel uzantısı değildir.',
-    'in'                   => 'Seçilen :attribute geçerli değildir.',
-    'integer'              => ':attribute bir integer değeri olmalıdır.',
-    'ip'                   => ':attribute geçerli bir IP adresi olmalıdır.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'exists'               => 'Seçilen :attribute geçersiz.',
+    'image'                => ':attribute, bir görsel olmalıdır.',
+    'image_extension'      => ':attribute, geçerli ve desteklenen bir görsel uzantısına sahip olmalıdır.',
+    'in'                   => 'Seçilen :attribute geçersizdir.',
+    'integer'              => ':attribute, bir tam sayı olmalıdır.',
+    'ip'                   => ':attribute, geçerli bir IP adresi olmalıdır.',
+    'ipv4'                 => ':attribute, geçerli bir IPv4 adresi olmalıdır.',
+    'ipv6'                 => ':attribute, geçerli bir IPv6 adresi olmalıdır.',
+    'json'                 => ':attribute, geçerli bir JSON dizimi olmalıdır.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute, :value değerinden küçük olmalıdır.',
+        'file'    => ':attribute, :value kilobayttan küçük olmalıdır.',
+        'string'  => ':attribute, :value karakterden küçük olmalıdır.',
+        'array'   => ':attribute, :value ögeden az olmalıdır.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute, en fazla :value değerine eşit olmalıdır.',
+        'file'    => ':attribute, en fazla :value kilobayt olmalıdır.',
+        'string'  => ':attribute, en fazla :value karakter içermelidir.',
+        'array'   => ':attribute, en fazla :value öge içermelidir.',
     ],
     'max'                  => [
-        'numeric' => ':attribute, :max değerinden büyük olmamalıdır.',
-        'file'    => ':attribute, :max kilobyte boyutundan büyük olmamalıdır.',
-        'string'  => ':attribute, :max karakter boyutundan büyük olmamalıdır.',
-        'array'   => ':attribute, en fazla :max öge içermelidir.',
+        'numeric' => ':attribute, :max değerinden büyük olmayabilir.',
+        'file'    => ':attribute, :max kilobayttan büyük olmayabilir.',
+        'string'  => ':attribute, :max karakterden daha fazla karakter içermiyor olabilir.',
+        'array'   => ':attribute, :max ögeden daha fazla öge içermiyor olabilir.',
     ],
-    'mimes'                => ':attribute :values dosya tipinde olmalıdır.',
+    'mimes'                => ':attribute, şu dosya tiplerinde olmalıdır: :values.',
     'min'                  => [
         'numeric' => ':attribute, :min değerinden az olmamalıdır.',
-        'file'    => ':attribute, :min kilobyte boyutundan küçük olmamalıdır.',
-        'string'  => ':attribute, :min karakter boyutundan küçük olmamalıdır.',
+        'file'    => ':attribute, :min kilobayttan küçük olmamalıdır.',
+        'string'  => ':attribute, en az :min karakter içermelidir.',
         'array'   => ':attribute, en az :min öge içermelidir.',
     ],
-    'no_double_extension'  => ':attribute sadece tek bir dosya tipinde olmalıdır.',
-    'not_in'               => 'Seçili :attribute geçerli değildir.',
-    'not_regex'            => 'The :attribute format is invalid.',
-    'numeric'              => ':attribute rakam olmalıdır.',
-    'regex'                => ':attribute formatı geçerli değildir.',
-    'required'             => 'The :attribute field is required. :attribute alanı gereklidir.',
-    'required_if'          => ':other alanı :value değerinde ise :attribute alanı gereklidir.',
-    'required_with'        => 'Eğer :values değeri geçerli ise :attribute alanı gereklidir.',
-    'required_with_all'    => 'Eğer :values değeri geçerli ise :attribute alanı gereklidir. ',
-    'required_without'     => 'Eğer :values değeri geçerli değil ise :attribute alanı gereklidir.',
-    'required_without_all' => 'Eğer :values değerlerinden hiçbiri geçerli değil ise :attribute alanı gereklidir.',
+    'no_double_extension'  => ':attribute, sadece tek bir dosya tipinde olmalıdır.',
+    'not_in'               => 'Seçili :attribute geçersiz.',
+    'not_regex'            => ':attribute formatı geçersiz.',
+    'numeric'              => ':attribute, bir sayı olmalıdır.',
+    'regex'                => ':attribute formatı geçersiz.',
+    'required'             => ':attribute alanı zorunludur.',
+    'required_if'          => ':other alanının :value olması, :attribute alanını zorunlu kılar.',
+    'required_with'        => ':values değerinin mevcudiyeti, :attribute alanını zorunlu kılar.',
+    'required_with_all'    => ':values değerlerinin mevcudiyeti, :attribute alanını zorunlu kılar.',
+    'required_without'     => ':values değerinin bulunmuyor olması, :attribute alanını zorunlu kılar.',
+    'required_without_all' => ':values değerlerinden hiçbirinin bulunmuyor olması, :attribute alanını zorunlu kılar.',
     'same'                 => ':attribute ve :other eşleşmelidir.',
+    'safe_url'             => 'Sağlanan bağlantı güvenli olmayabilir.',
     'size'                 => [
         'numeric' => ':attribute, :size boyutunda olmalıdır.',
-        'file'    => ':attribute, :size kilobyte boyutunda olmalıdır.',
+        'file'    => ':attribute, :size kilobayt olmalıdır.',
         'string'  => ':attribute, :size karakter uzunluğunda olmalıdır.',
         'array'   => ':attribute, :size sayıda öge içermelidir.',
     ],
-    'string'               => ':attribute string olmalıdır.',
-    'timezone'             => ':attribute geçerli bir alan olmalıdır.',
-    'unique'               => ':attribute daha önce alınmış.',
-    'url'                  => ':attribute formatı geçerli değil.',
-    'uploaded'             => 'Dosya yüklemesi başarısız oldu. Server bu boyutta dosyaları kabul etmiyor olabilir.',
+    'string'               => ':attribute, string olmalıdır.',
+    'timezone'             => ':attribute, geçerli bir bölge olmalıdır.',
+    'unique'               => ':attribute zaten alınmış.',
+    'url'                  => ':attribute formatı geçersiz.',
+    'uploaded'             => 'Dosya yüklemesi başarısız oldu. Sunucu, bu boyuttaki dosyaları kabul etmiyor olabilir.',
 
     // Custom validation lines
     'custom' => [
         'password-confirm' => [
-            'required_with' => 'Parola onayı gereklidir.',
+            'required_with' => 'Şifre onayı zorunludur',
         ],
     ],
 
index 2ed0d5560aeff67d8eac1369cb56aa333a907a9a..51cf9db19058b4f8a980b23884bb1fee9d3caa44 100644 (file)
@@ -36,7 +36,7 @@ return [
     'book_sort_notification'      => 'Книгу успішно відновлено',
 
     // Bookshelves
-    'bookshelf_create'            => 'Ñ\81Ñ\82воÑ\80ено книжкову полицю',
+    'bookshelf_create'            => 'Ñ\81Ñ\82воÑ\80ив книжкову полицю',
     'bookshelf_create_notification'    => 'Книжкову полицю успішно створено',
     'bookshelf_update'                 => 'оновив книжкову полицю',
     'bookshelf_update_notification'    => 'Книжкову полицю успішно оновлено',
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => 'прокоментував',
+    'permissions_update'          => 'updated permissions',
 ];
index 559c7be77359a3905e37c17080e500205147c804..e11848a2092125e02ab979bc7e4de2fec4c47dc3 100644 (file)
@@ -12,25 +12,25 @@ return [
     // Login & Register
     'sign_up' => 'Реєстрація',
     'log_in' => 'Увійти',
-    'log_in_with' => 'Увійти з :socialDriver',
-    'sign_up_with' => 'Ð\97аÑ\80еÑ\94Ñ\81Ñ\82Ñ\80Ñ\83ваÑ\82иÑ\81Ñ\8c з :socialDriver',
+    'log_in_with' => 'Увійти через :socialDriver',
+    'sign_up_with' => 'Ð\97аÑ\80еÑ\94Ñ\81Ñ\82Ñ\80Ñ\83ваÑ\82иÑ\81Ñ\8f Ñ\87еÑ\80ез :socialDriver',
     'logout' => 'Вихід',
 
     'name' => 'Ім\'я',
     'username' => 'Логін',
-    'email' => 'Email',
+    'email' => 'Адреса електронної пошти',
     'password' => 'Пароль',
     'password_confirm' => 'Підтвердження пароля',
-    'password_hint' => 'Має бути більше 7 символів',
+    'password_hint' => 'Має бути більше ніж 7 символів',
     'forgot_password' => 'Забули пароль?',
-    'remember_me' => 'Запамятати мене',
+    'remember_me' => 'Запам\'ятати мене',
     'ldap_email_hint' => 'Введіть email для цього облікового запису.',
     'create_account' => 'Створити обліковий запис',
     'already_have_account' => 'Вже є обліковий запис?',
     'dont_have_account' => 'Немає облікового запису?',
     'social_login' => 'Вхід через соціальну мережу',
     'social_registration' => 'Реєстрація через соціальну мережу',
-    'social_registration_text' => 'Реєстрація і вхід через інший сервіс',
+    'social_registration_text' => 'Реєстрація і вхід через інший сервіс.',
 
     'register_thanks' => 'Дякуємо за реєстрацію!',
     'register_confirm' => 'Будь ласка, перевірте свою електронну пошту та натисніть кнопку підтвердження, щоб отримати доступ до :appName.',
@@ -42,8 +42,8 @@ return [
     // Password Reset
     'reset_password' => 'Скинути пароль',
     'reset_password_send_instructions' => 'Введіть адресу електронної пошти нижче, і вам буде надіслано електронне повідомлення з посиланням на зміну пароля.',
-    'reset_password_send_button' => 'Надіслати посилання для скидання',
-    'reset_password_sent_success' => 'Посилання для скидання пароля було надіслано на :email.',
+    'reset_password_send_button' => 'Надіслати посилання для скидання пароля',
+    'reset_password_sent' => 'Посилання для скидання пароля буде надіслано на :email, якщо ця електронна адреса вказана в системі.',
     'reset_password_success' => 'Ваш пароль успішно скинуто.',
     'email_reset_subject' => 'Скинути ваш пароль :appName',
     'email_reset_text' => 'Ви отримали цей електронний лист, оскільки до нас надійшов запит на скидання пароля для вашого облікового запису.',
@@ -59,7 +59,7 @@ return [
     'email_confirm_success' => 'Ваш електронну адресу підтверджено!',
     'email_confirm_resent' => 'Лист з підтвердженням надіслано, перевірте свою пошту.',
 
-    'email_not_confirmed' => 'Адреса Email не підтверджена',
+    'email_not_confirmed' => 'Адресу електронної скриньки не підтверджено',
     'email_not_confirmed_text' => 'Ваша електронна адреса ще не підтверджена.',
     'email_not_confirmed_click_link' => 'Будь-ласка, натисніть на посилання в електронному листі, яке було надіслано після реєстрації.',
     'email_not_confirmed_resend' => 'Якщо ви не можете знайти електронний лист, ви можете повторно надіслати підтвердження електронною поштою, на формі нижче.',
index 651fb6d1ae28041c786f9256969ba73d245a0dfa..79fe3c24bf86c16bdfcf521d2e65bed2c435bd68 100644 (file)
@@ -33,11 +33,13 @@ return [
     'copy' => 'Копіювати',
     'reply' => 'Відповісти',
     'delete' => 'Видалити',
+    'delete_confirm' => 'Підтвердити видалення',
     'search' => 'Шукати',
     'search_clear' => 'Очистити пошук',
     'reset' => 'Скинути',
     'remove' => 'Видалити',
     'add' => 'Додати',
+    'fullscreen' => 'На весь екран',
 
     // Sort Options
     'sort_options' => 'Параметри сортування',
@@ -59,12 +61,14 @@ return [
     'grid_view' => 'Вигляд Сіткою',
     'list_view' => 'Вигляд Списком',
     'default' => 'За замовчуванням',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Навігація',
 
     // Header
     'profile_menu' => 'Меню профілю',
     'view_profile' => 'Переглянути профіль',
     'edit_profile' => 'Редагувати профіль',
+    'dark_mode' => 'Темний режим',
+    'light_mode' => 'Світлий режим',
 
     // Layout tabs
     'tab_info' => 'Інфо',
index 0cd7e8804d55beda2ba7d7a79d68511ba2d30640..a37d196eb522c7e2afa6af82a75feba12b637f1e 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Завантажити ще',
     'image_image_name' => 'Назва зображення',
     'image_delete_used' => 'Це зображення використовується на наступних сторінках.',
-    'image_delete_confirm' => 'Натисніть кнопку Видалити ще раз, щоб підтвердити, що хочете видалити це зображення.',
+    'image_delete_confirm_text' => 'Ви дійсно хочете видалити це зображення?',
     'image_select_image' => 'Вибрати зображення',
     'image_dropzone' => 'Перетягніть зображення, або натисніть тут для завантаження',
     'images_deleted' => 'Зображень видалено',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Редагувати код',
     'code_language' => 'Мова коду',
     'code_content' => 'Вміст коду',
+    'code_session_history' => 'Історія сесії',
     'code_save' => 'Зберегти Код',
 ];
index 9c59fdb39e3a64feefe543b68de3319caa290819..3ca3915f40a376049d319deb75ca495ca4d2eed4 100644 (file)
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => ':user створив :timeLength',
     'meta_updated' => 'Оновлено :timeLength',
     'meta_updated_name' => ':user оновив :timeLength',
+    'meta_owned_name' => 'Owned by :user',
     'entity_select' => 'Вибір об\'єкта',
     'images' => 'Зображення',
     'my_recent_drafts' => 'Мої останні чернетки',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => 'Після ввімкнення ці дозволи будуть мати вищий пріоритет ніж інші дозволи ролей.',
     'permissions_enable' => 'Увімкнути спеціальні дозволи',
     'permissions_save' => 'Зберегти дозволи',
+    'permissions_owner' => 'Owner',
 
     // Search
     'search_results' => 'Результати пошуку',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => 'Немає сторінок, які відповідають цьому пошуку',
     'search_for_term' => 'Шукати :term',
     'search_more' => 'Більше результатів',
-    'search_filters' => 'Фільтри пошуку',
+    'search_advanced' => 'Розширений пошук',
+    'search_terms' => 'Пошукові фрази',
     'search_content_type' => 'Тип вмісту',
     'search_exact_matches' => 'Точна відповідність',
     'search_tags' => 'Пошукові теги',
@@ -145,7 +148,7 @@ return [
     'chapters_create' => 'Створити новий розділ',
     'chapters_delete' => 'Видалити розділ',
     'chapters_delete_named' => 'Видалити розділ :chapterName',
-    'chapters_delete_explain' => 'Ця дія видалить розділ з назвою \':chapterName\'. Всі сторінки будуть вилучені, та додані безпосередньо до батьківської книги.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => 'Ви впевнені, що хочете видалити цей розділ?',
     'chapters_edit' => 'Редагувати розділ',
     'chapters_edit_named' => 'Редагувати розділ :chapterName',
@@ -181,7 +184,7 @@ return [
     'pages_edit_draft' => 'Редагувати чернетку сторінки',
     'pages_editing_draft' => 'Редагування чернетки',
     'pages_editing_page' => 'Редагування сторінки',
-    'pages_edit_draft_save_at' => 'Чернетку зберегти в ',
+    'pages_edit_draft_save_at' => 'Чернетка збережена о ',
     'pages_edit_delete_draft' => 'Видалити чернетку',
     'pages_edit_discard_draft' => 'Відхилити чернетку',
     'pages_edit_set_changelog' => 'Встановити журнал змін',
@@ -207,6 +210,7 @@ return [
     'pages_revisions' => 'Версія сторінки',
     'pages_revisions_named' => 'Версії сторінки для :pageName',
     'pages_revision_named' => 'Версія сторінки для :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => 'Створена',
     'pages_revisions_date' => 'Дата версії',
     'pages_revisions_number' => '#',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => 'Завантажити файл',
     'attachments_link' => 'Приєднати посилання',
     'attachments_set_link' => 'Встановити посилання',
-    'attachments_delete_confirm' => 'Натисніть кнопку Видалити ще раз, щоб підтвердити, що ви хочете видалити це вкладення.',
+    'attachments_delete' => 'Дійсно хочете видалити це вкладення?',
     'attachments_dropzone' => 'Перетягніть файли, або натисніть тут щоб прикріпити файл',
     'attachments_no_files' => 'Файли не завантажені',
     'attachments_explain_link' => 'Ви можете приєднати посилання, якщо не бажаєте завантажувати файл. Це може бути посилання на іншу сторінку або посилання на файл у хмарі.',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => 'Посилання на файл',
     'attachments_link_url_hint' => 'URL-адреса сайту або файлу',
     'attach' => 'Приєднати',
+    'attachments_insert_link' => 'Додати посилання на вкладення',
     'attachments_edit_file' => 'Редагувати файл',
     'attachments_edit_file_name' => 'Назва файлу',
     'attachments_edit_drop_upload' => 'Перетягніть файли, або натисніть тут щоб завантажити та перезаписати',
@@ -275,10 +280,10 @@ return [
     'attachments_link_attached' => 'Посилання успішно додано до сторінки',
     'templates' => 'Шаблони',
     'templates_set_as_template' => 'Сторінка це шаблон',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
+    'templates_explain_set_as_template' => 'Ви можете встановити цю сторінку як шаблон, щоб її вміст використовувався під час створення інших сторінок. Інші користувачі зможуть користуватися цим шаблоном, якщо вони мають права перегляду для цієї сторінки.',
     'templates_replace_content' => 'Замінити вміст сторінки',
     'templates_append_content' => 'Додати до вмісту сторінки',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates_prepend_content' => 'Додати на початок вмісту сторінки',
 
     // Profile View
     'profile_user_for_x' => 'Користувач вже :time',
index 2dacf077e658323c396d74e2ca6cbeb001592117..eb20bae74bf57e921caf9cbce25e2448d228e049 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'Електронна пошта вже підтверджена, спробуйте увійти.',
     'email_confirmation_invalid' => 'Цей токен підтвердження недійсний або вже був використаний, будь ласка, спробуйте знову зареєструватися.',
     'email_confirmation_expired' => 'Термін дії токена підтвердження минув, новий електронний лист підтвердження був відправлений.',
+    'email_confirmation_awaiting' => 'Потрібно підтвердити адресу електронної пошти для облікового запису, який використовується',
     'ldap_fail_anonymous' => 'LDAP-доступ невдалий, з використання анонімного зв\'язку',
     'ldap_fail_authed' => 'LDAP-доступ невдалий, використовуючи задані параметри dn та password',
     'ldap_extension_not_installed' => 'Розширення PHP LDAP не встановлено',
     'ldap_cannot_connect' => 'Неможливо підключитися до ldap-сервера, Помилка з\'єднання',
+    'saml_already_logged_in' => 'Вже увійшли',
+    'saml_user_not_registered' => 'Користувач «:name» не зареєстрований, а автоматична реєстрація вимкнена',
+    'saml_no_email_address' => 'Не вдалося знайти електронну адресу для цього користувача у даних, наданих зовнішньою системою аутентифікації',
+    'saml_invalid_response_id' => 'Запит із зовнішньої системи аутентифікації не розпізнається процесом, розпочатим цим додатком. Повернення назад після входу могла спричинити цю проблему.',
+    'saml_fail_authed' => 'Вхід із використанням «:system» не вдався, система не здійснила успішну авторизацію',
     'social_no_action_defined' => 'Жодних дій не визначено',
     'social_login_bad_response' => "Помилка, отримана під час входу з :socialAccount помилка : \n:error",
     'social_account_in_use' => 'Цей :socialAccount обліковий запис вже використовується, спробуйте ввійти з параметрами :socialAccount.',
@@ -27,7 +33,7 @@ return [
     'social_account_register_instructions' => 'Якщо у вас ще немає облікового запису, ви можете зареєструвати обліковий запис за допомогою параметра :socialAccount.',
     'social_driver_not_found' => 'Драйвер для СоціальноїМережі не знайдено',
     'social_driver_not_configured' => 'Ваші соціальні настройки :socialAccount не правильно налаштовані.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Термін дії цього запрошення закінчився. Замість цього ви можете спробувати скинути пароль свого облікового запису.',
 
     // System
     'path_not_writable' => 'Не вдається завантажити шлях до файлу :filePath. Переконайтеся, що він доступний для запису на сервер.',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => 'Тайм-аут при завантаженні файлу',
 
     // Attachments
-    'attachment_page_mismatch' => 'Невідповідність сторінки при оновленні вкладень',
     'attachment_not_found' => 'Вкладення не знайдено',
 
     // Pages
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Сторінку не знайдено',
     'sorry_page_not_found' => 'Вибачте, сторінку, яку ви шукали, не знайдено.',
+    'sorry_page_not_found_permission_warning' => 'Якщо ви очікували що ця сторінки існує – можливо у вас немає дозволу на її перегляд.',
     'return_home' => 'Повернутися на головну',
     'error_occurred' => 'Виникла помилка',
     'app_down' => ':appName зараз недоступний',
     'back_soon' => 'Він повернеться найближчим часом.',
 
+    // API errors
+    'api_no_authorization_found' => 'У запиті не знайдено токен авторизації',
+    'api_bad_authorization_format' => 'У запиті знайдено токен авторизації, але формат недійсний',
+    'api_user_token_not_found' => 'Не знайдено відповідного API-токена для наданого токена авторизації',
+    'api_incorrect_token_secret' => 'Секрет, наданий для даного використовуваного токена API є неправильним',
+    'api_user_no_api_permission' => 'Власник використовуваного токена API не має дозволу здійснювати виклики API',
+    'api_user_token_expired' => 'Термін дії токена авторизації закінчився',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Помилка під час надсилання тестового електронного листа:',
+
 ];
index cae38db5e22b3e7e402684768c77126ad553c530..90c31777c9876fb8c1f3da3ada0fd54998256957 100644 (file)
@@ -6,10 +6,10 @@
  */
 return [
 
-    'password' => 'Ð\9fаÑ\80олÑ\96 Ð¿Ð¾Ð²Ð¸Ð½Ð½Ñ\96 Ð¼Ñ\96Ñ\81Ñ\82иÑ\82и Ð¿Ñ\80инаймнÑ\96 Ñ\88Ñ\96Ñ\81Ñ\82Ñ\8c Ñ\81имволÑ\96в Ñ\96 Ð²Ñ\96дповÑ\96даÑ\82и Ð¿Ñ\96дÑ\82веÑ\80дженнÑ\8e.',
+    'password' => 'Ð\9fаÑ\80олÑ\8c Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ Ð¼Ñ\96Ñ\81Ñ\82иÑ\82и Ð½Ðµ Ð¼ÐµÐ½Ñ\88е Ð²Ð¾Ñ\81Ñ\8cми Ñ\81имволÑ\96в Ñ\96 Ð·Ð±Ñ\96гаÑ\82иÑ\81Ñ\8c Ð· Ð¿Ñ\96дÑ\82веÑ\80дженнÑ\8fм.',
     'user' => "Ми не можемо знайти користувача з цією адресою електронної пошти.",
-    'token' => 'Цей Ñ\82окен Ð´Ð»Ñ\8f Ñ\81киданнÑ\8f Ð¿Ð°Ñ\80олÑ\8f Ð½ÐµÐ´Ñ\96йÑ\81ний.',
-    'sent' => 'Ð\9cи Ð½Ð°Ð´Ñ\96Ñ\81лали Ð²Ð°Ð¼ ÐµÐ»ÐµÐºÑ\82Ñ\80онний Ð»Ð¸Ñ\81Ñ\82 Ñ\96з Ð¿Ð¾Ñ\81иланнÑ\8fм Ð½Ð° скидання пароля!',
-    'reset' => 'Ваш пароль був скинутий!',
+    'token' => 'Токен Ñ\81киданнÑ\8f Ð¿Ð°Ñ\80олÑ\8f Ð½ÐµÐ´Ñ\96йÑ\81ний Ð´Ð»Ñ\8f Ñ\86Ñ\96Ñ\94Ñ\97 Ð°Ð´Ñ\80еÑ\81и ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и.',
+    'sent' => 'Ð\9cи Ð½Ð°Ð´Ñ\96Ñ\81лали Ð\92ам ÐµÐ»ÐµÐºÑ\82Ñ\80онний Ð»Ð¸Ñ\81Ñ\82 Ñ\96з Ð¿Ð¾Ñ\81иланнÑ\8fм Ð´Ð»Ñ\8f скидання пароля!',
+    'reset' => 'Ваш пароль скинуто!',
 
 ];
index 37428ec99795d253d125b84b2df38d8b39ed3d69..b94ae4e8c03d64af50902105d8c06c2984d2bad7 100644 (file)
@@ -29,7 +29,7 @@ return [
     'app_editor_desc' => 'Виберіть, який редактор буде використовуватися всіма користувачами для редагування сторінок.',
     'app_custom_html' => 'Користувацький вміст HTML-заголовку',
     'app_custom_html_desc' => 'Будь-який доданий тут вміст буде вставлено в нижню частину розділу <head> кожної сторінки. Це зручно для перевизначення стилів, або додавання коду аналітики.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'На цій сторінці налаштувань відключений користувацький вміст заголовка HTML, щоб гарантувати, що будь-які невдалі зміни можна буде відновити.',
     'app_logo' => 'Логотип програми',
     'app_logo_desc' => 'Це зображення має бути висотою 43px. <br>Великі зображення будуть зменшені.',
     'app_primary_color' => 'Основний колір програми',
@@ -41,12 +41,22 @@ return [
     'app_disable_comments_toggle' => 'Вимкнути коментарі',
     'app_disable_comments_desc' => 'Вимкнути коментарі на всіх сторінках програми. Існуючі коментарі не відображаються.',
 
+    // Color settings
+    'content_colors' => 'Кольори вмісту',
+    'content_colors_desc' => 'Встановлює кольори для всіх елементів в ієрархії організації сторінок. Рекомендуємо вибирати кольори із яскравістю, схожою на кольори за замовчуванням, для кращої читабельності.',
+    'bookshelf_color' => 'Колір полиці',
+    'book_color' => 'Колір книги',
+    'chapter_color' => 'Колір глави',
+    'page_color' => 'Колір сторінки',
+    'page_draft_color' => 'Колір чернетки',
+
     // Registration Settings
     'reg_settings' => 'Реєстрація',
     'reg_enable' => 'Дозволити реєстрацію',
     'reg_enable_toggle' => 'Дозволити реєстрацію',
     'reg_enable_desc' => 'При включенні реєстрації відвідувач зможе зареєструватися як користувач програми. Після реєстрації їм надається єдина роль користувача за замовчуванням.',
     'reg_default_role' => 'Роль користувача за умовчанням після реєстрації',
+    'reg_enable_external_warning' => 'Цей параметр ігнорується, якщо активна зовнішня автентифікація LDAP або SAML. Облікові записи користувачів для неіснуючих учасників будуть створені автоматично, якщо аутентифікація у зовнішній системі буде успішною.',
     'reg_email_confirmation' => 'Підтвердження електронною поштою',
     'reg_email_confirmation_toggle' => 'Необхідне підтвердження електронною поштою',
     'reg_confirm_email_desc' => 'Якщо використовується обмеження домену, то підтвердження електронною поштою буде потрібно, а нижче значення буде проігноровано.',
@@ -58,11 +68,53 @@ return [
     'maint' => 'Обслуговування',
     'maint_image_cleanup' => 'Очищення зображень',
     'maint_image_cleanup_desc' => "Сканує вміст сторінки та версій, щоб перевірити, які зображення та малюнки в даний час використовуються, а також які зображення зайві. Переконайтеся, що ви створили повну резервну копію бази даних та зображення, перш ніж запускати це.",
-    'maint_image_cleanup_ignore_revisions' => 'Ігнорувати зображення в версіях',
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
     'maint_image_cleanup_run' => 'Запустити очищення',
     'maint_image_cleanup_warning' => ':count потенційно невикористаних зображень було знайдено. Ви впевнені, що хочете видалити ці зображення?',
     'maint_image_cleanup_success' => ':count потенційно невикористані зображення знайдено і видалено!',
     'maint_image_cleanup_nothing_found' => 'Не знайдено невикористовуваних зображень, нічого не видалено!',
+    'maint_send_test_email' => 'Надіслати тестове повідомлення',
+    'maint_send_test_email_desc' => 'Надіслати тестового листа на адресу електронної пошти, що вказана у вашому профілі.',
+    'maint_send_test_email_run' => 'Надіслати тестовий лист',
+    'maint_send_test_email_success' => 'Лист відправлений на : адреса',
+    'maint_send_test_email_mail_subject' => 'Перевірка електронної пошти',
+    'maint_send_test_email_mail_greeting' => 'Доставляння електронної пошти працює!',
+    'maint_send_test_email_mail_text' => 'Вітаємо! Оскільки ви отримали цього листа, поштова скринька налаштована правильно.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Журнал аудиту',
+    'audit_desc' => 'Цей журнал аудиту показує список відстежуваних у системі дій. Цей список нефільтрований, на відміну від подібних списків активності в системі, де застосовуються фільтри дозволів.',
+    'audit_event_filter' => 'Фільтр подій',
+    'audit_event_filter_no_filter' => 'Без фільтра',
+    'audit_deleted_item' => 'Видалений елемент',
+    'audit_deleted_item_name' => 'Назва: :name',
+    'audit_table_user' => 'Користувач',
+    'audit_table_event' => 'Подія',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Дата активності',
+    'audit_date_from' => 'Діапазон дат від',
+    'audit_date_to' => 'Діапазон дат до',
 
     // Role Settings
     'roles' => 'Ролі',
@@ -85,9 +137,11 @@ return [
     'role_manage_roles' => 'Керування правами ролей та ролями',
     'role_manage_entity_permissions' => 'Керування всіма правами на книги, розділи та сторінки',
     'role_manage_own_entity_permissions' => 'Керування дозволами на власну книгу, розділ та сторінки',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => 'Управління шаблонами сторінок',
+    'role_access_api' => 'Доступ до системного API',
     'role_manage_settings' => 'Керування налаштуваннями програми',
     'role_asset' => 'Дозволи',
+    'roles_system_warning' => 'Майте на увазі, що доступ до будь-якого з вищезазначених трьох дозволів може дозволити користувачеві змінювати власні привілеї або привілеї інших в системі. Ролі з цими дозволами призначайте лише довіреним користувачам.',
     'role_asset_desc' => 'Ці дозволи контролюють стандартні доступи всередині системи. Права на книги, розділи та сторінки перевизначать ці дозволи.',
     'role_asset_admins' => 'Адміністратори автоматично отримують доступ до всього вмісту, але ці параметри можуть відображати або приховувати параметри інтерфейсу користувача.',
     'role_all' => 'Все',
@@ -103,6 +157,7 @@ return [
     'user_profile' => 'Профіль користувача',
     'users_add_new' => 'Додати нового користувача',
     'users_search' => 'Пошук користувачів',
+    'users_latest_activity' => 'Latest Activity',
     'users_details' => 'Відомості про користувача',
     'users_details_desc' => 'Встановіть ім\'я та електронну адресу для цього користувача. Адреса електронної пошти буде використана для входу до програми.',
     'users_details_desc_no_email' => 'Встановіть ім\'я для цього користувача, щоб інші могли його розпізнати.',
@@ -110,17 +165,20 @@ return [
     'users_role_desc' => 'Виберіть, до яких ролей буде призначено цього користувача. Якщо користувачеві призначено декілька ролей, дозволи з цих ролей будуть складатись і вони отримуватимуть усі можливості призначених ролей.',
     'users_password' => 'Пароль користувача',
     'users_password_desc' => 'Встановіть пароль для входу. Він повинен містити принаймні 5 символів.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Надіслати лист із запрошенням користувачу',
+    'users_send_invite_text' => 'Ви можете надіслати цьому користувачеві лист із запрошенням, що дозволить йому встановити пароль власноруч, або ви можете встановити йому пароль самостійно.',
+    'users_send_invite_option' => 'Надіслати листа із запрошенням користувачу',
     'users_external_auth_id' => 'Зовнішній ID автентифікації',
-    'users_external_auth_id_desc' => 'Цей ID використовується для пошуку збігу цього користувача під час зв\'язку з LDAP.',
+    'users_external_auth_id_desc' => 'Цей ідентифікатор використовується для ідентифікації цього користувача під час взаємодії із зовнішньою системою автентифікації.',
     'users_password_warning' => 'Тільки якщо ви хочете змінити свій пароль, заповніть поля нижче:',
     'users_system_public' => 'Цей користувач представляє будь-яких гостьових користувачів, які відвідують ваш екземпляр. Його не можна використовувати для входу, але він призначається автоматично.',
     'users_delete' => 'Видалити користувача',
     'users_delete_named' => 'Видалити користувача :userName',
     'users_delete_warning' => 'Це повне видалення цього користувача з ім\'ям \':userName\' з системи.',
     'users_delete_confirm' => 'Ви впевнені, що хочете видалити цього користувача?',
-    'users_delete_success' => 'Користувачі успішно видалені',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Редагувати користувача',
     'users_edit_profile' => 'Редагувати профіль',
     'users_edit_success' => 'Користувача успішно оновлено',
@@ -134,6 +192,32 @@ return [
     'users_social_disconnect' => 'Від\'єднати обліковий запис',
     'users_social_connected' => 'Обліковий запис :socialAccount успішно додано до вашого профілю.',
     'users_social_disconnected' => 'Обліковий запис :socialAccount був успішно відключений від вашого профілю.',
+    'users_api_tokens' => 'API токени',
+    'users_api_tokens_none' => 'Жодного токена API не створено для цього користувача',
+    'users_api_tokens_create' => 'Створити токен',
+    'users_api_tokens_expires' => 'Закінчується',
+    'users_api_tokens_docs' => 'Документація API',
+
+    // API Tokens
+    'user_api_token_create' => 'Створити токен API',
+    'user_api_token_name' => 'Назва',
+    'user_api_token_name_desc' => 'Дайте своєму токену читабельну назву як майбутнє нагадування про його пряме призначення.',
+    'user_api_token_expiry' => 'Дата закінчення',
+    'user_api_token_expiry_desc' => 'Встановіть дату закінчення терміну дії цього токена. Після цієї дати запити, зроблені за допомогою цього токена, більше не працюватимуть. Якщо залишити це поле порожнім, термін дії токена закінчиться через 100 років.',
+    'user_api_token_create_secret_message' => 'Відразу після створення цього токена буде створено та показано «Ідентифікатор токена» та «Ключ токена». Ключ буде показано лише один раз, тому перед тим, як продовжити, не забудьте скопіювати значення ключа в надійне та безпечне місце.',
+    'user_api_token_create_success' => 'Токен API успішно створено',
+    'user_api_token_update_success' => 'Токен API успішно оновлено',
+    'user_api_token' => 'Токен API',
+    'user_api_token_id' => 'Ідентифікатор (ID) токена',
+    'user_api_token_id_desc' => 'Системний ідентифікатор цього токена, який потрібно буде вказати в запитах API. Його редагування неможливе.',
+    'user_api_token_secret' => 'Ключ токена',
+    'user_api_token_secret_desc' => 'Це ключ, згенерований системою для цього токена, його потрібно буде надати в запитах API. Він буде видимий лише цього разу, тому скопіюйте це значення в безпечне та надійне місце.',
+    'user_api_token_created' => 'Токен створено :timeAgo',
+    'user_api_token_updated' => 'Токен оновлено :timeAgo',
+    'user_api_token_delete' => 'Видалити токен',
+    'user_api_token_delete_warning' => 'Ця дія повністю видалить цей токен API із назвою \':tokenName\' з системи.',
+    'user_api_token_delete_confirm' => 'Дійсно хочете видалити цей токен API?',
+    'user_api_token_delete_success' => 'Токен API успішно видалено',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 5e8172889bf75a0ddebe16d0e751fc1bcd0c6053..51b9a09990ff024bf83d2dac2edab6ccce92f05d 100644 (file)
@@ -8,98 +8,99 @@
 return [
 
     // Standard laravel validation lines
-    'accepted'             => ':attribute повинен бути прийнятий.',
-    'active_url'           => ':attribute не є дійсною URL-адресою.',
-    'after'                => ':attribute повинно бути датою після :date.',
-    'alpha'                => ':attribute може містити лише літери.',
-    'alpha_dash'           => ':attribute може містити лише літери, цифри та дефіси.',
-    'alpha_num'            => ':attribute може містити лише літери та цифри.',
-    'array'                => ':attribute повинен бути масивом.',
-    'before'               => ':attribute повинен бути датою до :date.',
+    'accepted'             => 'Ви повинні прийняти :attribute.',
+    'active_url'           => 'Поле :attribute не є правильним URL.',
+    'after'                => 'Поле :attribute має містити дату не раніше :date.',
+    'alpha'                => 'Поле :attribute має містити лише літери.',
+    'alpha_dash'           => 'Поле :attribute має містити лише літери, цифри, дефіси та підкреслення.',
+    'alpha_num'            => 'Поле :attribute має містити лише літери та цифри.',
+    'array'                => 'Поле :attribute має бути масивом.',
+    'before'               => 'Поле :attribute має містити дату не пізніше :date.',
     'between'              => [
-        'numeric' => ':attribute повинен бути між :min та :max.',
-        'file'    => ':attribute повинен бути між :min та :max кілобайт.',
-        'string'  => ':attribute повинен бути між :min та :max символів.',
-        'array'   => ':attribute повинен бути між :min та :max елементів.',
+        'numeric' => 'Поле :attribute має бути між :min та :max.',
+        'file'    => 'Розмір файлу в полі :attribute має бути не менше :min та не більше :max кілобайт.',
+        'string'  => 'Текст в полі :attribute має бути не менше :min та не більше :max символів.',
+        'array'   => 'Поле :attribute має містити від :min до :max елементів.',
     ],
-    'boolean'              => ':attribute поле має бути true або false.',
-    'confirmed'            => ':attribute підтвердження не збігається.',
-    'date'                 => ':attribute не є дійсною датою.',
-    'date_format'          => ':attribute не відповідає формату :format.',
-    'different'            => ':attribute та :other повинні бути різними.',
-    'digits'               => ':attribute повинні бути :digits цифрами.',
-    'digits_between'       => ':attribute має бути між :min та :max цифр.',
-    'email'                => ':attribute повинна бути дійсною електронною адресою.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
-    'filled'               => ':attribute поле обов\'язкове.',
+    'boolean'              => 'Поле :attribute повинне містити true чи false.',
+    'confirmed'            => 'Поле :attribute не збігається з підтвердженням.',
+    'date'                 => 'Поле :attribute не є датою.',
+    'date_format'          => 'Поле :attribute не відповідає формату :format.',
+    'different'            => 'Поля :attribute та :other повинні бути різними.',
+    'digits'               => 'Довжина цифрового поля :attribute повинна дорівнювати :digits.',
+    'digits_between'       => 'Довжина цифрового поля :attribute повинна бути від :min до :max.',
+    'email'                => 'Поле :attribute повинне містити коректну електронну адресу.',
+    'ends_with' => 'Поле :attribute має закінчуватися одним з наступних значень: :values',
+    'filled'               => 'Поле :attribute є обов\'язковим для заповнення.',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => ':attribute має бути більшим ніж :value кілобайт.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => 'Поле :attribute має бути більше ніж :value.',
+        'file'    => 'Поле :attribute має бути більше ніж :value кілобайт.',
+        'string'  => 'Поле :attribute має бути більше ніж :value символів.',
+        'array'   => 'Поле :attribute має містити більше ніж :value елементів.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => 'Поле :attribute має дорівнювати чи бути більше ніж :value.',
+        'file'    => 'Поле :attribute має дорівнювати чи бути більше ніж :value кілобайт.',
+        'string'  => 'Поле :attribute має дорівнювати чи бути більше ніж :value символів.',
+        'array'   => 'Поле :attribute має містити :value чи більше елементів.',
     ],
-    'exists'               => 'Ð\92ибÑ\80аний :attribute Ð½ÐµÐ´Ñ\96йÑ\81ний.',
-    'image'                => ':attribute повинен бути зображенням.',
-    'image_extension'      => ':attribute повинен мати дійсне та підтримуване розширення зображення.',
-    'in'                   => 'Ð\92ибÑ\80аний :attribute Ð½ÐµÐ´Ñ\96йÑ\81ний.',
-    'integer'              => ':attribute повинен бути цілим числом.',
-    'ip'                   => ':attribute повинна бути дійсною IP-адресою.',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'exists'               => 'Ð\92ибÑ\80ане Ð´Ð»Ñ\8f :attribute Ð·Ð½Ð°Ñ\87еннÑ\8f Ð½Ðµ ÐºÐ¾Ñ\80екÑ\82не.',
+    'image'                => 'Поле :attribute має містити зображення.',
+    'image_extension'      => 'Поле :attribute має містити дійсне та підтримуване розширення зображення.',
+    'in'                   => 'Ð\92ибÑ\80ане Ð´Ð»Ñ\8f :attribute Ð·Ð½Ð°Ñ\87еннÑ\8f Ð½Ðµ ÐºÐ¾Ñ\80екÑ\82не.',
+    'integer'              => 'Поле :attribute має містити ціле число.',
+    'ip'                   => 'Поле :attribute має містити IP адресу.',
+    'ipv4'                 => 'Поле :attribute має містити IPv4 адресу.',
+    'ipv6'                 => 'Поле :attribute має містити IPv6 адресу.',
+    'json'                 => 'Дані поля :attribute мають бути в форматі JSON.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => 'Поле :attribute має бути менше ніж :value.',
+        'file'    => 'Поле :attribute має бути менше ніж :value кілобайт.',
+        'string'  => 'Поле :attribute має бути менше ніж :value символів.',
+        'array'   => 'Поле :attribute має містити менше ніж :value елементів.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => 'Поле :attribute має дорівнювати чи бути менше ніж :value.',
+        'file'    => 'Поле :attribute має дорівнювати чи бути менше ніж :value кілобайт.',
+        'string'  => 'Поле :attribute має дорівнювати чи бути менше ніж :value символів.',
+        'array'   => 'Поле :attribute має містити не більше ніж :value елементів.',
     ],
     'max'                  => [
-        'numeric' => ':attribute не може бути більшим за :max.',
-        'file'    => ':attribute не може бути більшим за :max кілобайт.',
-        'string'  => ':attribute не може бути більшим за :max символів.',
-        'array'   => ':attribute не може бути більше ніж :max елементів.',
+        'numeric' => 'Поле :attribute має бути не більше :max.',
+        'file'    => 'Файл в полі :attribute має бути не більше :max кілобайт.',
+        'string'  => 'Текст в полі :attribute повинен мати довжину не більшу за :max.',
+        'array'   => 'Поле :attribute повинне містити не більше :max елементів.',
     ],
-    'mimes'                => ':attribute повинен бути файлом типу: :values.',
+    'mimes'                => 'Поле :attribute повинне містити файл одного з типів: :values.',
     'min'                  => [
-        'numeric' => ':attribute повинен бути принаймні :min.',
-        'file'    => ':attribute повинен бути принаймні :min кілобайт.',
-        'string'  => ':attribute повинен бути принаймні :min символів.',
-        'array'   => ':attribute повинен містити принаймні :min елементів.',
+        'numeric' => 'Поле :attribute повинне бути не менше :min.',
+        'file'    => 'Розмір файлу в полі :attribute має бути не меншим :min кілобайт.',
+        'string'  => 'Текст в полі :attribute повинен містити не менше :min символів.',
+        'array'   => 'Поле :attribute повинне містити не менше :min елементів.',
     ],
-    'no_double_extension'  => ':attribute повинен мати тільки одне розширення файлу.',
-    'not_in'               => 'Вибраний :attribute недійсний.',
-    'not_regex'            => 'The :attribute format is invalid.',
-    'numeric'              => ':attribute повинен бути числом.',
-    'regex'                => ':attribute формат недійсний.',
-    'required'             => ':attribute поле обов\'язкове.',
-    'required_if'          => ':attribute поле бов\'язкове, коли :other з значенням :value.',
-    'required_with'        => ':attribute поле бов\'язкове, коли :values встановлено.',
-    'required_with_all'    => ':attribute поле бов\'язкове, коли :values встановлені.',
-    'required_without'     => ':attribute поле бов\'язкове, коли :values не встановлені.',
-    'required_without_all' => ':attribute поле бов\'язкове, коли жодне з :values не встановлене.',
-    'same'                 => ':attribute та :other мають збігатись.',
+    'no_double_extension'  => 'Поле :attribute повинне містити тільки одне розширення файлу.',
+    'not_in'               => 'Вибране для :attribute значення не коректне.',
+    'not_regex'            => 'Формат поля :attribute не вірний.',
+    'numeric'              => 'Поле :attribute повинно містити число.',
+    'regex'                => 'Поле :attribute має хибний формат.',
+    'required'             => 'Поле :attribute є обов\'язковим для заповнення.',
+    'required_if'          => 'Поле :attribute є обов\'язковим для заповнення, коли :other є рівним :value.',
+    'required_with'        => 'Поле :attribute є обов\'язковим для заповнення, коли :values вказано.',
+    'required_with_all'    => 'Поле :attribute є обов\'язковим для заповнення, коли :values вказано.',
+    'required_without'     => 'Поле :attribute є обов\'язковим для заповнення, коли :values не вказано.',
+    'required_without_all' => 'Поле :attribute є обов\'язковим для заповнення, коли :values не вказано.',
+    'same'                 => 'Поля :attribute та :other мають збігатися.',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
-        'numeric' => ':attribute має бути :size.',
-        'file'    => ':attribute має бути :size кілобайт.',
-        'string'  => ':attribute має бути :size символів.',
-        'array'   => ':attribute має містити :size елементів.',
+        'numeric' => 'Поле :attribute має бути довжини :size.',
+        'file'    => 'Файл в полі :attribute має бути розміром :size кілобайт.',
+        'string'  => 'Текст в полі :attribute повинен містити :size символів.',
+        'array'   => 'Поле :attribute повинне містити :size елементів.',
     ],
-    'string'               => ':attribute повинен бути рядком.',
-    'timezone'             => ':attribute повинен бути дійсною зоною.',
-    'unique'               => ':attribute вже є.',
-    'url'                  => ':attribute формат недійсний.',
+    'string'               => 'Поле :attribute повинне містити текст.',
+    'timezone'             => 'Поле :attribute повинне містити коректну часову зону.',
+    'unique'               => 'Вказане значення поля :attribute вже існує.',
+    'url'                  => 'Формат поля :attribute неправильний.',
     'uploaded'             => 'Не вдалося завантажити файл. Сервер може не приймати файли такого розміру.',
 
     // Custom validation lines
diff --git a/resources/lang/vi/activities.php b/resources/lang/vi/activities.php
new file mode 100644 (file)
index 0000000..42b34f3
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'đã tạo trang',
+    'page_create_notification'    => 'Trang đã được tạo thành công',
+    'page_update'                 => 'đã cập nhật trang',
+    'page_update_notification'    => 'Trang đã được cập nhật thành công',
+    'page_delete'                 => 'đã xóa trang',
+    'page_delete_notification'    => 'Trang đã được xóa thành công',
+    'page_restore'                => 'đã khôi phục trang',
+    'page_restore_notification'   => 'Trang đã được khôi phục thành công',
+    'page_move'                   => 'đã di chuyển trang',
+
+    // Chapters
+    'chapter_create'              => 'đã tạo chương',
+    'chapter_create_notification' => 'Chương đã được tạo thành công',
+    'chapter_update'              => 'đã cập nhật chương',
+    'chapter_update_notification' => 'Chương đã được cập nhật thành công',
+    'chapter_delete'              => 'đã xóa chương',
+    'chapter_delete_notification' => 'Chương đã được xóa thành công',
+    'chapter_move'                => 'đã di chuyển chương',
+
+    // Books
+    'book_create'                 => 'đã tạo sách',
+    'book_create_notification'    => 'Sách đã được tạo thành công',
+    'book_update'                 => 'đã cập nhật sách',
+    'book_update_notification'    => 'Sách đã được cập nhật thành công',
+    'book_delete'                 => 'đã xóa sách',
+    'book_delete_notification'    => 'Sách đã được xóa thành công',
+    'book_sort'                   => 'đã sắp xếp sách',
+    'book_sort_notification'      => 'Sách đã được sắp xếp lại thành công',
+
+    // Bookshelves
+    'bookshelf_create'            => 'đã tạo giá sách',
+    'bookshelf_create_notification'    => 'Giá sách đã được tạo thành công',
+    'bookshelf_update'                 => 'cập nhật giá sách',
+    'bookshelf_update_notification'    => 'Giá sách đã tạo thành công',
+    'bookshelf_delete'                 => 'đã xóa giá sách',
+    'bookshelf_delete_notification'    => 'Giá sách đã được xóa thành công',
+
+    // Other
+    'commented_on'                => 'đã bình luận về',
+    'permissions_update'          => 'updated permissions',
+];
diff --git a/resources/lang/vi/auth.php b/resources/lang/vi/auth.php
new file mode 100644 (file)
index 0000000..5ba2db3
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'Thông tin đăng nhập này không khớp với dữ liệu của chúng tôi.',
+    'throttle' => 'Quá nhiều lần đăng nhập sai. Vui lòng thử lại sau :seconds giây.',
+
+    // Login & Register
+    'sign_up' => 'Đăng ký',
+    'log_in' => 'Đăng nhập',
+    'log_in_with' => 'Đăng nhập với :socialDriver',
+    'sign_up_with' => 'Đăng kí với :socialDriver',
+    'logout' => 'Đăng xuất',
+
+    'name' => 'Tên',
+    'username' => 'Tên đăng nhập',
+    'email' => 'Email',
+    'password' => 'Mật khẩu',
+    'password_confirm' => 'Xác nhận mật khẩu',
+    'password_hint' => 'Cần tối thiểu 7 kí tự',
+    'forgot_password' => 'Quên Mật khẩu?',
+    'remember_me' => 'Ghi nhớ đăng nhập',
+    'ldap_email_hint' => 'Vui lòng điền một địa chỉ email để sử dụng tài khoản này.',
+    'create_account' => 'Tạo Tài khoản',
+    'already_have_account' => 'Bạn đã có tài khoản?',
+    'dont_have_account' => 'Bạn không có tài khoản?',
+    'social_login' => 'Đăng nhập bằng MXH',
+    'social_registration' => 'Đăng kí bằng MXH',
+    'social_registration_text' => 'Đăng kí và đăng nhập bằng dịch vụ khác.',
+
+    'register_thanks' => 'Cảm ơn bạn đã đăng ký!',
+    'register_confirm' => 'Vui lòng kiểm tra email và bấm vào nút xác nhận để truy cập :appName.',
+    'registrations_disabled' => 'Việc đăng kí đang bị tắt',
+    'registration_email_domain_invalid' => 'Tên miền của email không có quyền truy cập tới ứng dụng này',
+    'register_success' => 'Cảm ơn bạn đã đăng kí! Bạn đã được xác nhận và đăng nhập.',
+
+
+    // Password Reset
+    'reset_password' => 'Đặt lại mật khẩu',
+    'reset_password_send_instructions' => 'Nhập email vào ô dưới đây và bạn sẽ nhận được một email với liên kết để đặt lại mật khẩu.',
+    'reset_password_send_button' => 'Gửi liên kết đặt lại mật khẩu',
+    'reset_password_sent' => 'Một đường dẫn đặt lại mật khẩu sẽ được gửi tới :email nếu địa chỉ email đó tồn tại trong hệ thống.',
+    'reset_password_success' => 'Mật khẩu đã được đặt lại thành công.',
+    'email_reset_subject' => 'Đặt lại mật khẩu của :appName',
+    'email_reset_text' => 'Bạn nhận được email này bởi vì chúng tôi nhận được một yêu cầu đặt lại mật khẩu cho tài khoản của bạn.',
+    'email_reset_not_requested' => 'Nếu bạn không yêu cầu đặt lại mật khẩu, không cần có bất cứ hành động nào khác.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Xác nhận email trên :appName',
+    'email_confirm_greeting' => 'Cảm ơn bạn đã tham gia :appName!',
+    'email_confirm_text' => 'Xin hãy xác nhận địa chỉa email bằng cách bấm vào nút dưới đây:',
+    'email_confirm_action' => 'Xác nhận Email',
+    'email_confirm_send_error' => 'Email xác nhận cần gửi nhưng hệ thống đã không thể gửi được email. Liên hệ với quản trị viên để chắc chắn email được thiết lập đúng.',
+    'email_confirm_success' => 'Email của bạn đã được xác nhận!',
+    'email_confirm_resent' => 'Email xác nhận đã được gửi lại, Vui lòng kiểm tra hộp thư.',
+
+    'email_not_confirmed' => 'Địa chỉ email chưa được xác nhận',
+    'email_not_confirmed_text' => 'Địa chỉ email của bạn hiện vẫn chưa được xác nhận.',
+    'email_not_confirmed_click_link' => 'Vui lòng bấm vào liên kết trong mail được gửi trong thời gian ngắn ngay sau khi bạn đăng kí.',
+    'email_not_confirmed_resend' => 'Nếu bạn không tìm thấy email bạn có thể yêu cầu gửi lại email xác nhận bằng cách gửi mẫu dưới đây.',
+    'email_not_confirmed_resend_button' => 'Gửi lại email xác nhận',
+
+    // User Invite
+    'user_invite_email_subject' => 'Bạn được mời tham gia :appName!',
+    'user_invite_email_greeting' => 'Một tài khoản đã được tạo dành cho bạn trên :appName.',
+    'user_invite_email_text' => 'Bấm vào nút dưới đây để đặt lại mật khẩu tài khoản và lấy quyền truy cập:',
+    'user_invite_email_action' => 'Đặt mật khẩu tài khoản',
+    'user_invite_page_welcome' => 'Chào mừng đến với :appName!',
+    'user_invite_page_text' => 'Để hoàn tất tài khoản và lấy quyền truy cập bạn cần đặt mật khẩu để sử dụng cho các lần đăng nhập sắp tới tại :appName.',
+    'user_invite_page_confirm_button' => 'Xác nhận Mật khẩu',
+    'user_invite_success' => 'Mật khẩu đã được thiết lập, bạn có quyền truy cập đến :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/vi/common.php b/resources/lang/vi/common.php
new file mode 100644 (file)
index 0000000..d1dc8a0
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Huỷ',
+    'confirm' => 'Xác nhận',
+    'back' => 'Quay lại',
+    'save' => 'Lưu',
+    'continue' => 'Tiếp tục',
+    'select' => 'Chọn',
+    'toggle_all' => 'Bật/tắt tất cả',
+    'more' => 'Thêm',
+
+    // Form Labels
+    'name' => 'Tên',
+    'description' => 'Mô tả',
+    'role' => 'Quyền',
+    'cover_image' => 'Ảnh bìa',
+    'cover_image_description' => 'Ảnh nên có kích thước 440x250px.',
+    
+    // Actions
+    'actions' => 'Hành động',
+    'view' => 'Xem',
+    'view_all' => 'Xem tất cả',
+    'create' => 'Tạo',
+    'update' => 'Cập nhật',
+    'edit' => 'Sửa',
+    'sort' => 'Sắp xếp',
+    'move' => 'Di chuyển',
+    'copy' => 'Sao chép',
+    'reply' => 'Trả lời',
+    'delete' => 'Xóa',
+    'delete_confirm' => 'Xác nhận Xóa',
+    'search' => 'Tìm kiếm',
+    'search_clear' => 'Xoá tìm kiếm',
+    'reset' => 'Thiết lập lại',
+    'remove' => 'Xóa bỏ',
+    'add' => 'Thêm',
+    'fullscreen' => 'Toàn màn hình',
+
+    // Sort Options
+    'sort_options' => 'Tùy Chọn Sắp Xếp',
+    'sort_direction_toggle' => 'Đảo chiều sắp xếp',
+    'sort_ascending' => 'Sắp xếp tăng dần',
+    'sort_descending' => 'Sắp xếp giảm dần',
+    'sort_name' => 'Tên',
+    'sort_created_at' => 'Ngày Tạo',
+    'sort_updated_at' => 'Ngày cập nhật',
+
+    // Misc
+    'deleted_user' => 'Người dùng bị xóa',
+    'no_activity' => 'Không có hoạt động nào',
+    'no_items' => 'Không có mục nào khả dụng',
+    'back_to_top' => 'Lên đầu trang',
+    'toggle_details' => 'Bật/tắt chi tiết',
+    'toggle_thumbnails' => 'Bật/tắt ảnh ảnh nhỏ',
+    'details' => 'Chi tiết',
+    'grid_view' => 'Hiển thị dạng lưới',
+    'list_view' => 'Hiển thị dạng danh sách',
+    'default' => 'Mặc định',
+    'breadcrumb' => 'Đường dẫn liên kết',
+
+    // Header
+    'profile_menu' => 'Menu Hồ sơ',
+    'view_profile' => 'Xem Hồ sơ',
+    'edit_profile' => 'Sửa Hồ sơ',
+    'dark_mode' => 'Chế độ Tối',
+    'light_mode' => 'Chế độ Sáng',
+
+    // Layout tabs
+    'tab_info' => 'Thông tin',
+    'tab_content' => 'Nội dung',
+
+    // Email Content
+    'email_action_help' => 'Nếu bạn đang có vấn đề trong việc bấm nút ":actionText", sao chép và dán địa chỉ URL dưới đây vào trình duyệt web:',
+    'email_rights' => 'Bản quyền đã được bảo hộ',
+];
diff --git a/resources/lang/vi/components.php b/resources/lang/vi/components.php
new file mode 100644 (file)
index 0000000..8a06162
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Chọn Ảnh',
+    'image_all' => 'Tất cả',
+    'image_all_title' => 'Xem tất cả các ảnh',
+    'image_book_title' => 'Xem các ảnh đã được tải lên sách này',
+    'image_page_title' => 'Xem các ảnh đã được tải lên trang này',
+    'image_search_hint' => 'Tìm kiếm ảnh bằng tên',
+    'image_uploaded' => 'Đã tải lên :uploadedDate',
+    'image_load_more' => 'Hiện thêm',
+    'image_image_name' => 'Tên Ảnh',
+    'image_delete_used' => 'Ảnh này được sử dụng trong các trang dưới đây.',
+    'image_delete_confirm_text' => 'Bạn có chắc chắn muốn xóa hình ảnh này?',
+    'image_select_image' => 'Chọn Ảnh',
+    'image_dropzone' => 'Thả các ảnh hoặc bấm vào đây để tải lên',
+    'images_deleted' => 'Các ảnh đã được xóa',
+    'image_preview' => 'Xem trước Ảnh',
+    'image_upload_success' => 'Ảnh đã tải lên thành công',
+    'image_update_success' => 'Chi tiết ảnh được cập nhật thành công',
+    'image_delete_success' => 'Ảnh đã được xóa thành công',
+    'image_upload_remove' => 'Xóa bỏ',
+
+    // Code Editor
+    'code_editor' => 'Sửa Mã',
+    'code_language' => 'Ngôn ngữ Mã',
+    'code_content' => 'Nội dung Mã',
+    'code_session_history' => 'Lịch sử Phiên',
+    'code_save' => 'Lưu Mã',
+];
diff --git a/resources/lang/vi/entities.php b/resources/lang/vi/entities.php
new file mode 100644 (file)
index 0000000..158bdad
--- /dev/null
@@ -0,0 +1,319 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Được tạo gần đây',
+    'recently_created_pages' => 'Trang được tạo gần đây',
+    'recently_updated_pages' => 'Trang được cập nhật gần đây',
+    'recently_created_chapters' => 'Chương được tạo gần đây',
+    'recently_created_books' => 'Sách được tạo gần đây',
+    'recently_created_shelves' => 'Giá sách được tạo gần đây',
+    'recently_update' => 'Được cập nhật gần đây',
+    'recently_viewed' => 'Được xem gần đây',
+    'recent_activity' => 'Hoạt động gần đây',
+    'create_now' => 'Tạo ngay',
+    'revisions' => 'Phiên bản',
+    'meta_revision' => 'Phiên bản #:revisionCount',
+    'meta_created' => 'Được tạo :timeLength',
+    'meta_created_name' => 'Được tạo :timeLength bởi :user',
+    'meta_updated' => 'Được cập nhật :timeLength',
+    'meta_updated_name' => 'Được cập nhật :timeLength bởi :user',
+    'meta_owned_name' => 'Owned by :user',
+    'entity_select' => 'Chọn thực thể',
+    'images' => 'Ảnh',
+    'my_recent_drafts' => 'Bản nháp gần đây của tôi',
+    'my_recently_viewed' => 'Xem gần đây',
+    'no_pages_viewed' => 'Bạn chưa xem bất cứ trang nào',
+    'no_pages_recently_created' => 'Không có trang nào được tạo gần đây',
+    'no_pages_recently_updated' => 'Không có trang nào được cập nhật gần đây',
+    'export' => 'Kết xuất',
+    'export_html' => 'Đang chứa tệp tin Web',
+    'export_pdf' => 'Tệp PDF',
+    'export_text' => 'Tệp văn bản thuần túy',
+
+    // Permissions and restrictions
+    'permissions' => 'Quyền',
+    'permissions_intro' => 'Một khi được bật, các quyền này sẽ được ưu tiên trên hết tất cả các quyền hạn khác.',
+    'permissions_enable' => 'Bật quyền hạn tùy chỉnh',
+    'permissions_save' => 'Lưu quyền hạn',
+    'permissions_owner' => 'Owner',
+
+    // Search
+    'search_results' => 'Kết quả Tìm kiếm',
+    'search_total_results_found' => 'Tìm thấy :count kết quả|:count tổng kết quả',
+    'search_clear' => 'Xoá tìm kiếm',
+    'search_no_pages' => 'Không trang nào khớp với tìm kiếm này',
+    'search_for_term' => 'Tìm kiếm cho :term',
+    'search_more' => 'Thêm kết quả',
+    'search_advanced' => 'Tìm kiếm Nâng cao',
+    'search_terms' => 'Cụm từ Tìm kiếm',
+    'search_content_type' => 'Kiểu Nội dung',
+    'search_exact_matches' => 'Hoàn toàn trùng khớp',
+    'search_tags' => 'Tìm kiếm Tag',
+    'search_options' => 'Tuỳ chọn',
+    'search_viewed_by_me' => 'Được xem bởi tôi',
+    'search_not_viewed_by_me' => 'Không được xem bởi tôi',
+    'search_permissions_set' => 'Phân quyền',
+    'search_created_by_me' => 'Được tạo bởi tôi',
+    'search_updated_by_me' => 'Được cập nhật bởi tôi',
+    'search_date_options' => 'Tùy chọn ngày',
+    'search_updated_before' => 'Đã được cập nhật trước đó',
+    'search_updated_after' => 'Đã được cập nhật sau',
+    'search_created_before' => 'Đã được tạo trước',
+    'search_created_after' => 'Đã được tạo sau',
+    'search_set_date' => 'Đặt ngày',
+    'search_update' => 'Cập nhật tìm kiếm',
+
+    // Shelves
+    'shelf' => 'Giá',
+    'shelves' => 'Giá',
+    'x_shelves' => ':count Giá |:count Giá',
+    'shelves_long' => 'Giá sách',
+    'shelves_empty' => 'Không có giá nào được tạo',
+    'shelves_create' => 'Tạo Giá mới',
+    'shelves_popular' => 'Các Giá phổ biến',
+    'shelves_new' => 'Các Giá mới',
+    'shelves_new_action' => 'Giá mới',
+    'shelves_popular_empty' => 'Các giá phổ biến sẽ xuất hiện ở đây.',
+    'shelves_new_empty' => 'Các Giá được tạo gần đây sẽ xuất hiện ở đây.',
+    'shelves_save' => 'Lưu Giá',
+    'shelves_books' => 'Sách trên Giá này',
+    'shelves_add_books' => 'Thêm sách vào Giá này',
+    'shelves_drag_books' => 'Kéo sách vào đây để thêm vào giá',
+    'shelves_empty_contents' => 'Giá này không có sách nào',
+    'shelves_edit_and_assign' => 'Chỉnh sửa kệ để gán sách',
+    'shelves_edit_named' => 'Chỉnh sửa kệ sách :name',
+    'shelves_edit' => 'Chỉnh sửa kệ sách',
+    'shelves_delete' => 'Xóa kệ sách',
+    'shelves_delete_named' => 'Xóa kệ sách :name',
+    'shelves_delete_explain' => "Chức năng này sẽ xóa kệ sách với tên ':name'. Các sách chứa trong nó sẽ không bị xóa.",
+    'shelves_delete_confirmation' => 'Bạn có chắc chắn muốn xóa kệ sách này?',
+    'shelves_permissions' => 'Các quyền đối với kệ sách',
+    'shelves_permissions_updated' => 'Các quyền với kệ sách đã được cập nhật',
+    'shelves_permissions_active' => 'Đang bật các quyền hạn từ Kệ sách',
+    'shelves_copy_permissions_to_books' => 'Sao chép các quyền cho sách',
+    'shelves_copy_permissions' => 'Sao chép các quyền',
+    'shelves_copy_permissions_explain' => 'Điều này sẽ áp dụng các cài đặt quyền của giá sách hiện tại với tất cả các cuốn sách bên trong. Trước khi kích hoạt, đảm bảo bất cứ thay đổi liên quan đến quyền của giá sách này đã được lưu.',
+    'shelves_copy_permission_success' => 'Các quyền của giá sách đã được sao chép tới :count quyển sách',
+
+    // Books
+    'book' => 'Sách',
+    'books' => 'Tất cả sách',
+    'x_books' => ':count Sách|:count Tất cả sách',
+    'books_empty' => 'Không có cuốn sách nào được tạo',
+    'books_popular' => 'Những cuốn sách phổ biến',
+    'books_recent' => 'Những cuốn sách gần đây',
+    'books_new' => 'Những cuốn sách mới',
+    'books_new_action' => 'Sách mới',
+    'books_popular_empty' => 'Những cuốn sách phổ biến nhất sẽ xuất hiện ở đây.',
+    'books_new_empty' => 'Những cuốn sách tạo gần đây sẽ được xuất hiện ở đây.',
+    'books_create' => 'Tạo cuốn sách mới',
+    'books_delete' => 'Xóa sách',
+    'books_delete_named' => 'Xóa sách :bookName',
+    'books_delete_explain' => 'Điều này sẽ xóa cuốn sách với tên \':bookName\'. Tất cả các trang và các chương sẽ bị xóa.',
+    'books_delete_confirmation' => 'Bạn có chắc chắn muốn xóa cuốn sách này?',
+    'books_edit' => 'Sửa sách',
+    'books_edit_named' => 'Sửa sách :bookName',
+    'books_form_book_name' => 'Tên sách',
+    'books_save' => 'Lưu sách',
+    'books_permissions' => 'Các quyền của cuốn sách',
+    'books_permissions_updated' => 'Các quyền của cuốn sách đã được cập nhật',
+    'books_empty_contents' => 'Không có trang hay chương nào được tạo cho cuốn sách này.',
+    'books_empty_create_page' => 'Tao một trang mới',
+    'books_empty_sort_current_book' => 'Sắp xếp cuốn sách này',
+    'books_empty_add_chapter' => 'Thêm một chương mới',
+    'books_permissions_active' => 'Đang bật các quyền hạn từ Sách',
+    'books_search_this' => 'Tìm cuốn sách này',
+    'books_navigation' => 'Điều hướng cuốn sách',
+    'books_sort' => 'Sắp xếp nội dung cuốn sách',
+    'books_sort_named' => 'Sắp xếp sách :bookName',
+    'books_sort_name' => 'Sắp xếp theo tên',
+    'books_sort_created' => 'Sắp xếp theo ngày tạo',
+    'books_sort_updated' => 'Sắp xếp theo ngày cập nhật',
+    'books_sort_chapters_first' => 'Các Chương đầu',
+    'books_sort_chapters_last' => 'Các Chương cuối',
+    'books_sort_show_other' => 'Hiển thị các Sách khác',
+    'books_sort_save' => 'Lưu thứ tự mới',
+
+    // Chapters
+    'chapter' => 'Chương',
+    'chapters' => 'Các chương',
+    'x_chapters' => ':count Chương|:count Chương',
+    'chapters_popular' => 'Các Chương phổ biến',
+    'chapters_new' => 'Chương mới',
+    'chapters_create' => 'Tạo Chương mới',
+    'chapters_delete' => 'Xóa Chương',
+    'chapters_delete_named' => 'Xóa Chương :chapterName',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_confirm' => 'Bạn có chắc chắn muốn xóa chương này?',
+    'chapters_edit' => 'Sửa Chương',
+    'chapters_edit_named' => 'Sửa chương :chapterName',
+    'chapters_save' => 'Lưu Chương',
+    'chapters_move' => 'Di chuyển Chương',
+    'chapters_move_named' => 'Di chuyển Chương :chapterName',
+    'chapter_move_success' => 'Chương được di chuyển đến :bookName',
+    'chapters_permissions' => 'Quyền hạn Chương',
+    'chapters_empty' => 'Không có trang nào hiện có trong chương này.',
+    'chapters_permissions_active' => 'Đang bật các quyền hạn từ Chương',
+    'chapters_permissions_success' => 'Quyền hạn Chương được cập nhật',
+    'chapters_search_this' => 'Tìm kiếm trong Chương này',
+
+    // Pages
+    'page' => 'Trang',
+    'pages' => 'Các trang',
+    'x_pages' => ':count Trang|:count Trang',
+    'pages_popular' => 'Các Trang phổ biến',
+    'pages_new' => 'Trang Mới',
+    'pages_attachments' => 'Các đính kèm',
+    'pages_navigation' => 'Điều hướng Trang',
+    'pages_delete' => 'Xóa Trang',
+    'pages_delete_named' => 'Xóa Trang :pageName',
+    'pages_delete_draft_named' => 'Xóa Trang Nháp :pageName',
+    'pages_delete_draft' => 'Xóa Trang Nháp',
+    'pages_delete_success' => 'Đã xóa Trang',
+    'pages_delete_draft_success' => 'Đã xóa trang Nháp',
+    'pages_delete_confirm' => 'Bạn có chắc chắn muốn xóa trang này?',
+    'pages_delete_draft_confirm' => 'Bạn có chắc chắn muốn xóa trang nháp này?',
+    'pages_editing_named' => 'Đang chỉnh sửa Trang :pageName',
+    'pages_edit_draft_options' => 'Tùy chọn bản nháp',
+    'pages_edit_save_draft' => 'Lưu Nháp',
+    'pages_edit_draft' => 'Sửa trang nháp',
+    'pages_editing_draft' => 'Đang chỉnh sửa Nháp',
+    'pages_editing_page' => 'Đang chỉnh sửa Trang',
+    'pages_edit_draft_save_at' => 'Bản nháp đã lưu lúc ',
+    'pages_edit_delete_draft' => 'Xóa Bản nháp',
+    'pages_edit_discard_draft' => 'Hủy bỏ Bản nháp',
+    'pages_edit_set_changelog' => 'Đặt Changelog',
+    'pages_edit_enter_changelog_desc' => 'Viết mô tả ngắn gọn cho các thay đổi mà bạn tạo',
+    'pages_edit_enter_changelog' => 'Viết Changelog',
+    'pages_save' => 'Lưu Trang',
+    'pages_title' => 'Tiêu đề Trang',
+    'pages_name' => 'Tên Trang',
+    'pages_md_editor' => 'Trình chỉnh sửa',
+    'pages_md_preview' => 'Xem trước',
+    'pages_md_insert_image' => 'Chèn hình ảnh',
+    'pages_md_insert_link' => 'Chèn liên kết thực thể',
+    'pages_md_insert_drawing' => 'Chèn bản vẽ',
+    'pages_not_in_chapter' => 'Trang không nằm trong một chương',
+    'pages_move' => 'Di chuyển Trang',
+    'pages_move_success' => 'Trang đã chuyển tới ":parentName"',
+    'pages_copy' => 'Sao chép Trang',
+    'pages_copy_desination' => 'Sao lưu đến',
+    'pages_copy_success' => 'Trang được sao chép thành công',
+    'pages_permissions' => 'Quyền hạn Trang',
+    'pages_permissions_success' => 'Quyền hạn Trang được cập nhật',
+    'pages_revision' => 'Phiên bản',
+    'pages_revisions' => 'Phiên bản Trang',
+    'pages_revisions_named' => 'Phiên bản Trang cho :pageName',
+    'pages_revision_named' => 'Phiên bản Trang cho :pageName',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions_created_by' => 'Tạo bởi',
+    'pages_revisions_date' => 'Ngày của Phiên bản',
+    'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'Phiên bản #:id',
+    'pages_revisions_numbered_changes' => 'Các thay đổi của phiên bản #:id',
+    'pages_revisions_changelog' => 'Nhật ký thay đổi',
+    'pages_revisions_changes' => 'Các thay đổi',
+    'pages_revisions_current' => 'Phiên bản hiện tại',
+    'pages_revisions_preview' => 'Xem trước',
+    'pages_revisions_restore' => 'Khôi phục',
+    'pages_revisions_none' => 'Trang này không có phiên bản nào',
+    'pages_copy_link' => 'Sao chép Liên kết',
+    'pages_edit_content_link' => 'Soạn thảo Nội dung',
+    'pages_permissions_active' => 'Đang bật các quyền hạn từ Trang',
+    'pages_initial_revision' => 'Đăng bài mở đầu',
+    'pages_initial_name' => 'Trang mới',
+    'pages_editing_draft_notification' => 'Bạn hiện đang chỉnh sửa một bản nháp được lưu cách đây :timeDiff.',
+    'pages_draft_edited_notification' => 'Trang này đã được cập nhật từ lúc đó. Bạn nên loại bỏ bản nháp này.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count người dùng đang bắt đầu chỉnh sửa trang này',
+        'start_b' => ':userName đang bắt đầu chỉnh sửa trang này',
+        'time_a' => 'kể từ khi thang được cập nhật lần cuối',
+        'time_b' => 'trong :minCount phút cuối',
+        'message' => ':start :time. Hãy cẩn thận đừng ghi đè vào các bản cập nhật của nhau!',
+    ],
+    'pages_draft_discarded' => 'Bản nháp đã được loại bỏ, Người sửa đã cập nhật trang với nội dung hiện tại',
+    'pages_specific' => 'Trang cụ thể',
+    'pages_is_template' => 'Biểu mẫu trang',
+
+    // Editor Sidebar
+    'page_tags' => 'Các Thẻ Trang',
+    'chapter_tags' => 'Các Thẻ Chương',
+    'book_tags' => 'Các Thẻ Sách',
+    'shelf_tags' => 'Các Thẻ Kệ',
+    'tag' => 'Nhãn',
+    'tags' =>  'Các Thẻ',
+    'tag_name' =>  'Tên Nhãn',
+    'tag_value' => 'Giá trị Thẻ (Tùy chọn)',
+    'tags_explain' => "Thêm vài thẻ để phân loại nội dung của bạn tốt hơn. \n Bạn có thể đặt giá trị cho thẻ để quản lí kĩ càng hơn.",
+    'tags_add' => 'Thêm thẻ khác',
+    'tags_remove' => 'Xóa thẻ này',
+    'attachments' => 'Các Đính kèm',
+    'attachments_explain' => 'Cập nhật một số tập tin và đính một số liên kết để hiển thị trên trang của bạn. Chúng được hiện trong sidebar của trang.',
+    'attachments_explain_instant_save' => 'Các thay đổi ở đây sẽ được lưu ngay lập tức.',
+    'attachments_items' => 'Đính kèm các Mục',
+    'attachments_upload' => 'Tải lên Tập tin',
+    'attachments_link' => 'Đính kèm Liên kết',
+    'attachments_set_link' => 'Đặt Liên kết',
+    'attachments_delete' => 'Bạn có chắc chắn muốn xóa tập tin đính kèm này?',
+    'attachments_dropzone' => 'Thả các tập tin hoặc bấm vào đây để đính kèm một tập tin',
+    'attachments_no_files' => 'Không có tập tin nào được tải lên',
+    'attachments_explain_link' => 'Bạn có thể đính kèm một liên kết nếu bạn lựa chọn không tải lên tập tin. Liên kết này có thể trỏ đến một trang khác hoặc một tập tin ở trên mạng (đám mây).',
+    'attachments_link_name' => 'Tên Liên kết',
+    'attachment_link' => 'Liên kết đính kèm',
+    'attachments_link_url' => 'Liên kết đến tập tin',
+    'attachments_link_url_hint' => 'URL của trang hoặc tập tin',
+    'attach' => 'Đính kèm',
+    'attachments_insert_link' => 'Thêm Đường dẫn Tập tin đính kèm vào Trang',
+    'attachments_edit_file' => 'Sửa tập tin',
+    'attachments_edit_file_name' => 'Tên tệp tin',
+    'attachments_edit_drop_upload' => 'Thả tập tin hoặc bấm vào đây để tải lên và ghi đè',
+    'attachments_order_updated' => 'Đã cập nhật thứ tự đính kèm',
+    'attachments_updated_success' => 'Đã cập nhật chi tiết đính kèm',
+    'attachments_deleted' => 'Đính kèm đã được xóa',
+    'attachments_file_uploaded' => 'Tập tin tải lên thành công',
+    'attachments_file_updated' => 'Tập tin cập nhật thành công',
+    'attachments_link_attached' => 'Liên kết được đính kèm đến trang thành công',
+    'templates' => 'Các Mẫu',
+    'templates_set_as_template' => 'Trang là một mẫu',
+    'templates_explain_set_as_template' => 'Bạn có thể đặt trang này làm mẫu, nội dung của nó sẽ được sử dụng lại khi tạo các trang mới. Người dùng khác có thể sử dụng mẫu này nếu học có quyền hạn xem trang này.',
+    'templates_replace_content' => 'Thay thế nội dung trang',
+    'templates_append_content' => 'Viết vào nội dung trang',
+    'templates_prepend_content' => 'Thêm vào đầu nội dung trang',
+
+    // Profile View
+    'profile_user_for_x' => 'Đã là người dùng trong :time',
+    'profile_created_content' => 'Đã tạo nội dung',
+    'profile_not_created_pages' => ':userName chưa tạo bất kỳ trang nào',
+    'profile_not_created_chapters' => ':userName chưa tạo bất kì chương nào',
+    'profile_not_created_books' => ':userName chưa tạo bất cứ sách nào',
+    'profile_not_created_shelves' => ':userName chưa tạo bất kỳ giá sách nào',
+
+    // Comments
+    'comment' => 'Bình luận',
+    'comments' => 'Các bình luận',
+    'comment_add' => 'Thêm bình luận',
+    'comment_placeholder' => 'Đăng bình luận tại đây',
+    'comment_count' => '{0} Không có bình luận|{1} 1 Bình luận|[2,*] :count Bình luận',
+    'comment_save' => 'Lưu bình luận',
+    'comment_saving' => 'Đang lưu bình luận...',
+    'comment_deleting' => 'Đang xóa bình luận...',
+    'comment_new' => 'Bình luận mới',
+    'comment_created' => 'đã bình luận :createDiff',
+    'comment_updated' => 'Đã cập nhật :updateDiff bởi :username',
+    'comment_deleted_success' => 'Bình luận đã bị xóa',
+    'comment_created_success' => 'Đã thêm bình luận',
+    'comment_updated_success' => 'Bình luận đã được cập nhật',
+    'comment_delete_confirm' => 'Bạn có chắc bạn muốn xóa bình luận này?',
+    'comment_in_reply_to' => 'Trả lời cho :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Bạn có chắc bạn muốn xóa phiên bản này?',
+    'revision_restore_confirm' => 'Bạn có chắc bạn muốn khôi phục phiên bản này? Nội dung trang hiện tại sẽ được thay thế.',
+    'revision_delete_success' => 'Phiên bản đã được xóa',
+    'revision_cannot_delete_latest' => 'Không thể xóa phiên bản mới nhất.'
+];
\ No newline at end of file
diff --git a/resources/lang/vi/errors.php b/resources/lang/vi/errors.php
new file mode 100644 (file)
index 0000000..6410fb9
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'Bạn không có quyền truy cập đến trang này.',
+    'permissionJson' => 'Bạn không có quyền để thực hiện hành động này.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'Đã có người sử dụng email :email nhưng với thông tin định danh khác.',
+    'email_already_confirmed' => 'Email đã được xác nhận trước đó, Đang đăng nhập.',
+    'email_confirmation_invalid' => 'Token xác nhận này không hợp lệ hoặc đã được sử dụng trước đó, Xin hãy thử đăng ký lại.',
+    'email_confirmation_expired' => 'Token xác nhận đã hết hạn, Một email xác nhận mới đã được gửi.',
+    'email_confirmation_awaiting' => 'Địa chỉ email của tài khoản bạn đang sử dụng cần phải được xác nhận',
+    'ldap_fail_anonymous' => 'Truy cập đến LDAP sử dụng gán ẩn danh thất bại',
+    'ldap_fail_authed' => 'Truy cập đến LDAP sử dụng dn và mật khẩu thất bại',
+    'ldap_extension_not_installed' => 'Tiện ích mở rộng LDAP PHP chưa được cài đặt',
+    'ldap_cannot_connect' => 'Không thể kết nối đến máy chủ LDAP, mở đầu kết nối thất bại',
+    'saml_already_logged_in' => 'Đã đăng nhập',
+    'saml_user_not_registered' => 'Người dùng :name chưa được đăng ký và tự động đăng ký đang bị tắt',
+    'saml_no_email_address' => 'Không tìm thấy địa chỉ email cho người dùng này trong dữ liệu được cung cấp bới hệ thống xác thực ngoài',
+    'saml_invalid_response_id' => 'Yêu cầu từ hệ thống xác thực bên ngoài không được nhận diện bởi quy trình chạy cho ứng dụng này. Điều hướng trở lại sau khi đăng nhập có thể đã gây ra vấn đề này.',
+    'saml_fail_authed' => 'Đăng nhập sử dụng :system thất bại, hệ thống không cung cấp được sự xác thực thành công',
+    'social_no_action_defined' => 'Không có hành động được xác định',
+    'social_login_bad_response' => "Xảy ra lỗi trong lúc đăng nhập :socialAccount: \n:error",
+    'social_account_in_use' => 'Tài khoản :socialAccount này đang được sử dụng, Vui lòng thử đăng nhập bằng tùy chọn :socialAccount.',
+    'social_account_email_in_use' => 'Địa chỉ email :email đã được sử dụng. Nếu bạn đã có tài khoản bạn có thể kết nối đến tài khoản :socialAccount của mình từ cài đặt cá nhân của bạn.',
+    'social_account_existing' => ':socialAccount đã được gắn với hồ sơ của bạn từ trước.',
+    'social_account_already_used_existing' => 'Tài khoản :socialAccount đã được sử dụng bởi một người dùng khác.',
+    'social_account_not_used' => 'Tài khoản :socialAccount này chưa được liên kết bởi bất cứ người dùng nào. Vui lòng liên kết nó tại cài đặt cá nhân của bạn. ',
+    'social_account_register_instructions' => 'Nếu bạn chưa có tài khoản, Bạn có thể đăng ký một tài khoản bằng tùy chọn :socialAccount.',
+    'social_driver_not_found' => 'Không tìm thấy driver cho MXH',
+    'social_driver_not_configured' => 'Cài đặt MXH :socialAccount của bạn đang không được cấu hình hợp lệ.',
+    'invite_token_expired' => 'Liên kết mời này đã hết hạn. Bạn có thể thử đặt lại mật khẩu của tài khoản.',
+
+    // System
+    'path_not_writable' => 'Đường dẫn tệp tin :filePath không thể tải đến được. Đảm bảo rằng đường dẫn này có thể ghi được ở trên máy chủ.',
+    'cannot_get_image_from_url' => 'Không thể lấy ảnh từ :url',
+    'cannot_create_thumbs' => 'Máy chủ không thể tạo ảnh nhỏ. Vui lòng kiểm tra bạn đã cài đặt tiện ích mở rộng GD PHP.',
+    'server_upload_limit' => 'Máy chủ không cho phép tải lên kích thước này. Vui lòng thử lại với tệp tin nhỏ hơn.',
+    'uploaded'  => 'Máy chủ không cho phép tải lên kích thước này. Vui lòng thử lại với tệp tin nhỏ hơn.',
+    'image_upload_error' => 'Đã xảy ra lỗi khi đang tải lên ảnh',
+    'image_upload_type_error' => 'Ảnh đang được tải lên không hợp lệ',
+    'file_upload_timeout' => 'Đã quá thời gian tải lên tệp tin.',
+
+    // Attachments
+    'attachment_not_found' => 'Không tìm thấy đính kèm',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Lưu bản nháp thất bại. Đảm bảo rằng bạn có kết nối đến internet trước khi lưu trang này',
+    'page_custom_home_deletion' => 'Không thể xóa trang khi nó đang được đặt là trang chủ',
+
+    // Entities
+    'entity_not_found' => 'Không tìm thấy thực thể',
+    'bookshelf_not_found' => 'Không tìm thấy giá sách',
+    'book_not_found' => 'Không tìm thấy sách',
+    'page_not_found' => 'Không tìm thấy trang',
+    'chapter_not_found' => 'Không tìm thấy chương',
+    'selected_book_not_found' => 'Không tìm thấy sách được chọn',
+    'selected_book_chapter_not_found' => 'Không tìm thấy Sách hoặc Chương được chọn',
+    'guests_cannot_save_drafts' => 'Khách không thể lưu bản nháp',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'Bạn không thể xóa quản trị viên duy nhất',
+    'users_cannot_delete_guest' => 'Bạn không thể xóa người dùng khách',
+
+    // Roles
+    'role_cannot_be_edited' => 'Không thể chỉnh sửa quyền này',
+    'role_system_cannot_be_deleted' => 'Quyền này là quyền hệ thống và không thể bị xóa',
+    'role_registration_default_cannot_delete' => 'Quyền này không thể bị xóa trong khi đang đặt là quyền mặc định khi đăng ký',
+    'role_cannot_remove_only_admin' => 'Người dùng này là người dùng duy nhất được chỉ định quyền quản trị viên. Gán quyền quản trị viên cho người dùng khác trước khi thử xóa người dùng này.',
+
+    // Comments
+    'comment_list' => 'Đã có lỗi xảy ra khi tải bình luận.',
+    'cannot_add_comment_to_draft' => 'Bạn không thể thêm bình luận vào bản nháp.',
+    'comment_add' => 'Đã xảy ra lỗi khi thêm / sửa bình luận.',
+    'comment_delete' => 'Đã xảy ra lỗi khi xóa bình luận.',
+    'empty_comment' => 'Không thể thêm bình luận bị bỏ trống.',
+
+    // Error pages
+    '404_page_not_found' => 'Không Tìm Thấy Trang',
+    'sorry_page_not_found' => 'Xin lỗi, Không tìm thấy trang bạn đang tìm kiếm.',
+    'sorry_page_not_found_permission_warning' => 'Nếu trang bạn tìm kiếm tồn tại, có thể bạn đang không có quyền truy cập.',
+    'return_home' => 'Quay lại trang chủ',
+    'error_occurred' => 'Đã xảy ra lỗi',
+    'app_down' => ':appName hiện đang ngoại tuyến',
+    'back_soon' => 'Nó sẽ sớm hoạt động trở lại.',
+
+    // API errors
+    'api_no_authorization_found' => 'Không tìm thấy token ủy quyền trong yêu cầu',
+    'api_bad_authorization_format' => 'Đã tìm thấy một token ủy quyền trong yêu cầu nhưng định dạng hiển thị không hợp lệ',
+    'api_user_token_not_found' => 'Không tìm thấy token API nào khớp với token ủy quyền được cung cấp',
+    'api_incorrect_token_secret' => 'Mã bí mật được cung cấp cho token API đang được sử dụng không hợp lệ',
+    'api_user_no_api_permission' => 'Chủ của token API đang sử dụng không có quyền gọi API',
+    'api_user_token_expired' => 'Token sử dụng cho việc ủy quyền đã hết hạn',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Lỗi khi gửi email thử:',
+
+];
diff --git a/resources/lang/vi/pagination.php b/resources/lang/vi/pagination.php
new file mode 100644 (file)
index 0000000..cb3a0a5
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; Trước',
+    'next'     => 'Tiếp &raquo;',
+
+];
diff --git a/resources/lang/vi/passwords.php b/resources/lang/vi/passwords.php
new file mode 100644 (file)
index 0000000..65b42b4
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+    'password' => 'Mật khẩu phải có tối thiểu 8 ký tự và và phải trùng với mật khẩu xác nhận.',
+    'user' => "Chúng tôi không tìm thấy người dùng với địa chỉ email đó.",
+    'token' => 'Mã token đặt lại mật khẩu cho địa chỉ email này không hợp lệ.',
+    'sent' => 'Chúng tôi đã gửi email chứa liên kết đặt lại mật khẩu cho bạn!',
+    'reset' => 'Mật khẩu của bạn đã được đặt lại!',
+
+];
diff --git a/resources/lang/vi/settings.php b/resources/lang/vi/settings.php
new file mode 100644 (file)
index 0000000..67fcb12
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Cài đặt',
+    'settings_save' => 'Lưu Cài đặt',
+    'settings_save_success' => 'Đã lưu cài đặt',
+
+    // App Settings
+    'app_customization' => 'Tuỳ biến',
+    'app_features_security' => 'Chức năng & Bảo mật',
+    'app_name' => 'Tên Ứng dụng',
+    'app_name_desc' => 'Tên này được hiển thị trong header và trong bất kì email hệ thống được gửi.',
+    'app_name_header' => 'Hiển thị tên trong header',
+    'app_public_access' => 'Quyền truy cập công khai',
+    'app_public_access_desc' => 'Bật tùy chọn này sẽ cho phép khách, người không cần đăng nhập, truy cập đến nội dung bản BookStack của bạn.',
+    'app_public_access_desc_guest' => 'Quyền truy cập của khách có thể được điều khiển thông qua người dùng "Guest".',
+    'app_public_access_toggle' => 'Cho phép truy cập công khai',
+    'app_public_viewing' => 'Cho phép xem công khai?',
+    'app_secure_images' => 'Bảo mật tốt hơn cho việc tải lên ảnh',
+    'app_secure_images_toggle' => 'Bật bảo mật tốt hơn cho các ảnh được tải lên',
+    'app_secure_images_desc' => 'Vì lí do hiệu năng, tất cả các ảnh đều được truy cập công khai. Tùy chọn này thêm một chuỗi ngẫu nhiên, khó đoán vào phần liên kết đến ảnh. Đảm bảo rằng tránh việc index thư mục để ngăn chặn việc truy cập đến ảnh một cách dễ dàng.',
+    'app_editor' => 'Soạn thảo Trang',
+    'app_editor_desc' => 'Chọn trình soạn thảo nào sẽ được sử dụng bởi tất cả người dùng để chỉnh sửa trang.',
+    'app_custom_html' => 'Tùy chọn nội dung Head HTML',
+    'app_custom_html_desc' => 'Bất cứ nội dung nào được thêm vào đây sẽ được đưa vào phần cuối của khu vực <head> của mỗi trang. Tiện cho việc ghi đè style hoặc thêm mã phân tích dữ liệu.',
+    'app_custom_html_disabled_notice' => 'Nội dung tùy biến HTML head bị tắt tại trang cài đặt này để đảm bảo mọi thay đổi làm hỏng hệ thống có để được khôi phục.',
+    'app_logo' => 'Logo Ứng dụng',
+    'app_logo_desc' => 'Ảnh này nên có kích thước chiều cao là 43px. <br>Ảnh lớn sẽ được điều chỉnh tỷ lệ xuống.',
+    'app_primary_color' => 'Màu chủ đạo của Ứng dụng',
+    'app_primary_color_desc' => 'Đặt màu chủ đạo của ứng dụng kể cả banner, các nút và các đường dẫn liên kết.',
+    'app_homepage' => 'Trang chủ Ứng dụng',
+    'app_homepage_desc' => 'Chọn hiển thị để hiện tại trang chủ thay cho hiển thị mặc định. Quyền cho trang được bỏ qua cho các trang được chọn.',
+    'app_homepage_select' => 'Chọn một trang',
+    'app_disable_comments' => 'Tắt bình luận',
+    'app_disable_comments_toggle' => 'Tắt bình luận',
+    'app_disable_comments_desc' => 'Tắt các bình luận trên tất cả các trang của ứng dụng. <br> Các bình luận đã tồn tại sẽ không được hiển thị.',
+
+    // Color settings
+    'content_colors' => 'Màu của phần Nội dung',
+    'content_colors_desc' => 'Đặt màu cho tất cả các thành phần trong trang theo sự tổ chức kế thừa. Việc chọn màu sắc với cùng độ sáng với màu mặc định là được khuyến nghị giúp cho việc đọc thuận lợi.',
+    'bookshelf_color' => 'Màu Giá sách',
+    'book_color' => 'Màu Sách',
+    'chapter_color' => 'Màu Chương',
+    'page_color' => 'Màu Trang',
+    'page_draft_color' => 'Màu Trang Nháp',
+
+    // Registration Settings
+    'reg_settings' => 'Đăng ký',
+    'reg_enable' => 'Bật Đăng ký',
+    'reg_enable_toggle' => 'Bật đăng ký',
+    'reg_enable_desc' => 'Khi đăng ký được bật người dùng sẽ có thể tự đăng ký để trở thành người dùng của ứng dụng. Khi đăng kí người dùng sẽ được cấp một quyền sử dụng mặc định.',
+    'reg_default_role' => 'Quyền người dùng sử dụng mặc định sau khi đăng kí',
+    'reg_enable_external_warning' => 'Tùy chọn trên bị bỏ qua khi xác thực từ bên ngoài LDAP hoặc SAML được bật. Tài khoản người dùng chưa phải là thành viên sẽ được tự động tạo nếu xác thực với hệ thống bên ngoài thành công.',
+    'reg_email_confirmation' => 'Xác nhận Email',
+    'reg_email_confirmation_toggle' => 'Yêu cầu xác nhận email',
+    'reg_confirm_email_desc' => 'Nếu giới hạn tên miền được sử dụng, xác nhận email là bắt buộc và tùy chọn này sẽ bị bỏ qua.',
+    'reg_confirm_restrict_domain' => 'Giới hạn tên miền',
+    'reg_confirm_restrict_domain_desc' => 'Điền dấu phẩy ngăn cách danh sách các tên miền email dành cho việc bạn muốn giới hạn đăng nhập. Người dùng sẽ nhận được email xác nhận địa chỉ của họ trước khi được phép tương tác với ứng dụng. <br> Lưu ý rằng người dùng có thể thay đổi địa chỉ email của họ sau khi đăng ký thành công.',
+    'reg_confirm_restrict_domain_placeholder' => 'Không có giới hạn nào được thiết lập',
+
+    // Maintenance settings
+    'maint' => 'Bảo trì',
+    'maint_image_cleanup' => 'Dọn dẹp ảnh',
+    'maint_image_cleanup_desc' => "Quét nội dung trang và phiên bản để kiểm tra xem các ảnh và hình vẽ nào đang được sử dụng và ảnh nào dư thừa. Đảm bảo rằng bạn đã tạo bản sao lưu toàn dữ liệu và ảnh trước khi chạy chức năng này.",
+    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_run' => 'Chạy Dọn dẹp',
+    'maint_image_cleanup_warning' => 'Đã tìm thấy :count ảnh có thể không được sử dụng. Bạn muốn chắc rằng muốn xóa các ảnh này?',
+    'maint_image_cleanup_success' => ':count ảnh có thể không được sử dụng đã được tìm thấy và xóa!',
+    'maint_image_cleanup_nothing_found' => 'Không tìm thấy ảnh nào không được xử dụng, Không có gì để xóa!',
+    'maint_send_test_email' => 'Gửi một email thử',
+    'maint_send_test_email_desc' => 'Chức năng này gửi một email thử đến địa chỉ email bạn chỉ định trong hồ sơ của mình.',
+    'maint_send_test_email_run' => 'Gửi email thử',
+    'maint_send_test_email_success' => 'Email đã được gửi đến :address',
+    'maint_send_test_email_mail_subject' => 'Thử Email',
+    'maint_send_test_email_mail_greeting' => 'Chức năng gửi email có vẻ đã hoạt động!',
+    'maint_send_test_email_mail_text' => 'Chúc mừng! Khi bạn nhận được email thông báo này, cài đặt email của bạn có vẻ đã được cấu hình đúng.',
+    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_open' => 'Mở Thùng Rác',
+
+    // Recycle Bin
+    'recycle_bin' => 'Thùng Rác',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Xóa Bởi',
+    'recycle_bin_deleted_at' => 'Thời điểm Xóa',
+    'recycle_bin_permanently_delete' => 'Xóa Vĩnh viễn',
+    'recycle_bin_restore' => 'Khôi phục',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Dọn dẹp Thùng Rác',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'Người dùng',
+    'audit_table_event' => 'Event',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
+    // Role Settings
+    'roles' => 'Quyền',
+    'role_user_roles' => 'Quyền người dùng',
+    'role_create' => 'Tạo quyền mới',
+    'role_create_success' => 'Quyền mới đã được tạo thành công',
+    'role_delete' => 'Xóa quyền',
+    'role_delete_confirm' => 'Chức năng này sẽ xóa quyền với tên \':roleName\'.',
+    'role_delete_users_assigned' => 'Quyền này có :userCount người dùng được gán. Nếu bạn muốn di dời các người dùng từ quyền này hãy chọn một quyền mới bên dưới.',
+    'role_delete_no_migration' => "Không di dời các người dùng",
+    'role_delete_sure' => 'Bạn có chắc rằng muốn xóa quyền này?',
+    'role_delete_success' => 'Quyền đã được xóa thành công',
+    'role_edit' => 'Sửa quyền',
+    'role_details' => 'Thông tin chi tiết Quyền',
+    'role_name' => 'Tên quyền',
+    'role_desc' => 'Thông tin vắn tắt của Quyền',
+    'role_external_auth_id' => 'Mã của xác thực ngoài',
+    'role_system' => 'Quyền Hệ thống',
+    'role_manage_users' => 'Quản lý người dùng',
+    'role_manage_roles' => 'Quản lý quyền và chức năng quyền',
+    'role_manage_entity_permissions' => 'Quản lý tất cả quyền của các sách, chương & trang',
+    'role_manage_own_entity_permissions' => 'Quản lý quyền trên sách, chương & trang bạn tạo ra',
+    'role_manage_page_templates' => 'Quản lý các mẫu trang',
+    'role_access_api' => 'Truy cập đến API hệ thống',
+    'role_manage_settings' => 'Quản lý cài đặt của ứng dụng',
+    'role_asset' => 'Quyền tài sản (asset)',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => 'Các quyền này điều khiển truy cập mặc định tới tài sản (asset) nằm trong hệ thống. Quyền tại Sách, Chường và Trang se ghi đè các quyền này.',
+    'role_asset_admins' => 'Quản trị viên được tự động cấp quyền truy cập đến toàn bộ nội dung, tuy nhiên các tùy chọn đó có thể hiện hoặc ẩn tùy chọn giao diện.',
+    'role_all' => 'Tất cả',
+    'role_own' => 'Sở hữu',
+    'role_controlled_by_asset' => 'Kiểm soát các tài sản (asset) người dùng tải lên',
+    'role_save' => 'Lưu Quyền',
+    'role_update_success' => 'Quyền đã được cập nhật thành công',
+    'role_users' => 'Người dùng được gán quyền này',
+    'role_users_none' => 'Không có người dùng nào hiện được gán quyền này',
+
+    // Users
+    'users' => 'Người dùng',
+    'user_profile' => 'Hồ sơ người dùng',
+    'users_add_new' => 'Thêm người dùng mới',
+    'users_search' => 'Tìm kiếm người dùng',
+    'users_latest_activity' => 'Latest Activity',
+    'users_details' => 'Chi tiết người dùng',
+    'users_details_desc' => 'Hiển thị tên và địa chỉ email cho người dùng này. Địa chỉ email sẽ được sử dụng để đăng nhập vào ứng dụng.',
+    'users_details_desc_no_email' => 'Đặt tên cho người dùng này để giúp người dùng khác nhận ra họ.',
+    'users_role' => 'Quyền người dùng',
+    'users_role_desc' => 'Chọn quyền mà người dùng sẽ được gán. Nếu người dùng được gán nhiều quyền, các quyền hạn sẽ ghi đè lên nhau và họ sẽ nhận được tất cả các quyền hạn từ quyền được gán.',
+    'users_password' => 'Mật khẩu người dùng',
+    'users_password_desc' => 'Đặt mật khẩu dùng để đăng nhập ứng dụng. Nó phải có độ dài tối thiểu 6 ký tự.',
+    'users_send_invite_text' => 'Bạn có thể chọn để gửi cho người dùng này một email mời, giúp họ có thể tự đặt mật khẩu cho chính họ. Nếu không bạn có thể đặt mật khẩu cho họ.',
+    'users_send_invite_option' => 'Gửi email mời người dùng',
+    'users_external_auth_id' => 'Mã của xác thực ngoài',
+    'users_external_auth_id_desc' => 'Đây là mã được sử dụng để xác thực với người dùng này khi giao tiếp với hệ thống xác thực bên ngoài.',
+    'users_password_warning' => 'Chỉ điền ô bên dưới nếu bạn muốn thay đổi mật khẩu.',
+    'users_system_public' => 'Người dùng này đại diện cho bất kỳ khách nào thăm trang của bạn. Nó được tự động gán và không thể dùng để đăng nhập.',
+    'users_delete' => 'Xóa Người dùng',
+    'users_delete_named' => 'Xóa người dùng :userName',
+    'users_delete_warning' => 'Chức năng này sẽ hoàn toàn xóa người dùng với tên \':userName\' từ hệ thống.',
+    'users_delete_confirm' => 'Bạn có chắc muốn xóa người dùng này không?',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => 'No user selected',
+    'users_delete_success' => 'User successfully removed',
+    'users_edit' => 'Sửa người dùng',
+    'users_edit_profile' => 'Sửa Hồ sơ',
+    'users_edit_success' => 'Người dùng được cập nhật thành công',
+    'users_avatar' => 'Ảnh đại diện',
+    'users_avatar_desc' => 'Chọn ảnh đê đại hiện cho người dùng này. Ảnh nên có kích cỡ hình vuông 256px.',
+    'users_preferred_language' => 'Ngôn ngữ ưu tiên',
+    'users_preferred_language_desc' => 'Tùy chọn này sẽ thay đổi ngôn ngư sử dụng cho giao diện người dùng của ứng dụng. Nó sẽ không ảnh hưởng đến bất cứ nội dung nào người dùng tạo ra.',
+    'users_social_accounts' => 'Tài khoản MXH',
+    'users_social_accounts_info' => 'Bạn có thể kết nối đến các tài khoản khác để đăng nhập nhanh chóng và dễ dàng. Ngắt kết nối đến một tài khoản ở đây không thu hồi việc ủy quyền truy cập trước đó. Thu hồi truy cập của các tài khoản kết nối MXH từ trang cài đặt hồ sở của bạn.',
+    'users_social_connect' => 'Kết nối tài khoản',
+    'users_social_disconnect' => 'Ngắt kết nối tài khoản',
+    'users_social_connected' => 'Tài khoản :socialAccount đã được liên kết với hồ sơ của bạn thành công.',
+    'users_social_disconnected' => 'Tài khoản :socialAccount đã được ngắt kết nối khỏi hồ sơ của bạn thành công.',
+    'users_api_tokens' => 'Các Token API',
+    'users_api_tokens_none' => 'Khong có Token API nào được tạo cho người dùng này',
+    'users_api_tokens_create' => 'Tạo Token',
+    'users_api_tokens_expires' => 'Hết hạn',
+    'users_api_tokens_docs' => 'Tài liệu API',
+
+    // API Tokens
+    'user_api_token_create' => 'Tạo Token API',
+    'user_api_token_name' => 'Tên',
+    'user_api_token_name_desc' => 'Đặt cho token của bạn một tên dễ đọc để nhắc nhở mục đích sử dụng của nó trong tương lai.',
+    'user_api_token_expiry' => 'Ngày hết hạn',
+    'user_api_token_expiry_desc' => 'Đặt một ngày hết hạn cho token này. Sau ngày này, các yêu cầu được tạo khi sử dụng token này sẽ không còn hoạt động. Để trống trường này sẽ đặt ngày hết hạn sau 100 năm tới.',
+    'user_api_token_create_secret_message' => 'Ngay sau khi tạo token này một "Mã Token" & "Mật khẩu Token" sẽ được tạo và hiển thị. Mật khẩu sẽ chỉ được hiện một lần duy nhất nên hãy chắc rằng bạn sao lưu giá trị của nó ở nơi an toàn và bảo mật trước khi tiếp tục.',
+    'user_api_token_create_success' => 'Token API đã được tạo thành công',
+    'user_api_token_update_success' => 'Token API đã được cập nhật thành công',
+    'user_api_token' => 'Token API',
+    'user_api_token_id' => 'Mã Token',
+    'user_api_token_id_desc' => 'Đây là hệ thống sinh ra định danh không thể chỉnh sửa cho token này, thứ mà sẽ cần phải cung cấp khi yêu cầu API.',
+    'user_api_token_secret' => 'Mật khẩu Token',
+    'user_api_token_secret_desc' => 'Đây là mật khẩu được hệ thống tạo ra cho token để phục vụ cho các yêu cầu API này. Nó sẽ chỉ được hiển thị một lần duy nhất nên hãy sao lưu nó vào nơi nào đó an toàn và bảo mật.',
+    'user_api_token_created' => 'Token được tạo :timeAgo',
+    'user_api_token_updated' => 'Token được cập nhật :timeAgo',
+    'user_api_token_delete' => 'Xóa Token',
+    'user_api_token_delete_warning' => 'Chức năng này sẽ hoàn toàn xóa token API với tên \':tokenName\' từ hệ thống.',
+    'user_api_token_delete_confirm' => 'Bạn có chắc rằng muốn xóa token API này?',
+    'user_api_token_delete_success' => 'Token API đã được xóa thành công',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Đan Mạch',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'Slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/vi/validation.php b/resources/lang/vi/validation.php
new file mode 100644 (file)
index 0000000..c002071
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => ':attribute phải được chấp nhận.',
+    'active_url'           => ':attribute không phải là một đường dẫn hợp lệ.',
+    'after'                => ':attribute phải là một ngày sau :date.',
+    'alpha'                => ':attribute chỉ được chứa chữ cái.',
+    'alpha_dash'           => ':attribute chỉ được chứa chữ cái, chữ số, gạch nối và gạch dưới.',
+    'alpha_num'            => ':attribute chỉ được chứa chữ cái hoặc chữ số.',
+    'array'                => ':attribute phải là một mảng.',
+    'before'               => ':attribute phải là một ngày trước :date.',
+    'between'              => [
+        'numeric' => ':attribute phải nằm trong khoảng :min đến :max.',
+        'file'    => ':attribute phải nằm trong khoảng :min đến :max KB.',
+        'string'  => ':attribute phải trong khoảng :min đến :max ký tự.',
+        'array'   => ':attribute phải nằm trong khoảng :min đến :max mục.',
+    ],
+    'boolean'              => 'Trường :attribute phải có giá trị đúng hoặc sai.',
+    'confirmed'            => 'Xác nhận :attribute không khớp.',
+    'date'                 => ':attribute không phải là ngày hợp lệ.',
+    'date_format'          => ':attribute không khớp với định dạng :format.',
+    'different'            => ':attribute và :other phải khác nhau.',
+    'digits'               => ':attribute phải có :digits chữ số.',
+    'digits_between'       => ':attribute phải có từ :min đến :max chữ số.',
+    'email'                => ':attribute phải là địa chỉ email hợp lệ.',
+    'ends_with' => ':attribute phải kết thúc bằng một trong các ký tự: :values',
+    'filled'               => 'Trường :attribute là bắt buộc.',
+    'gt'                   => [
+        'numeric' => ':attribute phải lớn hơn :value.',
+        'file'    => ':attribute phải lớn hơn :value KB.',
+        'string'  => ':attribute phải có nhiều hơn :value ký tự.',
+        'array'   => ':attribute phải có nhiều hơn :value mục.',
+    ],
+    'gte'                  => [
+        'numeric' => ':attribute phải lớn hơn hoặc bằng :value.',
+        'file'    => ':attribute phải lớn hơn hoặc bằng :value KB.',
+        'string'  => ':attribute phải có nhiều hơn hoặc bằng :value ký tự.',
+        'array'   => ':attribute phải có :value mục trở lên.',
+    ],
+    'exists'               => ':attribute đã chọn không hợp lệ.',
+    'image'                => ':attribute phải là ảnh.',
+    'image_extension'      => ':attribute phải có định dạng ảnh hợp lệ và được hỗ trợ.',
+    'in'                   => ':attribute đã chọn không hợp lệ.',
+    'integer'              => ':attribute phải là một số nguyên.',
+    'ip'                   => ':attribute phải là một địa chỉ IP hợp lệ.',
+    'ipv4'                 => ':attribute phải là địa chỉ IPv4 hợp lệ.',
+    'ipv6'                 => ':attribute phải là địa chỉ IPv6 hợp lệ.',
+    'json'                 => ':attribute phải là một chuỗi JSON hợp lệ.',
+    'lt'                   => [
+        'numeric' => ':attribute phải nhỏ hơn :value.',
+        'file'    => ':attribute phải nhỏ hơn :value KB.',
+        'string'  => ':attribute phải có it hơn :value ký tự.',
+        'array'   => ':attribute phải có ít hơn :value mục.',
+    ],
+    'lte'                  => [
+        'numeric' => ':attribute phải nhỏ hơn hoặc bằng :value.',
+        'file'    => ':attribute phải nhỏ hơn hoặc bằng :value KB.',
+        'string'  => ':attribute phải có ít hơn hoặc bằng :value ký tự.',
+        'array'   => ':attribute không được có nhiều hơn :value mục.',
+    ],
+    'max'                  => [
+        'numeric' => ':attribute không được lớn hơn :max.',
+        'file'    => ':attribute không được lớn hơn :max KB.',
+        'string'  => ':attribute không được nhiều hơn :max ký tự.',
+        'array'   => ':attribute không thể có nhiều hơn :max mục.',
+    ],
+    'mimes'                => ':attribute phải là tệp tin có kiểu: :values.',
+    'min'                  => [
+        'numeric' => ':attribute phải tối thiểu là :min.',
+        'file'    => ':attribute phải tối thiểu là :min KB.',
+        'string'  => ':attribute phải có tối thiểu :min ký tự.',
+        'array'   => ':attribute phải có tối thiểu :min mục.',
+    ],
+    'no_double_extension'  => ':attribute chỉ được có một định dạng mở rộng duy nhất.',
+    'not_in'               => ':attribute đã chọn không hợp lệ.',
+    'not_regex'            => 'Định dạng của :attribute không hợp lệ.',
+    'numeric'              => ':attribute phải là một số.',
+    'regex'                => 'Định dạng của :attribute không hợp lệ.',
+    'required'             => 'Trường :attribute là bắt buộc.',
+    'required_if'          => 'Trường :attribute là bắt buộc khi :other là :value.',
+    'required_with'        => 'Trường :attribute là bắt buộc khi :values tồn tại.',
+    'required_with_all'    => 'Trường :attribute là bắt buộc khi :values tồn tại.',
+    'required_without'     => 'Trường :attribute là bắt buộc khi :values không tồn tại.',
+    'required_without_all' => 'Trường :attribute là bắt buộc khi không có bất cứ :values nào tồn tại.',
+    'same'                 => ':attribute và :other phải trùng khớp với nhau.',
+    'safe_url'             => 'Đường dẫn cung cấp có thể không an toàn.',
+    'size'                 => [
+        'numeric' => ':attribute phải có cỡ :size.',
+        'file'    => ':attribute phải có cỡ :size KB.',
+        'string'  => ':attribute phải có :size ký tự.',
+        'array'   => ':attribute phải chứa :size mục.',
+    ],
+    'string'               => ':attribute phải là một chuỗi.',
+    'timezone'             => ':attribute phải là một khu vực hợp lệ.',
+    'unique'               => ':attribute đã có người sử dụng.',
+    'url'                  => 'Định dạng của :attribute không hợp lệ.',
+    'uploaded'             => 'Tệp tin đã không được tải lên. Máy chủ không chấp nhận các tệp tin với dung lượng lớn như tệp tin trên.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Bắt buộc xác nhận mật khẩu',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 676a1dd92db0c0c2556ae858b6a0ada0c236b472..717c7dfdf98a7bbd4b292201a4274bf49cd746ea 100644 (file)
@@ -45,4 +45,5 @@ return [
 
     // Other
     'commented_on'                => '评论',
+    'permissions_update'          => '权限已更新',
 ];
index 62cd0c2432fcd0d531b72b641a4e80bc5fa96f63..a7f8017cb68e81bf4b6b2a05c0c85414a1580e6f 100644 (file)
@@ -26,11 +26,11 @@ return [
     'remember_me' => '记住我',
     'ldap_email_hint' => '请输入用于此帐户的电子邮件。',
     'create_account' => '创建账户',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
+    'already_have_account' => '您已经有账号?',
+    'dont_have_account' => '您还没注册?',
     'social_login' => 'SNS登录',
     'social_registration' => 'SNS注册',
-    'social_registration_text' => '其他服务注册/登录.',
+    'social_registration_text' => '其他服务注册/登录',
 
     'register_thanks' => '注册完成!',
     'register_confirm' => '请点击查收您的Email,并点击确认。',
@@ -43,7 +43,7 @@ return [
     'reset_password' => '重置密码',
     'reset_password_send_instructions' => '在下面输入您的Email地址,您将收到一封带有密码重置链接的邮件。',
     'reset_password_send_button' => '发送重置链接',
-    'reset_password_sent_success' => '密码重置链接已发送到:email。',
+    'reset_password_sent' => '重置密码的链接将通过您的电子邮箱发送:email。',
     'reset_password_success' => '您的密码已成功重置。',
     'email_reset_subject' => '重置您的:appName密码',
     'email_reset_text' => '您收到此电子邮件是因为我们收到了您的帐户的密码重置请求。',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => '重新发送确认Email',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => '您已受邀加入 :appName!',
+    'user_invite_email_greeting' => ' :appName 已为您创建了一个帐户。',
+    'user_invite_email_text' => '点击下面的按钮以设置帐户密码并获得访问权限:',
+    'user_invite_email_action' => '设置帐号密码',
+    'user_invite_page_welcome' => '欢迎来到 :appName!',
+    'user_invite_page_text' => '要完成您的帐户并获得访问权限,您需要设置一个密码,该密码将在以后访问时用于登录 :appName。',
+    'user_invite_page_confirm_button' => '确认密码',
+    'user_invite_success' => '已设置密码,您现在可以访问 :appName!'
 ];
\ No newline at end of file
index 04a826e6fbafe84975e40a622873dcac4836b047..e96edaf1e990881438461ac00ec051a612e898eb 100644 (file)
@@ -11,7 +11,7 @@ return [
     'save' => '保存',
     'continue' => '继续',
     'select' => '选择',
-    'toggle_all' => 'Toggle All',
+    'toggle_all' => '切换全部',
     'more' => '更多',
 
     // Form Labels
@@ -24,7 +24,7 @@ return [
     // Actions
     'actions' => '操作',
     'view' => '浏览',
-    'view_all' => 'View All',
+    'view_all' => '查看全部',
     'create' => '创建',
     'update' => '更新',
     'edit' => '编辑',
@@ -33,20 +33,22 @@ return [
     'copy' => '复制',
     'reply' => '回复',
     'delete' => '删除',
+    'delete_confirm' => '确认删除',
     'search' => '搜索',
     'search_clear' => '清除搜索',
     'reset' => '重置',
     'remove' => '删除',
     'add' => '添加',
+    'fullscreen' => '全屏',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
-    'sort_name' => 'Name',
-    'sort_created_at' => 'Created Date',
-    'sort_updated_at' => 'Updated Date',
+    'sort_options' => '排序选项',
+    'sort_direction_toggle' => '排序方向切换',
+    'sort_ascending' => '升序',
+    'sort_descending' => '降序',
+    'sort_name' => '名称',
+    'sort_created_at' => '创建时间',
+    'sort_updated_at' => '更新时间',
 
     // Misc
     'deleted_user' => '删除用户',
@@ -59,18 +61,20 @@ return [
     'grid_view' => '网格视图',
     'list_view' => '列表视图',
     'default' => '默认',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => '面包屑导航',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => '个人资料',
     'view_profile' => '查看资料',
     'edit_profile' => '编辑资料',
+    'dark_mode' => '夜间模式',
+    'light_mode' => '日间模式',
 
     // Layout tabs
-    'tab_info' => 'Info',
-    'tab_content' => 'Content',
+    'tab_info' => '信息',
+    'tab_content' => '内容',
 
     // Email Content
     'email_action_help' => '如果您无法点击“:actionText”按钮,请将下面的网址复制到您的浏览器中打开:',
-    'email_rights' => 'All rights reserved',
+    'email_rights' => '版权所有',
 ];
index 54d0fb085731550136fa218feb6628def6d11e5a..ab0b7cb469ec5a25179e1b26902cf47fd2401aa2 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => '显示更多',
     'image_image_name' => '图片名称',
     'image_delete_used' => '该图像用于以下页面。',
-    'image_delete_confirm' => '如果你想删除它,请再次按下按钮。',
+    'image_delete_confirm_text' => '您确认要删除此图片吗?',
     'image_select_image' => '选择图片',
     'image_dropzone' => '拖放图片或点击此处上传',
     'images_deleted' => '图片已删除',
@@ -29,5 +29,6 @@ return [
     'code_editor' => '编辑代码',
     'code_language' => '编程语言',
     'code_content' => '代码内容',
+    'code_session_history' => '会话历史',
     'code_save' => '保存代码',
 ];
index 04e8e25bc427835f07a2e4fa2550dc6ea3d5318f..986476119fd863bfdf17e847b8310ba78593b460 100644 (file)
@@ -8,10 +8,10 @@ return [
     // Shared
     'recently_created' => '最近创建',
     'recently_created_pages' => '最近创建的页面',
-    'recently_updated_pages' => '最页面',
+    'recently_updated_pages' => '最近更新的页面',
     'recently_created_chapters' => '最近创建的章节',
     'recently_created_books' => '最近创建的图书',
-    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_created_shelves' => '最近创建的书架',
     'recently_update' => '最近更新',
     'recently_viewed' => '最近查看',
     'recent_activity' => '近期活动',
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => '由 :user 创建于 :timeLength',
     'meta_updated' => '更新于 :timeLength',
     'meta_updated_name' => '由 :user 更新于 :timeLength',
+    'meta_owned_name' => '拥有者 :user',
     'entity_select' => '实体选择',
     'images' => '图片',
     'my_recent_drafts' => '我最近的草稿',
@@ -39,6 +40,7 @@ return [
     'permissions_intro' => '本设置优先于每个用户角色本身所具有的权限。',
     'permissions_enable' => '启用自定义权限',
     'permissions_save' => '保存权限',
+    'permissions_owner' => '拥有者',
 
     // Search
     'search_results' => '搜索结果',
@@ -47,7 +49,8 @@ return [
     'search_no_pages' => '没有找到相匹配的页面',
     'search_for_term' => '“:term”的搜索结果',
     'search_more' => '更多结果',
-    'search_filters' => '过滤搜索结果',
+    'search_advanced' => '高级搜索',
+    'search_terms' => '搜索关键词',
     'search_content_type' => '种类',
     'search_exact_matches' => '精确匹配',
     'search_tags' => '标签搜索',
@@ -68,13 +71,13 @@ return [
     // Shelves
     'shelf' => '书架',
     'shelves' => '书架',
-    'x_shelves' => ':count Shelf|:count Shelves',
+    'x_shelves' => ':count 书架|:count 书架',
     'shelves_long' => '书架',
     'shelves_empty' => '当前未创建书架',
     'shelves_create' => '创建新书架',
     'shelves_popular' => '热门书架',
     'shelves_new' => '新书架',
-    'shelves_new_action' => 'New Shelf',
+    'shelves_new_action' => '新书架',
     'shelves_popular_empty' => '最热门的书架',
     'shelves_new_empty' => '最新创建的书架',
     'shelves_save' => '保存书架',
@@ -105,7 +108,7 @@ return [
     'books_popular' => '热门图书',
     'books_recent' => '最近的书',
     'books_new' => '新书',
-    'books_new_action' => 'New Book',
+    'books_new_action' => '新书',
     'books_popular_empty' => '最受欢迎的图书将出现在这里。',
     'books_new_empty' => '最近创建的图书将出现在这里。',
     'books_create' => '创建图书',
@@ -128,11 +131,11 @@ return [
     'books_navigation' => '图书导航',
     'books_sort' => '排序图书内容',
     'books_sort_named' => '排序图书「:bookName」',
-    'books_sort_name' => 'Sort by Name',
-    'books_sort_created' => 'Sort by Created Date',
-    'books_sort_updated' => 'Sort by Updated Date',
-    'books_sort_chapters_first' => 'Chapters First',
-    'books_sort_chapters_last' => 'Chapters Last',
+    'books_sort_name' => '按名称排序',
+    'books_sort_created' => '创建时间排序',
+    'books_sort_updated' => '按更新时间排序',
+    'books_sort_chapters_first' => '章节正序',
+    'books_sort_chapters_last' => '章节倒序',
     'books_sort_show_other' => '显示其他图书',
     'books_sort_save' => '保存新顺序',
 
@@ -145,7 +148,7 @@ return [
     'chapters_create' => '创建章节',
     'chapters_delete' => '删除章节',
     'chapters_delete_named' => '删除章节「:chapterName」',
-    'chapters_delete_explain' => '这将删除章节「:chapterName」。所有的页面将被删除并添加到其所在的书籍。',
+    'chapters_delete_explain' => '这将删除名为“:chapterName”的章节。本章节中存在的所有页面也将被删除。',
     'chapters_delete_confirm' => '您确定要删除此章节吗?',
     'chapters_edit' => '编辑章节',
     'chapters_edit_named' => '编辑章节「:chapterName」',
@@ -176,7 +179,7 @@ return [
     'pages_delete_confirm' => '您确定要删除此页面吗?',
     'pages_delete_draft_confirm' => '您确定要删除此草稿页面吗?',
     'pages_editing_named' => '正在编辑页面“:pageName”',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => '草稿选项',
     'pages_edit_save_draft' => '保存草稿',
     'pages_edit_draft' => '编辑页面草稿',
     'pages_editing_draft' => '正在编辑草稿',
@@ -207,11 +210,12 @@ return [
     'pages_revisions' => '页面修订',
     'pages_revisions_named' => '“:pageName”页面修订',
     'pages_revision_named' => '“:pageName”页面修订',
+    'pages_revision_restored_from' => '从 #:id; :summary 恢复',
     'pages_revisions_created_by' => '创建者',
     'pages_revisions_date' => '修订日期',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_numbered' => '修订 #:id',
+    'pages_revisions_numbered_changes' => '修改 #:id ',
     'pages_revisions_changelog' => '更新说明',
     'pages_revisions_changes' => '说明',
     'pages_revisions_current' => '当前版本',
@@ -234,7 +238,7 @@ return [
     ],
     'pages_draft_discarded' => '草稿已丢弃,编辑器已更新到当前页面内容。',
     'pages_specific' => '具体页面',
-    'pages_is_template' => 'Page Template',
+    'pages_is_template' => '页面模板',
 
     // Editor Sidebar
     'page_tags' => '页面标签',
@@ -243,19 +247,19 @@ return [
     'shelf_tags' => '书架标签',
     'tag' => '标签',
     'tags' =>  '标签',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  '标签名称',
     'tag_value' => '标签值 (Optional)',
     'tags_explain' => "添加一些标签以更好地对您的内容进行分类。\n您可以为标签分配一个值,以进行更深入的组织。",
     'tags_add' => '添加另一个标签',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => '删除此标签',
     'attachments' => '附件',
     'attachments_explain' => '上传一些文件或附加一些链接显示在您的网页上。这些在页面的侧边栏中可见。',
-    'attachments_explain_instant_save' => '这里的更改将立即保存。Changes here are saved instantly.',
+    'attachments_explain_instant_save' => '这里的更改将立即保存。',
     'attachments_items' => '附加项目',
     'attachments_upload' => '上传文件',
     'attachments_link' => '附加链接',
     'attachments_set_link' => '设置链接',
-    'attachments_delete_confirm' => '确认您想要删除此附件后,请点击删除。',
+    'attachments_delete' => '您确定要删除此附件吗?',
     'attachments_dropzone' => '删除文件或点击此处添加文件',
     'attachments_no_files' => '尚未上传文件',
     'attachments_explain_link' => '如果您不想上传文件,则可以附加链接,这可以是指向其他页面的链接,也可以是指向云端文件的链接。',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => '链接到文件',
     'attachments_link_url_hint' => '网站或文件的网址',
     'attach' => '附加',
+    'attachments_insert_link' => '将附加链接添加到页面',
     'attachments_edit_file' => '编辑文件',
     'attachments_edit_file_name' => '文件名',
     'attachments_edit_drop_upload' => '删除文件或点击这里上传并覆盖',
@@ -273,12 +278,12 @@ return [
     'attachments_file_uploaded' => '附件上传成功',
     'attachments_file_updated' => '附件更新成功',
     'attachments_link_attached' => '链接成功附加到页面',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => '模板',
+    'templates_set_as_template' => '设置为模板',
+    'templates_explain_set_as_template' => '您可以将此页面设置为模板,以便在创建其他页面时利用其内容。 如果其他用户对此页面具有查看权限,则将可以使用此模板。',
+    'templates_replace_content' => '替换页面内容',
+    'templates_append_content' => '附加到页面内容',
+    'templates_prepend_content' => '追加到页面内容',
 
     // Profile View
     'profile_user_for_x' => '来这里:time了',
@@ -286,7 +291,7 @@ return [
     'profile_not_created_pages' => ':userName尚未创建任何页面',
     'profile_not_created_chapters' => ':userName尚未创建任何章节',
     'profile_not_created_books' => ':userName尚未创建任何图书',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_not_created_shelves' => ':userName 尚未创建任何书架',
 
     // Comments
     'comment' => '评论',
@@ -303,12 +308,12 @@ return [
     'comment_deleted_success' => '评论已删除',
     'comment_created_success' => '评论已添加',
     'comment_updated_success' => '评论已更新',
-    'comment_delete_confirm' => '确定要删除这条评论?',
+    'comment_delete_confirm' => '确定要删除这条评论?',
     'comment_in_reply_to' => '回复 :commentId',
 
     // Revision
     'revision_delete_confirm' => '您确定要删除此修订版吗?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_restore_confirm' => '您确定要恢复到此修订版吗?恢复后原有内容将会被替换。',
     'revision_delete_success' => '修订删除',
     'revision_cannot_delete_latest' => '无法删除最新版本。'
 ];
\ No newline at end of file
index f500a58dc6bfba41c6111c1c881d71997220fd61..b41e21ac3e83b9858edece28d99428dfa581f2b8 100644 (file)
@@ -13,10 +13,16 @@ return [
     'email_already_confirmed' => 'Email已被确认,请尝试登录。',
     'email_confirmation_invalid' => '此确认令牌无效或已被使用,请重新注册。',
     'email_confirmation_expired' => '确认令牌已过期,已发送新的确认电子邮件。',
+    'email_confirmation_awaiting' => '需要认证账户的电子邮箱地址',
     'ldap_fail_anonymous' => '使用匿名绑定的LDAP访问失败。',
     'ldap_fail_authed' => '带有标识名称和密码的LDAP访问失败。',
     'ldap_extension_not_installed' => '未安装LDAP PHP扩展程序',
     'ldap_cannot_connect' => '无法连接到ldap服务器,初始连接失败',
+    'saml_already_logged_in' => '您已经登陆了',
+    'saml_user_not_registered' => '用户 :name 未注册且自动注册功能已被禁用',
+    'saml_no_email_address' => '无法找到有效Email地址,此用户数据由外部身份验证系统托管',
+    'saml_invalid_response_id' => '来自外部身份验证系统的请求没有被本应用程序认证,在登录后返回上一页可能会导致此问题。',
+    'saml_fail_authed' => '使用 :system 登录失败,登录系统未返回成功登录授权信息。',
     'social_no_action_defined' => '没有定义行为',
     'social_login_bad_response' => "在 :socialAccount 登录时遇到错误:\n:error",
     'social_account_in_use' => ':socialAccount 账户已被使用,请尝试通过 :socialAccount 选项登录。',
@@ -27,20 +33,19 @@ return [
     'social_account_register_instructions' => '如果您还没有帐户,您可以使用 :socialAccount 选项注册账户。',
     'social_driver_not_found' => '未找到社交驱动程序',
     'social_driver_not_configured' => '您的:socialAccount社交设置不正确。',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => '此邀请链接已过期。 您可以尝试重置您的帐户密码。',
 
     // System
     'path_not_writable' => '无法上传到文件路径“:filePath”,请确保它可写入服务器。',
     'cannot_get_image_from_url' => '无法从 :url 中获取图片',
     'cannot_create_thumbs' => '服务器无法创建缩略图,请检查您是否安装了GD PHP扩展。',
     'server_upload_limit' => '服务器不允许上传此大小的文件。 请尝试较小的文件。',
-    'uploaded'  => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'uploaded'  => '服务器不允许上传此大小的文件。 请尝试较小的文件。',
     'image_upload_error' => '上传图片时发生错误',
     'image_upload_type_error' => '上传的图像类型无效',
     'file_upload_timeout' => '文件上传已超时。',
 
     // Attachments
-    'attachment_page_mismatch' => '附件更新期间的页面不匹配',
     'attachment_not_found' => '找不到附件',
 
     // Pages
@@ -65,7 +70,7 @@ return [
     'role_cannot_be_edited' => '无法编辑该角色',
     'role_system_cannot_be_deleted' => '无法删除系统角色',
     'role_registration_default_cannot_delete' => '无法删除设置为默认注册的角色',
-    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+    'role_cannot_remove_only_admin' => '该用户是分配给管理员角色的唯一用户。 在尝试在此处删除管理员角色之前,请将其分配给其他用户。',
 
     // Comments
     'comment_list' => '提取评论时出现错误。',
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => '无法找到页面',
     'sorry_page_not_found' => '对不起,无法找到您想访问的页面。',
+    'sorry_page_not_found_permission_warning' => '您可能没有查看权限。',
     'return_home' => '返回主页',
     'error_occurred' => '出现错误',
     'app_down' => ':appName现在正在关闭',
     'back_soon' => '请耐心等待网站的恢复。',
 
+    // API errors
+    'api_no_authorization_found' => '未在请求中找到授权令牌',
+    'api_bad_authorization_format' => '已在请求中找到授权令牌,但格式貌似不正确',
+    'api_user_token_not_found' => '未能找到所匹配的API提供的授权令牌',
+    'api_incorrect_token_secret' => '给已给出的API所提供的密钥不正确',
+    'api_user_no_api_permission' => '使用过的 API 令牌的所有者没有进行API 调用的权限',
+    'api_user_token_expired' => '所使用的身份令牌已过期',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => '发送测试电子邮件时出现错误:',
+
 ];
index afdad84238a5df72b4a4d2865a66e28598374ca3..8d8272ee01fd9f774e2b30c4cf2a5f35a2677405 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => '密码必须至少包含六个字符并与确认相符。',
     'user' => "使用该Email地址的用户不存在。",
-    'token' => '此密码重置令牌无效。',
+    'token' => '重置密码链接无法发送至此邮件地址。',
     'sent' => '我们已经通过Email发送您的密码重置链接!',
     'reset' => '您的密码已被重置!',
 
index bc489376c21a67624ddeaf863167e18a1aad42d0..5d58ace2c6c9a33d26313934fbda4a45f4fd4b08 100755 (executable)
@@ -12,43 +12,53 @@ return [
     'settings_save_success' => '设置已保存',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
-    'app_name' => 'App名',
+    'app_customization' => '定制',
+    'app_features_security' => '功能与安全',
+    'app_name' => '站点名称',
     'app_name_desc' => '此名称将在网页头部和Email中显示。',
     'app_name_header' => '在网页头部显示应用名?',
-    'app_public_access' => 'Public Access',
-    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
-    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
-    'app_public_access_toggle' => 'Allow public access',
+    'app_public_access' => '访问权限',
+    'app_public_access_desc' => '启用此选项将允许未登录的用户访问站点内容。',
+    'app_public_access_desc_guest' => '可以通过“访客”用户来控制公共访问者的访问。',
+    'app_public_access_toggle' => '允许公众访问',
     'app_public_viewing' => '允许公众查看?',
     'app_secure_images' => '启用更高安全性的图片上传?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_toggle' => '启用更高安全性的图片上传',
     'app_secure_images_desc' => '出于性能原因,所有图像都是公开的。这个选项会在图像的网址前添加一个随机的,难以猜测的字符串,从而使直接访问变得困难。',
     'app_editor' => '页面编辑器',
     'app_editor_desc' => '选择所有用户将使用哪个编辑器来编辑页面。',
     'app_custom_html' => '自定义HTML头部内容',
     'app_custom_html_desc' => '此处添加的任何内容都将插入到每个页面的<head>部分的底部,这对于覆盖样式或添加分析代码很方便。',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
-    'app_logo' => 'App Logo',
+    'app_custom_html_disabled_notice' => '在此设置页面上禁用了自定义HTML标题内容,以确保可以恢复所有重大更改。',
+    'app_logo' => '站点Logo',
     'app_logo_desc' => '这个图片的高度应该为43px。<br>大图片将会被缩小。',
-    'app_primary_color' => 'App主色',
+    'app_primary_color' => '站点主色',
     'app_primary_color_desc' => '这应该是一个十六进制值。<br>保留为空以重置为默认颜色。',
-    'app_homepage' => 'App主页',
+    'app_homepage' => '站点主页',
     'app_homepage_desc' => '选择要在主页上显示的页面来替换默认的视图,选定页面的访问权限将被忽略。',
-    'app_homepage_select' => 'Select a page',
+    'app_homepage_select' => '选择一个页面',
     'app_disable_comments' => '禁用评论',
-    'app_disable_comments_toggle' => 'Disable comments',
-    'app_disable_comments_desc' => '在App的所有页面上禁用评论,现有评论也不会显示出来。',
+    'app_disable_comments_toggle' => '禁用评论',
+    'app_disable_comments_desc' => '在站点的所有页面上禁用评论,现有评论也不会显示出来。',
+
+    // Color settings
+    'content_colors' => '内容颜色',
+    'content_colors_desc' => '设置页面组织层次中所有元素的颜色。建议选择与默认颜色相似的亮度的颜色。',
+    'bookshelf_color' => '书架颜色',
+    'book_color' => '图书颜色',
+    'chapter_color' => '章节颜色',
+    'page_color' => '页面颜色',
+    'page_draft_color' => '页面草稿颜色',
 
     // Registration Settings
     'reg_settings' => '注册设置',
-    'reg_enable' => 'Enable Registration',
-    'reg_enable_toggle' => 'Enable registration',
-    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_enable' => '启用注册',
+    'reg_enable_toggle' => '启用注册',
+    'reg_enable_desc' => '启用注册后,用户将可以自己注册为站点用户。 注册后,他们将获得一个默认的单一用户角色。',
     'reg_default_role' => '注册后的默认用户角色',
-    'reg_email_confirmation' => 'Email Confirmation',
-    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_enable_external_warning' => '当启用外部LDAP或者SAML认证时,上面的选项会被忽略。当使用外部系统认证认证成功时,将自动创建非现有会员的用户账户。',
+    'reg_email_confirmation' => '邮件确认',
+    'reg_email_confirmation_toggle' => '需要电子邮件确认',
     'reg_confirm_email_desc' => '如果使用域名限制,则需要Email验证,并且该值将被忽略。',
     'reg_confirm_restrict_domain' => '域名限制',
     'reg_confirm_restrict_domain_desc' => '输入您想要限制注册的Email域名列表,用逗号隔开。在被允许与应用程序交互之前,用户将被发送一封Email来确认他们的地址。<br>注意用户在注册成功后可以修改他们的Email地址。',
@@ -58,11 +68,53 @@ return [
     'maint' => '维护',
     'maint_image_cleanup' => '清理图像',
     'maint_image_cleanup_desc' => "扫描页面和修订内容以检查哪些图像是正在使用的以及哪些图像是多余的。确保在运行前创建完整的数据库和映像备份。",
-    'maint_image_cleanup_ignore_revisions' => '忽略修订记录中的图像',
+    'maint_delete_images_only_in_revisions' => '同时删除只存在于旧页面版本中的图片',
     'maint_image_cleanup_run' => '运行清理',
     'maint_image_cleanup_warning' => '发现了 :count 张可能未使用的图像。您确定要删除这些图像吗?',
     'maint_image_cleanup_success' => '找到并删除了 :count 张可能未使用的图像!',
     'maint_image_cleanup_nothing_found' => '找不到未使用的图像,没有删除!',
+    'maint_send_test_email' => '发送测试电子邮件',
+    'maint_send_test_email_desc' => '这将发送测试邮件到您的个人资料中指定的电子邮件地址。',
+    'maint_send_test_email_run' => '发送测试邮件',
+    'maint_send_test_email_success' => '电子邮件已发送至 :address',
+    'maint_send_test_email_mail_subject' => '测试电子邮件',
+    'maint_send_test_email_mail_greeting' => '邮件发送功能看起来工作正常!',
+    'maint_send_test_email_mail_text' => '恭喜!您收到了此邮件通知,您的电子邮件设置看起来已配置正确。',
+    'maint_recycle_bin_desc' => '被删除的书架、书籍、章节和页面会被存入回收站,您可以还原或永久删除它们。回收站中较旧的项目可能会在系统设置的一段时间后被自动删除。',
+    'maint_recycle_bin_open' => '打开回收站',
+
+    // Recycle Bin
+    'recycle_bin' => '回收站',
+    'recycle_bin_desc' => '在这里,您可以还原已删除的项目,或选择将其从系统中永久删除。与系统中过滤过的类似的活动记录不同,这个表会显示所有操作。',
+    'recycle_bin_deleted_item' => '被删除的项目',
+    'recycle_bin_deleted_by' => '删除者',
+    'recycle_bin_deleted_at' => '删除时间',
+    'recycle_bin_permanently_delete' => '永久删除',
+    'recycle_bin_restore' => '恢复',
+    'recycle_bin_contents_empty' => '回收站当前为空',
+    'recycle_bin_empty' => '清空回收站',
+    'recycle_bin_empty_confirm' => '这将永久性销毁回收站中的所有项目(包括每个项目中包含的内容,例如图片)。您确定要清空回收站吗?',
+    'recycle_bin_destroy_confirm' => '此操作将从系统中永久删除此项目以及下面列出的所有子元素,并且您将无法还原此内容。您确定要永久删除该项目吗?',
+    'recycle_bin_destroy_list' => '要销毁的项目',
+    'recycle_bin_restore_list' => '要恢复的项目',
+    'recycle_bin_restore_confirm' => '此操作会将已删除的项目及其所有子元素恢复到原始位置。如果项目的原始位置已被删除,并且现在位于回收站中,则要恢复项目的上级项目也需要恢复。',
+    'recycle_bin_restore_deleted_parent' => '该项目的上级项目也已被删除。这些项目将保持被删除状态,直到上级项目被恢复。',
+    'recycle_bin_destroy_notification' => '从回收站中删除了 :count 个项目。',
+    'recycle_bin_restore_notification' => '从回收站中恢复了 :count 个项目。',
+
+    // Audit Log
+    'audit' => '审核日志',
+    'audit_desc' => '这份审核日志显示所有被系统跟踪的活动。与系统中过滤过的类似的活动记录不同,这个表会显示所有操作。',
+    'audit_event_filter' => '事件过滤器',
+    'audit_event_filter_no_filter' => '无过滤器',
+    'audit_deleted_item' => '被删除的项目',
+    'audit_deleted_item_name' => '名称: :name',
+    'audit_table_user' => '用户',
+    'audit_table_event' => '事件',
+    'audit_table_related' => '相关项目或详细信息',
+    'audit_table_date' => '活动日期',
+    'audit_date_from' => '日期范围从',
+    'audit_date_to' => '日期范围至',
 
     // Role Settings
     'roles' => '角色',
@@ -85,9 +137,11 @@ return [
     'role_manage_roles' => '管理角色与角色权限',
     'role_manage_entity_permissions' => '管理所有图书,章节和页面的权限',
     'role_manage_own_entity_permissions' => '管理自己的图书,章节和页面的权限',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => '管理页面模板',
+    'role_access_api' => '访问系统 API',
     'role_manage_settings' => '管理App设置',
     'role_asset' => '资源许可',
+    'roles_system_warning' => '请注意,具有上述三个权限中的任何一个都可以允许用户更改自己的特权或系统中其他人的特权。 只将具有这些权限的角色分配给受信任的用户。',
     'role_asset_desc' => '对系统内资源的默认访问许可将由这些权限控制。单独设置在书籍,章节和页面上的权限将覆盖这里的权限设定。',
     'role_asset_admins' => '管理员可自动获得对所有内容的访问权限,但这些选项可能会显示或隐藏UI选项。',
     'role_all' => '全部的',
@@ -103,37 +157,67 @@ return [
     'user_profile' => '用户资料',
     'users_add_new' => '添加用户',
     'users_search' => '搜索用户',
-    'users_details' => 'User Details',
-    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
-    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_latest_activity' => '最新活动',
+    'users_details' => '用户详细资料',
+    'users_details_desc' => '设置该用户的显示名称和电子邮件地址。 该电子邮件地址将用于登录本站。',
+    'users_details_desc_no_email' => '设置此用户的昵称,以便其他人识别。',
     'users_role' => '用户角色',
-    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
-    'users_password' => 'User Password',
-    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
+    'users_role_desc' => '选择将分配给该用户的角色。 如果将一个用户分配给多个角色,则这些角色的权限将堆叠在一起,并且他们将获得分配的角色的所有功能。',
+    'users_password' => '用户密码',
+    'users_password_desc' => '设置用于登录应用程序的密码。 该长度必须至少为6个字符。',
+    'users_send_invite_text' => '您可以向该用户发送邀请电子邮件,允许他们设置自己的密码,否则,您可以自己设置他们的密码。',
+    'users_send_invite_option' => '发送邀请用户电子邮件',
     'users_external_auth_id' => '外部身份认证ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+    'users_external_auth_id_desc' => '这是用于与您的外部身份验证系统通信时匹配此用户的ID。',
     'users_password_warning' => '如果您想更改密码,请填写以下内容:',
     'users_system_public' => '此用户代表访问您的App的任何访客。它不能用于登录,而是自动分配。',
     'users_delete' => '删除用户',
     'users_delete_named' => '删除用户 :userName',
     'users_delete_warning' => '这将从系统中完全删除名为 \':userName\' 的用户。',
     'users_delete_confirm' => '您确定要删除这个用户?',
-    'users_delete_success' => '用户删除成功。',
+    'users_migrate_ownership' => '迁移拥有权',
+    'users_migrate_ownership_desc' => '如果您想要当前用户拥有的全部项目转移到另一个用户(更改拥有者),请在此处选择一个用户。',
+    'users_none_selected' => '没有选中用户',
+    'users_delete_success' => '已成功移除用户',
     'users_edit' => '编辑用户',
     'users_edit_profile' => '编辑资料',
     'users_edit_success' => '用户更新成功',
     'users_avatar' => '用户头像',
     'users_avatar_desc' => '当前图片应该为约256px的正方形。',
     'users_preferred_language' => '语言',
-    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_preferred_language_desc' => '此选项将更改用于应用程序用户界面的语言。 这不会影响任何用户创建的内容。',
     'users_social_accounts' => '社交账户',
     'users_social_accounts_info' => '在这里,您可以绑定您的其他帐户,以便更快更轻松地登录。如果您选择解除绑定,之后将不能通过此社交账户登录,请设置社交账户来取消本App的访问权限。',
     'users_social_connect' => '绑定账户',
     'users_social_disconnect' => '解除绑定账户',
     'users_social_connected' => ':socialAccount 账户已经成功绑定到您的资料。',
     'users_social_disconnected' => ':socialAccount 账户已经成功解除绑定。',
+    'users_api_tokens' => 'API令牌',
+    'users_api_tokens_none' => '没有创建任何API令牌给此用户',
+    'users_api_tokens_create' => '创建令牌',
+    'users_api_tokens_expires' => '过期',
+    'users_api_tokens_docs' => 'API文档',
+
+    // API Tokens
+    'user_api_token_create' => '创建 API 令牌',
+    'user_api_token_name' => '姓名',
+    'user_api_token_name_desc' => '请给您的可读令牌一个命名以在未来提醒您它的预期用途',
+    'user_api_token_expiry' => '过期期限',
+    'user_api_token_expiry_desc' => '请设置一个此令牌的过期时间,过期后此令牌所给出的请求将失效,若将此处留为空白将自动设置过期时间为100年。',
+    'user_api_token_create_secret_message' => '创建此令牌后会立即生成“令牌ID”和“令牌密钥”。该密钥只会显示一次,所以请确保在继续操作之前将密钥记录或复制到一个安全的地方。',
+    'user_api_token_create_success' => '成功创建API令牌。',
+    'user_api_token_update_success' => '成功更新API令牌。',
+    'user_api_token' => 'API令牌',
+    'user_api_token_id' => '令牌ID',
+    'user_api_token_id_desc' => '这是系统生成的一个不可编辑的令牌标识符,需要在API请求中才能提供。',
+    'user_api_token_secret' => '令牌密钥',
+    'user_api_token_secret_desc' => '这是此令牌系统生成的密钥,需要在API请求中才可以提供。 这只会显示一次,因此请将其复制到安全的地方。',
+    'user_api_token_created' => '创建的令牌:timeAgo',
+    'user_api_token_updated' => '令牌更新:timeAgo',
+    'user_api_token_delete' => '删除令牌',
+    'user_api_token_delete_warning' => '这将会从系统中完全删除名为“令牌命名”的API令牌',
+    'user_api_token_delete_confirm' => '您确定要删除此API令牌吗?',
+    'user_api_token_delete_success' => '成功删除API令牌',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => '保加利亚语',
+        'cs' => 'Česky',
+        'da' => '丹麦',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => '挪威语 (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index e328f6c38714aa3ae93c9bbd077ff306c9a9b37c..8bb8a207a00099893bf52e37d37ab3ffc4da83b9 100644 (file)
@@ -30,40 +30,40 @@ return [
     'digits'               => ':attribute 必须为:digits位数。',
     'digits_between'       => ':attribute 必须为:min到:max位数。',
     'email'                => ':attribute 必须是有效的电子邮件地址。',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ' :attribute 必须以 :values 后缀结尾',
     'filled'               => ':attribute 字段是必需的。',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute必须大于 :value.',
+        'file'    => ':attribute 必须大于 :value k',
+        'string'  => ':attribute 必须大于 :value 字符。',
+        'array'   => ':attribute 必须包含多个 :value 项目。',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => ':attribute 必须大于或等于 :value.',
+        'file'    => ':attribute 必须大于或等于 :value k。',
+        'string'  => ':attribute 必须大于或等于 :value 字符。',
+        'array'   => ':attribute 必须具有 :value 项或更多',
     ],
     'exists'               => '选中的 :attribute 无效。',
     'image'                => ':attribute 必须是一个图片。',
-    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'image_extension'      => ':attribute 必须具有有效且受支持的图像扩展名。',
     'in'                   => '选中的 :attribute 无效。',
     'integer'              => ':attribute 必须是一个整数。',
     'ip'                   => ':attribute 必须是一个有效的IP地址。',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ipv4'                 => ':attribute 必须是有效的IPv4地址。',
+    'ipv6'                 => ':attribute必须是有效的IPv6地址。',
+    'json'                 => ':attribute 必须是JSON类型.',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute 必须小于 :value.',
+        'file'    => ':attribute 必须小于 :value k。',
+        'string'  => ':attribute 必须小于 :value 字符。',
+        'array'   => ':attribute 必须小于 :value 项.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute 必须小于或等于 :value.',
+        'file'    => ':attribute 必须小于或等于 :value k。',
+        'string'  => ':attribute 必须小于或等于 :value 字符。',
+        'array'   => ':attribute 不得超过 :value 项。',
     ],
     'max'                  => [
         'numeric' => ':attribute 不能超过:max。',
@@ -78,9 +78,9 @@ return [
         'string'  => ':attribute 至少为:min个字符。',
         'array'   => ':attribute 至少有:min项。',
     ],
-    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'no_double_extension'  => ':attribute 必须具有一个扩展名。',
     'not_in'               => '选中的 :attribute 无效。',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => ':attribute 格式错误。',
     'numeric'              => ':attribute 必须是一个数。',
     'regex'                => ':attribute 格式无效。',
     'required'             => ':attribute 字段是必需的。',
@@ -90,6 +90,7 @@ return [
     'required_without'     => '当:values不存在时,:attribute 字段是必需的。',
     'required_without_all' => '当:values均不存在时,:attribute 字段是必需的。',
     'same'                 => ':attribute 与 :other 必须匹配。',
+    'safe_url'             => '提供的链接可能不安全。',
     'size'                 => [
         'numeric' => ':attribute 必须为:size。',
         'file'    => ':attribute 必须为:size KB。',
@@ -100,7 +101,7 @@ return [
     'timezone'             => ':attribute 必须是有效的区域。',
     'unique'               => ':attribute 已经被使用。',
     'url'                  => ':attribute 格式无效。',
-    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+    'uploaded'             => '无法上传文件。 服务器可能不接受此大小的文件。',
 
     // Custom validation lines
     'custom' => [
index 5cf2bd3cf01c6ccd7d1e1994049b3085a7c661e3..dedcdc0cf0cd469a6ea977b2d7f53c400e3527cc 100644 (file)
@@ -6,43 +6,44 @@
 return [
 
     // Pages
-    'page_create'                 => '建ç«\8bäº\86頁面',
+    'page_create'                 => '已建ç«\8b頁面',
     'page_create_notification'    => '頁面已建立成功',
-    'page_update'                 => '更新了頁面',
+    'page_update'                 => '已更新頁面',
     'page_update_notification'    => '頁面已更新成功',
-    'page_delete'                 => 'å\88ªé\99¤äº\86頁面',
+    'page_delete'                 => 'å·²å\88ªé\99¤頁面',
     'page_delete_notification'    => '頁面已刪除成功',
-    'page_restore'                => '恢複了頁面',
-    'page_restore_notification'   => '頁面已恢複成功',
+    'page_restore'                => '已還原頁面',
+    'page_restore_notification'   => '頁面已還原成功',
     'page_move'                   => '移動了頁面',
 
     // Chapters
-    'chapter_create'              => '建ç«\8bäº\86章節',
+    'chapter_create'              => '已建ç«\8b章節',
     'chapter_create_notification' => '章節已建立成功',
-    'chapter_update'              => '更新了章節',
+    'chapter_update'              => '已更新章節',
     'chapter_update_notification' => '章節已建立成功',
-    'chapter_delete'              => 'å\88ªé\99¤äº\86章節',
+    'chapter_delete'              => 'å·²å\88ªé\99¤章節',
     'chapter_delete_notification' => '章節已刪除成功',
-    'chapter_move'                => '移動了章節',
+    'chapter_move'                => '已移動章節',
 
     // Books
-    'book_create'                 => '建ç«\8bäº\86å\9c\96æ\9b¸',
-    'book_create_notification'    => '圖書已建立成功',
+    'book_create'                 => '已建ç«\8bæ\9b¸æ\9c¬',
+    'book_create_notification'    => '書本已建立成功',
     'book_update'                 => '更新了圖書',
-    'book_update_notification'    => '圖書已更新成功',
-    'book_delete'                 => 'å\88ªé\99¤äº\86å\9c\96æ\9b¸',
-    'book_delete_notification'    => '圖書已刪除成功',
-    'book_sort'                   => '排序了圖書',
-    'book_sort_notification'      => '圖書已重新排序成功',
+    'book_update_notification'    => '書本已更新成功',
+    'book_delete'                 => 'å·²å\88ªé\99¤æ\9b¸æ\9c¬',
+    'book_delete_notification'    => '書本已刪除成功',
+    'book_sort'                   => '已排序書本',
+    'book_sort_notification'      => '書本已重新排序成功',
 
     // Bookshelves
-    'bookshelf_create'            => '建ç«\8bäº\86書架',
+    'bookshelf_create'            => '已建ç«\8b書架',
     'bookshelf_create_notification'    => '書架已建立成功',
-    'bookshelf_update'                 => '更新了書架',
+    'bookshelf_update'                 => '已更新書架',
     'bookshelf_update_notification'    => '書架已更新成功',
-    'bookshelf_delete'                 => 'å\88ªé\99¤äº\86書架',
+    'bookshelf_delete'                 => 'å·²å\88ªé\99¤書架',
     'bookshelf_delete_notification'    => '書架已刪除成功',
 
     // Other
     'commented_on'                => '評論',
+    'permissions_update'          => '更新權限',
 ];
index 4e834f70068498acedda9a70e3b5eea253184f71..2ad648589af9cd61cb95c88e3a9ff76d0a138e73 100644 (file)
@@ -18,32 +18,32 @@ return [
 
     'name' => '名稱',
     'username' => '使用者名稱',
-    'email' => 'Email位址',
+    'email' => '電子郵件',
     'password' => '密碼',
     'password_confirm' => '確認密碼',
-    'password_hint' => '必須超過7個字元',
+    'password_hint' => '必須超過 7 個字元',
     'forgot_password' => '忘記密碼?',
     'remember_me' => '記住我',
-    'ldap_email_hint' => '請輸入用於此帳號的電子郵件。',
-    'create_account' => '建立帳',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
-    'social_login' => 'SNS登入',
-    'social_registration' => 'SNS註冊',
-    'social_registration_text' => '其他服務註冊/登入.',
+    'ldap_email_hint' => '請輸入此帳號使用的電子郵件。',
+    'create_account' => '建立帳',
+    'already_have_account' => '已經擁有帳戶?',
+    'dont_have_account' => '沒有帳戶?',
+    'social_login' => '社群網站登入',
+    'social_registration' => '社群網站帳戶註冊',
+    'social_registration_text' => '使用其他服務註冊及登入。',
 
-    'register_thanks' => '註冊完成!',
-    'register_confirm' => '請點選查收您的Email,並點選確認。',
-    'registrations_disabled' => '註冊目前被禁用',
-    'registration_email_domain_invalid' => '此Email域名沒有權限進入本系統',
+    'register_thanks' => '感謝您的註冊!',
+    'register_confirm' => '請檢查您的電子郵件,並按下確認按鈕以使用 :appName 。',
+    'registrations_disabled' => '目前已停用註冊',
+    'registration_email_domain_invalid' => '這個電子郵件網域沒有權限使用',
     'register_success' => '感謝您註冊:appName,您現在已經登入。',
 
 
     // Password Reset
     'reset_password' => '重置密碼',
-    'reset_password_send_instructions' => '在下方輸入您的Email位址,您將收到一封帶有密碼重置連結的郵件。',
+    'reset_password_send_instructions' => '在下方輸入您的電子郵件,您將收到一封帶有密碼重置連結的郵件。',
     'reset_password_send_button' => '發送重置連結',
-    'reset_password_sent_success' => '密碼重置連結已發送到:email。',
+    'reset_password_sent' => '重置密碼的連結會發送至電子郵件地址:email(如果系統記錄中存在此電子郵件地址)',
     'reset_password_success' => '您的密碼已成功重置。',
     'email_reset_subject' => '重置您的:appName密碼',
     'email_reset_text' => '您收到此電子郵件是因為我們收到了您的帳號的密碼重置請求。',
@@ -51,7 +51,7 @@ return [
 
 
     // Email Confirmation
-    'email_confirm_subject' => '確認您在:appName的Email位址',
+    'email_confirm_subject' => '確認您在:appName的電子郵件',
     'email_confirm_greeting' => '感謝您加入:appName!',
     'email_confirm_text' => '請點選下面的按鈕確認您的Email位址:',
     'email_confirm_action' => '確認Email',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => '重新發送確認Email',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => '您受邀請加入:appName!',
+    'user_invite_email_greeting' => '我們為您在:appName上創建了一個新賬戶。',
+    'user_invite_email_text' => '請點擊下面的按鈕設置賬戶密碼并獲取訪問權限:',
+    'user_invite_email_action' => '請設置賬戶密碼',
+    'user_invite_page_welcome' => '歡迎使用:appName',
+    'user_invite_page_text' => '要完成設置您的賬戶並獲取訪問權限,您需要設置一個密碼。該密碼將在以後訪問時用於登陸:appName',
+    'user_invite_page_confirm_button' => '請確定密碼',
+    'user_invite_success' => '密碼已設置,您現在可以進入:appName了啦!'
 ];
\ No newline at end of file
index 80147e91a7152dbf0df810cfbf7304f706d4fb4c..1d26ded298f6ca1ed15435438d76b843ce066225 100644 (file)
@@ -11,7 +11,7 @@ return [
     'save' => '儲存',
     'continue' => '繼續',
     'select' => '選擇',
-    'toggle_all' => 'Toggle All',
+    'toggle_all' => '轉換全部',
     'more' => '更多',
 
     // Form Labels
@@ -24,7 +24,7 @@ return [
     // Actions
     'actions' => '動作',
     'view' => '檢視',
-    'view_all' => 'View All',
+    'view_all' => '驗視全部',
     'create' => '建立',
     'update' => '更新',
     'edit' => '編輯',
@@ -33,20 +33,22 @@ return [
     'copy' => '複製',
     'reply' => '回覆',
     'delete' => '刪除',
+    'delete_confirm' => '確認刪除',
     'search' => '搜尋',
     'search_clear' => '清除搜尋',
     'reset' => '重置',
     'remove' => '刪除',
     'add' => '新增',
+    'fullscreen' => '全屏顯示',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
-    'sort_name' => 'Name',
-    'sort_created_at' => 'Created Date',
-    'sort_updated_at' => 'Updated Date',
+    'sort_options' => '選項分類',
+    'sort_direction_toggle' => '順序方向切換',
+    'sort_ascending' => '升序',
+    'sort_descending' => '降序',
+    'sort_name' => '名稱',
+    'sort_created_at' => '創建日期',
+    'sort_updated_at' => '更新日期',
 
     // Misc
     'deleted_user' => '刪除使用者',
@@ -59,18 +61,20 @@ return [
     'grid_view' => '縮圖檢視',
     'list_view' => '清單撿視',
     'default' => '預設',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => '導覽路徑',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => '個人資料菜單',
     'view_profile' => '檢視資料',
     'edit_profile' => '編輯資料',
+    'dark_mode' => '深色模式',
+    'light_mode' => '明亮模式',
 
     // Layout tabs
-    'tab_info' => 'Info',
-    'tab_content' => 'Content',
+    'tab_info' => '訊息',
+    'tab_content' => '內容',
 
     // Email Content
     'email_action_help' => '如果您無法點選“:actionText”按鈕,請將下面的網址複製到您的瀏覽器中打開:',
-    'email_rights' => 'All rights reserved',
+    'email_rights' => '版權所有',
 ];
index bcadecbb6bafb1788ec2f2898afec2ace7a37b89..b200fa7948506c3d56e5e653642b2c38a690f8ca 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => '載入更多',
     'image_image_name' => '圖片名稱',
     'image_delete_used' => '所使用圖片目前用於以下頁面。',
-    'image_delete_confirm' => '如果你想刪除它,請再次按下按鈕。',
+    'image_delete_confirm_text' => '您確認想要刪除這個圖片?',
     'image_select_image' => '選擇圖片',
     'image_dropzone' => '拖曳圖片或點選這裡上傳',
     'images_deleted' => '圖片已刪除',
@@ -29,5 +29,6 @@ return [
     'code_editor' => '編輯程式碼',
     'code_language' => '程式語言',
     'code_content' => '程式碼內容',
+    'code_session_history' => 'Session 歷程',
     'code_save' => '儲存程式碼',
 ];
index 8d49ff2ffe6d917592fa15ee5aaa558e8363b276..ca98df0d4d321734edc9d96ca6eb186fcc514df4 100644 (file)
@@ -11,7 +11,7 @@ return [
     'recently_updated_pages' => '最新頁面',
     'recently_created_chapters' => '最近建立的章節',
     'recently_created_books' => '最近建立的書本',
-    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_created_shelves' => '最近建立的章節',
     'recently_update' => '最近更新',
     'recently_viewed' => '最近看過',
     'recent_activity' => '近期活動',
@@ -22,6 +22,7 @@ return [
     'meta_created_name' => '由 :user 建立於 :timeLength',
     'meta_updated' => '更新於 :timeLength',
     'meta_updated_name' => '由 :user 更新於 :timeLength',
+    'meta_owned_name' => ':user 所擁有',
     'entity_select' => '選擇項目',
     'images' => '圖片',
     'my_recent_drafts' => '我最近的草稿',
@@ -39,15 +40,17 @@ return [
     'permissions_intro' => '本設定優先權高於每個使用者角色本身所具有的權限。',
     'permissions_enable' => '啟用自訂權限',
     'permissions_save' => '儲存權限',
+    'permissions_owner' => '擁有者',
 
     // Search
     'search_results' => '搜尋結果',
-    'search_total_results_found' => '共找到了:count個結果',
+    'search_total_results_found' => '共找到了:count個結果|共找到了:count個結果',
     'search_clear' => '清除搜尋',
     'search_no_pages' => '沒有找到符合的頁面',
     'search_for_term' => '“:term”的搜尋結果',
     'search_more' => '更多結果',
-    'search_filters' => '過濾搜尋結果',
+    'search_advanced' => '進階搜尋',
+    'search_terms' => '搜尋字串',
     'search_content_type' => '種類',
     'search_exact_matches' => '精確符合',
     'search_tags' => '標籤搜尋',
@@ -68,13 +71,13 @@ return [
     // Shelves
     'shelf' => '書架',
     'shelves' => '書架',
-    'x_shelves' => ':count Shelf|:count Shelves',
+    'x_shelves' => ':count 書架|:count 章節',
     'shelves_long' => '書架',
     'shelves_empty' => '不存在已建立的書架',
     'shelves_create' => '建立書架',
     'shelves_popular' => '熱門書架',
     'shelves_new' => '新書架',
-    'shelves_new_action' => 'New Shelf',
+    'shelves_new_action' => '建立新的書架',
     'shelves_popular_empty' => '最受歡迎的書架將出現在這裡。',
     'shelves_new_empty' => '最近建立的書架將出現在這裡。',
     'shelves_save' => '儲存書架',
@@ -100,12 +103,12 @@ return [
     // Books
     'book' => '書本',
     'books' => '書本',
-    'x_books' => ':count本書',
+    'x_books' => ':count本書|:count本書',
     'books_empty' => '不存在已建立的書',
     'books_popular' => '熱門書本',
     'books_recent' => '最近的書',
     'books_new' => '新書',
-    'books_new_action' => 'New Book',
+    'books_new_action' => '新增一本書',
     'books_popular_empty' => '最受歡迎的書本將出現在這裡。',
     'books_new_empty' => '最近建立的書本將出現在這裡。',
     'books_create' => '建立書本',
@@ -128,24 +131,24 @@ return [
     'books_navigation' => '書本導覽',
     'books_sort' => '排序書本內容',
     'books_sort_named' => '排序書本「:bookName」',
-    'books_sort_name' => 'Sort by Name',
-    'books_sort_created' => 'Sort by Created Date',
-    'books_sort_updated' => 'Sort by Updated Date',
-    'books_sort_chapters_first' => 'Chapters First',
-    'books_sort_chapters_last' => 'Chapters Last',
+    'books_sort_name' => '按名稱排序',
+    'books_sort_created' => '按創建時間排序',
+    'books_sort_updated' => '按更新時間排序',
+    'books_sort_chapters_first' => '第一章',
+    'books_sort_chapters_last' => '最後一章',
     'books_sort_show_other' => '顯示其他書本',
     'books_sort_save' => '儲存新順序',
 
     // Chapters
     'chapter' => '章節',
     'chapters' => '章節',
-    'x_chapters' => ':count個章節',
+    'x_chapters' => ':count個章節|:count個章節',
     'chapters_popular' => '熱門章節',
     'chapters_new' => '新章節',
     'chapters_create' => '建立章節',
     'chapters_delete' => '刪除章節',
     'chapters_delete_named' => '刪除章節「:chapterName」',
-    'chapters_delete_explain' => '這將刪除章節「:chapterName」。所有的頁面將被刪除並加入到其所在的書籍。',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
     'chapters_delete_confirm' => '您確定要刪除此章節嗎?',
     'chapters_edit' => '編輯章節',
     'chapters_edit_named' => '編輯章節「:chapterName」',
@@ -162,7 +165,7 @@ return [
     // Pages
     'page' => '頁面',
     'pages' => '頁面',
-    'x_pages' => ':count個頁面',
+    'x_pages' => ':count個頁面|:count個頁面',
     'pages_popular' => '熱門頁面',
     'pages_new' => '新頁面',
     'pages_attachments' => '附件',
@@ -176,7 +179,7 @@ return [
     'pages_delete_confirm' => '您確定要刪除此頁面嗎?',
     'pages_delete_draft_confirm' => '您確定要刪除此草稿頁面嗎?',
     'pages_editing_named' => '正在編輯頁面“:pageName”',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => '草稿選項',
     'pages_edit_save_draft' => '儲存草稿',
     'pages_edit_draft' => '編輯頁面草稿',
     'pages_editing_draft' => '正在編輯草稿',
@@ -207,13 +210,14 @@ return [
     'pages_revisions' => '頁面修訂',
     'pages_revisions_named' => '“:pageName”頁面修訂',
     'pages_revision_named' => '“:pageName”頁面修訂',
+    'pages_revision_restored_from' => 'Restored from #:id; :summary',
     'pages_revisions_created_by' => '建立者',
     'pages_revisions_date' => '修訂日期',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+    'pages_revisions_numbered' => '修訂編號:id',
+    'pages_revisions_numbered_changes' => '修訂編號:id 更改',
     'pages_revisions_changelog' => '更新說明',
-    'pages_revisions_changes' => '說明',
+    'pages_revisions_changes' => '更新紀錄',
     'pages_revisions_current' => '目前版本',
     'pages_revisions_preview' => '預覽',
     'pages_revisions_restore' => '恢複',
@@ -234,7 +238,7 @@ return [
     ],
     'pages_draft_discarded' => '草稿已丟棄,編輯器已更新到目前頁面內容。',
     'pages_specific' => '指定頁面',
-    'pages_is_template' => 'Page Template',
+    'pages_is_template' => '頁面模板',
 
     // Editor Sidebar
     'page_tags' => '頁面標籤',
@@ -242,12 +246,12 @@ return [
     'book_tags' => '書本標籤',
     'shelf_tags' => '書架標籤',
     'tag' => '標籤',
-    'tags' =>  'Tags',
-    'tag_name' =>  'Tag Name',
+    'tags' =>  '標籤',
+    'tag_name' =>  '標籤名稱',
     'tag_value' => '標籤值 (非必要)',
     'tags_explain' => "加入一些標籤以更好地對您的內容進行分類。\n您可以為標籤分配一個值,以進行更深入的組織。",
     'tags_add' => '加入另一個標籤',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => '移除此標籤',
     'attachments' => '附件',
     'attachments_explain' => '上傳一些檔案或附加連結顯示在您的網頁上。將顯示在在頁面的側邊欄。',
     'attachments_explain_instant_save' => '這裡的更改將立即儲存。Changes here are saved instantly.',
@@ -255,7 +259,7 @@ return [
     'attachments_upload' => '上傳檔案',
     'attachments_link' => '附加連結',
     'attachments_set_link' => '設定連結',
-    'attachments_delete_confirm' => '確認您想要刪除此附件後,請點選刪除。',
+    'attachments_delete' => '確定要刪除此附件嗎?',
     'attachments_dropzone' => '刪除檔案或點選此處加入檔案',
     'attachments_no_files' => '尚未上傳檔案',
     'attachments_explain_link' => '如果您不想上傳檔案,則可以附加連結,這可以是指向其他頁面的連結,也可以是指向雲端檔案的連結。',
@@ -264,6 +268,7 @@ return [
     'attachments_link_url' => '連結到檔案',
     'attachments_link_url_hint' => '網站或檔案的網址',
     'attach' => '附加',
+    'attachments_insert_link' => '將附件連結增加到頁面',
     'attachments_edit_file' => '編輯檔案',
     'attachments_edit_file_name' => '檔案名稱',
     'attachments_edit_drop_upload' => '刪除檔案或點選這裡上傳並覆蓋',
@@ -273,12 +278,12 @@ return [
     'attachments_file_uploaded' => '附件上傳成功',
     'attachments_file_updated' => '附件更新成功',
     'attachments_link_attached' => '連結成功附加到頁面',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => '樣本',
+    'templates_set_as_template' => '頁面是模板',
+    'templates_explain_set_as_template' => '您可以將此頁面設置為模板,以便在創建其他頁面時利用其內容。 如果其他用戶對此頁面擁有查看權限,則將可以使用此模板。',
+    'templates_replace_content' => '替換頁面內容',
+    'templates_append_content' => '附加到頁面內容',
+    'templates_prepend_content' => '前置頁面內容',
 
     // Profile View
     'profile_user_for_x' => '來這裡:time了',
@@ -286,20 +291,20 @@ return [
     'profile_not_created_pages' => ':userName尚未建立任何頁面',
     'profile_not_created_chapters' => ':userName尚未建立任何章節',
     'profile_not_created_books' => ':userName尚未建立任何書本',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_not_created_shelves' => ':userName 沒有創建任何書架',
 
     // Comments
     'comment' => '評論',
     'comments' => '評論',
     'comment_add' => '新增評論',
     'comment_placeholder' => '在這裡評論',
-    'comment_count' => '{0} 無評論|[1,*] :count條評論',
+    'comment_count' => '{0} 無評論|{1} :count條評論|[2,*] :count條評論',
     'comment_save' => '儲存評論',
     'comment_saving' => '正在儲存評論...',
     'comment_deleting' => '正在刪除評論...',
     'comment_new' => '新評論',
     'comment_created' => '評論於 :createDiff',
-    'comment_updated' => '更新於 :updateDiff (:username)',
+    'comment_updated' => '由 :username 於 :updateDiff 更新',
     'comment_deleted_success' => '評論已刪除',
     'comment_created_success' => '評論已加入',
     'comment_updated_success' => '評論已更新',
@@ -308,7 +313,7 @@ return [
 
     // Revision
     'revision_delete_confirm' => '您確定要刪除此修訂版嗎?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_restore_confirm' => '您確定要還原此修訂版嗎? 當前頁面內容將被替換。',
     'revision_delete_success' => '修訂刪除',
     'revision_cannot_delete_latest' => '無法刪除最新版本。'
 ];
\ No newline at end of file
index 39bdbd6f6bc5dcc0d6c05453d1a0e481813c3212..8604cc6c2690f24f8e62093adedd79f31926faa1 100644 (file)
@@ -13,21 +13,27 @@ return [
     'email_already_confirmed' => 'Email已被確認,請嘗試登錄。',
     'email_confirmation_invalid' => '此確認 Session 無效或已被使用,請重新註冊。',
     'email_confirmation_expired' => '確認 Session 已過期,已發送新的確認電子郵件。',
+    'email_confirmation_awaiting' => '用於此賬戶的電子郵箱需要認證',
     'ldap_fail_anonymous' => '使用匿名綁定的LDAP進入失敗。',
     'ldap_fail_authed' => '帶有標識名稱和密碼的LDAP進入失敗。',
     'ldap_extension_not_installed' => '未安裝LDAP PHP外掛程式',
     'ldap_cannot_connect' => '無法連接到ldap伺服器,第一次連接失敗',
+    'saml_already_logged_in' => '已登陸',
+    'saml_user_not_registered' => '用戶:name未註冊,自動註冊不可用',
+    'saml_no_email_address' => '在外部認證系統提供的數據中找不到該用戶的電子郵件地址',
+    'saml_invalid_response_id' => '該應用程序啟動的進程無法識別來自外部身份驗證系統的請求。 登錄後返回可能會導致此問題。',
+    'saml_fail_authed' => '使用 :system 登錄失敗,系統未提供成功的授權',
     'social_no_action_defined' => '沒有定義行為',
     'social_login_bad_response' => "在 :socialAccount 登錄時遇到錯誤:\n:error",
     'social_account_in_use' => ':socialAccount 帳號已被使用,請嘗試透過 :socialAccount 選項登錄。',
     'social_account_email_in_use' => 'Email :email 已經被使用。如果您已有帳號,則可以在個人資料設定中綁定您的 :socialAccount。',
     'social_account_existing' => ':socialAccount已經被綁定到您的帳號。',
     'social_account_already_used_existing' => ':socialAccount帳號已經被其他使用者使用。',
-    'social_account_not_used' => ':socialAccount帳號沒有綁定到任何使用者,請在您的個人資料設定中綁定。',
+    'social_account_not_used' => ':socialAccount帳號沒有綁定到任何使用者,請在您的個人資料設定中綁定。 ',
     'social_account_register_instructions' => '如果您還沒有帳號,您可以使用 :socialAccount 選項註冊帳號。',
     'social_driver_not_found' => '未找到社交驅動程式',
     'social_driver_not_configured' => '您的:socialAccount社交設定不正確。',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => '此邀請鏈接已過期,您可以嘗試重置您的賬戶密碼。',
 
     // System
     'path_not_writable' => '無法上傳到檔案路徑“:filePath”,請確保它可寫入伺服器。',
@@ -40,7 +46,6 @@ return [
     'file_upload_timeout' => '文件上傳已超時。',
 
     // Attachments
-    'attachment_page_mismatch' => '附件更新期間的頁面不符合',
     'attachment_not_found' => '沒有找到附件',
 
     // Pages
@@ -65,7 +70,7 @@ return [
     'role_cannot_be_edited' => '無法編輯這個角色',
     'role_system_cannot_be_deleted' => '無法刪除系統角色',
     'role_registration_default_cannot_delete' => '無法刪除設定為預設註冊的角色',
-    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+    'role_cannot_remove_only_admin' => '該用戶是分配作為管理員職務的唯一用戶。 在嘗試在此處刪除管理員職務之前,請將其分配給其他用戶。',
 
     // Comments
     'comment_list' => '讀取評論時發生錯誤。',
@@ -77,9 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => '無法找到頁面',
     'sorry_page_not_found' => '對不起,無法找到您想進入的頁面。',
+    'sorry_page_not_found_permission_warning' => '如果您確認這個頁面存在,則代表可能沒有查看它的權限。',
     'return_home' => '返回首頁',
     'error_occurred' => '發生錯誤',
     'app_down' => ':appName現在正在關閉',
     'back_soon' => '請耐心等待網站的恢複。',
 
+    // API errors
+    'api_no_authorization_found' => '在請求上找不到授權令牌',
+    'api_bad_authorization_format' => '在請求中找到授權令牌,但格式似乎不正確',
+    'api_user_token_not_found' => '找不到提供的授權令牌的匹配API令牌',
+    'api_incorrect_token_secret' => '給定使用的API令牌提供的密鑰不正確',
+    'api_user_no_api_permission' => '使用的API令牌的擁有者者無權進行API調用',
+    'api_user_token_expired' => '授權令牌已過期',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => '寄送測試電子郵件時發生錯誤:',
+
 ];
index d1f0aeb08646d9c255c04032d65c4bbc99afaa5e..7040cd89095879af50403ac2be3c886a3074f364 100644 (file)
@@ -7,9 +7,9 @@
 return [
 
     'password' => '密碼必須至少包含六個字元並與確認相符。',
-    'user' => "使用該Email位址的使用者不存在。",
-    'token' => '此密碼重置 Session 無效。',
-    'sent' => '我們已經透過Email發送您的密碼重置連結。',
+    'user' => "沒有使用這個電子郵件位址的使用者。",
+    'token' => '這個電子郵件位址的密碼重置權仗無效。',
+    'sent' => '我們已經透過電子郵件發送您的密碼重置連結。',
     'reset' => '您的密碼已被重置。',
 
 ];
index 0ad899a27e0da558b4e427067d19a3518747d528..50ef79cbb55b25647d51563e8e46cea0d62b85b0 100644 (file)
@@ -12,24 +12,24 @@ return [
     'settings_save_success' => '設定已儲存',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
+    'app_customization' => '自定義',
+    'app_features_security' => '功能與安全',
     'app_name' => 'App名',
     'app_name_desc' => '此名稱將在網頁頂端和Email中顯示。',
     'app_name_header' => '在網頁頂端顯示應用名稱?',
-    'app_public_access' => 'Public Access',
-    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
-    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
-    'app_public_access_toggle' => 'Allow public access',
+    'app_public_access' => '公共訪問',
+    'app_public_access_desc' => '啟用此選項將允許未登錄的訪問者訪問BookStack實例中的內容。',
+    'app_public_access_desc_guest' => '可以通過“訪客”控制公共訪問者的訪問。',
+    'app_public_access_toggle' => '允許公開訪問',
     'app_public_viewing' => '開放公開閱覽?',
     'app_secure_images' => '啟用更高安全性的圖片上傳?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_toggle' => '啟用更高安全性的圖片上傳',
     'app_secure_images_desc' => '出於效能考量,所有圖片都是公開的。這個選項會在圖片的網址前加入一個隨機並難以猜測的字元串,從而使直接進入變得困難。',
     'app_editor' => '頁面編輯器',
     'app_editor_desc' => '選擇所有使用者將使用哪個編輯器來編輯頁面。',
     'app_custom_html' => '自訂HTML頂端內容',
     'app_custom_html_desc' => '此處加入的任何內容都將插入到每個頁面的<head>部分的底部,這對於覆蓋樣式或加入分析程式碼很方便。',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => '在此設置頁面上禁用了自定義HTML標題內容,以確保可以恢復所有重大更改。',
     'app_logo' => 'App Logo',
     'app_logo_desc' => '這個圖片的高度應該為43px。<br>大圖片將會被縮小。',
     'app_primary_color' => 'App主要配色',
@@ -38,18 +38,28 @@ return [
     'app_homepage_desc' => '選擇要做為首頁的頁面,這將會替換預設首頁,而且這個頁面的權限設定將被忽略。',
     'app_homepage_select' => '預設首頁選擇',
     'app_disable_comments' => '關閉評論',
-    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_toggle' => '禁用評論',
     'app_disable_comments_desc' => '在App的所有頁面上關閉評論,已經存在的評論也不會顯示。',
 
+    // Color settings
+    'content_colors' => '內容顏色',
+    'content_colors_desc' => '為頁面組織層次結構中的所有元素設置顏色。 為了提高可讀性,建議選擇亮度與默認顏色相似的顏色。',
+    'bookshelf_color' => '書架顔色',
+    'book_color' => '書本顔色',
+    'chapter_color' => '章節顔色',
+    'page_color' => '頁面顔色',
+    'page_draft_color' => '頁面草稿顏色',
+
     // Registration Settings
     'reg_settings' => '註冊設定',
-    'reg_enable' => 'Enable Registration',
-    'reg_enable_toggle' => 'Enable registration',
-    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+    'reg_enable' => '啟用註冊',
+    'reg_enable_toggle' => '啟用註冊',
+    'reg_enable_desc' => '啟用註冊後,用戶將可以自己註冊為應用程序用戶。註冊後,他們將獲得一個默認的單一用戶角色。',
     'reg_default_role' => '註冊後的預設使用者角色',
-    'reg_email_confirmation' => 'Email Confirmation',
-    'reg_email_confirmation_toggle' => 'Require email confirmation',
-    'reg_confirm_email_desc' => '如果使用網域名稱限制,則需要Email驗證,並且本設定將被忽略。',
+    'reg_enable_external_warning' => '當外部 LDAP 或 SAML 身份驗證啟用時,將會忽略上述選項。如果外部身份驗證成功,將會自動在本系統建立使用者帳戶。',
+    'reg_email_confirmation' => '電子郵件驗證',
+    'reg_email_confirmation_toggle' => '需要電子郵件驗證',
+    'reg_confirm_email_desc' => '如果使用網域名稱限制,則需要電子郵件驗證,並且本設定將被忽略。',
     'reg_confirm_restrict_domain' => '網域名稱限制',
     'reg_confirm_restrict_domain_desc' => '輸入您想要限制註冊的Email域域名稱列表,用逗號隔開。在被允許與本系統連結之前,使用者會收到一封Email來確認他們的位址。<br>注意,使用者在註冊成功後可以修改他們的Email位址。',
     'reg_confirm_restrict_domain_placeholder' => '尚未設定限制的網域',
@@ -58,11 +68,53 @@ return [
     'maint' => '維護',
     'maint_image_cleanup' => '清理圖像',
     'maint_image_cleanup_desc' => "掃描頁面和修訂內容以檢查哪些圖像是正在使用的以及哪些圖像是多余的。確保在運行前創建完整的數據庫和映像備份。",
-    'maint_image_cleanup_ignore_revisions' => '忽略修訂記錄中的圖像',
+    'maint_delete_images_only_in_revisions' => '包含刪除僅在舊頁面修訂版中存在的圖像',
     'maint_image_cleanup_run' => '運行清理',
-    'maint_image_cleanup_warning' => '發現了 :count 張可能未使用的圖像。您確定要刪除這些圖像嗎?',
-    'maint_image_cleanup_success' => '找到並刪除了 :count 張可能未使用的圖像!',
-    'maint_image_cleanup_nothing_found' => '找不到未使用的圖像,沒有刪除!',
+    'maint_image_cleanup_warning' => '發現了:count 張可能未使用的圖像。您確定要刪除這些圖像嗎?',
+    'maint_image_cleanup_success' => '找到並刪除了:count 張可能未使用的圖像!',
+    'maint_image_cleanup_nothing_found' => '找不到未使用的圖像,未刪除任何檔案!',
+    'maint_send_test_email' => '發送測試電子郵件',
+    'maint_send_test_email_desc' => '這會將測試電子郵件發送到您的個人資料中指定的電子郵件地址。',
+    'maint_send_test_email_run' => '發送測試郵件',
+    'maint_send_test_email_success' => '郵件發送到 :address',
+    'maint_send_test_email_mail_subject' => '測試郵件',
+    'maint_send_test_email_mail_greeting' => '電子郵件傳遞似乎有效!',
+    'maint_send_test_email_mail_text' => '恭喜你! 收到此電子郵件通知時,您的電子郵件設置已經認證成功。',
+    'maint_recycle_bin_desc' => '刪除的書架,書籍,章節和頁面將發送到回收站,以便可以還原或永久刪除它們。 回收站中的較舊項目可能會在一段時間後自動刪除,具體取決於系統配置。',
+    'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => '資源回收筒',
+    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_deleted_by' => 'Deleted By',
+    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_permanently_delete' => 'Permanently Delete',
+    'recycle_bin_restore' => 'Restore',
+    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_empty' => 'Empty Recycle Bin',
+    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+    'recycle_bin_destroy_list' => 'Items to be Destroyed',
+    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+    // Audit Log
+    'audit' => '稽核記錄',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => '使用者',
+    'audit_table_event' => '活動',
+    'audit_table_related' => 'Related Item or Detail',
+    'audit_table_date' => '最後活動日期',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
 
     // Role Settings
     'roles' => '角色',
@@ -85,9 +137,11 @@ return [
     'role_manage_roles' => '管理角色與角色權限',
     'role_manage_entity_permissions' => '管理所有圖書,章節和頁面的權限',
     'role_manage_own_entity_permissions' => '管理自己的圖書,章節和頁面的權限',
-    'role_manage_page_templates' => 'Manage page templates',
+    'role_manage_page_templates' => '管理頁面模板',
+    'role_access_api' => '存取系統API',
     'role_manage_settings' => '管理App設定',
     'role_asset' => '資源項目',
+    'roles_system_warning' => '請注意,具有上述三項權限任何一項的用戶能更改自己或系統中其他人的權限。應只將具這些權限的角色分配予受信任的用戶。',
     'role_asset_desc' => '對系統內資源的預設權限將由這裡的權限控制。若有單獨設定在書本、章節和頁面上的權限,將會覆蓋這裡的權限設定。',
     'role_asset_admins' => '管理員會自動獲得對所有內容的存取權限,但這些選項可能會顯示或隱藏UI的選項。',
     'role_all' => '全部',
@@ -103,37 +157,67 @@ return [
     'user_profile' => '使用者資料',
     'users_add_new' => '加入使用者',
     'users_search' => '搜尋使用者',
-    'users_details' => 'User Details',
-    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
-    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+    'users_latest_activity' => '最新活動',
+    'users_details' => '用戶詳情',
+    'users_details_desc' => '請設置用戶的顯示名稱和電子郵件地址, 該電子郵件地址將用於登錄該應用。',
+    'users_details_desc_no_email' => '設置一個用戶的顯示名稱,以便其他人可以認出你。',
     'users_role' => '使用者角色',
-    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
-    'users_password' => 'User Password',
-    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
+    'users_role_desc' => '選擇一個將分配給該用戶的角色。 如果將一個用戶分配給多個角色,則這些角色的權限將堆疊在一起,並且他們將獲得分配的角色的所有功能。',
+    'users_password' => '用戶密碼',
+    'users_password_desc' => '設置用於登錄賬戶的密碼, 密碼長度必須至少為6個字符。',
+    'users_send_invite_text' => '您可以選擇向該用戶發送邀請電子郵件,允許他們設置自己的密碼,或者您可以自己設置他們的密碼。',
+    'users_send_invite_option' => '向用戶發送邀請電子郵件',
     'users_external_auth_id' => '外部身份驗證ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+    'users_external_auth_id_desc' => '這個 ID 將於外部身份驗證系統時,用於比對使用者。',
     'users_password_warning' => '如果您想更改密碼,請填寫以下內容:',
     'users_system_public' => '此使用者代表進入您的App的任何訪客。它不能用於登入,而是自動分配。',
     'users_delete' => '刪除使用者',
     'users_delete_named' => '刪除使用者 :userName',
     'users_delete_warning' => '這將從系統中完全刪除名為 \':userName\' 的使用者。',
     'users_delete_confirm' => '您確定要刪除這個使用者?',
-    'users_delete_success' => '使用者刪除成功。',
+    'users_migrate_ownership' => 'Migrate Ownership',
+    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_none_selected' => '沒有選定的使用者',
+    'users_delete_success' => 'User successfully removed',
     'users_edit' => '編輯使用者',
     'users_edit_profile' => '編輯資料',
     'users_edit_success' => '使用者更新成功',
     'users_avatar' => '使用者大頭照',
     'users_avatar_desc' => '目前圖片應該為約256px的正方形。',
     'users_preferred_language' => '語言',
-    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_preferred_language_desc' => '此選項將更改用於應用用戶界面的語言,但 這不會影響任何用戶創建的內容。',
     'users_social_accounts' => '社群網站帳號',
     'users_social_accounts_info' => '在這里,您可以連結您的其他帳號,以便方便地登入。如果您選擇解除連結,之後將不能透過此社群網站帳號登入,請設定社群網站帳號來取消本系統p的進入權限。',
     'users_social_connect' => '連結帳號',
     'users_social_disconnect' => '解除連結帳號',
     'users_social_connected' => ':socialAccount 帳號已經成功連結到您的資料。',
     'users_social_disconnected' => ':socialAccount 帳號已經成功解除連結。',
+    'users_api_tokens' => 'API令牌',
+    'users_api_tokens_none' => '尚未為此用戶創建API令牌',
+    'users_api_tokens_create' => '創建令牌',
+    'users_api_tokens_expires' => '過期',
+    'users_api_tokens_docs' => 'API檔案',
+
+    // API Tokens
+    'user_api_token_create' => '創建 API 令牌',
+    'user_api_token_name' => '名稱',
+    'user_api_token_name_desc' => '給您的令牌一個易讀的名稱,以備將來提醒其預期的用途。',
+    'user_api_token_expiry' => '過期日期',
+    'user_api_token_expiry_desc' => '設置此令牌到期的日期, 在此日期之後,使用此令牌發出的請求將不再起作用。 若該項留空則自動將在100年後到期。',
+    'user_api_token_create_secret_message' => '創建此令牌後,將立即生成並顯示“令牌ID”和“令牌密鑰”,該密鑰只會顯示一次,因此請確保在繼續操作之前將其複製到安全的地方。',
+    'user_api_token_create_success' => '成功創建API令牌',
+    'user_api_token_update_success' => '成功更新API令牌',
+    'user_api_token' => 'API 令牌',
+    'user_api_token_id' => '令牌 ID',
+    'user_api_token_id_desc' => '這是此令牌的不可編輯的系統生成的標識符,需要在API請求中提供。',
+    'user_api_token_secret' => '令牌密鑰',
+    'user_api_token_secret_desc' => '這是此令牌的系統生成的密鑰,需要在API請求中提供。 這只會顯示一次,因此請將其複製到安全的地方。',
+    'user_api_token_created' => '令牌已創建於 :timeAgo',
+    'user_api_token_updated' => '令牌已更新:timeAgo',
+    'user_api_token_delete' => '刪除令牌',
+    'user_api_token_delete_warning' => '這將從系統中完全刪除名稱為 ":tokenName" 的API令牌。',
+    'user_api_token_delete_confirm' => '您確定要刪除這個API令牌嗎?',
+    'user_api_token_delete_success' => 'API令牌成功刪除',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -141,26 +225,32 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => '丹麥',
         'de' => 'Deutsch (Sie)',
         'de_informal' => 'Deutsch (Du)',
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
+        'he' => '希伯來語',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
         'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
         'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
         'sk' => 'Slovensky',
-        'cs' => 'Česky',
+        'sl' => 'Slovenščina',
         'sv' => 'Svenska',
-        'ko' => '한국어',
-        'ja' => '日本語',
-        'pl' => 'Polski',
-        'it' => 'Italian',
-        'ru' => 'Русский',
+        'tr' => 'Türkçe',
         'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
         'zh_CN' => '简体中文',
         'zh_TW' => '繁體中文',
-        'hu' => 'Magyar',
-        'tr' => 'Türkçe',
     ]
     //!////////////////////////////////
 ];
index 33a3c7119ac324a2d8f53034760dda41f44e0800..7a46288ad2664c3e9cd848817d1a169215b5ac0d 100644 (file)
@@ -30,40 +30,40 @@ return [
     'digits'               => ':attribute 必須為:digits位數。',
     'digits_between'       => ':attribute 必須為:min到:max位數。',
     'email'                => ':attribute 必須是有效的電子郵件位址。',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ':attribute必須以下列之一結尾::values',
     'filled'               => ':attribute 字段是必需的。',
     'gt'                   => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file'    => 'The :attribute must be greater than :value kilobytes.',
-        'string'  => 'The :attribute must be greater than :value characters.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute必須大於:value。',
+        'file'    => ':attribute必須大於:value千字節。',
+        'string'  => ':attribute必須多於:value個字符。',
+        'array'   => ':attribute必須包含比:value多的項目。',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => 'The :attribute必須大於或等於:value。',
+        'file'    => 'The :attribute必須大於或等於:value千字節。',
+        'string'  => 'The :attribute必須多於或等於:value個字符。',
+        'array'   => ':attribute必須具有:value或更多項。',
     ],
     'exists'               => '選中的 :attribute 無效。',
     'image'                => ':attribute 必須是一個圖片。',
-    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'image_extension'      => 'The :attribute必須具有有效且受支持的圖像擴展名.',
     'in'                   => '選中的 :attribute 無效。',
     'integer'              => ':attribute 必須是一個整數。',
     'ip'                   => ':attribute 必須是一個有效的IP位址。',
-    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
-    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
-    'json'                 => 'The :attribute must be a valid JSON string.',
+    'ipv4'                 => 'The :attribute必須是有效的IPv4地址。',
+    'ipv6'                 => 'The :attribute必須是有效的IPv6地址。',
+    'json'                 => 'The :attribute必須是有效的JSON字符串。',
     'lt'                   => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file'    => 'The :attribute must be less than :value kilobytes.',
-        'string'  => 'The :attribute must be less than :value characters.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute必須小於:value。',
+        'file'    => 'The :attribute必須小於:value千字節。',
+        'string'  => ':attribute必須少於:value個字符。',
+        'array'   => ':attribute必須少於:value個項目。',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute必須小於或等於:value。',
+        'file'    => ':attribute必須小於或等於:value KB。',
+        'string'  => ':attribute必須少於或等於:value個字符。',
+        'array'   => ':attribute不得多過:value個項目。',
     ],
     'max'                  => [
         'numeric' => ':attribute 不能超過:max。',
@@ -74,13 +74,13 @@ return [
     'mimes'                => ':attribute 必須是 :values 類型的檔案。',
     'min'                  => [
         'numeric' => ':attribute 至少為:min。',
-        'file'    => ':attribute 至少為:min KB。',
+        'file'    => ':attribute 必須至少為:min KB。',
         'string'  => ':attribute 至少為:min個字元。',
         'array'   => ':attribute 至少有:min項。',
     ],
-    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'no_double_extension'  => 'The :attribute必須僅具有一個文件擴展名。',
     'not_in'               => '選中的 :attribute 無效。',
-    'not_regex'            => 'The :attribute format is invalid.',
+    'not_regex'            => 'The :attribute格式無效。',
     'numeric'              => ':attribute 必須是一個數。',
     'regex'                => ':attribute 格式無效。',
     'required'             => ':attribute 字段是必需的。',
@@ -90,6 +90,7 @@ return [
     'required_without'     => '當:values不存在時,:attribute 字段是必需的。',
     'required_without_all' => '當:values均不存在時,:attribute 字段是必需的。',
     'same'                 => ':attribute 與 :other 必須匹配。',
+    'safe_url'             => 'The provided link may not be safe.',
     'size'                 => [
         'numeric' => ':attribute 必須為:size。',
         'file'    => ':attribute 必須為:size KB。',
@@ -100,7 +101,7 @@ return [
     'timezone'             => ':attribute 必須是有效的區域。',
     'unique'               => ':attribute 已經被使用。',
     'url'                  => ':attribute 格式無效。',
-    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+    'uploaded'             => '無法上傳文件, 服務器可能不接受此大小的文件。',
 
     // Custom validation lines
     'custom' => [
index 2cb17a18db90e8f7185ffde7e52973ae1dd249b3..75adf12aacde34a99b795c7278320f6ebdfee62b 100644 (file)
@@ -3,11 +3,12 @@
  * Callouts
  */
 .callout {
-  border-left: 3px solid #BBB;
+  border-inline-start: 3px solid #BBB;
   background-color: #EEE;
   padding: $-s $-s $-s $-xl;
   display: block;
   position: relative;
+  overflow: auto;
   &:before {
     background-image: url('data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiMwMTUzODAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+ICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4gICAgPHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6bTEgMTVoLTJ2LTZoMnY2em0wLThoLTJWN2gydjJ6Ii8+PC9zdmc+');
     background-repeat: no-repeat;
   }
   &.success {
     border-left-color: $positive;
-    background-color: lighten($positive, 68%);
-    color: darken($positive, 16%);
+    @include lightDark(background-color, lighten($positive, 68%), darken($positive, 22%));
+    @include lightDark(color, darken($positive, 16%), lighten($positive, 5%));
   }
   &.success:before {
     background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiMzNzZjMzkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+ICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4gICAgPHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6bS0yIDE1bC01LTUgMS40MS0xLjQxTDEwIDE0LjE3bDcuNTktNy41OUwxOSA4bC05IDl6Ii8+PC9zdmc+");
   }
   &.danger {
     border-left-color: $negative;
-    background-color: lighten($negative, 56%);
-    color: darken($negative, 20%);
+    @include lightDark(background-color, lighten($negative, 56%), darken($negative, 30%));
+    @include lightDark(color, darken($negative, 20%), lighten($negative, 5%));
   }
   &.danger:before {
     background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiNiOTE4MTgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+ICAgIDxwYXRoIGQ9Ik0xNS43MyAzSDguMjdMMyA4LjI3djcuNDZMOC4yNyAyMWg3LjQ2TDIxIDE1LjczVjguMjdMMTUuNzMgM3pNMTIgMTcuM2MtLjcyIDAtMS4zLS41OC0xLjMtMS4zIDAtLjcyLjU4LTEuMyAxLjMtMS4zLjcyIDAgMS4zLjU4IDEuMyAxLjMgMCAuNzItLjU4IDEuMy0xLjMgMS4zem0xLTQuM2gtMlY3aDJ2NnoiLz4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPjwvc3ZnPg==");
   }
   &.info {
     border-left-color: $info;
-    background-color: lighten($info, 50%);
-    color: darken($info, 20%);
+    @include lightDark(color, darken($info, 20%), lighten($info, 10%));
+    @include lightDark(background-color, lighten($info, 50%), darken($info, 35%));
   }
   &.warning {
     border-left-color: $warning;
-    background-color: lighten($warning, 50%);
-    color: darken($warning, 20%);
+    @include lightDark(background-color, lighten($warning, 50%), darken($warning, 36%));
+    @include lightDark(color, darken($warning, 20%), $warning);
   }
   &.warning:before {
     background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiNiNjUzMWMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+ICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4gICAgPHBhdGggZD0iTTEgMjFoMjJMMTIgMiAxIDIxem0xMi0zaC0ydi0yaDJ2MnptMC00aC0ydi00aDJ2NHoiLz48L3N2Zz4=");
   }
+  a {
+    color: inherit;
+    text-decoration: underline;
+  }
 }
 
 /**
@@ -58,7 +63,7 @@
  */
 
 .card {
-  background-color: #FFF;
+  @include lightDark(background-color, #FFF, #222);
   box-shadow: $bs-card;
   border-radius: 3px;
   border: 1px solid transparent;
@@ -89,6 +94,8 @@
 
 .card.drag-card {
   border: 1px solid #DDD;
+  @include lightDark(border-color, #ddd, #000);
+  @include lightDark(background-color, #fff, #333);
   border-radius: 4px;
   display: flex;
   padding: 0 0 0 ($-s + 28px);
       background-color: #EEE;
     }
     .svg-icon {
-      margin-right: 0px;
+      margin-inline-end: 0px;
     }
   }
-  > div .outline input {
+  .outline input {
     margin: $-s 0;
     width: 100%;
   }
+  .outline {
+    position: relative;
+  }
   .handle {
-    background-color: #EEE;
+    @include lightDark(background-color, #eee, #2d2d2d);
     left: 0;
     position: absolute;
     top: 0;
   display: flex;
   flex-direction: column;
   border: 1px solid #ddd;
+  @include lightDark(border-color, #ddd, #000);
   margin-bottom: $-l;
   border-radius: 4px;
   overflow: hidden;
   &:hover {
     color: $text-dark;
     text-decoration: none;
-    box-shadow: $bs-card;
+    @include lightDark(box-shadow, $bs-card, $bs-card-dark);
   }
   h2 {
     width: 100%;
 
 .content-wrap.card {
   padding: $-m $-xxl;
-  margin-left: auto;
-  margin-right: auto;
+  margin-inline-start: auto;
+  margin-inline-end: auto;
   margin-bottom: $-xl;
   overflow: initial;
   min-height: 60vh;
 .tag-item {
   display: inline-flex;
   margin-bottom: $-xs;
-  margin-right: $-xs;
+  margin-inline-end: $-xs;
   border-radius: 4px;
   border: 1px solid #CCC;
   overflow: hidden;
   font-size: 0.85em;
   a, a:hover, a:active {
     padding: 4px 8px;
-    color: #777;
+    @include lightDark(color, #777, #999);
     transition: background-color ease-in-out 80ms;
     text-decoration: none;
   }
   a:hover {
-    background-color: rgba(255, 255, 255, 0.7);
+    @include lightDark(background-color, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.3));
   }
   svg {
     fill: #888;
   }
   .tag-value {
-    border-left: 1px solid #DDD;
-    background-color: rgba(255, 255, 255, 0.5);
+    border-inline-start: 1px solid #DDD;
+    @include lightDark(background-color, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.2))
   }
 }
 
 .tag-list div:last-child .tag-item {
   margin-bottom: 0;
-}
\ No newline at end of file
+}
+
+/**
+ * API Docs
+ */
+.api-method {
+  font-size: 0.75rem;
+  background-color: #888;
+  padding: $-xs;
+  line-height: 1.3;
+  opacity: 0.7;
+  vertical-align: top;
+  border-radius: 3px;
+  color: #FFF;
+  display: inline-block;
+  min-width: 60px;
+  text-align: center;
+  font-weight: bold;
+  &[data-method="GET"] { background-color: #077b70 }
+  &[data-method="POST"] { background-color: #cf4d03 }
+  &[data-method="PUT"] { background-color: #0288D1 }
+  &[data-method="DELETE"] { background-color: #ab0f0e }
+}
+
+.sticky-sidebar {
+  position: sticky;
+  top: $-m;
+  max-height: calc(100vh - #{$-m});
+  overflow-y: auto;
+}
index e3d9e17cad825beec15792dac617ce3914c45d9b..bd76090523ca70a8ce24cba5ec5037ed7f5b74ae 100644 (file)
@@ -22,17 +22,17 @@ button {
   box-shadow: none;
   background-color: var(--color-primary);
   color: #FFF;
-  fill: #FFF;
   text-transform: uppercase;
   border: 1px solid var(--color-primary);
   vertical-align: top;
+  @include lightDark(filter, none, saturate(0.8) brightness(0.8));
   &:hover, &:focus, &:active {
     background-color: var(--color-primary);
     text-decoration: none;
     color: #FFFFFF;
   }
   &:hover {
-    box-shadow: $bs-light;
+    @include lightDark(box-shadow, $bs-light, $bs-dark);
     filter: brightness(110%);
   }
   &:focus {
@@ -48,13 +48,14 @@ button {
 
 .button.outline {
   background-color: transparent;
-  color: #666;
+  @include lightDark(color, #666, #aaa);
   fill: currentColor;
   border: 1px solid #CCC;
   &:hover, &:focus, &:active {
     border: 1px solid #CCC;
     box-shadow: none;
     background-color: #F2F2F2;
+    @include lightDark(background-color, #f2f2f2, #555);
     filter: none;
   }
   &:active {
@@ -66,7 +67,7 @@ button {
 }
 
 .button + .button {
-  margin-left: $-s;
+  margin-inline-start: $-s;
 }
 
 .button.small {
@@ -84,7 +85,9 @@ button {
   font-size: 0.75rem;
   line-height: 1.4em;
   color: var(--color-primary);
-  fill: var(--color-primary);
+  @include whenDark {
+    color: #AAA;
+  }
   &:active {
     outline: 0;
   }
@@ -99,26 +102,28 @@ button {
 
 .button.block {
   width: 100%;
-  text-align: left;
+  text-align: start;
   display: block;
 }
 
 .button.icon {
   .svg-icon {
-    margin-right: 0;
+    margin-inline-end: 0;
   }
 }
 
 .button.svg {
+  display: flex;
+  align-items: center;
+  padding: $-s $-m;
+  padding-bottom: ($-s - 2px);
   svg {
     display: inline-block;
-    position: absolute;
-    left: $-m;
-    top: $-s - 2px;
     width: 24px;
     height: 24px;
+    bottom: auto;
+    margin-inline-end: $-m;
   }
-  padding: $-s $-m ($-s - 2px) ($-m*2 + 24px);
 }
 
 .button[disabled] {
index e3f7028eb349c1566019aff588df91656310d8b5..e419ab524e63112c1c383996cc326644e4b6ab4c 100644 (file)
@@ -2,8 +2,10 @@
 
 .CodeMirror {
   /* Set height, width, borders, and global font properties here */
+  font-family: monospace;
   height: 300px;
   color: black;
+  direction: ltr;
 }
 
 /* PADDING */
@@ -11,7 +13,8 @@
 .CodeMirror-lines {
   padding: 4px 0; /* Vertical padding around content */
 }
-.CodeMirror pre {
+.CodeMirror pre.CodeMirror-line,
+.CodeMirror pre.CodeMirror-line-like {
   padding: 0 4px; /* Horizontal padding of content */
 }
 
 .cm-fat-cursor div.CodeMirror-cursors {
   z-index: 1;
 }
-
+.cm-fat-cursor-mark {
+  background-color: rgba(20, 255, 20, 0.5);
+  -webkit-animation: blink 1.06s steps(1) infinite;
+  -moz-animation: blink 1.06s steps(1) infinite;
+  animation: blink 1.06s steps(1) infinite;
+}
 .cm-animate-fat-cursor {
   width: auto;
   border: 0;
@@ -89,7 +97,7 @@
 
 .CodeMirror-rulers {
   position: absolute;
-  left: 0; right: 0; top: -50px; bottom: -20px;
+  left: 0; right: 0; top: -50px; bottom: 0;
   overflow: hidden;
 }
 .CodeMirror-ruler {
 .cm-s-default .cm-property,
 .cm-s-default .cm-operator {}
 .cm-s-default .cm-variable-2 {color: #05a;}
-.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
 .cm-s-default .cm-comment {color: #a50;}
 .cm-s-default .cm-string {color: #a11;}
 .cm-s-default .cm-string-2 {color: #f50;}
 
 /* Default styles for common addons */
 
-div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
-div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
 .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
 .CodeMirror-activeline-background {background: #e8f2ff;}
 
@@ -156,17 +164,17 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 
 .CodeMirror-scroll {
   overflow: scroll !important; /* Things will break if this is overridden */
-  /* 30px is the magic margin used to hide the element's real scrollbars */
+  /* 50px is the magic margin used to hide the element's real scrollbars */
   /* See overflow: hidden in .CodeMirror */
-  margin-bottom: -30px; margin-right: -30px;
-  padding-bottom: 30px;
+  margin-bottom: -50px; margin-right: -50px;
+  padding-bottom: 50px;
   height: 100%;
   outline: none; /* Prevent dragging from highlighting the element */
   position: relative;
 }
 .CodeMirror-sizer {
   position: relative;
-  border-right: 30px solid transparent;
+  border-right: 50px solid transparent;
 }
 
 /* The fake, visible scrollbars. Used to force redraw during scrolling
@@ -176,6 +184,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   position: absolute;
   z-index: 6;
   display: none;
+  outline: none;
 }
 .CodeMirror-vscrollbar {
   right: 0; top: 0;
@@ -204,7 +213,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   height: 100%;
   display: inline-block;
   vertical-align: top;
-  margin-bottom: -30px;
+  margin-bottom: -50px;
 }
 .CodeMirror-gutter-wrapper {
   position: absolute;
@@ -229,11 +238,13 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   cursor: text;
   min-height: 1px; /* prevents collapsing before first draw */
 }
-.CodeMirror pre {
+.CodeMirror pre.CodeMirror-line,
+.CodeMirror pre.CodeMirror-line-like {
   /* Reset some styles that the rest of the page might have set */
   -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
   border-width: 0;
   background: transparent;
+  font-family: inherit;
   font-size: inherit;
   margin: 0;
   white-space: pre;
@@ -246,12 +257,9 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   -webkit-tap-highlight-color: transparent;
   -webkit-font-variant-ligatures: contextual;
   font-variant-ligatures: contextual;
-  &:after {
-    content: none;
-    display: none;
-  }
 }
-.CodeMirror-wrap pre {
+.CodeMirror-wrap pre.CodeMirror-line,
+.CodeMirror-wrap pre.CodeMirror-line-like {
   word-wrap: break-word;
   white-space: pre-wrap;
   word-break: normal;
@@ -266,7 +274,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 .CodeMirror-linewidget {
   position: relative;
   z-index: 2;
-  overflow: auto;
+  padding: 0.1px; /* Force widget margins to stay inside of the container */
 }
 
 .CodeMirror-widget {}
@@ -321,8 +329,8 @@ div.CodeMirror-dragcursors {
 .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
 
 .cm-searching {
-  background: #ffa;
-  background: rgba(255, 255, 0, .4);
+  background-color: #ffa;
+  background-color: rgba(255, 255, 0, .4);
 }
 
 /* Used to force a border model for a node */
@@ -341,46 +349,65 @@ div.CodeMirror-dragcursors {
 /* Help users use markselection to safely style text background */
 span.CodeMirror-selectedtext { background: none; }
 
+/* STOP */
 
-/*
-
-    Name:       Base16 Default Light
-    Author:     Chris Kempson (http://chriskempson.com)
-
-    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)
-    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)
-
-*/
-
-.cm-s-base16-light.CodeMirror { background: #f8f8f8; color: #444444; }
-.cm-s-base16-light div.CodeMirror-selected { background: #e0e0e0; }
-.cm-s-base16-light .CodeMirror-line::selection, .cm-s-base16-light .CodeMirror-line > span::selection, .cm-s-base16-light .CodeMirror-line > span > span::selection { background: #e0e0e0; }
-.cm-s-base16-light .CodeMirror-line::-moz-selection, .cm-s-base16-light .CodeMirror-line > span::-moz-selection, .cm-s-base16-light .CodeMirror-line > span > span::-moz-selection { background: #e0e0e0; }
-.cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 0px; }
-.cm-s-base16-light .CodeMirror-guttermarker { color: #ac4142; }
-.cm-s-base16-light .CodeMirror-guttermarker-subtle { color: #b0b0b0; }
-.cm-s-base16-light .CodeMirror-linenumber { color: #b0b0b0; }
-.cm-s-base16-light .CodeMirror-cursor { border-left: 1px solid #505050; }
-
-.cm-s-base16-light span.cm-comment { color: #8f5536; }
-.cm-s-base16-light span.cm-atom { color: #aa759f; }
-.cm-s-base16-light span.cm-number { color: #aa759f; }
-
-.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #678c30; }
-.cm-s-base16-light span.cm-keyword { color: #ac4142; }
-.cm-s-base16-light span.cm-string { color: #e09c3c; }
+/**
+ * Codemirror Darcula theme
+ */
 
-.cm-s-base16-light span.cm-builtin { color: #4c7f9e; }
-.cm-s-base16-light span.cm-variable { color: #90a959; }
-.cm-s-base16-light span.cm-variable-2 { color: #6a9fb5; }
-.cm-s-base16-light span.cm-def { color: #d28445; }
-.cm-s-base16-light span.cm-bracket { color: #202020; }
-.cm-s-base16-light span.cm-tag { color: #ac4142; }
-.cm-s-base16-light span.cm-link { color: #aa759f; }
-.cm-s-base16-light span.cm-error { background: #ac4142; color: #505050; }
+/**
+    Name: IntelliJ IDEA darcula theme
+    From IntelliJ IDEA by JetBrains
+ */
 
-.cm-s-base16-light .CodeMirror-activeline-background { background: #DDDCDC; }
-.cm-s-base16-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }
+.cm-s-darcula  { font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;}
+.cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; }
+
+.cm-s-darcula span.cm-meta { color: #BBB529; }
+.cm-s-darcula span.cm-number { color: #6897BB; }
+.cm-s-darcula span.cm-keyword { color: #CC7832; line-height: 1em; font-weight: bold; }
+.cm-s-darcula span.cm-def { color: #A9B7C6; font-style: italic; }
+.cm-s-darcula span.cm-variable { color: #A9B7C6; }
+.cm-s-darcula span.cm-variable-2 { color: #A9B7C6; }
+.cm-s-darcula span.cm-variable-3 { color: #9876AA; }
+.cm-s-darcula span.cm-type { color: #AABBCC; font-weight: bold; }
+.cm-s-darcula span.cm-property { color: #FFC66D; }
+.cm-s-darcula span.cm-operator { color: #A9B7C6; }
+.cm-s-darcula span.cm-string { color: #6A8759; }
+.cm-s-darcula span.cm-string-2 { color: #6A8759; }
+.cm-s-darcula span.cm-comment { color: #61A151; font-style: italic; }
+.cm-s-darcula span.cm-link { color: #CC7832; }
+.cm-s-darcula span.cm-atom { color: #CC7832; }
+.cm-s-darcula span.cm-error { color: #BC3F3C; }
+.cm-s-darcula span.cm-tag { color: #629755; font-weight: bold; font-style: italic; text-decoration: underline; }
+.cm-s-darcula span.cm-attribute { color: #6897bb; }
+.cm-s-darcula span.cm-qualifier { color: #6A8759; }
+.cm-s-darcula span.cm-bracket { color: #A9B7C6; }
+.cm-s-darcula span.cm-builtin { color: #FF9E59; }
+.cm-s-darcula span.cm-special { color: #FF9E59; }
+.cm-s-darcula span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;}
+.cm-s-darcula span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;}
+
+.cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #A9B7C6; }
+.cm-s-darcula .CodeMirror-activeline-background { background: #323232; }
+.cm-s-darcula .CodeMirror-gutters { background: #313335; border-right: 1px solid #313335; }
+.cm-s-darcula .CodeMirror-guttermarker { color: #FFEE80; }
+.cm-s-darcula .CodeMirror-guttermarker-subtle { color: #D0D0D0; }
+.cm-s-darcula .CodeMirrir-linenumber { color: #606366; }
+.cm-s-darcula .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; }
+
+.cm-s-darcula div.CodeMirror-selected { background: #214283; }
+
+.CodeMirror-hints.darcula {
+  font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
+  color: #9C9E9E;
+  background-color: #3B3E3F !important;
+}
+
+.CodeMirror-hints.darcula .CodeMirror-hint-active {
+  background-color: #494D4E !important;
+  color: #9C9E9E !important;
+}
 
 /**
  * Custom BookStack overrides
@@ -392,9 +419,17 @@ span.CodeMirror-selectedtext { background: none; }
   font-size: 12px;
   height: auto;
   margin-bottom: $-l;
-  border: 1px solid #DDD;;
+  border: 1px solid;
+  @include lightDark(border-color, #DDD, #111);
+}
+.CodeMirror pre::after {
+  display: none;
+}
+html.dark-mode .CodeMirror pre {
+  background-color: transparent;
 }
-.cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 1px solid #DDD; }
+
+.cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; border-left: 0; color: #333; }
 
 .code-fill .CodeMirror {
   position: absolute;
@@ -403,6 +438,7 @@ span.CodeMirror-selectedtext { background: none; }
   left: 0;
   width: 100%;
   height: 100%;
+  margin-bottom: 0;
 }
 
 /**
@@ -413,24 +449,25 @@ span.CodeMirror-selectedtext { background: none; }
   top: -1px;
   right: -1px;
   background-color: #EEE;
+  border: 1px solid #DDD;
+  @include lightDark(background-color, #eee, #333);
+  @include lightDark(border-color, #ddd, #444);
+  @include lightDark(fill, #444, #888);
   padding: $-xs;
   line-height: 0;
-  border: 1px solid #DDD;
   cursor: pointer;
-  fill: #444;
   z-index: 5;
-  transition: all ease-in 240ms;
   user-select: none;
   opacity: 0;
   pointer-events: none;
   svg {
-    transition: transform ease-in 240ms;
+    transition: all ease-in 240ms;
     transform: translateY(0);
   }
   &.success {
     background-color: lighten($positive, 10%);
-    fill: #FFF;
     svg {
+      fill: #FFF;
       transform: translateY(-3px);
     }
   }
index 8623d374af4b1f63ffa4a9f8a352b26b1de6247a..c51f0165922b3c274e200fa79585a2ca246dc158 100644 (file)
@@ -7,6 +7,12 @@
 }
 .primary-background-light {
   background-color: var(--color-primary-light);
+  @include whenDark {
+    background: #000;
+    .text-primary {
+      color: #AAA !important;
+    }
+  }
 }
 
 /*
 }
 
 .text-muted {
-  color: #575757 !important;
-  fill: #575757 !important;
+  @include lightDark(color, #575757, #888888, true);
+  fill: currentColor !important;
+}
+
+.text-dark {
+  @include lightDark(color, #222, #ccc, true);
+  fill: currentColor !important;
+}
+
+.text-white {
+  color: #fff;
+  fill: currentColor !important;
 }
 
 /*
@@ -76,6 +92,6 @@
 .bg-chapter {
   background-color: var(--color-chapter);
 }
-.bg-shelf {
+.bg-bookshelf {
   background-color: var(--color-bookshelf);
-}
\ No newline at end of file
+}
index 2085e06ea4c4aeb35e9bcb09d1fdd771eb6808c1..ede26c51ca3d16bd68530105bfab92b4482c5aaa 100644 (file)
@@ -6,8 +6,9 @@
   margin: $-xl;
   padding: $-m $-l;
   background-color: #FFF;
+  @include lightDark(background-color, #fff, #444);
   border-radius: 4px;
-  border-left: 6px solid currentColor;
+  border-inline-start: 6px solid currentColor;
   box-shadow: $bs-large;
   z-index: 999999;
   cursor: pointer;
@@ -15,7 +16,7 @@
   transition: transform ease-in-out 280ms;
   transform: translateX(580px);
   display: grid;
-  grid-template-columns: 42px 1fr;
+  grid-template-columns: 42px 1fr 12px;
   color: #444;
   font-weight: 700;
   span, svg {
   svg {
     width: 2.8rem;
     height: 2.8rem;
-    padding-right: $-s;
+    padding-inline-end: $-s;
     fill: currentColor;
   }
+  .dismiss {
+    margin-top: -8px;
+    svg {
+      height: 1.0rem;
+      @include lightDark(color, #444, #888);
+    }
+  }
   span {
     vertical-align: middle;
     line-height: 1.3;
+    @include whenDark {
+      color: #BBB;
+    }
   }
   &.pos {
     color: $positive;
@@ -56,7 +67,7 @@
   transition: all ease-in-out 180ms;
   user-select: none;
   svg[data-icon="caret-right"] {
-    margin-right: 0;
+    margin-inline-end: 0;
     font-size: 1rem;
     transition: all ease-in-out 180ms;
     transform: rotate(0deg);
     transform: rotate(90deg);
   }
   svg[data-icon="caret-right"] + * {
-    margin-left: $-xs;
+    margin-inline-start: $-xs;
   }
 }
 
-[overlay] {
-  background-color: rgba(0, 0, 0, 0.333);
+[overlay], .popup-background {
+  @include lightDark(background-color, rgba(0, 0, 0, 0.333), rgba(0, 0, 0, 0.6));
   position: fixed;
   z-index: 95536;
   width: 100%;
 }
 
 .popup-body {
-  background-color: #FFF;
+  @include lightDark(background-color, #fff, #333);
   max-height: 90%;
-  width: 1200px;
+  max-width: 1200px;
+  width: 90%;
   height: auto;
-  margin: 2% 5%;
+  margin: 2% auto;
   border-radius: 4px;
   box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.3);
   overflow: hidden;
@@ -167,7 +179,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 
 .dropzone-container {
   position: relative;
-  background-color: #EEE;
+  @include lightDark(background-color, #eee, #222);
   background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3E%3Cpath fill='%23a9a9a9' fill-opacity='0.52' d='M1 3h1v1H1V3zm2-2h1v1H3V1z'%3E%3C/path%3E%3C/svg%3E");
 }
 
@@ -180,16 +192,13 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   cursor: pointer;
   width: (100%/6);
   height: auto;
-  border: 1px solid #DDD;
+  @include lightDark(border-color, #ddd, #000);
   box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
   transition: all cubic-bezier(.4, 0, 1, 1) 160ms;
   overflow: hidden;
   &.selected {
-    //transform: scale3d(0.92, 0.92, 0.92);
-    border: 4px solid #FFF;
-    overflow: hidden;
-    border-radius: 8px;
-    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
+    transform: scale3d(0.92, 0.92, 0.92);
+    outline: currentColor 2px solid;
   }
   img {
     width: 100%;
@@ -219,10 +228,10 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   }
 }
 
-#image-manager .load-more {
+.image-manager .load-more {
   display: block;
   text-align: center;
-  background-color: #EEE;
+  @include lightDark(background-color, #EEE, #444);
   padding: $-s $-m;
   color: #AAA;
   clear: both;
@@ -231,12 +240,18 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   font-style: italic;
 }
 
+.image-manager .loading-container {
+  text-align: center;
+}
+
 .image-manager-sidebar {
   width: 300px;
   overflow-y: auto;
   overflow-x: hidden;
-  border-left: 1px solid #DDD;
+  border-inline-start: 1px solid #DDD;
+  @include lightDark(border-color, #ddd, #000);
   .inner {
+    min-height: auto;
     padding: $-m;
   }
   img {
@@ -257,6 +272,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   }
   .dropzone-container {
     border-bottom: 1px solid #DDD;
+    @include lightDark(border-color, #ddd, #000);
   }
 }
 
@@ -277,6 +293,12 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   }
 }
 
+.image-manager .corner-button {
+  margin: 0;
+  border-radius: 0;
+  padding: $-m;
+}
+
 // Dropzone
 /*
  * The MIT License
@@ -284,7 +306,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
  */
 .dz-message {
   font-size: 1em;
-  line-height: 2.35;
+  line-height: 2.85;
   font-style: italic;
   color: #888;
   text-align: center;
@@ -469,7 +491,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   display: block;
   top: 50%;
   left: 50%;
-  margin-left: -27px;
+  margin-inline-start: -27px;
   margin-top: -35px;
 }
 
@@ -503,7 +525,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   top: 50%;
   margin-top: -8px;
   width: 80px;
-  margin-left: -40px;
+  margin-inline-start: -40px;
   background: rgba(255, 255, 255, 0.9);
   transform: scale(1);
   border-radius: 8px;
@@ -560,19 +582,20 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   left: 44px;
   width: 0;
   height: 0;
-  border-left: 6px solid transparent;
-  border-right: 6px solid transparent;
+  border-inline-start: 6px solid transparent;
+  border-inline-end: 6px solid transparent;
   border-bottom: 6px solid $negative;
 }
 
 
 .tab-container .nav-tabs {
-  text-align: left;
+  text-align: start;
   border-bottom: 1px solid #DDD;
+  @include lightDark(border-color, #ddd, #444);
   margin-bottom: $-m;
   .tab-item {
     padding: $-s;
-    color: #666;
+    @include lightDark(color, #666, #999);
     &.selected {
       border-bottom-width: 3px;
     }
@@ -584,12 +607,16 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   a, .tab-item {
     padding: $-m;
     display: inline-block;
-    color: #666;
-    fill: #666;
+    @include lightDark(color, #666, #999);
     cursor: pointer;
+    border-right: 1px solid rgba(0, 0, 0, 0.1);
+    border-bottom: 2px solid transparent;
     &.selected {
       border-bottom: 2px solid var(--color-primary);
     }
+    &:last-child {
+      border-right: 0;
+    }
   }
 }
 
@@ -597,32 +624,33 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   display: none;
 }
 
-#code-editor .CodeMirror {
+.code-editor .CodeMirror {
   height: 400px;
 }
 
-#code-editor .lang-options {
+.code-editor .lang-options {
   max-width: 480px;
   margin-bottom: $-s;
   a {
-    margin-right: $-xs;
+    margin-inline-end: $-xs;
     text-decoration: underline;
   }
 }
 
 @include smaller-than($m) {
-  #code-editor .lang-options {
+  .code-editor .lang-options {
     max-width: 100%;
   }
-  #code-editor .CodeMirror {
+  .code-editor .CodeMirror {
     height: 200px;
   }
 }
 
 .comment-box {
-  border: 1px solid #DDD;
   border-radius: 4px;
-  background-color: #FFF;
+  border: 1px solid #DDD;
+  @include lightDark(border-color, #ddd, #000);
+  @include lightDark(background-color, #FFF, #222);
   .content {
     font-size: 0.666em;
     p, ul, ol {
@@ -651,7 +679,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
     }
     a { color: #666; }
     span {
-      padding-left: $-xxs;
+      padding-inline-start: $-xxs;
     }
   }
   .text-muted {
@@ -684,7 +712,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
     height: 100%;
     display: flex;
     flex-direction: column;
-    border-left: 1px solid #DDD;
+    border-inline-start: 1px solid #DDD;
   }
   .template-item-actions button {
     cursor: pointer;
@@ -696,4 +724,65 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   .template-item-actions button:first-child {
     border-top: 0;
   }
+}
+
+.dropdown-search-dropdown {
+  box-shadow: $bs-med;
+  overflow: hidden;
+  min-height: 100px;
+  width: 240px;
+  display: none;
+  position: absolute;
+  z-index: 80;
+  right: -$-m;
+  @include rtl {
+    right: auto;
+    left: -$-m;
+  }
+  .dropdown-search-search .svg-icon {
+    position: absolute;
+    left: $-s;
+    @include rtl {
+      right: $-s;
+      left: auto;
+    }
+    top: 11px;
+    fill: #888;
+    pointer-events: none;
+  }
+  .dropdown-search-list {
+    max-height: 400px;
+    overflow-y: scroll;
+    text-align: start;
+  }
+  .dropdown-search-item {
+    padding: $-s $-m;
+    &:hover,&:focus {
+      background-color: #F2F2F2;
+      text-decoration: none;
+    }
+  }
+  input {
+    padding-inline-start: $-xl;
+    border-radius: 0;
+    border: 0;
+    border-bottom: 1px solid #DDD;
+  }
+}
+
+@include smaller-than($m) {
+  .dropdown-search-dropdown {
+    position: fixed;
+    right: auto;
+    left: $-m;
+  }
+  .dropdown-search-dropdown .dropdown-search-list {
+    max-height: 240px;
+  }
+}
+
+.custom-select-input {
+  max-width: 280px;
+  border: 1px solid #DDD;
+  border-radius: 4px;
 }
\ No newline at end of file
index 64308b29e725bbe0470189b0e259e31630ee0b73..11ea1cc7f89bc6b29ff52001613b10e568f868e0 100644 (file)
@@ -1,12 +1,13 @@
 
 .input-base {
-  background-color: #FFF;
   border-radius: 3px;
   border: 1px solid #D4D4D4;
+  @include lightDark(background-color, #fff, #333);
+  @include lightDark(border-color, #d4d4d4, #111);
+  @include lightDark(color, #666, #AAA);
   display: inline-block;
-  font-size: $fs-s;
+  font-size: $fs-m;
   padding: $-xs*1.5;
-  color: #666;
   width: 250px;
   max-width: 100%;
 
@@ -19,6 +20,9 @@
   &.disabled, &[disabled] {
     background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAMUlEQVQIW2NkwAGuXbv2nxGbHEhCS0uLEUMSJgHShCKJLIEiiS4Bl8QmAZbEJQGSBAC62BuJ+tt7zgAAAABJRU5ErkJggg==);
   }
+  &[readonly] {
+    background-color: #f8f8f8;
+  }
   &:focus {
     border-color: var(--color-primary);
     outline: 1px solid var(--color-primary);
     display: flex;
     flex-direction: column;
     border: 1px solid #DDD;
+    @include lightDark(border-color, #ddd, #000);
     width: 50%;
     max-width: 50%;
   }
+  &.fullscreen {
+    position: fixed;
+    top: 0;
+    left: 0;
+    height: 100%;
+    z-index: 2;
+  }
 }
 
 @include smaller-than($m) {
@@ -71,6 +83,7 @@
   #markdown-editor .markdown-editor-wrap {
     width: 100%;
     max-width: 100%;
+    flex-grow: 1;
   }
   #markdown-editor .editor-toolbar {
     padding: 0;
     border-bottom: 1px solid #DDD;
     display: block;
   }
-  .markdown-editor-wrap:not(.active) .editor-toolbar + div, .markdown-editor-wrap:not(.active) .editor-toolbar .buttons {
+  .markdown-editor-wrap:not(.active) .editor-toolbar + div,
+  .markdown-editor-wrap:not(.active) .editor-toolbar .buttons,
+  .markdown-editor-wrap:not(.active) .markdown-display {
     display: none;
   }
   #markdown-editor .markdown-editor-wrap:not(.active) {
     flex-grow: 0;
     flex: none;
+    min-height: 0;
   }
 }
 
 .markdown-display {
-  margin-left: -1px;
+  margin-inline-start: -1px;
 }
 
 .markdown-editor-display {
-  background-color: #FFFFFF;
+  background-color: #fff;
   body {
-    background-color: #FFFFFF;
-    padding-left: 16px;
-    padding-right: 16px;
+    background-color: #fff;
+    padding-inline-start: 16px;
+    padding-inline-end: 16px;
   }
   [drawio-diagram]:hover {
     outline: 2px solid var(--color-primary);
   }
 }
 
+html.markdown-editor-display.dark-mode {
+  background-color: #222;
+  body {
+    background-color: #222;
+  }
+}
+
 .editor-toolbar {
   width: 100%;
   padding: $-xs $-m;
   line-height: 1.6;
   border-bottom: 1px solid #DDD;
   background-color: #EEE;
+  @include lightDark(background-color, #eee, #111);
+  @include lightDark(border-color, #ddd, #000);
   flex: none;
   &:after {
     content: '';
     display: block;
     clear: both;
   }
+  @include whenDark {
+    button {
+      color: #AAA;
+    }
+  }
 }
 
 
 label {
+  @include lightDark(color, #666, #ddd);
   display: block;
   line-height: 1.4em;
   font-size: 0.94em;
   font-weight: 400;
-  color: #666;
   padding-bottom: 2px;
   margin-bottom: 0.2em;
   &.inline {
@@ -141,12 +171,12 @@ label.radio, label.checkbox {
   font-weight: 400;
   user-select: none;
   input[type="radio"], input[type="checkbox"] {
-    margin-right: $-xs;
+    margin-inline-end: $-xs;
   }
 }
 
 label.inline.checkbox {
-  margin-right: $-m;
+  margin-inline-end: $-m;
 }
 
 label + p.small {
@@ -172,6 +202,11 @@ input[type=date] {
 
 input[type=color] {
   height: 60px;
+  &.small {
+    height: 42px;
+    width: 60px;
+    padding: 2px;
+  }
 }
 
 .toggle-switch {
@@ -269,12 +304,12 @@ input[type=color] {
   border: 1px solid #DDD;
   border-radius: 4px;
   .collapse-title {
-    margin-left: -$-m;
-    margin-right: -$-m;
+    margin-inline-start: -$-m;
+    margin-inline-end: -$-m;
     padding: $-s $-m;
     display: block;
     width: calc(100% + 32px);
-    text-align: left;
+    text-align: start;
   }
   .collapse-title, .collapse-title label {
     cursor: pointer;
@@ -287,7 +322,7 @@ input[type=color] {
   .collapse-title label:before {
     display: inline-block;
     content: '▸';
-    margin-right: $-m;
+    margin-inline-end: $-m;
     transition: all ease-in-out 400ms;
     transform: rotate(0);
   }
@@ -314,6 +349,7 @@ input[type=color] {
 
 .title-input.page-title {
   font-size: 0.8em;
+  @include lightDark(background-color, #fff, #333);
   .input {
     border: 0;
     margin-bottom: -1px;
@@ -340,6 +376,7 @@ input[type=color] {
 
 div[editor-type="markdown"] .title-input.page-title input[type="text"] {
   max-width: 100%;
+  border-radius: 0;
 }
 
 .search-box {
@@ -348,16 +385,20 @@ div[editor-type="markdown"] .title-input.page-title input[type="text"] {
   button {
     background-color: transparent;
     border: none;
-    fill: #666;
+    @include lightDark(color, #666, #AAA);
     padding: 0;
     cursor: pointer;
     position: absolute;
     left: 8px;
     top: 9px;
+    @include rtl {
+      right: 8px;
+      left: auto;
+    }
   }
   input {
     display: block;
-    padding-left: $-l + 4px;
+    padding-inline-start: $-l + 4px;
     width: 300px;
     max-width: 100%;
   }
index 687ddd8d2259c83dcf0cb94530e7b34b7d5e9b9d..246ef4b5bdabdfba587b93d96b363edd67407808 100644 (file)
@@ -18,11 +18,14 @@ header {
   display: block;
   z-index: 11;
   top: 0;
-  color: #fff;
-  fill: #fff;
+  color: rgb(250, 250, 250);
   border-bottom: 1px solid #DDD;
   box-shadow: $bs-card;
   padding: $-xxs 0;
+  @include lightDark(border-bottom-color, #DDD, #000);
+  @include whenDark {
+    filter: saturate(0.8) brightness(0.8);
+  }
   .links {
     display: inline-block;
     vertical-align: top;
@@ -31,11 +34,10 @@ header {
     display: inline-block;
     padding: $-m;
     color: #FFF;
-    fill: #FFF;
   }
   .dropdown-container {
-    padding-left: $-m;
-    padding-right: 0;
+    padding-inline-start: $-m;
+    padding-inline-end: 0;
   }
   .avatar, .user-name {
     display: inline-block;
@@ -53,7 +55,7 @@ header {
       vertical-align: top;
     }
     > span {
-      padding-left: $-xs;
+      padding-inline-start: $-xs;
       display: inline-block;
       padding-top: $-xxs;
     }
@@ -62,7 +64,7 @@ header {
       font-size: 18px;
     }
     @include between($l, $xl) {
-      padding-left: $-xs;
+      padding-inline-start: $-xs;
       .name {
         display: none;
       }
@@ -87,18 +89,22 @@ header .search-box {
     border-radius: 40px;
     color: #EEE;
     z-index: 2;
-    padding-left: 40px;
+    padding-inline-start: 40px;
     &:focus {
       outline: none;
       border: 1px solid rgba(255, 255, 255, 0.6);
     }
   }
   button {
-    fill: #EEE;
     z-index: 1;
     left: 16px;
+    @include lightDark(color, rgba(255, 255, 255, 0.8), #AAA);
+    @include rtl {
+      left: auto;
+      right: 16px;
+    }
     svg {
-      margin-right: 0;
+      margin-block-end: 0;
     }
   }
   ::-webkit-input-placeholder { /* Chrome/Opera/Safari */
@@ -124,12 +130,12 @@ header .search-box {
   font-size: 1.8em;
   color: #fff;
   font-weight: 400;
-  padding: 14px $-l 14px 0;
+  @include padding(14px, $-l, 14px, 0);
   vertical-align: top;
   line-height: 1;
 }
 .logo-image {
-  margin: $-xs $-s $-xs 0;
+  @include margin($-xs, $-s, $-xs, 0);
   vertical-align: top;
   height: 43px;
 }
@@ -151,12 +157,18 @@ header .search-box {
     margin: 0;
     bottom: -2px;
   }
+  @include rtl() {
+    left: $-m;
+    right: auto;
+  }
 }
 
+
+
 @include smaller-than($l) {
   header .header-links {
+    @include lightDark(background-color, #fff, #333);
     display: none;
-    background-color: #FFF;
     z-index: 10;
     right: $-m;
     border-radius: 4px;
@@ -168,25 +180,24 @@ header .search-box {
       display: block;
     }
   }
-  header .links a, header .dropdown-container ul li a {
-    text-align: left;
+  header .links a, header .dropdown-container ul li a, header .dropdown-container ul li button {
+    text-align: start;
     display: block;
     padding: $-s $-m;
     color: $text-dark;
-    fill: $text-dark;
+    @include lightDark(color, $text-dark, #eee);
     svg {
-      margin-right: $-s;
+      margin-inline-end: $-s;
     }
     &:hover {
-      background-color: #EEE;
-      color: #444;
-      fill: #444;
+      @include lightDark(background-color, #eee, #333);
+      @include lightDark(color, #000, #fff);
       text-decoration: none;
     }
   }
   header .dropdown-container {
     display: block;
-    padding-left: 0;
+    padding-inline-start: 0;
   }
   header .links {
     display: block;
@@ -215,7 +226,7 @@ header .search-box {
   border-bottom: 3px solid #BBB;
   cursor: pointer;
   &:first-child {
-    border-right: 1px solid #DDD;
+    border-inline-end: 1px solid #DDD;
   }
   &.active {
     border-bottom-color: currentColor;
@@ -253,14 +264,14 @@ header .search-box {
       display: none;
     }
     > span:first-child {
-      margin-right: 0;
+      margin-inline-end: 0;
     }
   }
 }
 
-.breadcrumb-listing {
+.dropdown-search {
   position: relative;
-  .breadcrumb-listing-toggle {
+  .dropdown-search-toggle {
     padding: 6px;
     border: 1px solid transparent;
     border-radius: 4px;
@@ -269,47 +280,7 @@ header .search-box {
     }
   }
   .svg-icon {
-    margin-right: 0;
-  }
-}
-
-.breadcrumb-listing-dropdown {
-  box-shadow: $bs-med;
-  overflow: hidden;
-  min-height: 100px;
-  width: 240px;
-  display: none;
-  position: absolute;
-  z-index: 80;
-  right: -$-m;
-  .breadcrumb-listing-search .svg-icon {
-    position: absolute;
-    left: $-s;
-    top: 11px;
-    fill: #888;
-    pointer-events: none;
-  }
-  .breadcrumb-listing-entity-list {
-    max-height: 400px;
-    overflow-y: scroll;
-    text-align: left;
-  }
-  input {
-    padding-left: $-xl;
-    border-radius: 0;
-    border: 0;
-    border-bottom: 1px solid #DDD;
-  }
-}
-
-@include smaller-than($m) {
-  .breadcrumb-listing-dropdown {
-    position: fixed;
-    right: auto;
-    left: $-m;
-  }
-  .breadcrumb-listing-dropdown .breadcrumb-listing-entity-list {
-    max-height: 240px;
+    margin-inline-end: 0;
   }
 }
 
@@ -337,25 +308,25 @@ header .search-box {
   display: inline-block;
   padding: $-xs $-s;
   &:last-child {
-    padding-right: 0;
+    padding-inline-end: 0;
   }
   &:first-child {
-    padding-left: 0;
+    padding-inline-start: 0;
   }
 }
 
 
 .action-buttons .dropdown-container:last-child a {
-  padding-right: 0;
-  padding-left: $-s;
+  padding-inline-end: 0;
+  padding-inline-start: $-s;
 }
 .action-buttons {
-  text-align: right;
+  text-align: end;
   &.text-left {
-    text-align: left;
+    text-align: start;
     .text-button {
-      padding-right: $-m;
-      padding-left: 0;
+      padding-inline-end: $-m;
+      padding-inline-start: 0;
     }
   }
   &.text-center {
@@ -368,6 +339,6 @@ header .search-box {
     padding: $-xs $-xs;
   }
   .action-buttons .dropdown-container:last-child a {
-    padding-left: $-xs;
+    padding-inline-start: $-xs;
   }
 }
\ No newline at end of file
index de48c8ed1bbe8eac685067c399c583faafe0abdc..57869d6520b04aa6a90ea5405c36d8a95de0927f 100644 (file)
@@ -1,6 +1,7 @@
 * {
   box-sizing: border-box;
-  outline-color: #444444;
+  outline-color: var(--color-primary);
+  outline-width: 1px;
 }
 
 *:focus {
@@ -14,12 +15,14 @@ html {
   &.flexbox {
     overflow-y: hidden;
   }
+  &.dark-mode {
+    background-color: #111;
+  }
 }
 
 body {
   font-size: $fs-m;
   line-height: 1.6;
-  color: #444;
+  @include lightDark(color, #444, #AAA);
   -webkit-font-smoothing: antialiased;
-  background-color: #F2F2F2;
 }
\ No newline at end of file
index 1a7ff2cab029e615c9f7ad7025b4b4fcc8bc2b9a..4873ff2da651ee1575a048a4973f95069fa0a343 100644 (file)
@@ -4,10 +4,10 @@
  */
 .container {
   max-width: $xxl;
-  margin-left: auto;
-  margin-right: auto;
-  padding-left: $-m;
-  padding-right: $-m;
+  margin-inline-start: auto;
+  margin-inline-end: auto;
+  padding-inline-start: $-m;
+  padding-inline-end: $-m;
   &.small {
     max-width: 840px;
   }
@@ -49,6 +49,9 @@
   &.v-center {
     align-items: center;
   }
+  &.v-end {
+    align-items: end;
+  }
   &.no-gap {
     grid-row-gap: 0;
     grid-column-gap: 0;
@@ -116,12 +119,42 @@ body.flexbox {
   min-height: 0;
   max-width: 100%;
   position: relative;
-  overflow-y: hidden;
+}
+
+.flex-container-row {
+  display: flex;
+  flex-direction: row;
+  &.v-center {
+    align-items: center;
+  }
+}
+
+.flex-container-column {
+  display: flex;
+  flex-direction: column;
+}
+
+.flex-container-column.wrap, .flex-container-row.wrap {
+  flex-wrap: wrap;
 }
 
 .flex {
   min-height: 0;
   flex: 1;
+  &.fit-content {
+    flex-basis: auto;
+    flex-grow: 0;
+  }
+}
+
+.justify-flex-end {
+  justify-content: flex-end;
+}
+.justify-center {
+  justify-content: center;
+}
+.items-center {
+  align-items: center;
 }
 
 
@@ -129,20 +162,24 @@ body.flexbox {
  * Display and float utilities
  */
 .block {
-  display: block;
+  display: block !important;
   position: relative;
 }
 
 .inline {
-  display: inline;
+  display: inline !important;
 }
 
 .block.inline {
-  display: inline-block;
+  display: inline-block !important;
 }
 
 .hidden {
-  display: none;
+  display: none !important;
+}
+
+.fill-height {
+  height: 100%;
 }
 
 .float {
@@ -185,12 +222,12 @@ body.flexbox {
 /**
  * Fixes
  */
-.clearfix:before,
-.clearfix:after {
+.clearfix::before,
+.clearfix::after {
   content: " ";
   display: table;
 }
-.clearfix:after {
+.clearfix::after {
   clear: both;
 }
 
@@ -199,8 +236,8 @@ body.flexbox {
  */
 .tri-layout-container {
   display: grid;
-  margin-left: $-xl;
-  margin-right: $-xl;
+  margin-inline-start: $-xl;
+  margin-inline-end: $-xl;
   grid-template-columns: 1fr 4fr 1fr;
   grid-template-areas: "a b c";
   grid-column-gap: $-xxl;
@@ -224,7 +261,7 @@ body.flexbox {
     ". b b";
     grid-template-columns: 1fr 3fr;
     grid-template-rows: min-content min-content 1fr;
-    padding-right: $-l;
+    padding-inline-end: $-l;
   }
 }
 @include between($l, $xxl) {
@@ -242,6 +279,7 @@ body.flexbox {
     min-height: 50vh;
     overflow-y: scroll;
     overflow-x: hidden;
+    height: 100%;
     scrollbar-width: none;
     -ms-overflow-style: none;
     &::-webkit-scrollbar {
@@ -259,11 +297,11 @@ body.flexbox {
     grid-template-areas:  none;
     grid-template-columns: 1fr;
     grid-column-gap: 0;
-    padding-right: $-xs;
-    padding-left: $-xs;
+    padding-inline-end: $-xs;
+    padding-inline-start: $-xs;
     .tri-layout-left-contents, .tri-layout-right-contents {
-      padding-left: $-m;
-      padding-right: $-m;
+      padding-inline-start: $-m;
+      padding-inline-end: $-m;
     }
     .tri-layout-left > *, .tri-layout-right > * {
       display: none;
@@ -317,7 +355,7 @@ body.flexbox {
 
 @include smaller-than($m) {
   .tri-layout-container {
-    margin-left: 0;
-    margin-right: 0;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
   }
-}
\ No newline at end of file
+}
index 2e8fa257aebcf270e87bf438b00659ca85efcebd..a3a58e6c6cd5c66f678d2681d6744f6847748a31 100644 (file)
@@ -6,7 +6,7 @@
     justify-self: stretch;
     align-self: stretch;
     height: auto;
-    margin-right: $-l;
+    margin-inline-end: $-l;
   }
   .icon:after {
     opacity: 0.5;
@@ -60,7 +60,7 @@
     border-radius: 0 4px 4px 0;
     padding: $-xs $-m;
     width: 100%;
-    text-align: left;
+    text-align: start;
   }
   .chapter-expansion-toggle:hover {
     background-color: rgba(0, 0, 0, 0.06);
 .sidebar-page-nav {
   $nav-indent: $-m;
   list-style: none;
-  margin: $-s 0 $-m $-xs;
+  @include margin($-s, 0, $-m, $-xs);
   position: relative;
   &:after {
     content: '';
     display: block;
     position: absolute;
     left: 0;
-    background-color: rgba(0, 0, 0, 0.2);
+    @include rtl {
+      left: auto;
+      right: 0;
+    }
+    @include lightDark(background-color, rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2));
     width: 2px;
     top: 5px;
     bottom: 5px;
     position: relative;
   }
   .h1 {
-    padding-left: $nav-indent;
+    padding-inline-start: $nav-indent;
   }
   .h2 {
-    padding-left: $nav-indent * 1.5;
+    padding-inline-start: $nav-indent * 1.5;
   }
   .h3 {
-    padding-left: $nav-indent * 2;
+    padding-inline-start: $nav-indent * 2;
   }
   .h4 {
-    padding-left: $nav-indent * 2.5;
+    padding-inline-start: $nav-indent * 2.5;
   }
   .h5 {
-    padding-left: $nav-indent*3;
+    padding-inline-start: $nav-indent*3;
   }
   .h6 {
-    padding-left: $nav-indent*3.5;
+    padding-inline-start: $nav-indent*3.5;
   }
   .current-heading {
     font-weight: bold;
   }
   li:not(.current-heading) .sidebar-page-nav-bullet {
-    background-color: #BBB !important;
+    @include lightDark(background-color, #BBB, #666, true);
   }
   .sidebar-page-nav-bullet {
     width: 6px;
     top: 30%;
     border-radius: 50%;
     box-shadow: 0 0 0 6px #F2F2F2;
+    @include lightDark(box-shadow, 0 0 0 6px #F2F2F2, 0 0 0 6px #111);
     z-index: 1;
+    @include rtl {
+      left: auto;
+      right: -2px;
+    }
   }
 }
 
 // Sidebar list
 .book-tree .sidebar-page-list  {
   list-style: none;
-  margin: $-xs -$-s 0 -$-s;
-  padding-left: 0;
-  padding-right: 0;
+  @include margin($-xs, -$-s, 0, -$-s);
+  padding-inline-start: 0;
+  padding-inline-end: 0;
   position: relative;
 
   &:after, .sub-menu:after {
     left: $-m;
     top: 1rem;
     bottom: 1rem;
-    border-left: 4px solid rgba(0, 0, 0, 0.1);
+    border-inline-start: 4px solid rgba(0, 0, 0, 0.1);
     z-index: 0;
+    @include rtl {
+      left: auto;
+      right: $-m;
+    }
   }
 
   ul {
     list-style: none;
-    padding-left: 1rem;
-    padding-right: 0;
+    padding-inline-start: 1rem;
+    padding-inline-end: 0;
   }
 
   .entity-list-item {
   }
   .entity-list-item.no-hover {
     margin-top: -$-xs;
-    padding-right: 0;
+    padding-inline-end: 0;
   }
   .entity-list-item-name {
     font-size: 1em;
   .chapter-child-menu {
     font-size: .8rem;
     margin-top: -.2rem;
-    margin-left: -1rem;
+    margin-inline-start: -1rem;
   }
   [chapter-toggle] {
-    padding-left: .7rem;
+    padding-inline-start: .7rem;
     padding-bottom: .2rem;
   }
   .entity-list-item .icon {
 .chapter-child-menu {
   ul.sub-menu {
     display: none;
-    padding-left: 0;
+    padding-inline-start: 0;
     position: relative;
   }
   [chapter-toggle].open + .sub-menu {
   justify-content: space-between;
 }
 .sort-box-options .button {
-  margin-left: 0;
+  margin-inline-start: 0;
 }
 .sortable-page-list {
-  margin-left: 0;
+  margin-inline-start: 0;
   padding: 0;
   .entity-list-item > span:first-child {
     align-self: flex-start;
   }
+  .sortable-selected  .entity-list-item, .sortable-selected  .entity-list-item:hover {
+    outline: 1px dotted var(--color-primary);
+    background-color: var(--color-primary-light) !important;
+  }
   .entity-list-item > div {
     display: block;
     flex: 1;
   }
   > ul {
-    margin-left: 0;
+    margin-inline-start: 0;
   }
   ul {
     margin-bottom: $-m;
     margin-top: 0;
-    padding-left: $-m;
+    padding-inline-start: $-m;
   }
   li {
     border: 1px solid #DDD;
     min-height: 38px;
   }
   li.text-page, li.text-chapter {
-    border-left: 2px solid currentColor;
+    border-inline-start: 2px solid currentColor;
   }
   li:first-child {
     margin-top: $-xs;
@@ -320,7 +337,7 @@ ul.pagination {
   display: inline-block;
   list-style: none;
   margin: $-m 0;
-  padding-left: 1px;
+  padding-inline-start: 1px;
   li {
     float: left;
   }
@@ -338,14 +355,17 @@ ul.pagination {
     display: block;
     padding: $-xxs $-s;
     border: 1px solid #CCC;
-    margin-left: -1px;
+    margin-inline-start: -1px;
     user-select: none;
-    &.disabled {
-      cursor: not-allowed;
-    }
+    @include lightDark(color, #555, #eee);
+    @include lightDark(border-color, #ccc, #666);
+  }
+  li.disabled {
+    cursor: not-allowed;
   }
   li.active span {
-    color: #FFF;
+    @include lightDark(color, #111, #eee);
+    @include lightDark(background-color, rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.5));
   }
 }
 
@@ -402,13 +422,13 @@ ul.pagination {
     color: #666;
   }
   > span:first-child {
-    margin-right: $-m;
+    margin-inline-end: $-m;
     flex-basis: 1.88em;
     flex: none;
   }
   > span:last-child {
     flex: 1;
-    text-align: left;
+    text-align: start;
   }
   &:not(.no-hover) {
     cursor: pointer;
@@ -426,7 +446,7 @@ ul.pagination {
     border-color: rgba(0, 0, 0, 0.1);
   }
   &:focus {
-    background-color: #eee;
+    @include lightDark(background-color, #eee, #222);
     outline: 1px dotted #666;
     outline-offset: -2px;
   }
@@ -438,12 +458,12 @@ ul.pagination {
   position: relative;
   top: 1px;
   svg {
-    margin-right: 0;
+    margin-inline-end: 0;
   }
 }
 
 .card .entity-list-item:not(.no-hover):hover {
-  background-color: #F2F2F2;
+  @include lightDark(background-color, #F2F2F2, #2d2d2d)
 }
 .card .entity-list-item .entity-list-item:hover {
   background-color: #EEEEEE;
@@ -460,7 +480,7 @@ ul.pagination {
     text-overflow: ellipsis;
     height: 2.5em;
     overflow: hidden;
-    text-align: left;
+    text-align: start;
     display: block;
     white-space: nowrap;
   }
@@ -474,17 +494,16 @@ ul.pagination {
   background-position: 50% 50%;
   border-radius: 3px;
   position: relative;
-  margin-right: $-l;
+  margin-inline-end: $-l;
 
   &.entity-list-item-image-wide {
     width: 220px;
   }
 
   .svg-icon {
-    color: #FFF;
-    fill: #FFF;
+    @include lightDark(color, #fff, rgba(255, 255, 255, 0.6));
     font-size: 1.66rem;
-    margin-right: 0;
+    margin-inline-end: 0;
     position: absolute;
     bottom: $-xs;
     left: $-xs;
@@ -542,15 +561,16 @@ ul.pagination {
   list-style: none;
   right: 0;
   margin: $-m 0;
-  background-color: #FFFFFF;
-  box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
+  @include lightDark(background-color, #fff, #333);
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
   border-radius: 1px;
-  border: 1px solid #EEE;
   min-width: 180px;
   padding: $-xs 0;
-  color: #555;
-  fill: #555;
-  text-align: left !important;
+  @include lightDark(color, #555, #eee);
+  fill: currentColor;
+  text-align: start !important;
+  max-height: 500px;
+  overflow-y: auto;
   &.wide {
     min-width: 220px;
   }
@@ -564,9 +584,11 @@ ul.pagination {
   a, button {
     display: block;
     padding: $-xs $-m;
-    color: #555;
+    @include lightDark(color, #555, #eee);
     fill: currentColor;
     white-space: nowrap;
+    line-height: 1.6;
+    cursor: pointer;
     &:hover, &:focus {
       text-decoration: none;
       background-color: var(--color-primary-light);
@@ -577,18 +599,21 @@ ul.pagination {
       outline-offset: -2px;
     }
     svg {
-      margin-right: $-s;
+      margin-inline-end: $-s;
       display: inline-block;
       width: 16px;
     }
   }
   button {
     width: 100%;
-    text-align: left;
+    text-align: start;
   }
   li.border-bottom {
     border-bottom: 1px solid #DDD;
   }
+  li hr {
+    margin: $-xs 0;
+  }
 }
 
 // Books grid view
@@ -612,10 +637,9 @@ ul.pagination {
 .featured-image-container-wrap {
   position: relative;
   .svg-icon {
-    color: #FFF;
-    fill: #FFF;
+    @include lightDark(color, #fff, rgba(255, 255, 255, 0.6));
     font-size: 2rem;
-    margin-right: 0;
+    margin-inline-end: 0;
     position: absolute;
     bottom: 10px;
     left: 6px;
@@ -641,11 +665,10 @@ ul.pagination {
     padding: $-s;
   }
   a:not(.active) {
-    color: #444;
-    fill: #444;
+    @include lightDark(color, #444, #666);
   }
   a:hover {
-    background-color: rgba(0, 0, 0, 0.05);
+    @include lightDark(background-color, rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
     border-radius: 3px;
     text-decoration: none;
   }
index 1c45ebd3080b92ff4f14ebce0f181e84dac71100..0a419c08abdeb1372e331277fcd157394d100918 100644 (file)
@@ -8,3 +8,41 @@
 @mixin between($min, $max) {
   @media screen and (min-width: $min) and (max-width: $max) { @content; }
 }
+
+// Padding shorthand using logical operators to better support RTL.
+@mixin padding($t, $r, $b, $l) {
+  padding-block-start: $t;
+  padding-block-end: $b;
+  padding-inline-start: $l;
+  padding-inline-end: $r;
+}
+
+// Margin shorthand using logical operators to better support RTL.
+@mixin margin($t, $r, $b, $l) {
+  margin-block-start: $t;
+  margin-block-end: $b;
+  margin-inline-start: $l;
+  margin-inline-end: $r;
+}
+
+// Create a RTL specific style block.
+// Mostly used as a patch until browser support improves for logical properties.
+@mixin rtl() {
+  html[dir=rtl] & {
+    @content;
+  }
+}
+
+// Define a property for both light and dark mode
+@mixin lightDark($prop, $light, $dark, $important: false) {
+  #{$prop}: if($important, $light !important, $light);
+  html.dark-mode & {
+    #{$prop}: if($important, $dark !important, $dark);
+  }
+}
+
+@mixin whenDark {
+    html.dark-mode & {
+      @content;
+    }
+}
\ No newline at end of file
index 709b1a7efe845b85dd6c55027d6d7be2fd5d3f37..4f249244b03f88a388c3b6c97855b01d27ac1d0a 100755 (executable)
@@ -3,7 +3,6 @@
   flex-direction: column;
   align-items: stretch;
   overflow: hidden;
-  background-color: #FFF;
 
   .edit-area {
     flex: 1;
   }
 }
 
-body.mce-fullscreen .page-editor .edit-area {
+body.mce-fullscreen .page-editor .edit-area,
+body.markdown-fullscreen .page-editor .edit-area {
   z-index: 12;
 }
 
+body.mce-fullscreen, body.markdown-fullscreen {
+  .page-editor, .flex-fill {
+    overflow: visible;
+  }
+}
+
 @include smaller-than($s) {
   .page-edit-toolbar {
     overflow-x: scroll;
@@ -52,7 +58,7 @@ body.mce-fullscreen .page-editor .edit-area {
   text-align: center;
   svg {
     fill: #FFF;
-    margin-right: 0;
+    margin-inline-end: 0;
   }
 }
 
@@ -145,15 +151,16 @@ body.mce-fullscreen .page-editor .edit-area {
 }
 .pointer {
   border: 1px solid #CCC;
+  @include lightDark(border-color, #ccc, #000);
   display: flex;
   align-items: center;
   justify-items: center;
   padding: $-s $-s;
   border-radius: 4px;
-  box-shadow: 0 0 12px 1px rgba(212, 209, 209, 0.3);
+  box-shadow: 0 0 12px 1px rgba(0, 0, 0, 0.1);
   position: absolute;
   top: -60px;
-  background-color:#FFF;
+  @include lightDark(background-color, #fff, #333);
   width: 275px;
   z-index: 55;
 
@@ -167,15 +174,16 @@ body.mce-fullscreen .page-editor .edit-area {
     bottom: -9px;
     width: 16px;
     height: 16px;
-    margin-left: -8px;
+    margin-inline-start: -8px;
     content: '';
     display: block;
-    background-color:#FFF;
     transform: rotate(45deg);
     transform-origin: 50% 50%;
-    border-bottom: 1px solid #CCC;
-    border-right: 1px solid #CCC;
+    border-block-end: 1px solid #CCC;
+    border-inline-end: 1px solid #CCC;
     z-index: 56;
+    @include lightDark(background-color, #fff, #333);
+    @include lightDark(border-color, #ccc, #000);
   }
   input, button, a {
     position: relative;
@@ -188,6 +196,7 @@ body.mce-fullscreen .page-editor .edit-area {
   input {
     background-color: #FFF;
     border: 1px solid #DDD;
+    @include lightDark(border-color, #ddd, #000);
     color: #666;
     width: 172px;
     z-index: 40;
@@ -212,12 +221,16 @@ body.mce-fullscreen .page-editor .edit-area {
     width: 1.2em;
     height: 1.2em;
   }
+  .button {
+    @include lightDark(border-color, #ddd, #000);
+  }
 }
 
 // Attribute form
 .floating-toolbox {
-  background-color: #FFF;
   border: 1px solid #DDD;
+  @include lightDark(background-color, #fff, #222);
+  @include lightDark(border-color, #DDD, #000);
   right: $-xl*2;
   width: 48px;
   overflow: hidden;
@@ -248,16 +261,17 @@ body.mce-fullscreen .page-editor .edit-area {
   }
   .tabs {
     display: block;
-    border-right: 1px solid #DDD;
+    border-inline-end: 1px solid #DDD;
+    @include lightDark(border-color, #ddd, #000);
     width: 48px;
     flex: 0 1 auto;
   }
   .tabs svg {
-    fill: rgba(0, 0, 0, 0.5);
     padding: 0;
     margin: 0;
   }
   .tabs > button {
+    @include lightDark(color, rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.5));
     display: block;
     cursor: pointer;
     padding: $-s $-m;
@@ -266,7 +280,7 @@ body.mce-fullscreen .page-editor .edit-area {
     border-bottom: 1px solid rgba(255, 255, 255, 0.3);
   }
   &.open .tabs > button.active {
-    fill: #444;
+    @include lightDark(color, #444, #EEE);
     background-color: rgba(0, 0, 0, 0.1);
   }
   div[toolbox-tab-content] {
@@ -274,7 +288,7 @@ body.mce-fullscreen .page-editor .edit-area {
     display: flex;
     flex: 1;
     flex-direction: column;
-    min-height: 0px;
+    min-height: 0;
     overflow-y: scroll;
   }
   h4 {
@@ -287,8 +301,8 @@ body.mce-fullscreen .page-editor .edit-area {
     width: 100%;
     min-width: 50px;
   }
-  .tags td, .tag-table > div > div > div {
-    padding-right: $-s;
+  .tags td, .inline-start-table > div > div > div {
+    padding-inline-end: $-s;
     padding-top: $-s;
     position: relative;
   }
@@ -312,62 +326,18 @@ body.mce-fullscreen .page-editor .edit-area {
   display: none;
 }
 
-.tag-display {
-  position: relative;
-  table {
-    width: 100%;
-    margin: 0;
-    padding: 0;
-  }
-  tr:first-child td {
-    padding-top: 0;
-  }
-  .heading th {
-    padding: $-xs $-s;
-    color: rgba(100, 100, 100, 0.7);
-    border: 0;
-    font-weight: 400;
-  }
-  td {
-    border: 0;
-    border-bottom: 1px solid #EEE;
-    padding: $-xs $-s;
-    color: #444;
-  }
-  tr td:first-child {
-    padding-left:0;
-  }
-  .tag-value {
-    color: #888;
-  }
-  tr:last-child td {
-    border-bottom: none;
-  }
-  .tag {
-    padding: $-s;
-  }
-}
-
 .suggestion-box {
-  position: absolute;
-  background-color: #FFF;
-  border: 1px solid #BBB;
-  box-shadow: $bs-light;
-  list-style: none;
-  z-index: 100;
+  top: auto;
+  margin: -4px 0 0;
+  right: auto;
+  left: 0;
   padding: 0;
-  margin: 0;
-  border-radius: 3px;
   li {
     display: block;
-    padding: $-xs $-s;
     border-bottom: 1px solid #DDD;
     &:last-child {
       border-bottom: 0;
     }
-    &.active {
-      background-color: #EEE;
-    }
   }
 }
 
index 69ed5a2d3c0174517ba02ed44d491ad098782d3d..40217de9b344c37541c063f295c51f928c5ade0d 100644 (file)
@@ -7,8 +7,8 @@
       #{$prop}: $size !important;
     }
     .#{$propLetter}x-#{$sizeLetter} {
-      #{$prop}-left: $size !important;
-      #{$prop}-right: $size !important;
+      #{$prop}-inline-start: $size !important;
+      #{$prop}-inline-end: $size !important;
     }
     .#{$propLetter}y-#{$sizeLetter} {
       #{$prop}-top: $size !important;
       #{$prop}-top: $size !important;
     }
     .#{$propLetter}r-#{$sizeLetter} {
-      #{$prop}-right: $size !important;
+      #{$prop}-inline-end: $size !important;
     }
     .#{$propLetter}b-#{$sizeLetter} {
       #{$prop}-bottom: $size !important;
     }
     .#{$propLetter}l-#{$sizeLetter} {
-      #{$prop}-left: $size !important;
+      #{$prop}-inline-start: $size !important;
     }
   }
 }
-@include spacing('margin', 'm')
-@include spacing('padding', 'p')
\ No newline at end of file
+@include spacing('margin', 'm');
+@include spacing('padding', 'p');
\ No newline at end of file
index a1a2fef0a44537057b186fdd1969bf7461edb175..315f979f3efdb2c215d8de56ab66609ca8e11e48 100644 (file)
@@ -2,7 +2,7 @@ table {
   min-width: 100px;
   max-width: 100%;
   thead {
-    background-color: #F8F8F8;
+    @include lightDark(background-color, #f8f8f8, #333);
     font-weight: 500;
   }
   td, th {
@@ -23,20 +23,21 @@ table.table {
     border-bottom: 1px solid rgba(0, 0, 0, 0.05);
   }
   th, td {
-    text-align: left;
+    text-align: start;
     border: none;
     padding: $-s $-s;
     vertical-align: middle;
     margin: 0;
+    overflow: visible;
   }
   th {
     font-weight: bold;
   }
   tr:hover {
-    background-color: #EEE;
+    @include lightDark(background-color, #eee, #333);
   }
   .text-right {
-    text-align: right;
+    text-align: end;
   }
   .text-center {
     text-align: center;
index cf78c162b95c72fc66e9643982bbc4c5aec90ec3..4322cb5a606da731f00a692ed97f1c8ee325a723 100644 (file)
@@ -42,7 +42,7 @@ h1, h2, h3, h4, h5, h6 {
   font-weight: 400;
   position: relative;
   display: block;
-  color: #222;
+  @include lightDark(color, #222, #BBB);
   .subheader {
     font-size: 0.5em;
     line-height: 1em;
@@ -91,7 +91,7 @@ h2.list-heading {
  */
 a {
   color: var(--color-primary);
-  fill: var(--color-primary);
+  fill: currentColor;
   cursor: pointer;
   text-decoration: none;
   transition: filter ease-in-out 80ms;
@@ -130,7 +130,7 @@ p, ul, ol, pre, table, blockquote {
 hr {
   border: 0;
   height: 1px;
-  background: #EAEAEA;
+  @include lightDark(background, #eaeaea, #555);
   margin-bottom: $-l;
   &.faded {
     background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF);
@@ -153,7 +153,7 @@ em, i, .italic {
 
 small, p.small, span.small, .text-small {
   font-size: 0.75rem;
-  color: lighten($text-dark, 10%);
+  @include lightDark(color, #5e5e5e, #999);
 }
 
 sup, .superscript {
@@ -168,8 +168,9 @@ sub, .subscript {
 
 pre {
   font-size: 12px;
-  background-color: #f5f5f5;
   border: 1px solid #DDD;
+  @include lightDark(background-color, #f5f5f5, #2B2B2B);
+  @include lightDark(border-color, #DDD, #111);
   padding-left: 31px;
   position: relative;
   padding-top: 3px;
@@ -181,9 +182,9 @@ pre {
     top: 0;
     width: 29px;
     left: 0;
-    background-color: #f5f5f5;
     height: 100%;
-    border-right: 1px solid #DDD;
+    @include lightDark(background-color, #f5f5f5, #313335);
+    @include lightDark(border-right, 1px solid #DDD, none);
   }
 }
 
@@ -200,8 +201,9 @@ blockquote {
   display: block;
   position: relative;
   border-left: 4px solid var(--color-primary);
-  background-color: #F8F8F8;
+  @include lightDark(background-color, #f8f8f8, #333);
   padding: $-s $-m $-s $-xl;
+  overflow: auto;
   &:before {
     content: "\201C";
     font-size: 2em;
@@ -213,11 +215,24 @@ blockquote {
   }
 }
 
+.text-mono {
+  font-family: $mono;
+}
+
+.text-uppercase {
+  text-transform: uppercase;
+}
+
+.text-capitals {
+  text-transform: capitalize;
+}
+
 .code-base {
-    background-color: #F8F8F8;
-    font-size: 0.80em;
-    border: 1px solid #DDD;
-    border-radius: 3px;
+  font-size: 0.84em;
+  border: 1px solid #DDD;
+  border-radius: 3px;
+  @include lightDark(background-color, #f8f8f8, #2b2b2b);
+  @include lightDark(border-color, #DDD, #444);
 }
 
 code {
@@ -226,7 +241,6 @@ code {
   padding: 1px 3px;
   white-space:pre-wrap;
   line-height: 1.2em;
-  margin-bottom: 1.2em;
 }
 
 span.code {
@@ -251,7 +265,6 @@ span.highlight {
  * Lists
  */
 ul, ol {
-  overflow: hidden;
   p {
     margin: 0;
   }
@@ -284,6 +297,13 @@ li.checkbox-item, li.task-list-item {
   }
 }
 
+li > ol, li > ul {
+  margin-block-end: 0px;
+  margin-block-start: 0px;
+  padding-block-end: 0px;
+  padding-block-start: 0px;
+}
+
 /*
  * Generic text styling classes
  */
@@ -295,10 +315,10 @@ li.checkbox-item, li.task-list-item {
   text-align: center;
 }
 .text-left {
-  text-align: left;
+  text-align: start;
 }
 .text-right {
-  text-align: right;
+  text-align: end;
 }
 
 @each $sizeLetter, $size in $screen-sizes {
@@ -307,10 +327,10 @@ li.checkbox-item, li.task-list-item {
       text-align: center;
     }
     .text-#{$sizeLetter}-left {
-      text-align: left;
+      text-align: start;
     }
     .text-#{$sizeLetter}-right {
-      text-align: right;
+      text-align: end;
     }
   }
 }
@@ -332,12 +352,21 @@ li.checkbox-item, li.task-list-item {
   overflow-wrap: break-word;
 }
 
-.limit-text {
+.text-limit-lines-1 {
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
 }
 
+.text-limit-lines-2 {
+  // -webkit use here is actually standardised cross-browser:
+  // https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 2;
+  overflow: hidden;
+}
+
 /**
  * Grouping
  */
@@ -366,6 +395,7 @@ span.sep {
   display: inline-block;
   position: relative;
   bottom: -0.105em;
-  margin-right: $-xs;
+  margin-inline-end: $-xs;
   pointer-events: none;
+  fill: currentColor;
 }
index 27c3b28d01b50874e035ce055362e3784f64967b..dfaf6683ed5888a236d1db4a42aecd21618d62fa 100644 (file)
 
 .mce-tinymce {
   .mce-panel {
-    background-color: #FFF;
+    @include lightDark(background-color, #fff, #333);
   }
   .mce-btn {
-    background-color: #FFF;
+    @include lightDark(background-color, #fff, #333);
   }
 }
 
@@ -51,6 +51,9 @@
       overflow:auto;
       iframe {
         flex: 1;
+        // Force TinyMCE iframe to render on its own layer
+        // for much greater performance in Safari
+        will-change: transform;
       }
     }
   }
index 2d4d3970af2d028b98f825cf2b5833395db81fb1..d0d1f27d3715b0fc8599b661b671f0c604c568d7 100644 (file)
@@ -35,7 +35,7 @@ $text: -apple-system, BlinkMacSystemFont,
 "Segoe UI", "Oxygen", "Ubuntu", "Roboto", "Cantarell",
 "Fira Sans", "Droid Sans", "Helvetica Neue",
 sans-serif;
-$mono: "Lucida Console", "DejaVu Sans Mono", "Ubunto Mono", Monaco, monospace;
+$mono: "Lucida Console", "DejaVu Sans Mono", "Ubuntu Mono", Monaco, monospace;
 $heading: $text;
 $fs-m: 14px;
 $fs-s: 12px;
@@ -63,7 +63,9 @@ $text-light: #EEE;
 
 // Shadows
 $bs-light: 0 0 4px 1px #CCC;
+$bs-dark: 0 0 4px 1px rgba(0, 0, 0, 0.5);
 $bs-med: 0 1px 3px 1px rgba(76, 76, 76, 0.26);
 $bs-large: 0 1px 6px 1px rgba(22, 22, 22, 0.2);
 $bs-card: 0 1px 6px -1px rgba(0, 0, 0, 0.1);
-$bs-hover: 0 2px 2px 1px rgba(0,0,0,.13);
\ No newline at end of file
+$bs-card-dark: 0 1px 6px -1px rgba(0, 0, 0, 0.5);
+$bs-hover: 0 2px 2px 1px rgba(0,0,0,.13);
index 958b788075cb50b788685345bfbe9d9b40b111ff..6d9a1a7182afb7aa734175ee4c2bdc898f32a6f7 100644 (file)
@@ -5,7 +5,6 @@
 @import "text";
 @import "layout";
 @import "blocks";
-@import "forms";
 @import "tables";
 @import "header";
 @import "lists";
index 296afbe76f0cb11efbb7c0b327885858016c949d..5cbd7f9d5e226b9c4710acaa3b5012862ea77c63 100644 (file)
@@ -20,8 +20,8 @@ html, body {
 .tri-layout-container {
   grid-template-columns: 1fr;
   grid-template-areas: "b";
-  margin-left: 0;
-  margin-right: 0;
+  margin-inline-start: 0;
+  margin-inline-end: 0;
   display: block;
 }
 
@@ -30,6 +30,6 @@ html, body {
 }
 
 .content-wrap.card {
-  padding-left: 0;
-  padding-right: 0;
+  padding-inline-start: 0;
+  padding-inline-end: 0;
 }
\ No newline at end of file
index 1f4d00f6b9606b1ebe15d94b0c5b8788dd747b6e..78d94f977f8d0043679557148fc00af97a8cd276 100644 (file)
 @import "lists";
 @import "pages";
 
-[v-cloak] {
-  display: none; opacity: 0;
-  animation-name: none !important;
-}
-
 // Jquery Sortable Styles
 .dragged {
   position: absolute;
@@ -36,7 +31,7 @@ body.dragging, body.dragging * {
 // User Avatar Images
 .avatar {
   border-radius: 100%;
-  background-color: #EEE;
+  @include lightDark(background-color, #eee, #000);
   width: 30px;
   height: 30px;
   &.med {
@@ -54,6 +49,11 @@ body.dragging, body.dragging * {
   &.square {
     border-radius: 3px;
   }
+  &[src$="user_avatar.png"] {
+    @include whenDark {
+      filter: invert(1);
+    }
+  }
 }
 
 // Loading icon
@@ -74,7 +74,7 @@ $loadingSize: 10px;
     animation-duration: 1.4s;
     animation-iteration-count: infinite;
     animation-timing-function: cubic-bezier(.62, .28, .23, .99);
-    margin-right: 4px;
+    margin-inline-end: 4px;
     background-color: var(--color-page);
     animation-delay: 0.3s;
   }
@@ -89,7 +89,7 @@ $loadingSize: 10px;
     animation-delay: 0.6s;
   }
   > span {
-    margin-left: $-s;
+    margin-inline-start: $-s;
     font-style: italic;
     color: #888;
     vertical-align: top;
@@ -110,7 +110,7 @@ $btt-size: 40px;
   svg {
     width: $btt-size / 1.5;
     height: $btt-size / 1.5;
-    margin-right: 4px;
+    margin-inline-end: 4px;
   }
   width: $btt-size;
   height: $btt-size;
@@ -135,10 +135,12 @@ $btt-size: 40px;
 
 .contained-search-box {
   display: flex;
+  height: 38px;
   input, button {
     border-radius: 0;
-    border: 1px solid #DDD;
-    margin-left: -1px;
+    border: 1px solid #ddd;
+    @include lightDark(border-color, #ddd, #000);
+    margin-inline-start: -1px;
   }
   input {
     flex: 5;
@@ -157,10 +159,14 @@ $btt-size: 40px;
     background-color: $negative;
     color: #EEE;
   }
+  svg {
+    margin: 0;
+  }
 }
 
 .entity-selector {
   border: 1px solid #DDD;
+  @include lightDark(border-color, #ddd, #111);
   border-radius: 3px;
   overflow: hidden;
   font-size: 0.8em;
@@ -176,12 +182,12 @@ $btt-size: 40px;
   .entity-list {
     overflow-y: scroll;
     height: 400px;
-    background-color: #EEEEEE;
-    margin-right: 0;
-    margin-left: 0;
+    @include lightDark(background-color, #eee, #222);
+    margin-inline-end: 0;
+    margin-inline-start: 0;
   }
   .entity-list-item {
-    background-color: #FFF;
+    @include lightDark(background-color, #fff, #222);
   }
   .entity-list-item p {
     margin-bottom: 0;
@@ -252,27 +258,29 @@ $btt-size: 40px;
   }
   .list-sort {
     display: inline-grid;
-    margin-left: $-s;
+    margin-inline-start: $-s;
     grid-template-columns: minmax(120px, max-content) 40px;
     font-size: 0.9rem;
     border: 2px solid #DDD;
+    @include lightDark(border-color, #ddd, #444);
     border-radius: 4px;
   }
   .list-sort-label {
     font-weight: bold;
     display: inline-block;
-    color: #555;
+    @include lightDark(color, #555, #888);
   }
   .list-sort-type {
-    text-align: left;
+    text-align: start;
   }
   .list-sort-type, .list-sort-dir {
     padding: $-xs $-s;
     cursor: pointer;
   }
   .list-sort-dir {
-    border-left: 2px solid #DDD;
-    fill: #888;
+    border-inline-start: 2px solid #DDD;
+    color: #888;
+    @include lightDark(border-color, #ddd, #444);
     .svg-icon {
       transition: transform ease-in-out 120ms;
     }
@@ -280,4 +288,15 @@ $btt-size: 40px;
       transform: rotate(180deg);
     }
   }
+}
+
+table.table .table-user-item {
+  display: grid;
+  grid-template-columns: 42px 1fr;
+  align-items: center;
+}
+table.table .table-entity-item {
+  display: grid;
+  grid-template-columns: 36px 1fr;
+  align-items: center;
 }
\ No newline at end of file
diff --git a/resources/views/api-docs/index.blade.php b/resources/views/api-docs/index.blade.php
new file mode 100644 (file)
index 0000000..d9c3d65
--- /dev/null
@@ -0,0 +1,244 @@
+@extends('simple-layout')
+
+@section('body')
+
+    <div class="container pt-xl">
+
+        <div class="grid right-focus reverse-collapse">
+            <div>
+
+                <div class="sticky-sidebar">
+                    <p class="text-uppercase text-muted mb-xm mt-l"><strong>Getting Started</strong></p>
+
+                    <div class="text-mono">
+                        <div class="mb-xs"><a href="#authentication">Authentication</a></div>
+                        <div class="mb-xs"><a href="#request-format">Request Format</a></div>
+                        <div class="mb-xs"><a href="#listing-endpoints">Listing Endpoints</a></div>
+                        <div class="mb-xs"><a href="#error-handling">Error Handling</a></div>
+                    </div>
+
+                    @foreach($docs as $model => $endpoints)
+                        <p class="text-uppercase text-muted mb-xm mt-l"><strong>{{ $model }}</strong></p>
+
+                        @foreach($endpoints as $endpoint)
+                            <div class="mb-xs">
+                                <a href="#{{ $endpoint['name'] }}" class="text-mono inline block mr-s">
+                                    <span class="api-method" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span>
+                                </a>
+                                <a href="#{{ $endpoint['name'] }}" class="text-mono">
+                                    {{ $endpoint['controller_method_kebab'] }}
+                                </a>
+                            </div>
+                        @endforeach
+                    @endforeach
+                </div>
+            </div>
+
+            <div style="overflow: auto;">
+
+                <section code-highlighter class="card content-wrap auto-height">
+                    <h1 class="list-heading text-capitals mb-l">Getting Started</h1>
+
+                    <h5 id="authentication" class="text-mono mb-m">Authentication</h5>
+                    <p>
+                        To access the API a user has to have the <em>"Access System API"</em> permission enabled on one of their assigned roles.
+                        Permissions to content accessed via the API is limited by the roles & permissions assigned to the user that's used to access the API.
+                    </p>
+                    <p>Authentication to use the API is primarily done using API Tokens. Once the <em>"Access System API"</em> permission has been assigned to a user, a "API Tokens" section should be visible when editing their user profile. Choose "Create Token" and enter an appropriate name and expiry date, relevant for your API usage then press "Save". A "Token ID" and "Token Secret" will be immediately displayed. These values should be used as a header in API HTTP requests in the following format:</p>
+                    <pre><code class="language-css">Authorization: Token &lt;token_id&gt;:&lt;token_secret&gt;</code></pre>
+                    <p>Here's an example of an authorized cURL request to list books in the system:</p>
+                    <pre><code class="language-shell">curl --request GET \
+  --url https://example.com/api/books \
+  --header 'Authorization: Token C6mdvEQTGnebsmVn3sFNeeuelGEBjyQp:NOvD3VlzuSVuBPNaf1xWHmy7nIRlaj22'</code></pre>
+                    <p>If already logged into the system within the browser, via a user account with permission to access the API, the system will also accept an existing session meaning you can browse API endpoints directly in the browser or use the browser devtools to play with the API.</p>
+
+                    <hr>
+
+                    <h5 id="request-format" class="text-mono mb-m">Request Format</h5>
+                    <p>The API is primarily design to be interfaced using JSON so the majority of API endpoints, that accept data, will read JSON request data although <code>application/x-www-form-urlencoded</code> request data is also accepted. Endpoints that receive file data will need data sent in a <code>multipart/form-data</code> format although this will be highlighted in the documentation for such endpoints.</p>
+                    <p>For endpoints in this documentation that accept data, a "Body Parameters" table will be available showing the parameters that will accepted in the request. Any rules for the values of such parameters, such as the data-type or if they're required, will be shown alongside the parameter name.</p>
+
+                    <hr>
+
+                    <h5 id="listing-endpoints" class="text-mono mb-m">Listing Endpoints</h5>
+                    <p>Some endpoints will return a list of data models. These endpoints will return an array of the model data under a <code>data</code> property along with a numeric <code>total</code> property to indicate the total number of records found for the query within the system. Here's an example of a listing response:</p>
+                    <pre><code class="language-json">{
+  "data": [
+    {
+      "id": 1,
+      "name": "BookStack User Guide",
+      "slug": "bookstack-user-guide",
+      "description": "This is a general guide on using BookStack on a day-to-day basis.",
+      "created_at": "2019-05-05 21:48:46",
+      "updated_at": "2019-12-11 20:57:31",
+      "created_by": 1,
+      "updated_by": 1,
+      "image_id": 3
+    }
+  ],
+  "total": 16
+}</code></pre>
+                    <p>
+                        There are a number of standard URL parameters that can be supplied to manipulate and page through the results returned from a listing endpoint:
+                    </p>
+                    <table class="table">
+                        <tr>
+                            <th>Parameter</th>
+                            <th>Details</th>
+                            <th width="30%">Examples</th>
+                        </tr>
+                        <tr>
+                            <td>count</td>
+                            <td>
+                                Specify how many records will be returned in the response. <br>
+                                (Default: {{ config('api.default_item_count') }}, Max: {{ config('api.max_item_count') }})
+                            </td>
+                            <td>Limit the count to 50<br><code>?count=50</code></td>
+                        </tr>
+                        <tr>
+                            <td>offset</td>
+                            <td>
+                                Specify how many records to skip over in the response. <br>
+                                (Default: 0)
+                            </td>
+                            <td>Skip over the first 100 records<br><code>?offset=100</code></td>
+                        </tr>
+                        <tr>
+                            <td>sort</td>
+                            <td>
+                                Specify what field is used to sort the data and the direction of the sort (Ascending or Descending).<br>
+                                Value is the name of a field, A <code>+</code> or <code>-</code> prefix dictates ordering. <br>
+                                Direction defaults to ascending. <br>
+                                Can use most fields shown in the response.
+                            </td>
+                            <td>
+                                Sort by name ascending<br><code>?sort=+name</code> <br> <br>
+                                Sort by "Created At" date descending<br><code>?sort=-created_at</code>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>filter[&lt;field&gt;]</td>
+                            <td>
+                                Specify a filter to be applied to the query. Can use most fields shown in the response. <br>
+                                By default a filter will apply a "where equals" query but the below operations are available using the format filter[&lt;field&gt;:&lt;operation&gt;] <br>
+                                <table>
+                                    <tr>
+                                        <td>eq</td>
+                                        <td>Where <code>&lt;field&gt;</code> equals the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>ne</td>
+                                        <td>Where <code>&lt;field&gt;</code> does not equal the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>gt</td>
+                                        <td>Where <code>&lt;field&gt;</code> is greater than the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>lt</td>
+                                        <td>Where <code>&lt;field&gt;</code> is less than the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>gte</td>
+                                        <td>Where <code>&lt;field&gt;</code> is greater than or equal to the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>lte</td>
+                                        <td>Where <code>&lt;field&gt;</code> is less than or equal to the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>like</td>
+                                        <td>
+                                            Where <code>&lt;field&gt;</code> is "like" the filter value. <br>
+                                            <code>%</code> symbols can be used as wildcards.
+                                        </td>
+                                    </tr>
+                                </table>
+                            </td>
+                            <td>
+                                Filter where id is 5: <br><code>?filter[id]=5</code><br><br>
+                                Filter where id is not 5: <br><code>?filter[id:ne]=5</code><br><br>
+                                Filter where name contains "cat": <br><code>?filter[name:like]=%cat%</code><br><br>
+                                Filter where created after 2020-01-01: <br><code>?filter[created_at:gt]=2020-01-01</code>
+                            </td>
+                        </tr>
+                    </table>
+
+                    <hr>
+
+                    <h5 id="error-handling" class="text-mono mb-m">Error Handling</h5>
+                    <p>
+                        Successful responses will return a 200 or 204 HTTP response code. Errors will return a 4xx or a 5xx HTTP response code depending on the type of error. Errors follow a standard format as shown below. The message provided may be translated depending on the configured language of the system in addition to the API users' language preference. The code provided in the JSON response will match the HTTP response code.
+                    </p>
+
+                    <pre><code class="language-json">{
+       "error": {
+               "code": 401,
+               "message": "No authorization token found on the request"
+       }
+}
+</code></pre>
+
+                </section>
+
+                @foreach($docs as $model => $endpoints)
+                    <section class="card content-wrap auto-height">
+                        <h1 class="list-heading text-capitals">{{ $model }}</h1>
+
+                        @foreach($endpoints as $endpoint)
+                            <h6 class="text-uppercase text-muted float right">{{ $endpoint['controller_method_kebab'] }}</h6>
+                            <h5 id="{{ $endpoint['name'] }}" class="text-mono mb-m">
+                                <span class="api-method" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span>
+                                @if($endpoint['controller_method_kebab'] === 'list')
+                                    <a style="color: inherit;" target="_blank" href="{{ url($endpoint['uri']) }}">{{ url($endpoint['uri']) }}</a>
+                                @else
+                                    {{ url($endpoint['uri']) }}
+                                @endif
+                            </h5>
+                            <p class="mb-m">{{ $endpoint['description'] ?? '' }}</p>
+                            @if($endpoint['body_params'] ?? false)
+                                <details class="mb-m">
+                                    <summary class="text-muted">Body Parameters</summary>
+                                    <table class="table">
+                                        <tr>
+                                            <th>Param Name</th>
+                                            <th>Value Rules</th>
+                                        </tr>
+                                        @foreach($endpoint['body_params'] as $paramName => $rules)
+                                        <tr>
+                                            <td>{{ $paramName }}</td>
+                                            <td>
+                                                @foreach($rules as $rule)
+                                                    <code class="mr-xs">{{ $rule }}</code>
+                                                @endforeach
+                                            </td>
+                                        </tr>
+                                        @endforeach
+                                    </table>
+                                </details>
+                            @endif
+                            @if($endpoint['example_request'] ?? false)
+                                <details details-highlighter class="mb-m">
+                                    <summary class="text-muted">Example Request</summary>
+                                    <pre><code class="language-json">{{ $endpoint['example_request'] }}</code></pre>
+                                </details>
+                            @endif
+                            @if($endpoint['example_response'] ?? false)
+                                <details details-highlighter class="mb-m">
+                                    <summary class="text-muted">Example Response</summary>
+                                    <pre><code class="language-json">{{ $endpoint['example_response'] }}</code></pre>
+                                </details>
+                            @endif
+                            @if(!$loop->last)
+                            <hr>
+                            @endif
+                        @endforeach
+                    </section>
+                @endforeach
+            </div>
+
+        </div>
+
+
+    </div>
+@stop
\ No newline at end of file
diff --git a/resources/views/attachments/list.blade.php b/resources/views/attachments/list.blade.php
new file mode 100644 (file)
index 0000000..8c9be82
--- /dev/null
@@ -0,0 +1,8 @@
+@foreach($attachments as $attachment)
+    <div class="attachment icon-list">
+        <a class="icon-list-item py-xs" href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif>
+            <span class="icon">@icon($attachment->external ? 'export' : 'file')</span>
+            <span>{{ $attachment->name }}</span>
+        </a>
+    </div>
+@endforeach
\ No newline at end of file
diff --git a/resources/views/attachments/manager-edit-form.blade.php b/resources/views/attachments/manager-edit-form.blade.php
new file mode 100644 (file)
index 0000000..ee86dc2
--- /dev/null
@@ -0,0 +1,48 @@
+<div component="ajax-form"
+     option:ajax-form:url="/attachments/{{ $attachment->id }}"
+     option:ajax-form:method="put"
+     option:ajax-form:response-container=".attachment-edit-container"
+     option:ajax-form:success-message="{{ trans('entities.attachments_updated_success') }}">
+    <h5>{{ trans('entities.attachments_edit_file') }}</h5>
+
+    <div class="form-group">
+        <label for="attachment_edit_name">{{ trans('entities.attachments_edit_file_name') }}</label>
+        <input type="text" id="attachment_edit_name"
+               name="attachment_edit_name"
+               value="{{ $attachment_edit_name ?? $attachment->name ?? '' }}"
+               placeholder="{{ trans('entities.attachments_edit_file_name') }}">
+        @if($errors->has('attachment_edit_name'))
+            <div class="text-neg text-small">{{ $errors->first('attachment_edit_name') }}</div>
+        @endif
+    </div>
+
+    <div component="tabs" class="tab-container">
+        <div class="nav-tabs">
+            <button refs="tabs@toggleFile" type="button" class="tab-item {{ $attachment->external ? '' : 'selected' }}">{{ trans('entities.attachments_upload') }}</button>
+            <button refs="tabs@toggleLink" type="button" class="tab-item {{ $attachment->external ? 'selected' : '' }}">{{ trans('entities.attachments_set_link') }}</button>
+        </div>
+        <div refs="tabs@contentFile" class="mb-m {{ $attachment->external ? 'hidden' : '' }}">
+            @include('components.dropzone', [
+                'placeholder' => trans('entities.attachments_edit_drop_upload'),
+                'url' =>  url('/attachments/upload/' . $attachment->id),
+                'successMessage' => trans('entities.attachments_file_updated'),
+            ])
+        </div>
+        <div refs="tabs@contentLink" class="{{ $attachment->external ? '' : 'hidden' }}">
+            <div class="form-group">
+                <label for="attachment_edit_url">{{ trans('entities.attachments_link_url') }}</label>
+                <input type="text" id="attachment_edit_url"
+                       name="attachment_edit_url"
+                       value="{{ $attachment_edit_url ?? ($attachment->external ? $attachment->path : '')  }}"
+                       placeholder="{{ trans('entities.attachment_link') }}">
+                @if($errors->has('attachment_edit_url'))
+                    <div class="text-neg text-small">{{ $errors->first('attachment_edit_url') }}</div>
+                @endif
+            </div>
+        </div>
+    </div>
+
+    <button component="event-emit-select"
+            option:event-emit-select:name="edit-back" type="button" class="button outline">{{ trans('common.back') }}</button>
+    <button refs="ajax-form@submit" type="button" class="button">{{ trans('common.save') }}</button>
+</div>
\ No newline at end of file
diff --git a/resources/views/attachments/manager-link-form.blade.php b/resources/views/attachments/manager-link-form.blade.php
new file mode 100644 (file)
index 0000000..b51daa4
--- /dev/null
@@ -0,0 +1,28 @@
+{{--
+@pageId
+--}}
+<div component="ajax-form"
+     option:ajax-form:url="/attachments/link"
+     option:ajax-form:method="post"
+     option:ajax-form:response-container=".link-form-container"
+     option:ajax-form:success-message="{{ trans('entities.attachments_link_attached') }}">
+    <input type="hidden" name="attachment_link_uploaded_to" value="{{ $pageId }}">
+    <p class="text-muted small">{{ trans('entities.attachments_explain_link') }}</p>
+    <div class="form-group">
+        <label for="attachment_link_name">{{ trans('entities.attachments_link_name') }}</label>
+        <input name="attachment_link_name" id="attachment_link_name" type="text" placeholder="{{ trans('entities.attachments_link_name') }}" value="{{ $attachment_link_name ?? '' }}">
+        @if($errors->has('attachment_link_name'))
+            <div class="text-neg text-small">{{ $errors->first('attachment_link_name') }}</div>
+        @endif
+    </div>
+    <div class="form-group">
+        <label for="attachment_link_url">{{ trans('entities.attachments_link_url') }}</label>
+        <input name="attachment_link_url" id="attachment_link_url" type="text" placeholder="{{ trans('entities.attachments_link_url_hint') }}" value="{{ $attachment_link_url ?? '' }}">
+        @if($errors->has('attachment_link_url'))
+            <div class="text-neg text-small">{{ $errors->first('attachment_link_url') }}</div>
+        @endif
+    </div>
+    <button refs="ajax-form@submit"
+            type="button"
+            class="button">{{ trans('entities.attach') }}</button>
+</div>
\ No newline at end of file
diff --git a/resources/views/attachments/manager-list.blade.php b/resources/views/attachments/manager-list.blade.php
new file mode 100644 (file)
index 0000000..313faa5
--- /dev/null
@@ -0,0 +1,42 @@
+<div component="sortable-list" option:sortable-list:handle-selector=".handle">
+    @foreach($attachments as $attachment)
+        <div component="ajax-delete-row"
+             option:ajax-delete-row:url="{{ url('/attachments/' . $attachment->id) }}"
+             data-id="{{ $attachment->id }}"
+             data-drag-content="{{ json_encode(['text/html' => $attachment->htmlLink(), 'text/plain' => $attachment->markdownLink()]) }}"
+             class="card drag-card">
+            <div class="handle">@icon('grip')</div>
+            <div class="py-s">
+                <a href="{{ $attachment->getUrl() }}" target="_blank">{{ $attachment->name }}</a>
+            </div>
+            <div class="flex-fill justify-flex-end">
+                <button component="event-emit-select"
+                        option:event-emit-select:name="insert"
+                        type="button"
+                        title="{{ trans('entities.attachments_insert_link') }}"
+                        class="drag-card-action text-center text-primary">@icon('link')                 </button>
+                <button component="event-emit-select"
+                        option:event-emit-select:name="edit"
+                        option:event-emit-select:id="{{ $attachment->id }}"
+                        type="button"
+                        title="{{ trans('common.edit') }}"
+                        class="drag-card-action text-center text-primary">@icon('edit')</button>
+                <div component="dropdown" class="flex-fill relative">
+                    <button refs="dropdown@toggle"
+                            type="button"
+                            title="{{ trans('common.delete') }}"
+                            class="drag-card-action text-center text-neg">@icon('close')</button>
+                    <div refs="dropdown@menu" class="dropdown-menu">
+                        <p class="text-neg small px-m mb-xs">{{ trans('entities.attachments_delete') }}</p>
+                        <button refs="ajax-delete-row@delete" type="button" class="text-primary small delete">{{ trans('common.confirm') }}</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    @endforeach
+    @if (count($attachments) === 0)
+        <p class="small text-muted">
+            {{ trans('entities.attachments_no_files') }}
+        </p>
+    @endif
+</div>
\ No newline at end of file
diff --git a/resources/views/attachments/manager.blade.php b/resources/views/attachments/manager.blade.php
new file mode 100644 (file)
index 0000000..4628f74
--- /dev/null
@@ -0,0 +1,39 @@
+<div style="display: block;" toolbox-tab-content="files"
+     component="attachments"
+     option:attachments:page-id="{{ $page->id ?? 0 }}">
+
+    <h4>{{ trans('entities.attachments') }}</h4>
+    <div class="px-l files">
+
+        <div refs="attachments@listContainer">
+            <p class="text-muted small">{{ trans('entities.attachments_explain') }} <span class="text-warn">{{ trans('entities.attachments_explain_instant_save') }}</span></p>
+
+            <div component="tabs" refs="attachments@mainTabs" class="tab-container">
+                <div class="nav-tabs">
+                    <button refs="tabs@toggleItems" type="button" class="selected tab-item">{{ trans('entities.attachments_items') }}</button>
+                    <button refs="tabs@toggleUpload" type="button" class="tab-item">{{ trans('entities.attachments_upload') }}</button>
+                    <button refs="tabs@toggleLinks" type="button" class="tab-item">{{ trans('entities.attachments_link') }}</button>
+                </div>
+                <div refs="tabs@contentItems attachments@list">
+                    @include('attachments.manager-list', ['attachments' => $page->attachments->all()])
+                </div>
+                <div refs="tabs@contentUpload" class="hidden">
+                    @include('components.dropzone', [
+                        'placeholder' => trans('entities.attachments_dropzone'),
+                        'url' =>  url('/attachments/upload?uploaded_to=' . $page->id),
+                        'successMessage' => trans('entities.attachments_file_uploaded'),
+                    ])
+                </div>
+                <div refs="tabs@contentLinks" class="hidden link-form-container">
+                    @include('attachments.manager-link-form', ['pageId' => $page->id])
+                </div>
+            </div>
+
+        </div>
+
+        <div refs="attachments@editContainer" class="hidden attachment-edit-container">
+
+        </div>
+
+    </div>
+</div>
\ No newline at end of file
index 2699fda00ac59801e454f748dd339eb8b6c7fac8..92eba80e8c8a8de8fa3cd26e19e41f2f1e2fc52c 100644 (file)
@@ -1,19 +1,28 @@
-<div class="form-group">
-    <label for="username">{{ trans('auth.username') }}</label>
-    @include('form.text', ['name' => 'username', 'autofocus' => true])
-</div>
+<form action="{{ url('/login') }}" method="POST" id="login-form" class="mt-l">
+    {!! csrf_field() !!}
 
-@if(session('request-email', false) === true)
-    <div class="form-group">
-        <label for="email">{{ trans('auth.email') }}</label>
-        @include('form.text', ['name' => 'email'])
-        <span class="text-neg">
-            {{ trans('auth.ldap_email_hint') }}
-        </span>
+    <div class="stretch-inputs">
+        <div class="form-group">
+            <label for="username">{{ trans('auth.username') }}</label>
+            @include('form.text', ['name' => 'username', 'autofocus' => true])
+        </div>
+
+        @if(session('request-email', false) === true)
+            <div class="form-group">
+                <label for="email">{{ trans('auth.email') }}</label>
+                @include('form.text', ['name' => 'email'])
+                <span class="text-neg">{{ trans('auth.ldap_email_hint') }}</span>
+            </div>
+        @endif
+
+        <div class="form-group">
+            <label for="password">{{ trans('auth.password') }}</label>
+            @include('form.password', ['name' => 'password'])
+        </div>
+
+        <div class="form-group text-right pt-s">
+            <button class="button">{{ Str::title(trans('auth.log_in')) }}</button>
+        </div>
     </div>
-@endif
 
-<div class="form-group">
-    <label for="password">{{ trans('auth.password') }}</label>
-    @include('form.password', ['name' => 'password'])
-</div>
\ No newline at end of file
+</form>
\ No newline at end of file
diff --git a/resources/views/auth/forms/login/saml2.blade.php b/resources/views/auth/forms/login/saml2.blade.php
new file mode 100644 (file)
index 0000000..7d65958
--- /dev/null
@@ -0,0 +1,11 @@
+<form action="{{ url('/saml2/login') }}" method="POST" id="login-form" class="mt-l">
+    {!! csrf_field() !!}
+
+    <div>
+        <button id="saml-login" class="button outline block svg">
+            @icon('saml2')
+            <span>{{ trans('auth.log_in_with', ['socialDriver' => config('saml2.name')]) }}</span>
+        </button>
+    </div>
+
+</form>
\ No newline at end of file
index 52fae3750ad38dbd9cccacedeea82328d63fd0b1..87603e2cb6dc8fd88a9c41bbb9b3ad0d5d35b992 100644 (file)
@@ -1,12 +1,36 @@
-<div class="form-group">
-    <label for="email">{{ trans('auth.email') }}</label>
-    @include('form.text', ['name' => 'email', 'autofocus' => true])
-</div>
-
-<div class="form-group">
-    <label for="password">{{ trans('auth.password') }}</label>
-    @include('form.password', ['name' => 'password'])
-    <span class="block small mt-s">
-        <a href="{{ url('/password/email') }}">{{ trans('auth.forgot_password') }}</a>
-    </span>
-</div>
+<form action="{{ url('/login') }}" method="POST" id="login-form" class="mt-l">
+    {!! csrf_field() !!}
+
+    <div class="stretch-inputs">
+        <div class="form-group">
+            <label for="email">{{ trans('auth.email') }}</label>
+            @include('form.text', ['name' => 'email', 'autofocus' => true])
+        </div>
+
+        <div class="form-group">
+            <label for="password">{{ trans('auth.password') }}</label>
+            @include('form.password', ['name' => 'password'])
+            <div class="small mt-s">
+                <a href="{{ url('/password/email') }}">{{ trans('auth.forgot_password') }}</a>
+            </div>
+        </div>
+    </div>
+
+    <div class="grid half collapse-xs gap-xl v-center">
+        <div class="text-left ml-xxs">
+            @include('components.custom-checkbox', [
+                'name' => 'remember',
+                'checked' => false,
+                'value' => 'on',
+                'label' => trans('auth.remember_me'),
+            ])
+        </div>
+
+        <div class="text-right">
+            <button class="button">{{ Str::title(trans('auth.log_in')) }}</button>
+        </div>
+    </div>
+
+</form>
+
+
index fbf540d7105fd6f0969956d7291413471d820b53..868e0555fd5fe7a13f6cad9f0ce5142892223492 100644 (file)
@@ -9,29 +9,7 @@
         <div class="card content-wrap auto-height">
             <h1 class="list-heading">{{ Str::title(trans('auth.log_in')) }}</h1>
 
-            <form action="{{ url('/login') }}" method="POST" id="login-form" class="mt-l">
-                {!! csrf_field() !!}
-
-                <div class="stretch-inputs">
-                    @include('auth.forms.login.' . $authMethod)
-                </div>
-
-                <div class="grid half collapse-xs gap-xl v-center">
-                    <div class="text-left ml-xxs">
-                        @include('components.custom-checkbox', [
-                            'name' => 'remember',
-                            'checked' => false,
-                            'value' => 'on',
-                            'label' => trans('auth.remember_me'),
-                        ])
-                    </div>
-
-                    <div class="text-right">
-                        <button class="button">{{ Str::title(trans('auth.log_in')) }}</button>
-                    </div>
-                </div>
-
-            </form>
+            @include('auth.forms.login.' . $authMethod)
 
             @if(count($socialDrivers) > 0)
                 <hr class="my-l">
                     <div>
                         <a id="social-login-{{$driver}}" class="button outline block svg" href="{{ url("/login/service/" . $driver) }}">
                             @icon('auth/' . $driver)
-                            {{ trans('auth.log_in_with', ['socialDriver' => $name]) }}
+                            <span>{{ trans('auth.log_in_with', ['socialDriver' => $name]) }}</span>
                         </a>
                     </div>
                 @endforeach
             @endif
 
-            @if(setting('registration-enabled', false))
+            @if(setting('registration-enabled') && config('auth.method') === 'standard')
                 <div class="text-center pb-s">
                     <hr class="my-l">
                     <a href="{{ url('/register') }}">{{ trans('auth.dont_have_account') }}</a>
@@ -54,4 +32,4 @@
         </div>
     </div>
 
-@stop
\ No newline at end of file
+@stop
index 0e996a00d2300e1d27ecba57976260af441e6ee0..34aa81d7bf991f078104f8cff5dbecca69773737 100644 (file)
                     <div>
                         <a id="social-register-{{$driver}}" class="button block outline svg" href="{{ url("/register/service/" . $driver) }}">
                             @icon('auth/' . $driver)
-                            {{ trans('auth.sign_up_with', ['socialDriver' => $name]) }}
+                            <span>{{ trans('auth.sign_up_with', ['socialDriver' => $name]) }}</span>
                         </a>
                     </div>
                 @endforeach
             @endif
+
         </div>
     </div>
 @stop
index 07548162067404fa82061695c6753fe5bd15e27c..a5404a36506ae9d70225034a6eeef749eeb86d28 100644 (file)
@@ -1,5 +1,7 @@
 <!DOCTYPE html>
-<html lang="{{ config('app.lang') }}" class="@yield('body-class')">
+<html lang="{{ config('app.lang') }}"
+      dir="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+      class="{{ setting()->getForCurrentUser('dark-mode-enabled') ? 'dark-mode ' : '' }}@yield('body-class')">
 <head>
     <title>{{ isset($pageTitle) ? $pageTitle . ' | ' : '' }}{{ setting('app-name') }}</title>
 
@@ -23,7 +25,6 @@
 
     <!-- Translations for JS -->
     @stack('translations')
-
 </head>
 <body class="@yield('body-class')">
 
index 1cf91046df1b5ab2043c5e7072ed265c9a9c5a89..f62b895827b7c87b0a3f47ed82d66037fd1d5ad1 100644 (file)
@@ -4,10 +4,9 @@
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
     <title>{{ $book->name }}</title>
 
+    @include('partials.export-styles', ['format' => $format])
+
     <style>
-        @if (!app()->environment('testing'))
-        {!! file_get_contents(public_path('/dist/export-styles.css')) !!}
-        @endif
         .page-break {
             page-break-after: always;
         }
@@ -42,9 +41,9 @@
         <ul class="contents">
             @foreach($bookChildren as $bookChild)
                 <li><a href="#{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</a></li>
-                @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
+                @if($bookChild->isA('chapter') && count($bookChild->visible_pages) > 0)
                     <ul>
-                        @foreach($bookChild->pages as $page)
+                        @foreach($bookChild->visible_pages as $page)
                             <li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
                         @endforeach
                     </ul>
@@ -60,8 +59,8 @@
         @if($bookChild->isA('chapter'))
             <p>{{ $bookChild->description }}</p>
 
-            @if(count($bookChild->pages) > 0)
-                @foreach($bookChild->pages as $page)
+            @if(count($bookChild->visible_pages) > 0)
+                @foreach($bookChild->visible_pages as $page)
                     <div class="page-break"></div>
                     <div class="chapter-hint">{{$bookChild->name}}</div>
                     <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
index a3235036e02c81c76bfefa799787d3cfd27bf7e3..840d0604c8e0c22185d6e33ea43eac02e880ff88 100644 (file)
@@ -31,7 +31,7 @@
         <label for="tag-manager">{{ trans('entities.book_tags') }}</label>
     </button>
     <div class="collapse-content" collapsible-content>
-        @include('components.tag-manager', ['entity' => $book ?? null, 'entityType' => 'chapter'])
+        @include('components.tag-manager', ['entity' => $book ?? null])
     </div>
 </div>
 
diff --git a/resources/views/books/grid-item.blade.php b/resources/views/books/grid-item.blade.php
deleted file mode 100644 (file)
index e1d3775..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<a href="{{$book->getUrl()}}" class="grid-card"  data-entity-type="book" data-entity-id="{{$book->id}}">
-    <div class="bg-book featured-image-container-wrap">
-        <div class="featured-image-container" @if($book->cover) style="background-image: url('{{ $book->getBookCover() }}')"@endif>
-        </div>
-        @icon('book')
-    </div>
-    <div class="grid-card-content">
-        <h2>{{$book->getShortName(35)}}</h2>
-        @if(isset($book->searchSnippet))
-            <p class="text-muted">{!! $book->searchSnippet !!}</p>
-        @else
-            <p class="text-muted">{{ $book->getExcerpt(130) }}</p>
-        @endif
-    </div>
-    <div class="grid-card-footer text-muted ">
-        <p>@icon('star')<span title="{{$book->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $book->created_at->diffForHumans()]) }}</span></p>
-        <p>@icon('edit')<span title="{{ $book->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $book->updated_at->diffForHumans()]) }}</span></p>
-    </div>
-</a>
\ No newline at end of file
index b9bd987a9c723224eddd35272a5f2ea2a61aa900..81fb66cfcd18148a796cd478ed53b43110a4817c 100644 (file)
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
         <div class="icon-list text-primary">
-            @if($currentUser->can('book-create-all'))
+            @if(user()->can('book-create-all'))
                 <a href="{{ url("/create-book") }}" class="icon-list-item">
                     <span>@icon('add')</span>
                     <span>{{ trans('entities.books_create') }}</span>
                 </a>
             @endif
 
-            @include('partials.view-toggle', ['view' => $view, 'type' => 'book'])
+            @include('partials.view-toggle', ['view' => $view, 'type' => 'books'])
         </div>
     </div>
 
index 42a2757f94e0e949d536791f6dd79f4daa5ea38a..52cd935d1182a7babb4b2926e56f8586ecf08cf8 100644 (file)
@@ -1,4 +1,3 @@
-
 <main class="content-wrap mt-m card">
     <div class="grid half v-center no-row-gap">
         <h1 class="list-heading">{{ trans('entities.books') }}</h1>
@@ -22,7 +21,7 @@
         @else
              <div class="grid third">
                 @foreach($books as $key => $book)
-                    @include('books.grid-item', ['book' => $book])
+                    @include('partials.entity-grid-item', ['entity' => $book])
                 @endforeach
              </div>
         @endif
index cbafdb4364b0d18e350369d83e6457775edba5e5..def198bddac2450b34958534cd4572da01ee0ba5 100644 (file)
@@ -1,9 +1,9 @@
 @extends('tri-layout')
 
 @section('container-attrs')
-    id="entity-dashboard"
-    entity-id="{{ $book->id }}"
-    entity-type="book"
+    component="entity-search"
+    option:entity-search:entity-id="{{ $book->id }}"
+    option:entity-search:entity-type="book"
 @stop
 
 @section('body')
     </div>
 
     <main class="content-wrap card">
-        <h1 class="break-text" v-pre>{{$book->name}}</h1>
-        <div class="book-content" v-show="!searching">
-            <p class="text-muted" v-pre>{!! nl2br(e($book->description)) !!}</p>
+        <h1 class="break-text">{{$book->name}}</h1>
+        <div refs="entity-search@contentView" class="book-content">
+            <p class="text-muted">{!! nl2br(e($book->description)) !!}</p>
             @if(count($bookChildren) > 0)
-                <div class="entity-list book-contents" v-pre>
+                <div class="entity-list book-contents">
                     @foreach($bookChildren as $childElement)
                         @if($childElement->isA('chapter'))
                             @include('chapters.list-item', ['chapter' => $childElement])
@@ -29,7 +29,7 @@
                     @endforeach
                 </div>
             @else
-                <div class="mt-xl" v-pre>
+                <div class="mt-xl">
                     <hr>
                     <p class="text-muted italic mb-m mt-xl">{{ trans('entities.books_empty_contents') }}</p>
 
             @endif
         </div>
 
-        @include('partials.entity-dashboard-search-results')
+        @include('partials.entity-search-results')
     </main>
 
 @stop
 
-
 @section('right')
-
     <div class="mb-xl">
         <h5>{{ trans('common.details') }}</h5>
         <div class="text-small text-muted blended-links">
@@ -76,7 +74,6 @@
         </div>
     </div>
 
-
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
         <div class="icon-list text-primary">
 
 @section('left')
 
-    @include('partials.entity-dashboard-search-box')
+    @include('partials.entity-search-form', ['label' => trans('entities.books_search_this')])
 
     @if($book->tags->count() > 0)
         <div class="mb-xl">
         </div>
     @endif
 
+    @if(count($bookParentShelves) > 0)
+        <div class="actions mb-xl">
+            <h5>{{ trans('entities.shelves_long') }}</h5>
+            @include('partials.entity-list', ['entities' => $bookParentShelves, 'style' => 'compact'])
+        </div>
+    @endif
+
     @if(count($activity) > 0)
         <div class="mb-xl">
             <h5>{{ trans('entities.recent_activity') }}</h5>
index 98f0af87eeeaa711408a6c010956bb68d9c273a3..f043735bbf4c9c0df001d5f40fc565756b467550 100644 (file)
@@ -13,8 +13,8 @@
     <ul class="sortable-page-list sort-list">
 
         @foreach($bookChildren as $bookChild)
-            <li class="text-{{ $bookChild->getClassName() }}"
-                data-id="{{$bookChild->id}}" data-type="{{ $bookChild->getClassName() }}"
+            <li class="text-{{ $bookChild->getType() }}"
+                data-id="{{$bookChild->id}}" data-type="{{ $bookChild->getType() }}"
                 data-name="{{ $bookChild->name }}" data-created="{{ $bookChild->created_at->timestamp }}"
                 data-updated="{{ $bookChild->updated_at->timestamp }}">
                 <div class="entity-list-item">
@@ -28,7 +28,7 @@
                 </div>
                 @if($bookChild->isA('chapter'))
                     <ul>
-                        @foreach($bookChild->pages as $page)
+                        @foreach($bookChild->visible_pages as $page)
                             <li class="text-page"
                                 data-id="{{$page->id}}" data-type="page"
                                 data-name="{{ $page->name }}" data-created="{{ $page->created_at->timestamp }}"
index 6137c34e8fce357653db2583e3a21cb413f01aba..a1358e1db4e0398cc8d339a79213df190a2ab3f5 100644 (file)
@@ -1,10 +1,10 @@
 <div class="chapter-child-menu">
     <button chapter-toggle type="button" aria-expanded="{{ $isOpen ? 'true' : 'false' }}"
             class="text-muted @if($isOpen) open @endif">
-        @icon('caret-right') @icon('page') <span>{{ trans_choice('entities.x_pages', $bookChild->pages->count()) }}</span>
+        @icon('caret-right') @icon('page') <span>{{ trans_choice('entities.x_pages', $bookChild->visible_pages->count()) }}</span>
     </button>
     <ul class="sub-menu inset-list @if($isOpen) open @endif" @if($isOpen) style="display: block;" @endif role="menu">
-        @foreach($bookChild->pages as $childPage)
+        @foreach($bookChild->visible_pages as $childPage)
             <li class="list-item-page {{ $childPage->isA('page') && $childPage->draft ? 'draft' : '' }}" role="presentation">
                 @include('partials.entity-list-item-basic', ['entity' => $childPage, 'classes' => $current->matches($childPage)? 'selected' : '' ])
             </li>
index 580c123ccf6d7901491f80e943ff03a264ae3bb6..506e8db3d40f7c18b5c7ca60fefb3ca4fe0c5ff6 100644 (file)
@@ -4,10 +4,9 @@
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
     <title>{{ $chapter->name }}</title>
 
+    @include('partials.export-styles', ['format' => $format])
+
     <style>
-        @if (!app()->environment('testing'))
-        {!! file_get_contents(public_path('/dist/export-styles.css')) !!}
-        @endif
         .page-break {
             page-break-after: always;
         }
@@ -20,7 +19,6 @@
             }
         }
     </style>
-    @yield('head')
     @include('partials.custom-head')
 </head>
 <body>
index cd240e685dd651f5501a4659e74d8e145c0c1760..60cfe6674f1b7eeaf7382575a24e970264698593 100644 (file)
@@ -16,7 +16,7 @@
         <label for="tags">{{ trans('entities.chapter_tags') }}</label>
     </button>
     <div class="collapse-content" collapsible-content>
-        @include('components.tag-manager', ['entity' => isset($chapter)?$chapter:null, 'entityType' => 'chapter'])
+        @include('components.tag-manager', ['entity' => $chapter ?? null])
     </div>
 </div>
 
index 7e2e0e1c539c9dca666540a245c5cf2342ea9384..9186983332eaae842d8af07aff34abbfcfd6f994 100644 (file)
@@ -1,4 +1,6 @@
-<a href="{{ $chapter->getUrl() }}" class="chapter entity-list-item @if($chapter->hasChildren()) has-children @endif" data-entity-type="chapter" data-entity-id="{{$chapter->id}}">
+{{--This view display child pages in a list if pre-loaded onto a 'visible_pages' property,--}}
+{{--To ensure that the pages have been loaded efficiently with permissions taken into account.--}}
+<a href="{{ $chapter->getUrl() }}" class="chapter entity-list-item @if($chapter->visible_pages->count() > 0) has-children @endif" data-entity-type="chapter" data-entity-id="{{$chapter->id}}">
     <span class="icon text-chapter">@icon('chapter')</span>
     <div class="content">
         <h4 class="entity-list-item-name break-text">{{ $chapter->name }}</h4>
@@ -7,16 +9,16 @@
         </div>
     </div>
 </a>
-@if ($chapter->hasChildren())
+@if ($chapter->visible_pages->count() > 0)
     <div class="chapter chapter-expansion">
         <span class="icon text-chapter">@icon('page')</span>
         <div class="content">
             <button type="button" chapter-toggle
                     aria-expanded="false"
-                    class="text-muted chapter-expansion-toggle">@icon('caret-right') <span>{{ trans_choice('entities.x_pages', $chapter->pages->count()) }}</span></button>
+                    class="text-muted chapter-expansion-toggle">@icon('caret-right') <span>{{ trans_choice('entities.x_pages', $chapter->visible_pages->count()) }}</span></button>
             <div class="inset-list">
                 <div class="entity-list-item-children">
-                    @include('partials.entity-list', ['entities' => $chapter->pages])
+                    @include('partials.entity-list', ['entities' => $chapter->visible_pages])
                 </div>
             </div>
         </div>
index 105cda760ff496c909aadf723c274fd12b64637b..db02ebcc4f9ae6b6d686a591a8eb2398f5d905d8 100644 (file)
@@ -1,9 +1,9 @@
 @extends('tri-layout')
 
 @section('container-attrs')
-    id="entity-dashboard"
-    entity-id="{{ $chapter->id }}"
-    entity-type="chapter"
+    component="entity-search"
+    option:entity-search:entity-id="{{ $chapter->id }}"
+    option:entity-search:entity-type="chapter"
 @stop
 
 @section('body')
     </div>
 
     <main class="content-wrap card">
-        <h1 class="break-text" v-pre>{{ $chapter->name }}</h1>
-        <div class="chapter-content" v-show="!searching">
-            <p v-pre class="text-muted break-text">{!! nl2br(e($chapter->description)) !!}</p>
+        <h1 class="break-text">{{ $chapter->name }}</h1>
+        <div refs="entity-search@contentView" class="chapter-content">
+            <p class="text-muted break-text">{!! nl2br(e($chapter->description)) !!}</p>
             @if(count($pages) > 0)
-                <div v-pre class="entity-list book-contents">
+                <div class="entity-list book-contents">
                     @foreach($pages as $page)
                         @include('pages.list-item', ['page' => $page])
                     @endforeach
                 </div>
             @else
-                <div class="mt-xl" v-pre>
+                <div class="mt-xl">
                     <hr>
                     <p class="text-muted italic mb-m mt-xl">{{ trans('entities.chapters_empty') }}</p>
 
@@ -49,7 +49,7 @@
             @endif
         </div>
 
-        @include('partials.entity-dashboard-search-results')
+        @include('partials.entity-search-results')
     </main>
 
 @stop
 
 @section('left')
 
-    @include('partials.entity-dashboard-search-box')
+    @include('partials.entity-search-form', ['label' => trans('entities.chapters_search_this')])
 
     @if($chapter->tags->count() > 0)
         <div class="mb-xl">
index ea96a9250dd24a22f2bf274fc093f52edcd6454e..322477ebdc4e98783c8a666043b048ae36fda804 100644 (file)
@@ -27,9 +27,9 @@
                     <button type="button" class="text-button" action="reply" aria-label="{{ trans('common.reply') }}" title="{{ trans('common.reply') }}">@icon('reply')</button>
                 @endif
                 @if(userCan('comment-delete', $comment))
-                    <div dropdown class="dropdown-container">
-                        <button type="button" dropdown-toggle aria-haspopup="true" aria-expanded="false" class="text-button" title="{{ trans('common.delete') }}">@icon('delete')</button>
-                        <ul class="dropdown-menu" role="menu">
+                    <div component="dropdown" class="dropdown-container">
+                        <button type="button" refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" class="text-button" title="{{ trans('common.delete') }}">@icon('delete')</button>
+                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                             <li class="px-m text-small text-muted pb-s">{{trans('entities.comment_delete_confirm')}}</li>
                             <li><button action="delete" type="button" class="text-button text-neg" >@icon('delete'){{ trans('common.delete') }}</button></li>
                         </ul>
index fc81f13ee73053e4c4e208f64cc64e9eb8183826..140d0d027d7ad1996a6795bec9cb61a9621769d9 100644 (file)
@@ -1,23 +1,23 @@
-<section page-comments page-id="{{ $page->id }}" class="comments-list" aria-label="{{ trans('entities.comments') }}">
+<section component="page-comments"
+         option:page-comments:page-id="{{ $page->id }}"
+         option:page-comments:updated-text="{{ trans('entities.comment_updated_success') }}"
+         option:page-comments:deleted-text="{{ trans('entities.comment_deleted_success') }}"
+         option:page-comments:created-text="{{ trans('entities.comment_created_success') }}"
+         option:page-comments:count-text="{{ trans('entities.comment_count') }}"
+         class="comments-list"
+         aria-label="{{ trans('entities.comments') }}">
 
-    @exposeTranslations([
-        'entities.comment_updated_success',
-        'entities.comment_deleted_success',
-        'entities.comment_created_success',
-        'entities.comment_count',
-    ])
-
-    <div comment-count-bar class="grid half left-focus v-center no-row-gap">
+    <div refs="page-comments@commentCountBar" class="grid half left-focus v-center no-row-gap">
         <h5 comments-title>{{ trans_choice('entities.comment_count', count($page->comments), ['count' => count($page->comments)]) }}</h5>
         @if (count($page->comments) === 0 && userCan('comment-create-all'))
-            <div class="text-m-right" comment-add-button-container>
+            <div class="text-m-right" refs="page-comments@addButtonContainer">
                 <button type="button" action="addComment"
                         class="button outline">{{ trans('entities.comment_add') }}</button>
             </div>
         @endif
     </div>
 
-    <div class="comment-container" comment-container>
+    <div refs="page-comments@commentContainer" class="comment-container">
         @foreach($page->comments as $comment)
             @include('comments.comment', ['comment' => $comment])
         @endforeach
@@ -27,7 +27,7 @@
         @include('comments.create')
 
         @if (count($page->comments) > 0)
-            <div class="text-right" comment-add-button-container>
+            <div refs="page-comments@addButtonContainer" class="text-right">
                 <button type="button" action="addComment"
                         class="button outline">{{ trans('entities.comment_add') }}</button>
             </div>
index 61e41a354fab3214883296238e9ee55ac7c9a130..12216b95b9bdb1f5ef33bfc3403094773b5aa73b 100644 (file)
@@ -1,6 +1,7 @@
-<div class="comment-box" comment-box style="display:none;">
+<div class="comment-box" style="display:none;">
+
     <div class="header p-s">{{ trans('entities.comment_new') }}</div>
-    <div comment-form-reply-to class="reply-row primary-background-light text-muted px-s py-xs mb-s" style="display: none;">
+    <div refs="page-comments@replyToRow" class="reply-row primary-background-light text-muted px-s py-xs mb-s" style="display: none;">
         <div class="grid left-focus v-center">
             <div>
                 {!! trans('entities.comment_in_reply_to', ['commentId' => '<a href=""></a>']) !!}
@@ -10,7 +11,8 @@
             </div>
         </div>
     </div>
-    <div class="content px-s" comment-form-container>
+
+    <div refs="page-comments@formContainer" class="content px-s">
         <form novalidate>
             <div class="form-group description-input">
                         <textarea name="markdown" rows="3"
@@ -26,4 +28,5 @@
             </div>
         </form>
     </div>
+
 </div>
\ No newline at end of file
index 19299695042e98ded5b55735865c31f6fee9528b..80e79410a04ff3aeee9c4324ca3978e6b4eabf35 100644 (file)
         </div>
 
         <div class="text-right">
-            <nav class="header-links" >
+            <nav class="header-links">
                 <div class="links text-center">
                     @if (hasAppAccess())
                         <a class="hide-over-l" href="{{ url('/search') }}">@icon('search'){{ trans('common.search') }}</a>
-                        @if(userCanOnAny('view', \BookStack\Entities\Bookshelf::class) || userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
+                        @if(userCanOnAny('view', \BookStack\Entities\Models\Bookshelf::class) || userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
                             <a href="{{ url('/shelves') }}">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
                         @endif
                         <a href="{{ url('/books') }}">@icon('books'){{ trans('entities.books') }}</a>
                     @endif
 
                     @if(!signedInUser())
-                        @if(setting('registration-enabled', false))
-                            <a href="{{ url('/register') }}">@icon('new-user') {{ trans('auth.sign_up') }}</a>
+                        @if(setting('registration-enabled') && config('auth.method') === 'standard')
+                            <a href="{{ url('/register') }}">@icon('new-user'){{ trans('auth.sign_up') }}</a>
                         @endif
-                        <a href="{{ url('/login') }}">@icon('login') {{ trans('auth.log_in') }}</a>
+                        <a href="{{ url('/login')  }}">@icon('login'){{ trans('auth.log_in') }}</a>
                     @endif
                 </div>
                 @if(signedInUser())
                     <?php $currentUser = user(); ?>
-                    <div class="dropdown-container" dropdown>
-                        <span class="user-name py-s hide-under-l" dropdown-toggle
+                    <div class="dropdown-container" component="dropdown">
+                        <span class="user-name py-s hide-under-l" refs="dropdown@toggle"
                               aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.profile_menu') }}" tabindex="0">
                             <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
                             <span class="name">{{ $currentUser->getShortName(9) }}</span> @icon('caret-down')
                         </span>
-                        <ul class="dropdown-menu" role="menu">
+                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                             <li>
                                 <a href="{{ url("/user/{$currentUser->id}") }}">@icon('user'){{ trans('common.view_profile') }}</a>
                             </li>
                                 <a href="{{ url("/settings/users/{$currentUser->id}") }}">@icon('edit'){{ trans('common.edit_profile') }}</a>
                             </li>
                             <li>
-                                <a href="{{ url('/logout') }}">@icon('logout'){{ trans('auth.logout') }}</a>
+                                @if(config('auth.method') === 'saml2')
+                                    <a href="{{ url('/saml2/logout') }}">@icon('logout'){{ trans('auth.logout') }}</a>
+                                @else
+                                    <a href="{{ url('/logout') }}">@icon('logout'){{ trans('auth.logout') }}</a>
+                                @endif
+                            </li>
+                            <li><hr></li>
+                            <li>
+                                @include('partials.dark-mode-toggle')
                             </li>
                         </ul>
                     </div>
@@ -73,4 +81,4 @@
         </div>
 
     </div>
-</header>
\ No newline at end of file
+</header>
index de2822261b9508fa581319ec1b300051c9e51780..1c18edb246bc1ce93040c7af6325d2a0250647c1 100644 (file)
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
         <div class="icon-list text-primary">
-            @if($currentUser->can('book-create-all'))
+            @if(user()->can('book-create-all'))
                 <a href="{{ url("/create-book") }}" class="icon-list-item">
                     <span>@icon('add')</span>
                     <span>{{ trans('entities.books_create') }}</span>
                 </a>
             @endif
-            @include('partials.view-toggle', ['view' => $view, 'type' => 'book'])
+            @include('partials.view-toggle', ['view' => $view, 'type' => 'books'])
             @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+            @include('partials.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
         </div>
     </div>
-@stop
\ No newline at end of file
+@stop
index 56e281dcb7fd107c52317ba0f856ce9c4310a103..e0820305746c799d5d2deb890849397b98f1f70d 100644 (file)
@@ -19,6 +19,7 @@
         <h5>{{ trans('common.actions') }}</h5>
         <div class="icon-list text-primary">
             @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+            @include('partials.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
         </div>
     </div>
 @stop
\ No newline at end of file
index 3935734d4667d3872b3be0d220fd427eecec536d..957fa6578fffd35e349c550fdf521a237fd09440 100644 (file)
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
         <div class="icon-list text-primary">
-            @if($currentUser->can('bookshelf-create-all'))
+            @if(user()->can('bookshelf-create-all'))
                 <a href="{{ url("/create-shelf") }}" class="icon-list-item">
                     <span>@icon('add')</span>
                     <span>{{ trans('entities.shelves_new_action') }}</span>
                 </a>
             @endif
-            @include('partials.view-toggle', ['view' => $view, 'type' => 'shelf'])
+            @include('partials.view-toggle', ['view' => $view, 'type' => 'shelves'])
             @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+            @include('partials.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
         </div>
     </div>
-@stop
\ No newline at end of file
+@stop
index 12adda618905a59033b8b40cd6326ee2ab26cf05..4c36ce61a9be9648f48dbc68462b2b75bc73f83a 100644 (file)
@@ -6,11 +6,11 @@
 @endif
 
 <div class="mb-xl">
-    <h5>{{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h5>
+    <h5>{{ trans('entities.' . (auth()->check() ? 'my_recently_viewed' : 'books_recent')) }}</h5>
     @include('partials.entity-list', [
         'entities' => $recents,
         'style' => 'compact',
-        'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
+        'emptyText' => auth()->check() ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
         ])
 </div>
 
index cd27ff5687e006fffa598cb30f4e2243601e4243..ad503463e46f1db404882fbf58018dfae39b227c 100644 (file)
@@ -3,8 +3,17 @@
 @section('body')
 
     <div class="container px-xl py-s">
-        <div class="icon-list inline block">
-            @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+        <div class="grid half">
+            <div>
+                <div class="icon-list inline block">
+                    @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+                </div>
+            </div>
+            <div class="text-m-right">
+                <div class="icon-list inline block">
+                    @include('partials.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
+                </div>
+            </div>
         </div>
     </div>
 
                     </div>
                 @endif
 
-                <div id="{{ $signedIn ? 'recently-viewed' : 'recent-books' }}" class="card mb-xl">
-                    <h3 class="card-title">{{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
+                <div id="{{ auth()->check() ? 'recently-viewed' : 'recent-books' }}" class="card mb-xl">
+                    <h3 class="card-title">{{ trans('entities.' . (auth()->check() ? 'my_recently_viewed' : 'books_recent')) }}</h3>
                     <div class="px-m">
                         @include('partials.entity-list', [
                         'entities' => $recents,
                         'style' => 'compact',
-                        'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
+                        'emptyText' => auth()->check() ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
                         ])
                     </div>
                 </div>
@@ -57,6 +66,4 @@
         </div>
     </div>
 
-
-
 @stop
index f8377b1208d49e03c3ac0bad95b7610ebd7cf164..011840465a63ff480f048f91b9e9f18bda3baf37 100644 (file)
@@ -1,10 +1,10 @@
-<div id="code-editor">
-    <div overlay ref="overlay" v-cloak @click="hide()">
-        <div class="popup-body" tabindex="-1" @click.stop>
+<div>
+    <div components="popup code-editor" class="popup-background code-editor">
+        <div refs="code-editor@container" class="popup-body" tabindex="-1">
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.code_editor') }}</div>
-                <button class="popup-header-close" @click="hide()">x</button>
+                <button class="popup-header-close" refs="popup@hide">x</button>
             </div>
 
             <div class="p-l popup-content">
                     <label for="code-editor-language">{{ trans('components.code_language') }}</label>
                     <div class="lang-options">
                         <small>
-                            <a @click="updateLanguage('CSS')">CSS</a>
-                            <a @click="updateLanguage('C')">C</a>
-                            <a @click="updateLanguage('C++')">C++</a>
-                            <a @click="updateLanguage('C#')">C#</a>
-                            <a @click="updateLanguage('Go')">Go</a>
-                            <a @click="updateLanguage('HTML')">HTML</a>
-                            <a @click="updateLanguage('INI')">INI</a>
-                            <a @click="updateLanguage('Java')">Java</a>
-                            <a @click="updateLanguage('JavaScript')">JavaScript</a>
-                            <a @click="updateLanguage('JSON')">JSON</a>
-                            <a @click="updateLanguage('Lua')">Lua</a>
-                            <a @click="updateLanguage('PHP')">PHP</a>
-                            <a @click="updateLanguage('Powershell')">Powershell</a>
-                            <a @click="updateLanguage('MarkDown')">MarkDown</a>
-                            <a @click="updateLanguage('Nginx')">Nginx</a>
-                            <a @click="updateLanguage('Python')">Python</a>
-                            <a @click="updateLanguage('Ruby')">Ruby</a>
-                            <a @click="updateLanguage('shell')">Shell/Bash</a>
-                            <a @click="updateLanguage('SQL')">SQL</a>
-                            <a @click="updateLanguage('XML')">XML</a>
-                            <a @click="updateLanguage('YAML')">YAML</a>
+                            <a refs="code-editor@languageLink" data-lang="CSS">CSS</a>
+                            <a refs="code-editor@languageLink" data-lang="C">C</a>
+                            <a refs="code-editor@languageLink" data-lang="C++">C++</a>
+                            <a refs="code-editor@languageLink" data-lang="C#">C#</a>
+                            <a refs="code-editor@languageLink" data-lang="Fortran">Fortran</a>
+                            <a refs="code-editor@languageLink" data-lang="Go">Go</a>
+                            <a refs="code-editor@languageLink" data-lang="HTML">HTML</a>
+                            <a refs="code-editor@languageLink" data-lang="INI">INI</a>
+                            <a refs="code-editor@languageLink" data-lang="Java">Java</a>
+                            <a refs="code-editor@languageLink" data-lang="JavaScript">JavaScript</a>
+                            <a refs="code-editor@languageLink" data-lang="JSON">JSON</a>
+                            <a refs="code-editor@languageLink" data-lang="Lua">Lua</a>
+                            <a refs="code-editor@languageLink" data-lang="MarkDown">MarkDown</a>
+                            <a refs="code-editor@languageLink" data-lang="Nginx">Nginx</a>
+                            <a refs="code-editor@languageLink" data-lang="PASCAL">Pascal</a>
+                            <a refs="code-editor@languageLink" data-lang="Perl">Perl</a>
+                            <a refs="code-editor@languageLink" data-lang="PHP">PHP</a>
+                            <a refs="code-editor@languageLink" data-lang="Powershell">Powershell</a>
+                            <a refs="code-editor@languageLink" data-lang="Python">Python</a>
+                            <a refs="code-editor@languageLink" data-lang="Ruby">Ruby</a>
+                            <a refs="code-editor@languageLink" data-lang="shell">Shell/Bash</a>
+                            <a refs="code-editor@languageLink" data-lang="SQL">SQL</a>
+                            <a refs="code-editor@languageLink" data-lang="VBScript">VBScript</a>
+                            <a refs="code-editor@languageLink" data-lang="XML">XML</a>
+                            <a refs="code-editor@languageLink" data-lang="YAML">YAML</a>
                         </small>
                     </div>
-                    <input @keypress.enter="save()" id="code-editor-language" type="text" @input="updateEditorMode(language)" v-model="language">
+                    <input refs="code-editor@languageInput" id="code-editor-language" type="text">
                 </div>
 
                 <div class="form-group">
-                    <label for="code-editor-content">{{ trans('components.code_content') }}</label>
-                    <textarea ref="editor" v-model="code"></textarea>
+                    <div class="grid half no-break v-end mb-xs">
+                        <div>
+                            <label for="code-editor-content">{{ trans('components.code_content') }}</label>
+                        </div>
+                        <div class="text-right">
+                            <div component="dropdown" refs="code-editor@historyDropDown" class="inline block">
+                                <button refs="dropdown@toggle" class="text-button text-small">@icon('history') {{ trans('components.code_session_history') }}</button>
+                                <ul refs="dropdown@menu code-editor@historyList" class="dropdown-menu"></ul>
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="clearfix"></div>
+                    <textarea refs="code-editor@editor"></textarea>
                 </div>
 
                 <div class="form-group">
-                    <button type="button" class="button" @click="save()">{{ trans('components.code_save') }}</button>
+                    <button refs="code-editor@saveButton" type="button" class="button">{{ trans('components.code_save') }}</button>
                 </div>
 
             </div>
diff --git a/resources/views/components/dropzone.blade.php b/resources/views/components/dropzone.blade.php
new file mode 100644 (file)
index 0000000..6c5ac49
--- /dev/null
@@ -0,0 +1,15 @@
+{{--
+@url - URL to upload to.
+@placeholder - Placeholder text
+@successMessage
+--}}
+<div component="dropzone"
+     option:dropzone:url="{{ $url }}"
+     option:dropzone:success-message="{{ $successMessage ?? '' }}"
+     option:dropzone:remove-message="{{ trans('components.image_upload_remove') }}"
+     option:dropzone:upload-limit-message="{{ trans('errors.server_upload_limit') }}"
+     option:dropzone:timeout-message="{{ trans('errors.file_upload_timeout') }}"
+
+     class="dropzone-container text-center">
+    <button type="button" class="dz-message">{{ $placeholder }}</button>
+</div>
\ No newline at end of file
index 0beee658d88334bd598d79b185dae116fe6aa838..ec8712b6a5bae9d533009d2f0c6b49b22e2c1a65 100644 (file)
@@ -1,13 +1,13 @@
 <div id="entity-selector-wrap">
-    <div overlay entity-selector-popup>
+    <div components="popup entity-selector-popup" class="popup-background">
         <div class="popup-body small" tabindex="-1">
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('entities.entity_select') }}</div>
-                <button type="button" class="popup-header-close">x</button>
+                <button refs="popup@hide" type="button" class="popup-header-close">x</button>
             </div>
             @include('components.entity-selector', ['name' => 'entity-selector'])
             <div class="popup-footer">
-                <button type="button" disabled="true" class="button entity-link-selector-confirm corner-button">{{ trans('common.select') }}</button>
+                <button refs="entity-selector-popup@select" type="button" disabled="true" class="button corner-button">{{ trans('common.select') }}</button>
             </div>
         </div>
     </div>
index a24f9ac1e9ec79c7d1c55ed95250f177bf9f42ee..0c14490386b0cfb968c00f7d06a535ab2570a6ea 100644 (file)
@@ -4,7 +4,7 @@ $key - Unique key for checking existing stored state.
 --}}
 <?php $isOpen = setting()->getForCurrentUser('section_expansion#'. $key); ?>
 <button type="button" expand-toggle="{{ $target }}"
-   expand-toggle-update-endpoint="{{ url('/settings/users/'. $currentUser->id .'/update-expansion-preference/' . $key) }}"
+   expand-toggle-update-endpoint="{{ url('/settings/users/'. user()->id .'/update-expansion-preference/' . $key) }}"
    expand-toggle-is-open="{{ $isOpen ? 'yes' : 'no' }}"
    class="text-muted icon-list-item text-primary">
     <span>@icon('expand-text')</span>
diff --git a/resources/views/components/image-manager-form.blade.php b/resources/views/components/image-manager-form.blade.php
new file mode 100644 (file)
index 0000000..e49a5fc
--- /dev/null
@@ -0,0 +1,60 @@
+<div class="image-manager-details">
+
+    <form component="ajax-form"
+          option:ajax-form:success-message="{{ trans('components.image_update_success') }}"
+          option:ajax-form:method="put"
+          option:ajax-form:response-container=".image-manager-details"
+          option:ajax-form:url="{{ url('images/' . $image->id) }}">
+
+        <div class="image-manager-viewer">
+            <a href="{{ $image->url }}" target="_blank" class="block">
+                <img src="{{ $image->thumbs['display'] }}"
+                     alt="{{ $image->name }}"
+                     class="anim fadeIn"
+                     title="{{ $image->name }}">
+            </a>
+        </div>
+        <div class="form-group stretch-inputs">
+            <label for="name">{{ trans('components.image_image_name') }}</label>
+            <input id="name" class="input-base" type="text" name="name" value="{{ $image->name }}">
+        </div>
+        <div class="grid half">
+            <div>
+                <button type="button"
+                        id="image-manager-delete"
+                        title="{{ trans('common.delete') }}"
+                        class="button icon outline">@icon('delete')</button>
+            </div>
+            <div class="text-right">
+                <button type="submit"
+                        class="button icon outline">{{ trans('common.save') }}</button>
+            </div>
+        </div>
+    </form>
+
+    @if(!is_null($dependantPages))
+        @if(count($dependantPages) > 0)
+            <p class="text-neg mb-xs mt-m">{{ trans('components.image_delete_used') }}</p>
+            <ul class="text-neg">
+                @foreach($dependantPages as $page)
+                    <li>
+                        <a href="{{ $page->url }}"
+                           target="_blank"
+                           class="text-neg">{{ $page->name }}</a>
+                    </li>
+                @endforeach
+            </ul>
+        @endif
+        <p class="text-neg mb-xs">{{ trans('components.image_delete_confirm_text') }}</p>
+        <form component="ajax-form"
+              option:ajax-form:success-message="{{ trans('components.image_delete_success') }}"
+              option:ajax-form:method="delete"
+              option:ajax-form:response-container=".image-manager-details"
+              option:ajax-form:url="{{ url('images/' . $image->id) }}">
+            <button type="submit" class="button neg">
+                {{ trans('common.delete_confirm') }}
+            </button>
+        </form>
+    @endif
+
+</div>
\ No newline at end of file
diff --git a/resources/views/components/image-manager-list.blade.php b/resources/views/components/image-manager-list.blade.php
new file mode 100644 (file)
index 0000000..e5562e1
--- /dev/null
@@ -0,0 +1,23 @@
+@foreach($images as $index => $image)
+<div>
+    <div component="event-emit-select"
+         option:event-emit-select:name="image"
+         option:event-emit-select:data="{{ json_encode($image) }}"
+         class="image anim fadeIn text-primary"
+         style="animation-delay: {{ $index > 26 ? '160ms' : ($index * 25) . 'ms' }};">
+        <img src="{{ $image->thumbs['gallery'] }}"
+             alt="{{ $image->name }}"
+             width="150"
+             height="150"
+             loading="lazy"
+             title="{{ $image->name }}">
+        <div class="image-meta">
+            <span class="name">{{ $image->name }}</span>
+            <span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => $image->created_at->format('Y-m-d H:i:s')]) }}</span>
+        </div>
+    </div>
+</div>
+@endforeach
+@if($hasMore)
+    <div class="load-more">{{ trans('components.image_load_more') }}</div>
+@endif
\ No newline at end of file
index 0971c3ed95ed7705da9c476c790d05acea3bc71d..4f03eeaec21c33634992c040bbb56014a6ada5ff 100644 (file)
-<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to ?? 0 }}">
+<div component="image-manager"
+     option:image-manager:uploaded-to="{{ $uploaded_to ?? 0 }}"
+     class="image-manager">
 
-    @exposeTranslations([
-        'components.image_delete_success',
-        'components.image_upload_success',
-        'errors.server_upload_limit',
-        'components.image_upload_remove',
-        'components.file_upload_timeout',
-    ])
-
-    <div overlay v-cloak @click="hide">
-        <div class="popup-body" tabindex="-1" @click.stop>
+    <div component="popup"
+         refs="image-manager@popup"
+         class="popup-background">
+        <div class="popup-body" tabindex="-1">
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.image_select') }}</div>
-                <button class="popup-header-close" @click="hide()">x</button>
+                <button refs="popup@hide" type="button" class="popup-header-close">x</button>
             </div>
 
             <div class="flex-fill image-manager-body">
 
                 <div class="image-manager-content">
-                    <div v-if="imageType === 'gallery' || imageType === 'drawio'" class="image-manager-header primary-background-light nav-tabs grid third">
-                        <div class="tab-item" title="{{ trans('components.image_all_title') }}" :class="{selected: !filter}" @click="setFilterType(null)">@icon('images') {{ trans('components.image_all') }}</div>
-                        <div class="tab-item" title="{{ trans('components.image_book_title') }}" :class="{selected: (filter=='book')}" @click="setFilterType('book')">@icon('book', ['class' => 'text-book svg-icon']) {{ trans('entities.book') }}</div>
-                        <div class="tab-item" title="{{ trans('components.image_page_title') }}" :class="{selected: (filter=='page')}" @click="setFilterType('page')">@icon('page', ['class' => 'text-page svg-icon']) {{ trans('entities.page') }}</div>
+                    <div class="image-manager-header primary-background-light nav-tabs grid third no-gap">
+                        <button refs="image-manager@filterTabs"
+                                data-filter="all"
+                                type="button" class="tab-item selected" title="{{ trans('components.image_all_title') }}">@icon('images') {{ trans('components.image_all') }}</button>
+                        <button refs="image-manager@filterTabs"
+                                data-filter="book"
+                                type="button" class="tab-item" title="{{ trans('components.image_book_title') }}">@icon('book', ['class' => 'text-book svg-icon']) {{ trans('entities.book') }}</button>
+                        <button refs="image-manager@filterTabs"
+                                data-filter="page"
+                                type="button" class="tab-item" title="{{ trans('components.image_page_title') }}">@icon('page', ['class' => 'text-page svg-icon']) {{ trans('entities.page') }}</button>
                     </div>
                     <div>
-                        <form @submit.prevent="searchImages" class="contained-search-box">
-                            <input placeholder="{{ trans('components.image_search_hint') }}" v-model="searchTerm">
-                            <button :class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" @click="cancelSearch()" class="text-button cancel">@icon('close')</button>
-                            <button title="{{ trans('common.search') }}" class="text-button">@icon('search')</button>
+                        <form refs="image-manager@searchForm" class="contained-search-box">
+                            <input refs="image-manager@searchInput"
+                                   placeholder="{{ trans('components.image_search_hint') }}"
+                                   type="text">
+                            <button refs="image-manager@cancelSearch"
+                                    title="{{ trans('common.search_clear') }}"
+                                    type="button"
+                                    class="cancel">@icon('close')</button>
+                            <button type="submit" class="primary-background text-white"
+                                    title="{{ trans('common.search') }}">@icon('search')</button>
                         </form>
                     </div>
-                    <div class="image-manager-list">
-                        <div v-if="images.length > 0" v-for="(image, idx) in images">
-                            <div class="image anim fadeIn" :style="{animationDelay: (idx > 26) ? '160ms' : ((idx * 25) + 'ms')}"
-                                 :class="{selected: (image==selectedImage)}" @click="imageSelect(image)">
-                                <img :src="image.thumbs.gallery" :alt="image.title" :title="image.name">
-                                <div class="image-meta">
-                                    <span class="name" v-text="image.name"></span>
-                                    <span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
-                                </div>
-                            </div>
-                        </div>
-                        <div class="load-more" v-show="hasMore" @click="fetchData">{{ trans('components.image_load_more') }}</div>
-                    </div>
+                    <div refs="image-manager@listContainer" class="image-manager-list"></div>
                 </div>
 
-                <div class="image-manager-sidebar">
-
-                    <dropzone v-if="imageType !== 'drawio'" ref="dropzone" placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
-
-                    <div class="inner">
-
-                        <div class="image-manager-details anim fadeIn" v-if="selectedImage">
-
-                            <form @submit.prevent="saveImageDetails">
-                                <div class="image-manager-viewer">
-                                    <a :href="selectedImage.url" target="_blank" style="display: block;">
-                                        <img :src="selectedImage.thumbs.display" :alt="selectedImage.name"
-                                             :title="selectedImage.name">
-                                    </a>
-                                </div>
-                                <div class="form-group">
-                                    <label for="name">{{ trans('components.image_image_name') }}</label>
-                                    <input id="name" class="input-base" name="name" v-model="selectedImage.name">
-                                </div>
-                            </form>
-
-                            <div class="clearfix">
-                                <div class="float left">
-                                    <button type="button" class="button icon outline" @click="deleteImage">@icon('delete')</button>
-
-                                </div>
-                                <button class="button anim fadeIn float right" v-show="selectedImage" @click="callbackAndHide(selectedImage)">
-                                    {{ trans('components.image_select_image') }}
-                                </button>
-                                <div class="clearfix"></div>
-                                <div v-show="dependantPages">
-                                    <p class="text-neg text-small">
-                                        {{ trans('components.image_delete_used') }}
-                                    </p>
-                                    <ul class="text-neg">
-                                        <li v-for="page in dependantPages">
-                                            <a :href="page.url" target="_blank" class="text-neg" v-text="page.name"></a>
-                                        </li>
-                                    </ul>
-                                </div>
-                                <div v-show="deleteConfirm" class="text-neg text-small">
-                                    {{ trans('components.image_delete_confirm') }}
-                                </div>
-                            </div>
-
-                        </div>
+                <div class="image-manager-sidebar flex-container-column">
 
+                    <div refs="image-manager@dropzoneContainer">
+                        @include('components.dropzone', [
+                            'placeholder' => trans('components.image_dropzone'),
+                            'successMessage' => trans('components.image_upload_success'),
+                            'url' => url('/images/gallery?' . http_build_query(['uploaded_to' => $uploaded_to ?? 0]))
+                        ])
+                    </div>
 
+                    <div refs="image-manager@formContainer" class="inner flex"></div>
 
-                    </div>
+                    <button refs="image-manager@selectButton" type="button" class="hidden button corner-button">
+                        {{ trans('components.image_select_image') }}
+                    </button>
                 </div>
 
             </div>
index e24ea49f1c82a7a374f8c8cf0c51392a40cac943..c59615d92a30a38fbb0aa66feba92d5a56c0ad1f 100644 (file)
@@ -3,7 +3,7 @@
 <div page-picker>
     <div class="input-base">
         <span @if($value) style="display: none" @endif page-picker-default class="text-muted italic">{{ $placeholder }}</span>
-        <a @if(!$value) style="display: none" @endif href="{{ url('/link/' . $value) }}" target="_blank" class="text-page" page-picker-display>#{{$value}}, {{$value ? \BookStack\Entities\Page::find($value)->name : '' }}</a>
+        <a @if(!$value) style="display: none" @endif href="{{ url('/link/' . $value) }}" target="_blank" class="text-page" page-picker-display>#{{$value}}, {{$value ? \BookStack\Entities\Models\Page::find($value)->name : '' }}</a>
     </div>
     <br>
     <input type="hidden" value="{{$value}}" name="{{$name}}" id="{{$name}}">
diff --git a/resources/views/components/setting-entity-color-picker.blade.php b/resources/views/components/setting-entity-color-picker.blade.php
new file mode 100644 (file)
index 0000000..3b99d0b
--- /dev/null
@@ -0,0 +1,21 @@
+{{--
+    @type - Name of entity type
+--}}
+<div setting-color-picker class="grid no-break half mb-l">
+    <div>
+        <label for="setting-{{ $type }}-color" class="text-dark">{{ trans('settings.'. str_replace('-', '_', $type) .'_color') }}</label>
+        <button type="button" class="text-button text-muted" setting-color-picker-default>{{ trans('common.default') }}</button>
+        <span class="sep">|</span>
+        <button type="button" class="text-button text-muted" setting-color-picker-reset>{{ trans('common.reset') }}</button>
+    </div>
+    <div>
+        <input type="color"
+               data-default="{{ config('setting-defaults.'. $type .'-color') }}"
+               data-current="{{ setting($type .'-color') }}"
+               value="{{ setting($type .'-color') }}"
+               name="setting-{{ $type }}-color"
+               id="setting-{{ $type }}-color"
+               placeholder="{{ config('setting-defaults.'. $type .'-color') }}"
+               class="small">
+    </div>
+</div>
diff --git a/resources/views/components/tag-manager-list.blade.php b/resources/views/components/tag-manager-list.blade.php
new file mode 100644 (file)
index 0000000..6fbce2f
--- /dev/null
@@ -0,0 +1,25 @@
+@foreach(array_merge($tags, [null, null]) as $index => $tag)
+    <div class="card drag-card {{ $loop->last ? 'hidden' : '' }}" @if($loop->last) refs="add-remove-rows@model" @endif>
+        <div class="handle">@icon('grip')</div>
+        @foreach(['name', 'value'] as $type)
+            <div component="auto-suggest"
+                 option:auto-suggest:url="{{ url('/ajax/tags/suggest/' . $type . 's') }}"
+                 option:auto-suggest:type="{{ $type }}"
+                 class="outline">
+                <input value="{{ $tag->$type ?? '' }}"
+                       placeholder="{{ trans('entities.tag_' . $type) }}"
+                       aria-label="{{ trans('entities.tag_' . $type) }}"
+                       name="tags[{{ $loop->parent->last ? 'randrowid' : $index }}][{{ $type }}]"
+                       type="text"
+                       refs="auto-suggest@input"
+                       autocomplete="off"/>
+                <ul refs="auto-suggest@list" class="suggestion-box dropdown-menu"></ul>
+            </div>
+        @endforeach
+        <button type="button"
+                aria-label="{{ trans('entities.tags_remove') }}"
+                class="text-center drag-card-action text-neg">
+            @icon('close')
+        </button>
+    </div>
+@endforeach
\ No newline at end of file
index 2878569374d6db6bd80928a3fe34d8477d982124..9e24ba3fdcedadae6140c81122e75fb4fd33750c 100644 (file)
@@ -1,22 +1,16 @@
-<div id="tag-manager" entity-id="{{ isset($entity) ? $entity->id : 0 }}" entity-type="{{ $entity ? $entity->getType() : $entityType }}">
-    <div class="tags">
+<div components="tag-manager add-remove-rows"
+     option:add-remove-rows:row-selector=".card"
+     option:add-remove-rows:remove-selector="button.text-neg"
+     option:tag-manager:row-selector=".card:not(.hidden)"
+     refs="tag-manager@add-remove"
+     class="tags">
+
         <p class="text-muted small">{!! nl2br(e(trans('entities.tags_explain'))) !!}</p>
 
-        <draggable :options="{handle: '.handle'}" :list="tags" element="div">
-            <div v-for="(tag, i) in tags" :key="tag.key" class="card drag-card">
-                <div class="handle" >@icon('grip')</div>
-                <div>
-                    <autosuggest url="{{ url('/ajax/tags/suggest/names') }}" type="name" class="outline" :name="getTagFieldName(i, 'name')"
-                                 v-model="tag.name" @input="tagChange(tag)" @blur="tagBlur(tag)" placeholder="{{ trans('entities.tag_name') }}"/>
-                </div>
-                <div>
-                    <autosuggest url="{{ url('/ajax/tags/suggest/values') }}" type="value" class="outline" :name="getTagFieldName(i, 'value')"
-                                 v-model="tag.value" @change="tagChange(tag)" @blur="tagBlur(tag)" placeholder="{{ trans('entities.tag_value') }}"/>
-                </div>
-                <button type="button" aria-label="{{ trans('entities.tags_remove') }}" v-show="tags.length !== 1" class="text-center drag-card-action text-neg" @click="removeTag(tag)">@icon('close')</button>
-            </div>
-        </draggable>
+        <div component="sortable-list"
+             option:sortable-list:handle-selector=".handle">
+            @include('components.tag-manager-list', ['tags' => $entity ? $entity->tags->all() : []])
+        </div>
 
-        <button @click="addEmptyTag" type="button" class="text-button">{{ trans('entities.tags_add') }}</button>
-    </div>
+        <button refs="add-remove-rows@add" type="button" class="text-button">{{ trans('entities.tags_add') }}</button>
 </div>
\ No newline at end of file
diff --git a/resources/views/components/user-select-list.blade.php b/resources/views/components/user-select-list.blade.php
new file mode 100644 (file)
index 0000000..2c49e96
--- /dev/null
@@ -0,0 +1,6 @@
+@foreach($users as $user)
+    <a href="#" class="flex-container-row items-center dropdown-search-item" data-id="{{ $user->id }}">
+        <img class="avatar mr-m" src="{{ $user->getAvatar(30) }}" alt="{{ $user->name }}">
+        <span>{{ $user->name }}</span>
+    </a>
+@endforeach
\ No newline at end of file
diff --git a/resources/views/components/user-select.blade.php b/resources/views/components/user-select.blade.php
new file mode 100644 (file)
index 0000000..2a07f0b
--- /dev/null
@@ -0,0 +1,34 @@
+<div class="dropdown-search custom-select-input" components="dropdown dropdown-search user-select"
+     option:dropdown-search:url="/search/users/select"
+>
+    <input refs="user-select@input" type="hidden" name="{{ $name }}" value="{{ $user->id ?? '' }}">
+    <div refs="dropdown@toggle"
+         class="dropdown-search-toggle flex-container-row items-center"
+         aria-haspopup="true" aria-expanded="false" tabindex="0">
+        <div refs="user-select@user-info" class="flex-container-row items-center px-s">
+            @if($user)
+                <img class="avatar mr-m" src="{{ $user->getAvatar(30) }}" alt="{{ $user->name }}">
+                <span>{{ $user->name }}</span>
+            @else
+                <span>{{ trans('settings.users_none_selected') }}</span>
+            @endif
+        </div>
+        <span style="font-size: 1.5rem; margin-left: auto;">
+            @icon('caret-down')
+        </span>
+    </div>
+    <div refs="dropdown@menu" class="dropdown-search-dropdown card" role="menu">
+        <div class="dropdown-search-search">
+            @icon('search')
+            <input refs="dropdown-search@searchInput"
+                   aria-label="{{ trans('common.search') }}"
+                   autocomplete="off"
+                   placeholder="{{ trans('common.search') }}"
+                   type="text">
+        </div>
+        <div refs="dropdown-search@loading" class="text-center">
+            @include('partials.loading-icon')
+        </div>
+        <div refs="dropdown-search@listContainer" class="dropdown-search-list"></div>
+    </div>
+</div>
\ No newline at end of file
index 9c599307ed0279c22de797ea8790717aa91cadf8..02f97fc546fcbc195ec04421731fba44b15cfb1d 100644 (file)
@@ -3,13 +3,17 @@
 @section('content')
 <div class="container mt-l">
 
-    <div class="card mb-xl px-l pb-xl pt-l">
+    <div class="card mb-xl px-l pb-l pt-l">
         <div class="grid half v-center">
             <div>
                 <h1 class="list-heading">{{ $message ?? trans('errors.404_page_not_found') }}</h1>
                 <h5>{{ trans('errors.sorry_page_not_found') }}</h5>
+                <p>{{ trans('errors.sorry_page_not_found_permission_warning') }}</p>
             </div>
             <div class="text-right">
+                @if(!signedInUser())
+                    <a href="{{ url('/login') }}" class="button outline">{{ trans('auth.log_in') }}</a>
+                @endif
                 <a href="{{ url('/') }}" class="button outline">{{ trans('errors.return_home') }}</a>
             </div>
         </div>
index 8c6822767a1f823e67782a3d0a0fb96ad89cd8e2..d06ddbc574a2707542a96d97349f86c473d36fdc 100644 (file)
@@ -2,14 +2,16 @@
 
 @section('content')
 
-    <div class="container">
-        <div class="card">
-            <h3 class="text-muted">{{ trans('errors.error_occurred') }}</h3>
+    <div class="container small py-xl">
+
+        <main class="card content-wrap auto-height">
             <div class="body">
-                <h5>{{ $message ?? 'An unknown error occurred' }}</h5>
+                <h3>{{ trans('errors.error_occurred') }}</h3>
+                <h5 class="mb-m">{{ $message ?? 'An unknown error occurred' }}</h5>
                 <p><a href="{{ url('/') }}" class="button outline">{{ trans('errors.return_home') }}</a></p>
             </div>
-        </div>
+        </main>
+
     </div>
 
 @stop
\ No newline at end of file
diff --git a/resources/views/form/date.blade.php b/resources/views/form/date.blade.php
new file mode 100644 (file)
index 0000000..c2e70b9
--- /dev/null
@@ -0,0 +1,9 @@
+<input type="date" id="{{ $name }}" name="{{ $name }}"
+       @if($errors->has($name)) class="text-neg" @endif
+       placeholder="{{ $placeholder ?? 'YYYY-MM-DD' }}"
+       @if($autofocus ?? false) autofocus @endif
+       @if($disabled ?? false) disabled="disabled" @endif
+       @if(isset($model) || old($name)) value="{{ old($name) ?? $model->$name->format('Y-m-d') ?? ''}}" @endif>
+@if($errors->has($name))
+    <div class="text-neg text-small">{{ $errors->first($name) }}</div>
+@endif
index 3581a545b1c96f6b082b11c3a379747251f13ad1..35490bed9a89e74f3eeb071e708e5618fa719807 100644 (file)
@@ -2,15 +2,26 @@
     {!! csrf_field() !!}
     <input type="hidden" name="_method" value="PUT">
 
-    <p class="mb-none">{{ trans('entities.permissions_intro') }}</p>
-
-    <div class="form-group">
-        @include('form.checkbox', [
-            'name' => 'restricted',
-            'label' => trans('entities.permissions_enable'),
-        ])
+    <div class="grid half left-focus v-center">
+        <div>
+            <p class="mb-none mt-m">{{ trans('entities.permissions_intro') }}</p>
+            <div>
+                @include('form.checkbox', [
+                    'name' => 'restricted',
+                    'label' => trans('entities.permissions_enable'),
+                ])
+            </div>
+        </div>
+        <div>
+            <div class="form-group">
+                <label for="owner">{{ trans('entities.permissions_owner') }}</label>
+                @include('components.user-select', ['user' => $model->ownedBy, 'name' => 'owned_by'])
+            </div>
+        </div>
     </div>
 
+    <hr>
+
     <table permissions-table class="table permissions-table toggle-switch-list" style="{{ !$model->restricted ? 'display: none' : '' }}">
         <tr>
             <th>{{ trans('common.role') }}</th>
index 580b332f39c7e1fc993c670693ba08a8ad818192..fc6ad93a8d13284680fd7fc5b267c2ce6453d89d 100644 (file)
@@ -3,10 +3,10 @@
     @foreach($roles as $role)
         <div>
             @include('components.custom-checkbox', [
-                'name' => $name . '[' . str_replace('.', 'DOT', $role->name) . ']',
+                'name' => $name . '[' . strval($role->id) . ']',
                 'label' => $role->display_name,
                 'value' => $role->id,
-                'checked' => old($name . '.' . str_replace('.', 'DOT', $role->name)) || (!old('name') && isset($model) && $model->hasRole($role->name))
+                'checked' => old($name . '.' . strval($role->id)) || (!old('name') && isset($model) && $model->hasRole($role->id))
             ])
         </div>
     @endforeach
index 4b3631a06566158d2286df4f0e662b0b4dfd376a..fabfab451680167937588b463be09735ed4fa933 100644 (file)
@@ -3,6 +3,7 @@
        @if(isset($placeholder)) placeholder="{{$placeholder}}" @endif
        @if($autofocus ?? false) autofocus @endif
        @if($disabled ?? false) disabled="disabled" @endif
+       @if($readonly ?? false) readonly="readonly" @endif
        @if(isset($model) || old($name)) value="{{ old($name) ? old($name) : $model->$name}}" @endif>
 @if($errors->has($name))
     <div class="text-neg text-small">{{ $errors->first($name) }}</div>
diff --git a/resources/views/pages/attachment-manager.blade.php b/resources/views/pages/attachment-manager.blade.php
deleted file mode 100644 (file)
index dd00678..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-<div toolbox-tab-content="files" id="attachment-manager" page-id="{{ $page->id ?? 0 }}">
-
-    @exposeTranslations([
-    'entities.attachments_file_uploaded',
-    'entities.attachments_file_updated',
-    'entities.attachments_link_attached',
-    'entities.attachments_updated_success',
-    'errors.server_upload_limit',
-    'components.image_upload_remove',
-    'components.file_upload_timeout',
-    ])
-
-    <h4>{{ trans('entities.attachments') }}</h4>
-    <div class="px-l files">
-
-        <div id="file-list" v-show="!fileToEdit">
-            <p class="text-muted small">{{ trans('entities.attachments_explain') }} <span class="text-warn">{{ trans('entities.attachments_explain_instant_save') }}</span></p>
-
-            <div class="tab-container">
-                <div class="nav-tabs">
-                    <button type="button" @click="tab = 'list'" :class="{selected: tab === 'list'}"
-                            class="tab-item">{{ trans('entities.attachments_items') }}</button>
-                    <button type="button" @click="tab = 'file'" :class="{selected: tab === 'file'}"
-                            class="tab-item">{{ trans('entities.attachments_upload') }}</button>
-                    <button type="button" @click="tab = 'link'" :class="{selected: tab === 'link'}"
-                            class="tab-item">{{ trans('entities.attachments_link') }}</button>
-                </div>
-                <div v-show="tab === 'list'">
-                    <draggable style="width: 100%;" :options="{handle: '.handle'}" @change="fileSortUpdate" :list="files" element="div">
-                        <div v-for="(file, index) in files" :key="file.id" class="card drag-card">
-                            <div class="handle">@icon('grip')</div>
-                            <div class="py-s">
-                                <a :href="getFileUrl(file)" target="_blank" v-text="file.name"></a>
-                                <div v-if="file.deleting">
-                                    <span class="text-neg small">{{ trans('entities.attachments_delete_confirm') }}</span>
-                                    <br>
-                                    <button type="button" class="text-primary small" @click="file.deleting = false;">{{ trans('common.cancel') }}</button>
-                                </div>
-                            </div>
-                            <button type="button" @click="startEdit(file)" class="drag-card-action text-center text-primary">@icon('edit')</button>
-                            <button type="button" @click="deleteFile(file)" class="drag-card-action text-center text-neg">@icon('close')</button>
-                        </div>
-                    </draggable>
-                    <p class="small text-muted" v-if="files.length === 0">
-                        {{ trans('entities.attachments_no_files') }}
-                    </p>
-                </div>
-                <div v-show="tab === 'file'">
-                    <dropzone placeholder="{{ trans('entities.attachments_dropzone') }}" :upload-url="getUploadUrl()" :uploaded-to="pageId" @success="uploadSuccess"></dropzone>
-                </div>
-                <div v-show="tab === 'link'" @keypress.enter.prevent="attachNewLink(file)">
-                    <p class="text-muted small">{{ trans('entities.attachments_explain_link') }}</p>
-                    <div class="form-group">
-                        <label for="attachment-via-link">{{ trans('entities.attachments_link_name') }}</label>
-                        <input type="text" placeholder="{{ trans('entities.attachments_link_name') }}" v-model="file.name">
-                        <p class="small text-neg" v-for="error in errors.link.name" v-text="error"></p>
-                    </div>
-                    <div class="form-group">
-                        <label for="attachment-via-link">{{ trans('entities.attachments_link_url') }}</label>
-                        <input type="text"  placeholder="{{ trans('entities.attachments_link_url_hint') }}" v-model="file.link">
-                        <p class="small text-neg" v-for="error in errors.link.link" v-text="error"></p>
-                    </div>
-                    <button @click.prevent="attachNewLink(file)" class="button">{{ trans('entities.attach') }}</button>
-
-                </div>
-            </div>
-
-        </div>
-
-        <div id="file-edit" v-if="fileToEdit" @keypress.enter.prevent="updateFile(fileToEdit)">
-            <h5>{{ trans('entities.attachments_edit_file') }}</h5>
-
-            <div class="form-group">
-                <label for="attachment-name-edit">{{ trans('entities.attachments_edit_file_name') }}</label>
-                <input type="text" id="attachment-name-edit" placeholder="{{ trans('entities.attachments_edit_file_name') }}" v-model="fileToEdit.name">
-                <p class="small text-neg" v-for="error in errors.edit.name" v-text="error"></p>
-            </div>
-
-            <div class="tab-container">
-                <div class="nav-tabs">
-                    <button type="button" @click="editTab = 'file'" :class="{selected: editTab === 'file'}" class="tab-item">{{ trans('entities.attachments_upload') }}</button>
-                    <button type="button" @click="editTab = 'link'" :class="{selected: editTab === 'link'}" class="tab-item">{{ trans('entities.attachments_set_link') }}</button>
-                </div>
-                <div v-if="editTab === 'file'">
-                    <dropzone :upload-url="getUploadUrl(fileToEdit)" :uploaded-to="pageId" placeholder="{{ trans('entities.attachments_edit_drop_upload') }}" @success="uploadSuccessUpdate"></dropzone>
-                    <br>
-                </div>
-                <div v-if="editTab === 'link'">
-                    <div class="form-group">
-                        <label for="attachment-link-edit">{{ trans('entities.attachments_link_url') }}</label>
-                        <input type="text" id="attachment-link-edit" placeholder="{{ trans('entities.attachment_link') }}" v-model="fileToEdit.link">
-                        <p class="small text-neg" v-for="error in errors.edit.link" v-text="error"></p>
-                    </div>
-                </div>
-            </div>
-
-            <button type="button" class="button outline" @click="cancelEdit">{{ trans('common.back') }}</button>
-            <button @click.enter.prevent="updateFile(fileToEdit)" class="button">{{ trans('common.save') }}</button>
-        </div>
-
-    </div>
-</div>
\ No newline at end of file
index cfb66fdd0e34ad87827be468764670b230bc2a3b..f580b06cf7cd1caeee8a0bb624e5ba20a8411d10 100644 (file)
@@ -8,7 +8,7 @@
 
 @section('content')
 
-    <div class="flex-fill flex">
+    <div class="flex-fill flex fill-height">
         <form action="{{ $page->getUrl() }}" autocomplete="off" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
             {{ csrf_field() }}
 
@@ -20,8 +20,7 @@
         </form>
     </div>
     
-    @include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
+    @include('components.image-manager', ['uploaded_to' => $page->id])
     @include('components.code-editor')
     @include('components.entity-selector-popup')
-
 @stop
\ No newline at end of file
index 6ea651820ef8f46f437f35a635b20712606847ce..87a9cc2de8bff90ba2036b0a312399cd41acbbb5 100644 (file)
     <div toolbox-tab-content="tags">
         <h4>{{ trans('entities.page_tags') }}</h4>
         <div class="px-l">
-            @include('components.tag-manager', ['entity' => $page, 'entityType' => 'page'])
+            @include('components.tag-manager', ['entity' => $page])
         </div>
     </div>
 
     @if(userCan('attachment-create-all'))
-        @include('pages.attachment-manager', ['page' => $page])
+        @include('attachments.manager', ['page' => $page])
     @endif
 
     <div toolbox-tab-content="templates">
index 4746a56f37842a5f54dfe13cc1c4a1a4d6586b96..47a4d870a041be8e10d4a11704e5b2bd9a4bb1d2 100644 (file)
@@ -4,12 +4,31 @@
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
     <title>{{ $page->name }}</title>
 
-    <style>
-        @if (!app()->environment('testing'))
-        {!! file_get_contents(public_path('/dist/export-styles.css')) !!}
-        @endif
-    </style>
-    @yield('head')
+    @include('partials.export-styles', ['format' => $format])
+
+    @if($format === 'pdf')
+        <style>
+            body {
+                font-size: 14px;
+                line-height: 1.2;
+            }
+
+            h1, h2, h3, h4, h5, h6 {
+                line-height: 1.2;
+            }
+
+            table {
+                max-width: 800px !important;
+                font-size: 0.8em;
+                width: 100% !important;
+            }
+
+            table td {
+                width: auto !important;
+            }
+        </style>
+    @endif
+
     @include('partials.custom-head')
 </head>
 <body>
index ffc286c2cadadc32f3f9834dfd72ffb6be54ee3c..7e8b2fdd64409f18a72378c7e620af5f8a14691a 100644 (file)
@@ -1,20 +1,19 @@
-<div class="page-editor flex-fill flex" id="page-editor"
-     drafts-enabled="{{ $draftsEnabled ? 'true' : 'false' }}"
-     drawio-enabled="{{ config('services.drawio') ? 'true' : 'false' }}"
-     editor-type="{{ setting('app-editor') }}"
-     page-id="{{ $model->id ?? 0 }}"
-     text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
-     page-new-draft="{{ $model->draft ?? 0 }}"
-     page-update-draft="{{ $model->isDraft ?? 0 }}">
-
-    @exposeTranslations([
-        'entities.pages_editing_draft',
-        'entities.pages_editing_page',
-        'errors.page_draft_autosave_fail',
-        'entities.pages_editing_page',
-        'entities.pages_draft_discarded',
-        'entities.pages_edit_set_changelog',
-    ])
+<div component="page-editor" class="page-editor flex-fill flex"
+     option:page-editor:drafts-enabled="{{ $draftsEnabled ? 'true' : 'false' }}"
+     @if(config('services.drawio'))
+        drawio-url="{{ is_string(config('services.drawio')) ? config('services.drawio') : 'https://embed.diagrams.net/?embed=1&proto=json&spin=1' }}"
+     @endif
+     @if($model->name === trans('entities.pages_initial_name'))
+        option:page-editor:has-default-title="true"
+     @endif
+     option:page-editor:editor-type="{{ setting('app-editor') }}"
+     option:page-editor:page-id="{{ $model->id ?? '0' }}"
+     option:page-editor:page-new-draft="{{ ($model->draft ?? false) ? 'true' : 'false' }}"
+     option:page-editor:draft-text="{{ ($model->draft || $model->isDraft) ? trans('entities.pages_editing_draft') : trans('entities.pages_editing_page') }}"
+     option:page-editor:autosave-fail-text="{{ trans('errors.page_draft_autosave_fail') }}"
+     option:page-editor:editing-page-text="{{ trans('entities.pages_editing_page') }}"
+     option:page-editor:draft-discarded-text="{{ trans('entities.pages_draft_discarded') }}"
+     option:page-editor:set-changelog-text="{{ trans('entities.pages_edit_set_changelog') }}">
 
     {{--Header Bar--}}
     <div class="primary-background-light toolbar page-edit-toolbar">
             </div>
 
             <div class="text-center px-m py-xs">
-                <div v-show="draftsEnabled" dropdown dropdown-move-menu class="dropdown-container draft-display text">
-                    <button type="button" dropdown-toggle aria-haspopup="true" aria-expanded="false" title="{{ trans('entities.pages_edit_draft_options') }}" class="text-primary text-button"><span class="faded-text" v-text="draftText"></span>&nbsp; @icon('more')</button>
-                    @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', ':class' => '{visible: draftUpdated}'])
-                    <ul class="dropdown-menu" role="menu">
+                <div component="dropdown"
+                     option:dropdown:move-menu="true"
+                     class="dropdown-container draft-display text {{ $draftsEnabled ? '' : 'hidden' }}">
+                    <button type="button" refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" title="{{ trans('entities.pages_edit_draft_options') }}" class="text-primary text-button"><span refs="page-editor@draftDisplay" class="faded-text"></span>&nbsp; @icon('more')</button>
+                    @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', 'refs' => 'page-editor@draftDisplayIcon'])
+                    <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                         <li>
-                            <button type="button" @click="saveDraft()" class="text-pos">@icon('save'){{ trans('entities.pages_edit_save_draft') }}</button>
+                            <button refs="page-editor@saveDraft" type="button" class="text-pos">@icon('save'){{ trans('entities.pages_edit_save_draft') }}</button>
                         </li>
-                        <li v-if="isNewDraft">
+                        @if ($model->draft)
+                        <li>
                             <a href="{{ $model->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('entities.pages_edit_delete_draft') }}</a>
                         </li>
-                        <li v-if="isUpdateDraft">
-                            <button type="button" @click="discardDraft" class="text-neg">@icon('cancel'){{ trans('entities.pages_edit_discard_draft') }}</button>
+                        @endif
+                        <li refs="page-editor@discardDraftWrap" class="{{ ($model->isDraft ?? false) ? '' : 'hidden' }}">
+                            <button refs="page-editor@discardDraft" type="button" class="text-neg">@icon('cancel'){{ trans('entities.pages_edit_discard_draft') }}</button>
                         </li>
                     </ul>
                 </div>
             </div>
 
-            <div class="action-buttons px-m py-xs" v-cloak>
-                <div dropdown dropdown-move-menu class="dropdown-container">
-                    <button type="button" dropdown-toggle aria-haspopup="true" aria-expanded="false" class="text-primary text-button">@icon('edit') <span v-text="changeSummaryShort"></span></button>
-                    <ul class="wide dropdown-menu">
+            <div class="action-buttons px-m py-xs">
+                <div component="dropdown" dropdown-move-menu class="dropdown-container">
+                    <button refs="dropdown@toggle" type="button" aria-haspopup="true" aria-expanded="false" class="text-primary text-button">@icon('edit') <span refs="page-editor@changelogDisplay">{{ trans('entities.pages_edit_set_changelog') }}</span></button>
+                    <ul refs="dropdown@menu" class="wide dropdown-menu">
                         <li class="px-l py-m">
                             <p class="text-muted pb-s">{{ trans('entities.pages_edit_enter_changelog_desc') }}</p>
-                            <input name="summary" id="summary-input" type="text" placeholder="{{ trans('entities.pages_edit_enter_changelog') }}" v-model="changeSummary" />
+                            <input refs="page-editor@changelogInput"
+                                   name="summary"
+                                   id="summary-input"
+                                   type="text"
+                                   placeholder="{{ trans('entities.pages_edit_enter_changelog') }}" />
                         </li>
                     </ul>
                     <span>{{-- Prevents button jumping on menu show --}}</span>
@@ -60,9 +67,9 @@
     </div>
 
     {{--Title input--}}
-    <div class="title-input page-title clearfix" v-pre>
-        <div class="input">
-            @include('form.text', ['name' => 'name', 'placeholder' => trans('entities.pages_title')])
+    <div class="title-input page-title clearfix">
+        <div refs="page-editor@titleContainer" class="input">
+            @include('form.text', ['name' => 'name', 'model' => $model, 'placeholder' => trans('entities.pages_title')])
         </div>
     </div>
 
@@ -81,5 +88,8 @@
 
     </div>
 
-    <button type="submit" id="save-button-mobile" title="{{ trans('entities.pages_save') }}" class="text-primary text-button hide-over-m page-save-mobile-button">@icon('save')</button>
+    <button type="submit"
+            id="save-button-mobile"
+            title="{{ trans('entities.pages_save') }}"
+            class="text-primary text-button hide-over-m page-save-mobile-button">@icon('save')</button>
 </div>
\ No newline at end of file
index 52644113802d419ed0e8bdafaede843dc0110fe4..a9d1f1174bbbabf9f3266f09a6e59a4694beed33 100644 (file)
@@ -1,7 +1,8 @@
-<div v-pre id="markdown-editor" markdown-editor class="flex-fill flex code-fill">
-    @exposeTranslations([
-        'errors.image_upload_error',
-    ])
+<div id="markdown-editor" component="markdown-editor"
+     option:markdown-editor:page-id="{{ $model->id ?? 0 }}"
+     option:markdown-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+     option:markdown-editor:image-upload-error-text="{{ trans('errors.image_upload_error') }}"
+     class="flex-fill flex code-fill">
 
     <div class="markdown-editor-wrap active">
         <div class="editor-toolbar">
             <div class="float right buttons">
                 @if(config('services.drawio'))
                     <button class="text-button" type="button" data-action="insertDrawing">@icon('drawing'){{ trans('entities.pages_md_insert_drawing') }}</button>
-                    &nbsp;|&nbsp
+                    <span class="mx-xs text-muted">|</span>
                 @endif
                 <button class="text-button" type="button" data-action="insertImage">@icon('image'){{ trans('entities.pages_md_insert_image') }}</button>
-                &nbsp;|&nbsp;
+                <span class="mx-xs text-muted">|</span>
                 <button class="text-button" type="button" data-action="insertLink">@icon('link'){{ trans('entities.pages_md_insert_link') }}</button>
+                <span class="mx-xs text-muted">|</span>
+                <button class="text-button" type="button" data-action="fullscreen">@icon('fullscreen'){{ trans('common.fullscreen') }}</button>
             </div>
         </div>
 
         <div markdown-input class="flex flex-fill">
-                        <textarea  id="markdown-editor-input"  name="markdown" rows="5"
-                                   @if($errors->has('markdown')) class="text-neg" @endif>@if(isset($model) || old('markdown')){{ old('markdown') ? old('markdown') : ($model->markdown === '' ? $model->html : $model->markdown) }}@endif</textarea>
+            <textarea id="markdown-editor-input"
+                      @if($errors->has('markdown')) class="text-neg" @endif
+                      name="markdown"
+                      rows="5">@if(isset($model) || old('markdown')){{ old('markdown') ?? ($model->markdown === '' ? $model->html : $model->markdown) }}@endif</textarea>
         </div>
 
     </div>
@@ -28,7 +33,7 @@
         <div class="editor-toolbar">
             <div class="editor-toolbar-label">{{ trans('entities.pages_md_preview') }}</div>
         </div>
-        <iframe srcdoc="" class="markdown-display" sandbox="allow-same-origin"></iframe>
+        <iframe src="about:blank" class="markdown-display" sandbox="allow-same-origin"></iframe>
     </div>
     <input type="hidden" name="html"/>
 
index e13632c1ec86a41b3c1818be59add6b46682679f..ba2a2c336fd60cd0fce5b8761d176285f207098d 100644 (file)
@@ -1,6 +1,6 @@
 <div dir="auto">
 
-    <h1 class="break-text" v-pre id="bkmrk-page-title">{{$page->name}}</h1>
+    <h1 class="break-text" id="bkmrk-page-title">{{$page->name}}</h1>
 
     <div style="clear:left;"></div>
 
diff --git a/resources/views/pages/pdf.blade.php b/resources/views/pages/pdf.blade.php
deleted file mode 100644 (file)
index 33a009f..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-@extends('pages/export')
-
-@section('head')
-    <style>
-        body {
-            font-size: 14px;
-            line-height: 1.2;
-        }
-
-        h1, h2, h3, h4, h5, h6 {
-            line-height: 1.2;
-        }
-
-        table {
-            max-width: 800px !important;
-            font-size: 0.8em;
-            width: 100% !important;
-        }
-
-        table td {
-            width: auto !important;
-        }
-
-        .page-content .float {
-            float: none !important;
-        }
-
-        .page-content img.align-left, .page-content img.align-right  {
-            float: none !important;
-            clear: both;
-            display: block;
-        }
-    </style>
-@stop
\ No newline at end of file
index feb3180775adaff018fe9dd739e21bc64354998c..6ff33c68de4b7e2baddc4db1301f111a0161c865 100644 (file)
@@ -50,9 +50,9 @@
                                 @else
                                     <a href="{{ $revision->getUrl() }}" target="_blank">{{ trans('entities.pages_revisions_preview') }}</a>
                                     <span class="text-muted">&nbsp;|&nbsp;</span>
-                                    <div dropdown class="dropdown-container">
-                                        <a dropdown-toggle href="#" aria-haspopup="true" aria-expanded="false">{{ trans('entities.pages_revisions_restore') }}</a>
-                                        <ul class="dropdown-menu" role="menu">
+                                    <div component="dropdown" class="dropdown-container">
+                                        <a refs="dropdown@toggle" href="#" aria-haspopup="true" aria-expanded="false">{{ trans('entities.pages_revisions_restore') }}</a>
+                                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                                             <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_restore_confirm')}}</small></li>
                                             <li>
                                                 <form action="{{ $revision->getUrl('/restore') }}" method="POST">
@@ -64,9 +64,9 @@
                                         </ul>
                                     </div>
                                     <span class="text-muted">&nbsp;|&nbsp;</span>
-                                    <div dropdown class="dropdown-container">
-                                        <a dropdown-toggle href="#" aria-haspopup="true" aria-expanded="false">{{ trans('common.delete') }}</a>
-                                        <ul class="dropdown-menu" role="menu">
+                                    <div component="dropdown" class="dropdown-container">
+                                        <a refs="dropdown@toggle" href="#" aria-haspopup="true" aria-expanded="false">{{ trans('common.delete') }}</a>
+                                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                                             <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_delete_confirm')}}</small></li>
                                             <li>
                                                 <form action="{{ $revision->getUrl('/delete/') }}" method="POST">
index 51ab5bbbe53531e315e6ae314694123adcfc7281..13125464a7114396012f3685fa56e38ae23b81b9 100644 (file)
@@ -11,7 +11,7 @@
     </div>
 
     <main class="content-wrap card">
-        <div class="page-content" page-display="{{ $page->id }}">
+        <div class="page-content clearfix" page-display="{{ $page->id }}">
             @include('pages.pointer', ['page' => $page])
             @include('pages.page-display')
         </div>
         <div id="page-attachments" class="mb-l">
             <h5>{{ trans('entities.pages_attachments') }}</h5>
             <div class="body">
-                @foreach($page->attachments as $attachment)
-                    <div class="attachment icon-list">
-                        <a class="icon-list-item py-xs" href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif>
-                            <span class="icon">@icon($attachment->external ? 'export' : 'file')</span>
-                            <span>{{ $attachment->name }}</span>
-                        </a>
-                    </div>
-                @endforeach
+                @include('attachments.list', ['attachments' => $page->attachments])
             </div>
         </div>
     @endif
@@ -56,7 +49,7 @@
                 <div class="sidebar-page-nav menu">
                     @foreach($pageNav as $navItem)
                         <li class="page-nav-item h{{ $navItem['level'] }}">
-                            <a href="{{ $navItem['link'] }}" class="limit-text block">{{ $navItem['text'] }}</a>
+                            <a href="{{ $navItem['link'] }}" class="text-limit-lines-1 block">{{ $navItem['text'] }}</a>
                             <div class="primary-background sidebar-page-nav-bullet"></div>
                         </li>
                     @endforeach
     </div>
 
     <div class="actions mb-xl">
-        <h5>Actions</h5>
+        <h5>{{ trans('common.actions') }}</h5>
 
         <div class="icon-list text-primary">
 
index 1a67ee76f2d0992f7eb552878359f6d151db3b51..d8b8b1c353c73f53c10b639635345fe78c86ee14 100644 (file)
@@ -1,10 +1,10 @@
-<div wysiwyg-editor class="flex-fill flex">
+<div component="wysiwyg-editor"
+     option:wysiwyg-editor:page-id="{{ $model->id ?? 0 }}"
+     option:wysiwyg-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+     option:wysiwyg-editor:image-upload-error-text="{{ trans('errors.image_upload_error') }}"
+     class="flex-fill flex">
 
-    @exposeTranslations([
-        'errors.image_upload_error',
-    ])
-
-    <textarea id="html-editor"  name="html" rows="5" v-pre
+    <textarea id="html-editor"  name="html" rows="5"
           @if($errors->has('html')) class="text-neg" @endif>@if(isset($model) || old('html')){{ old('html') ? old('html') : $model->html }}@endif</textarea>
 </div>
 
index 39fb35fe2728c4c50c2ee5392414f9dfdee0a5c8..eebfb591a4af40713816963b5c978d5a6b3ba171 100644 (file)
@@ -7,7 +7,7 @@
     @endif
 </div>
 
-<div v-pre>
+<div>
     @if($activity->user)
         <a href="{{ $activity->user->getProfileUrl() }}">{{ $activity->user->name }}</a>
     @else
 
     {{ $activity->getText() }}
 
-    @if($activity->entity)
+    @if($activity->entity && is_null($activity->entity->deleted_at))
         <a href="{{ $activity->entity->getUrl() }}">{{ $activity->entity->name }}</a>
     @endif
 
+    @if($activity->entity && !is_null($activity->entity->deleted_at))
+        "{{ $activity->entity->name }}"
+    @endif
+
     @if($activity->extra) "{{ $activity->extra }}" @endif
 
     <br>
index c288e63674ff38b456a4b13ed530746510f11e29..15b5832897d01756c7fc59b22a89805bdf7a8bbe 100644 (file)
@@ -1,4 +1,7 @@
-<nav id="book-tree" class="book-tree mb-xl" v-pre aria-label="{{ trans('entities.books_navigation') }}">
+<nav id="book-tree"
+     class="book-tree mb-xl"
+     aria-label="{{ trans('entities.books_navigation') }}">
+
     <h5>{{ trans('entities.books_navigation') }}</h5>
 
     <ul class="sidebar-page-list mt-xs menu entity-list">
         @endif
 
         @foreach($sidebarTree as $bookChild)
-            <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
+            <li class="list-item-{{ $bookChild->getType() }} {{ $bookChild->getType() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
                 @include('partials.entity-list-item-basic', ['entity' => $bookChild, 'classes' => $current->matches($bookChild)? 'selected' : ''])
 
-                @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
+                @if($bookChild->isA('chapter') && count($bookChild->visible_pages) > 0)
                     <div class="entity-list-item no-hover">
                         <span role="presentation" class="icon text-chapter"></span>
                         <div class="content">
index e53cb4c53b9cf59200427f67d6134d90ce1c5bbd..2a559aa7d5f7f28b39b253698da83c4ab3664051 100644 (file)
@@ -1,14 +1,23 @@
-<div class="breadcrumb-listing" dropdown breadcrumb-listing="{{ $entity->getType() }}:{{ $entity->id }}">
-    <div class="breadcrumb-listing-toggle" dropdown-toggle
+<div class="dropdown-search" components="dropdown dropdown-search"
+     option:dropdown-search:url="/search/entity/siblings?entity_type={{$entity->getType()}}&entity_id={{ $entity->id }}"
+     option:dropdown-search:local-search-selector=".entity-list-item"
+>
+    <div class="dropdown-search-toggle" refs="dropdown@toggle"
          aria-haspopup="true" aria-expanded="false" tabindex="0">
         <div class="separator">@icon('chevron-right')</div>
     </div>
-    <div dropdown-menu class="breadcrumb-listing-dropdown card" role="menu">
-        <div class="breadcrumb-listing-search">
+    <div refs="dropdown@menu" class="dropdown-search-dropdown card" role="menu">
+        <div class="dropdown-search-search">
             @icon('search')
-            <input autocomplete="off" type="text" name="entity-search" placeholder="{{ trans('common.search') }}" aria-label="{{ trans('common.search') }}">
+            <input refs="dropdown-search@searchInput"
+                   aria-label="{{ trans('common.search') }}"
+                   autocomplete="off"
+                   placeholder="{{ trans('common.search') }}"
+                   type="text">
         </div>
-        @include('partials.loading-icon')
-        <div class="breadcrumb-listing-entity-list px-m"></div>
+        <div refs="dropdown-search@loading">
+            @include('partials.loading-icon')
+        </div>
+        <div refs="dropdown-search@listContainer" class="dropdown-search-list px-m"></div>
     </div>
 </div>
\ No newline at end of file
index 58ccd51257e0338686e746762781d36b318b5d70..065aa842026e91ca481dae18a5a606a5bcfe341d 100644 (file)
@@ -2,7 +2,7 @@
     <?php $breadcrumbCount = 0; ?>
 
     {{-- Show top level books item --}}
-    @if (count($crumbs) > 0 && ($crumbs[0] ?? null) instanceof  \BookStack\Entities\Book)
+    @if (count($crumbs) > 0 && ($crumbs[0] ?? null) instanceof  \BookStack\Entities\Models\Book)
         <a href="{{  url('/books')  }}" class="text-book icon-list-item outline-hover">
             <span>@icon('books')</span>
             <span>{{ trans('entities.books') }}</span>
@@ -11,7 +11,7 @@
     @endif
 
     {{-- Show top level shelves item --}}
-    @if (count($crumbs) > 0 && ($crumbs[0] ?? null) instanceof  \BookStack\Entities\Bookshelf)
+    @if (count($crumbs) > 0 && ($crumbs[0] ?? null) instanceof  \BookStack\Entities\Models\Bookshelf)
         <a href="{{  url('/shelves')  }}" class="text-bookshelf icon-list-item outline-hover">
             <span>@icon('bookshelf')</span>
             <span>{{ trans('entities.shelves') }}</span>
@@ -20,7 +20,7 @@
     @endif
 
     @foreach($crumbs as $key => $crumb)
-        <?php $isEntity = ($crumb instanceof \BookStack\Entities\Entity); ?>
+        <?php $isEntity = ($crumb instanceof \BookStack\Entities\Models\Entity); ?>
 
         @if (is_null($crumb))
             <?php continue; ?>
index 9080790825f74c4014f53368458f7c3fe3705570..f819391098022152e96be375893cb21b259832e3 100644 (file)
@@ -2,5 +2,10 @@
     :root {
         --color-primary: {{ setting('app-color') }};
         --color-primary-light: {{ setting('app-color-light') }};
+        --color-bookshelf: {{ setting('bookshelf-color')}};
+        --color-book: {{ setting('book-color')}};
+        --color-chapter: {{ setting('chapter-color')}};
+        --color-page: {{ setting('page-color')}};
+        --color-page-draft: {{ setting('page-draft-color')}};
     }
-</style>
\ No newline at end of file
+</style>
diff --git a/resources/views/partials/dark-mode-toggle.blade.php b/resources/views/partials/dark-mode-toggle.blade.php
new file mode 100644 (file)
index 0000000..0812e48
--- /dev/null
@@ -0,0 +1,9 @@
+<form action="{{ url('/settings/users/toggle-dark-mode') }}" method="post">
+    {{ csrf_field() }}
+    {{ method_field('patch') }}
+    @if(setting()->getForCurrentUser('dark-mode-enabled'))
+        <button class="{{ $classes ?? '' }}"><span>@icon('light-mode')</span><span>{{ trans('common.light_mode') }}</span></button>
+    @else
+        <button class="{{ $classes ?? '' }}"><span>@icon('dark-mode')</span><span>{{ trans('common.dark_mode') }}</span></button>
+    @endif
+</form>
\ No newline at end of file
diff --git a/resources/views/partials/entity-dashboard-search-box.blade.php b/resources/views/partials/entity-dashboard-search-box.blade.php
deleted file mode 100644 (file)
index 2e03952..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<div class="mb-xl">
-    <form v-on:submit.prevent="searchBook" class="search-box flexible" role="search">
-        <input v-model="searchTerm" v-on:change="checkSearchForm" type="text" aria-label="{{ trans('entities.books_search_this') }}" name="term" placeholder="{{ trans('entities.books_search_this') }}">
-        <button type="submit" aria-label="{{ trans('common.search') }}">@icon('search')</button>
-        <button v-if="searching" v-cloak class="search-box-cancel text-neg" v-on:click="clearSearch"
-                type="button" aria-label="{{ trans('common.search_clear') }}">@icon('close')</button>
-    </form>
-</div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-dashboard-search-results.blade.php b/resources/views/partials/entity-dashboard-search-results.blade.php
deleted file mode 100644 (file)
index 68c6f53..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<div class="search-results" v-cloak v-show="searching">
-    <div class="grid half v-center">
-        <h3 class="text-muted px-none">
-            {{ trans('entities.search_results') }}
-        </h3>
-        <div class="text-right">
-            <a v-if="searching" v-on:click="clearSearch" class="button outline">{{ trans('entities.search_clear') }}</a>
-        </div>
-    </div>
-
-    <div v-if="!searchResults">
-        @include('partials.loading-icon')
-    </div>
-    <div class="book-contents" v-html="searchResults"></div>
-</div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-display-item.blade.php b/resources/views/partials/entity-display-item.blade.php
new file mode 100644 (file)
index 0000000..d6633ed
--- /dev/null
@@ -0,0 +1,7 @@
+<?php $type = $entity->getType(); ?>
+<div class="{{$type}} {{$type === 'page' && $entity->draft ? 'draft' : ''}} {{$classes ?? ''}} entity-list-item no-hover">
+    <span role="presentation" class="icon text-{{$type}} {{$type === 'page' && $entity->draft ? 'draft' : ''}}">@icon($type)</span>
+    <div class="content">
+        <div class="entity-list-item-name break-text">{{ $entity->name }}</div>
+    </div>
+</div>
\ No newline at end of file
index 630d640bf3214fe8b44fa02801bf94b11661939c..4d847bcaef801c4700a74a84de8fc7a025fc8fe6 100644 (file)
@@ -1,10 +1,10 @@
-<div dropdown class="dropdown-container" id="export-menu">
-    <div dropdown-toggle class="icon-list-item"
+<div component="dropdown" class="dropdown-container" id="export-menu">
+    <div refs="dropdown@toggle" class="icon-list-item"
          aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('entities.export') }}" tabindex="0">
         <span>@icon('export')</span>
         <span>{{ trans('entities.export') }}</span>
     </div>
-    <ul class="wide dropdown-menu" role="menu">
+    <ul refs="dropdown@menu" class="wide dropdown-menu" role="menu">
         <li><a href="{{ $entity->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
         <li><a href="{{ $entity->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
         <li><a href="{{ $entity->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
diff --git a/resources/views/partials/entity-grid-item.blade.php b/resources/views/partials/entity-grid-item.blade.php
new file mode 100644 (file)
index 0000000..ee31b53
--- /dev/null
@@ -0,0 +1,16 @@
+<a href="{{ $entity->getUrl() }}" class="grid-card"
+   data-entity-type="{{ $entity->getType() }}" data-entity-id="{{ $entity->id }}">
+    <div class="bg-{{ $entity->getType() }} featured-image-container-wrap">
+        <div class="featured-image-container" @if($entity->cover) style="background-image: url('{{ $entity->getBookCover() }}')"@endif>
+        </div>
+        @icon($entity->getType())
+    </div>
+    <div class="grid-card-content">
+        <h2 class="text-limit-lines-2">{{ $entity->name }}</h2>
+        <p class="text-muted">{{ $entity->getExcerpt(130) }}</p>
+    </div>
+    <div class="grid-card-footer text-muted ">
+        <p>@icon('star')<span title="{{ $entity->created_at->toDayDateTimeString() }}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span></p>
+        <p>@icon('edit')<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span></p>
+    </div>
+</a>
\ No newline at end of file
index f759ea25b174628de3a3af0d411f1f6a076b5dda..8996df9bb67d0f491d9a8de5e5d18b56560883fb 100644 (file)
@@ -1,34 +1,50 @@
 <div class="entity-meta">
     @if($entity->isA('revision'))
-        @icon('history'){{ trans('entities.pages_revision') }}
-        {{ trans('entities.pages_revisions_number') }}{{ $entity->revision_number == 0 ? '' : $entity->revision_number }}
-        <br>
+        <div>
+            @icon('history'){{ trans('entities.pages_revision') }}
+            {{ trans('entities.pages_revisions_number') }}{{ $entity->revision_number == 0 ? '' : $entity->revision_number }}
+        </div>
     @endif
 
     @if ($entity->isA('page'))
-        @if (userCan('page-update', $entity)) <a href="{{ $entity->getUrl('/revisions') }}"> @endif
-            @icon('history'){{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }} <br>
+        <div>
+            @if (userCan('page-update', $entity)) <a href="{{ $entity->getUrl('/revisions') }}"> @endif
+            @icon('history'){{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }}
             @if (userCan('page-update', $entity))</a>@endif
+        </div>
     @endif
 
+    @if ($entity->ownedBy && $entity->ownedBy->id !== $entity->createdBy->id)
+        <div>
+            @icon('user'){!! trans('entities.meta_owned_name', [
+            'user' => "<a href='{$entity->ownedBy->getProfileUrl()}'>".e($entity->ownedBy->name). "</a>"
+        ]) !!}
+        </div>
+    @endif
 
     @if ($entity->createdBy)
-        @icon('star'){!! trans('entities.meta_created_name', [
+        <div>
+            @icon('star'){!! trans('entities.meta_created_name', [
             'timeLength' => '<span title="'.$entity->created_at->toDayDateTimeString().'">'.$entity->created_at->diffForHumans() . '</span>',
-            'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".htmlentities($entity->createdBy->name). "</a>"
+            'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".e($entity->createdBy->name). "</a>"
             ]) !!}
+        </div>
     @else
-        @icon('star')<span title="{{$entity->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span>
+        <div>
+            @icon('star')<span title="{{$entity->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span>
+        </div>
     @endif
 
-    <br>
-
     @if ($entity->updatedBy)
-        @icon('edit'){!! trans('entities.meta_updated_name', [
+        <div>
+            @icon('edit'){!! trans('entities.meta_updated_name', [
                 'timeLength' => '<span title="' . $entity->updated_at->toDayDateTimeString() .'">' . $entity->updated_at->diffForHumans() .'</span>',
-                'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"
+                'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".e($entity->updatedBy->name). "</a>"
             ]) !!}
+        </div>
     @elseif (!$entity->isA('revision'))
-        @icon('edit')<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
+        <div>
+            @icon('edit')<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
+        </div>
     @endif
 </div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-search-form.blade.php b/resources/views/partials/entity-search-form.blade.php
new file mode 100644 (file)
index 0000000..b65d5df
--- /dev/null
@@ -0,0 +1,10 @@
+{{--
+@label - Placeholder/aria-label text
+--}}
+<div class="mb-xl">
+    <form refs="entity-search@searchForm" class="search-box flexible" role="search">
+        <input refs="entity-search@searchInput" type="text"
+               aria-label="{{ $label }}" name="term" placeholder="{{ $label }}">
+        <button type="submit" aria-label="{{ trans('common.search') }}">@icon('search')</button>
+    </form>
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-search-results.blade.php b/resources/views/partials/entity-search-results.blade.php
new file mode 100644 (file)
index 0000000..7461983
--- /dev/null
@@ -0,0 +1,15 @@
+<div refs="entity-search@searchView" class="search-results hidden">
+    <div class="grid half v-center">
+        <h3 class="text-muted px-none">
+            {{ trans('entities.search_results') }}
+        </h3>
+        <div class="text-right">
+            <a refs="entity-search@clearButton" class="button outline">{{ trans('entities.search_clear') }}</a>
+        </div>
+    </div>
+
+    <div refs="entity-search@loadingBlock">
+        @include('partials.loading-icon')
+    </div>
+    <div class="book-contents" refs="entity-search@searchResults"></div>
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/export-styles.blade.php b/resources/views/partials/export-styles.blade.php
new file mode 100644 (file)
index 0000000..52bfda2
--- /dev/null
@@ -0,0 +1,29 @@
+<style>
+    @if (!app()->environment('testing'))
+        {!! file_get_contents(public_path('/dist/export-styles.css')) !!}
+    @endif
+</style>
+
+@if ($format === 'pdf')
+    <style>
+        /* Patches for CSS variable colors */
+        a {
+            color: {{ setting('app-color') }};
+        }
+
+        blockquote {
+            border-left-color: {{ setting('app-color') }};
+        }
+
+        /* Patches for content layout */
+        .page-content .float {
+            float: none !important;
+        }
+
+        .page-content img.align-left, .page-content img.align-right  {
+            float: none !important;
+            clear: both;
+            display: block;
+        }
+    </style>
+@endif
\ No newline at end of file
index 52687149928b299777276912b6e1870c4d9c90e7..752920917570a9a11d974f7088bf7e572a5e6a0f 100644 (file)
@@ -1,11 +1,11 @@
 <div notification="success" style="display: none;" data-autohide class="pos" role="alert" @if(session()->has('success')) data-show @endif>
-    @icon('check-circle') <span>{!! nl2br(htmlentities(session()->get('success'))) !!}</span>
+    @icon('check-circle') <span>{!! nl2br(htmlentities(session()->get('success'))) !!}</span><div class="dismiss">@icon('close')</div>
 </div>
 
 <div notification="warning" style="display: none;" class="warning" role="alert" @if(session()->has('warning')) data-show @endif>
-    @icon('info') <span>{!! nl2br(htmlentities(session()->get('warning'))) !!}</span>
+    @icon('info') <span>{!! nl2br(htmlentities(session()->get('warning'))) !!}</span><div class="dismiss">@icon('close')</div>
 </div>
 
 <div notification="error" style="display: none;" class="neg" role="alert" @if(session()->has('error')) data-show @endif>
-    @icon('danger') <span>{!! nl2br(htmlentities(session()->get('error'))) !!}</span>
+    @icon('danger') <span>{!! nl2br(htmlentities(session()->get('error'))) !!}</span><div class="dismiss">@icon('close')</div>
 </div>
index 09c61d01383fa01f2d5e6f0c1af54b586dff75c7..bf90873975c7b89e483a82210ea67c263b20a79c 100644 (file)
@@ -4,7 +4,7 @@
 ?>
 <div class="list-sort-container" list-sort-control>
     <div class="list-sort-label">{{ trans('common.sort') }}</div>
-    <form action="{{ url("/settings/users/{$currentUser->id}/change-sort/{$type}") }}" method="post">
+    <form action="{{ url("/settings/users/". user()->id ."/change-sort/{$type}") }}" method="post">
 
         {!! csrf_field() !!}
         {!! method_field('PATCH') !!}
@@ -12,9 +12,9 @@
         <input type="hidden" value="{{ $order }}" name="order">
 
         <div class="list-sort">
-            <div class="list-sort-type dropdown-container" dropdown>
-                <div dropdown-toggle aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" tabindex="0">{{ $options[$selectedSort] }}</div>
-                <ul class="dropdown-menu">
+            <div component="dropdown" class="list-sort-type dropdown-container">
+                <div refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" tabindex="0">{{ $options[$selectedSort] }}</div>
+                <ul refs="dropdown@menu" class="dropdown-menu">
                     @foreach($options as $key => $label)
                         <li @if($key === $selectedSort) class="active" @endif><a href="#" data-sort-value="{{$key}}">{{ $label }}</a></li>
                     @endforeach
diff --git a/resources/views/partials/table-user.blade.php b/resources/views/partials/table-user.blade.php
new file mode 100644 (file)
index 0000000..a8f2777
--- /dev/null
@@ -0,0 +1,12 @@
+{{--
+$user - User mode to display, Can be null.
+$user_id - Id of user to show. Must be provided.
+--}}
+@if($user)
+    <a href="{{ $user->getEditUrl() }}" class="table-user-item">
+        <div><img class="avatar block" src="{{ $user->getAvatar(40)}}" alt="{{ $user->name }}"></div>
+        <div>{{ $user->name }}</div>
+    </a>
+@else
+    [ID: {{ $user_id }}] {{ trans('common.deleted_user') }}
+@endif
\ No newline at end of file
index 9f911c88231d1775366263e0e29766705690df94..9ff1b49277d035c17f2df0169f90eab1ddbd2bfc 100644 (file)
@@ -1,5 +1,5 @@
 <div>
-    <form action="{{ url("/settings/users/{$currentUser->id}/switch-${type}-view") }}" method="POST" class="inline">
+    <form action="{{ url("/settings/users/". user()->id ."/switch-${type}-view") }}" method="POST" class="inline">
         {!! csrf_field() !!}
         {!! method_field('PATCH') !!}
         <input type="hidden" value="{{ $view === 'list'? 'grid' : 'list' }}" name="view_type">
index f19e560a2d5f633b01bf063c074d5396a5819419..df137bd2a3bcbef73662ff1355b921df8d76f14c 100644 (file)
 @extends('simple-layout')
 
 @section('body')
-    <input type="hidden" name="searchTerm" value="{{$searchTerm}}">
-
-    <div class="container" id="search-system">
-
-        <div class="my-s">
-            &nbsp;
-        </div>
+    <div class="container mt-xl" id="search-system">
 
         <div class="grid right-focus reverse-collapse gap-xl">
             <div>
                 <div>
-                    <h5>{{ trans('entities.search_filters') }}</h5>
+                    <h5>{{ trans('entities.search_advanced') }}</h5>
 
-                    <form v-on:submit="updateSearch" v-cloak class="v-cloak anim fadeIn">
-                        <h6 class="text-muted">{{ trans('entities.search_content_type') }}</h6>
+                    <form method="get" action="{{ url('/search') }}">
+                        <h6>{{ trans('entities.search_terms') }}</h6>
+                        <input type="text" name="search" value="{{ implode(' ', $options->searches) }}">
+
+                        <h6>{{ trans('entities.search_content_type') }}</h6>
                         <div class="form-group">
-                            <label class="inline checkbox text-page"><input type="checkbox" v-on:change="typeChange" v-model="search.type.page" value="page">{{ trans('entities.page') }}</label>
-                            <label class="inline checkbox text-chapter"><input type="checkbox" v-on:change="typeChange" v-model="search.type.chapter" value="chapter">{{ trans('entities.chapter') }}</label>
+
+                            <?php
+                            $types = explode('|', $options->filters['type'] ?? '');
+                            $hasTypes = $types[0] !== '';
+                            ?>
+                            @include('search.form.type-filter', ['checked' => !$hasTypes || in_array('page', $types), 'entity' => 'page', 'transKey' => 'page'])
+                            @include('search.form.type-filter', ['checked' => !$hasTypes || in_array('chapter', $types), 'entity' => 'chapter', 'transKey' => 'chapter'])
                             <br>
-                            <label class="inline checkbox text-book"><input type="checkbox" v-on:change="typeChange" v-model="search.type.book" value="book">{{ trans('entities.book') }}</label>
-                            <label class="inline checkbox text-bookshelf"><input type="checkbox" v-on:change="typeChange" v-model="search.type.bookshelf" value="bookshelf">{{ trans('entities.shelf') }}</label>
+                                @include('search.form.type-filter', ['checked' => !$hasTypes || in_array('book', $types), 'entity' => 'book', 'transKey' => 'book'])
+                                @include('search.form.type-filter', ['checked' => !$hasTypes || in_array('bookshelf', $types), 'entity' => 'bookshelf', 'transKey' => 'shelf'])
                         </div>
 
-                        <h6 class="text-muted">{{ trans('entities.search_exact_matches') }}</h6>
-                        <table cellpadding="0" cellspacing="0" border="0" class="no-style">
-                            <tr v-for="(term, i) in search.exactTerms">
-                                <td style="padding: 0 12px 6px 0;">
-                                    <input class="exact-input outline" v-on:input="exactChange" type="text" v-model="search.exactTerms[i]"></td>
-                                <td>
-                                    <button type="button" class="text-neg text-button" v-on:click="removeExact(i)">
-                                        @icon('close')
-                                    </button>
-                                </td>
-                            </tr>
-                            <tr>
-                                <td colspan="2">
-                                    <button type="button" class="text-button" v-on:click="addExact">
-                                        @icon('add-circle'){{ trans('common.add') }}
-                                    </button>
-                                </td>
-                            </tr>
-                        </table>
-
-                        <h6 class="text-muted">{{ trans('entities.search_tags') }}</h6>
-                        <table cellpadding="0" cellspacing="0" border="0" class="no-style">
-                            <tr v-for="(term, i) in search.tagTerms">
-                                <td style="padding: 0 12px 6px 0;">
-                                    <input class="tag-input outline" v-on:input="tagChange" type="text" v-model="search.tagTerms[i]"></td>
-                                <td>
-                                    <button type="button" class="text-neg text-button" v-on:click="removeTag(i)">
-                                        @icon('close')
-                                    </button>
-                                </td>
-                            </tr>
-                            <tr>
-                                <td colspan="2">
-                                    <button type="button" class="text-button" v-on:click="addTag">
-                                        @icon('add-circle'){{ trans('common.add') }}
-                                    </button>
-                                </td>
-                            </tr>
-                        </table>
+                        <h6>{{ trans('entities.search_exact_matches') }}</h6>
+                        @include('search.form.term-list', ['type' => 'exact', 'currentList' => $options->exacts])
+
+                        <h6>{{ trans('entities.search_tags') }}</h6>
+                        @include('search.form.term-list', ['type' => 'tags', 'currentList' => $options->tags])
 
                         @if(signedInUser())
-                            <h6 class="text-muted">{{ trans('entities.search_options') }}</h6>
-                            <label class="checkbox">
-                                <input type="checkbox" v-on:change="optionChange('viewed_by_me')"
-                                       v-model="search.option.viewed_by_me" value="page">
+                            <h6>{{ trans('entities.search_options') }}</h6>
+
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'viewed_by_me', 'value' => null])
                                 {{ trans('entities.search_viewed_by_me') }}
-                            </label>
-                            <label class="checkbox">
-                                <input type="checkbox" v-on:change="optionChange('not_viewed_by_me')"
-                                       v-model="search.option.not_viewed_by_me" value="page">
+                            @endcomponent
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'not_viewed_by_me', 'value' => null])
                                 {{ trans('entities.search_not_viewed_by_me') }}
-                            </label>
-                            <label class="checkbox">
-                                <input type="checkbox" v-on:change="optionChange('is_restricted')"
-                                       v-model="search.option.is_restricted" value="page">
+                            @endcomponent
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'is_restricted', 'value' => null])
                                 {{ trans('entities.search_permissions_set') }}
-                            </label>
-                            <label class="checkbox">
-                                <input type="checkbox" v-on:change="optionChange('created_by:me')"
-                                       v-model="search.option['created_by:me']" value="page">
+                            @endcomponent
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'created_by', 'value' => 'me'])
                                 {{ trans('entities.search_created_by_me') }}
-                            </label>
-                            <label class="checkbox">
-                                <input type="checkbox" v-on:change="optionChange('updated_by:me')"
-                                       v-model="search.option['updated_by:me']" value="page">
+                            @endcomponent
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'updated_by', 'value' => 'me'])
                                 {{ trans('entities.search_updated_by_me') }}
-                            </label>
+                            @endcomponent
                         @endif
 
-                        <h6 class="text-muted">{{ trans('entities.search_date_options') }}</h6>
-                        <table cellpadding="0" cellspacing="0" border="0" class="no-style form-table">
-                            <tr>
-                                <td width="200">{{ trans('entities.search_updated_after') }}</td>
-                                <td width="80">
-                                    <button type="button" class="text-button" v-if="!search.dates.updated_after"
-                                            v-on:click="enableDate('updated_after')">{{ trans('entities.search_set_date') }}</button>
-
-                                </td>
-                            </tr>
-                            <tr v-if="search.dates.updated_after">
-                                <td>
-                                    <input v-if="search.dates.updated_after" class="tag-input"
-                                           v-on:input="dateChange('updated_after')" type="date" v-model="search.dates.updated_after"
-                                           pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
-                                </td>
-                                <td>
-                                    <button v-if="search.dates.updated_after" type="button" class="text-neg text-button"
-                                            v-on:click="dateRemove('updated_after')">
-                                        @icon('close')
-                                    </button>
-                                </td>
-                            </tr>
-                            <tr>
-                                <td>{{ trans('entities.search_updated_before') }}</td>
-                                <td>
-                                    <button type="button" class="text-button" v-if="!search.dates.updated_before"
-                                            v-on:click="enableDate('updated_before')">{{ trans('entities.search_set_date') }}</button>
-
-                                </td>
-                            </tr>
-                            <tr v-if="search.dates.updated_before">
-                                <td>
-                                    <input v-if="search.dates.updated_before" class="tag-input"
-                                           v-on:input="dateChange('updated_before')" type="date" v-model="search.dates.updated_before"
-                                           pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
-                                </td>
-                                <td>
-                                    <button v-if="search.dates.updated_before" type="button" class="text-neg text-button"
-                                            v-on:click="dateRemove('updated_before')">
-                                        @icon('close')
-                                    </button>
-                                </td>
-                            </tr>
-                            <tr>
-                                <td>{{ trans('entities.search_created_after') }}</td>
-                                <td>
-                                    <button type="button" class="text-button" v-if="!search.dates.created_after"
-                                            v-on:click="enableDate('created_after')">{{ trans('entities.search_set_date') }}</button>
-
-                                </td>
-                            </tr>
-                            <tr v-if="search.dates.created_after">
-                                <td>
-                                    <input v-if="search.dates.created_after" class="tag-input"
-                                           v-on:input="dateChange('created_after')" type="date" v-model="search.dates.created_after"
-                                           pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
-                                </td>
-                                <td>
-                                    <button v-if="search.dates.created_after" type="button" class="text-neg text-button"
-                                            v-on:click="dateRemove('created_after')">
-                                        @icon('close')
-                                    </button>
-                                </td>
-                            </tr>
-                            <tr>
-                                <td>{{ trans('entities.search_created_before') }}</td>
-                                <td>
-                                    <button type="button" class="text-button" v-if="!search.dates.created_before"
-                                            v-on:click="enableDate('created_before')">{{ trans('entities.search_set_date') }}</button>
-
-                                </td>
-                            </tr>
-                            <tr v-if="search.dates.created_before">
-                                <td>
-                                    <input v-if="search.dates.created_before" class="tag-input"
-                                           v-on:input="dateChange('created_before')" type="date" v-model="search.dates.created_before"
-                                           pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
-                                </td>
-                                <td>
-                                    <button v-if="search.dates.created_before" type="button" class="text-neg text-button"
-                                            v-on:click="dateRemove('created_before')">
-                                        @icon('close')
-                                    </button>
-                                </td>
-                            </tr>
-                        </table>
-
+                        <h6>{{ trans('entities.search_date_options') }}</h6>
+                        @include('search.form.date-filter', ['name' => 'updated_after', 'filters' => $options->filters])
+                        @include('search.form.date-filter', ['name' => 'updated_before', 'filters' => $options->filters])
+                        @include('search.form.date-filter', ['name' => 'created_after', 'filters' => $options->filters])
+                        @include('search.form.date-filter', ['name' => 'created_before', 'filters' => $options->filters])
 
                         <button type="submit" class="button">{{ trans('entities.search_update') }}</button>
                     </form>
                 </div>
             </div>
             <div>
-                <div v-pre class="card content-wrap">
+                <div class="card content-wrap">
                     <h1 class="list-heading">{{ trans('entities.search_results') }}</h1>
+
                     <form action="{{ url('/search') }}" method="GET"  class="search-box flexible hide-over-l">
                         <input value="{{$searchTerm}}" type="text" name="term" placeholder="{{ trans('common.search') }}">
                         <button type="submit">@icon('search')</button>
-                        <button v-if="searching" v-cloak class="search-box-cancel text-neg" v-on:click="clearSearch" type="button">@icon('close')</button>
                     </form>
+
                     <h6 class="text-muted">{{ trans_choice('entities.search_total_results_found', $totalResults, ['count' => $totalResults]) }}</h6>
                     <div class="book-contents">
                         @include('partials.entity-list', ['entities' => $entities, 'showPath' => true])
                     </div>
+
                     @if($hasNextPage)
                         <div class="text-right mt-m">
                             <a href="{{ $nextPageLink }}" class="button outline">{{ trans('entities.search_more') }}</a>
diff --git a/resources/views/search/book.blade.php b/resources/views/search/book.blade.php
deleted file mode 100644 (file)
index 36732c2..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<div class="page-list">
-    @if(count($pages) > 0)
-        @foreach($pages as $pageIndex => $page)
-            <div class="anim searchResult" style="animation-delay: {{$pageIndex*50 . 'ms'}};">
-                @include('pages.list-item', ['page' => $page])
-                <hr>
-            </div>
-        @endforeach
-    @else
-        <p class="text-muted">{{ trans('entities.search_no_pages') }}</p>
-    @endif
-</div>
-
-@if(count($chapters) > 0)
-    <div class="page-list">
-        @foreach($chapters as $chapterIndex => $chapter)
-            <div class="anim searchResult" style="animation-delay: {{($chapterIndex+count($pages))*50 . 'ms'}};">
-                @include('chapters.list-item', ['chapter' => $chapter, 'hidePages' => true])
-                <hr>
-            </div>
-        @endforeach
-    </div>
-@endif
-
diff --git a/resources/views/search/form/boolean-filter.blade.php b/resources/views/search/form/boolean-filter.blade.php
new file mode 100644 (file)
index 0000000..1dc9bf0
--- /dev/null
@@ -0,0 +1,12 @@
+{{--
+$filters - Array of search filter values
+$name - Name of filter to limit use.
+$value - Value of filter to use
+--}}
+<label class="checkbox">
+    <input type="checkbox"
+           name="filters[{{ $name }}]"
+           @if (isset($filters[$name]) && (!$value || ($value && $value === $filters[$name]))) checked="checked" @endif
+           value="{{ $value ?: 'true' }}">
+    {{ $slot }}
+</label>
\ No newline at end of file
diff --git a/resources/views/search/form/date-filter.blade.php b/resources/views/search/form/date-filter.blade.php
new file mode 100644 (file)
index 0000000..05ab4c1
--- /dev/null
@@ -0,0 +1,29 @@
+{{--
+@filters - Active search filters
+@name - Name of filter
+--}}
+<table class="no-style form-table mb-xs">
+    <tr>
+        <td width="200">{{ trans('entities.search_' . $name) }}</td>
+        <td width="80"></td>
+    </tr>
+    <tr component="optional-input">
+        <td>
+            <button type="button" refs="optional-input@show"
+                    class="text-button {{ ($filters[$name] ?? false) ? 'hidden' : '' }}">{{ trans('entities.search_set_date') }}</button>
+            <input class="tag-input {{ ($filters[$name] ?? false) ? '' : 'hidden' }}"
+                   refs="optional-input@input"
+                   value="{{ $filters[$name] ?? '' }}"
+                   type="date"
+                   name="filters[{{ $name }}]"
+                   pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
+        </td>
+        <td>
+            <button type="button"
+                    refs="optional-input@remove"
+                    class="text-neg text-button {{ ($filters[$name] ?? false) ? '' : 'hidden' }}">
+                @icon('close')
+            </button>
+        </td>
+    </tr>
+</table>
\ No newline at end of file
diff --git a/resources/views/search/form/term-list.blade.php b/resources/views/search/form/term-list.blade.php
new file mode 100644 (file)
index 0000000..3fbfa18
--- /dev/null
@@ -0,0 +1,26 @@
+{{--
+@type - Type of term (exact, tag)
+@currentList
+--}}
+<table component="add-remove-rows"
+       option:add-remove-rows:remove-selector="button.text-neg"
+       option:add-remove-rows:row-selector="tr"
+       class="no-style">
+    @foreach(array_merge($currentList, ['']) as $term)
+        <tr @if(empty($term)) class="hidden" refs="add-remove-rows@model" @endif>
+            <td class="pb-s pr-m">
+                <input class="exact-input outline" type="text" name="{{$type}}[]" value="{{ $term }}">
+            </td>
+            <td>
+                <button type="button" class="text-neg text-button">@icon('close')</button>
+            </td>
+        </tr>
+    @endforeach
+    <tr>
+        <td colspan="2">
+            <button refs="add-remove-rows@add" type="button" class="text-button">
+                @icon('add-circle'){{ trans('common.add') }}
+            </button>
+        </td>
+    </tr>
+</table>
\ No newline at end of file
diff --git a/resources/views/search/form/type-filter.blade.php b/resources/views/search/form/type-filter.blade.php
new file mode 100644 (file)
index 0000000..b885ebd
--- /dev/null
@@ -0,0 +1,10 @@
+{{--
+@checked - If the option should be pre-checked
+@entity - Entity Name
+@transKey - Translation Key
+--}}
+<label class="inline checkbox text-{{$entity}}">
+    <input type="checkbox" name="types[]"
+           @if($checked) checked @endif
+           value="{{$entity}}">{{ trans('entities.' . $transKey) }}
+</label>
\ No newline at end of file
diff --git a/resources/views/settings/audit.blade.php b/resources/views/settings/audit.blade.php
new file mode 100644 (file)
index 0000000..c52390f
--- /dev/null
@@ -0,0 +1,93 @@
+@extends('simple-layout')
+
+@section('body')
+<div class="container">
+
+    <div class="grid left-focus v-center no-row-gap">
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'audit'])
+        </div>
+    </div>
+
+    <div class="card content-wrap auto-height">
+        <h2 class="list-heading">{{ trans('settings.audit') }}</h2>
+        <p class="text-muted">{{ trans('settings.audit_desc') }}</p>
+
+        <div class="flex-container-row">
+            <div component="dropdown" class="list-sort-type dropdown-container mr-m">
+                <label for="">{{ trans('settings.audit_event_filter') }}</label>
+                <button refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" class="input-base text-left">{{ $listDetails['event'] ?: trans('settings.audit_event_filter_no_filter') }}</button>
+                <ul refs="dropdown@menu" class="dropdown-menu">
+                    <li @if($listDetails['event'] === '') class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => '']) }}">{{ trans('settings.audit_event_filter_no_filter') }}</a></li>
+                    @foreach($activityTypes as $type)
+                        <li @if($type === $listDetails['event']) class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => $type]) }}">{{ $type }}</a></li>
+                    @endforeach
+                </ul>
+            </div>
+
+            @foreach(['date_from', 'date_to'] as $filterKey)
+                <form action="{{ url('/settings/audit') }}" method="get" class="block mr-m">
+                    @foreach($listDetails as $param => $val)
+                        @if(!empty($val) && $param !== $filterKey)
+                            <input type="hidden" name="{{ $param }}" value="{{ $val }}">
+                        @endif
+                    @endforeach
+                    <label for="audit_filter_{{ $filterKey }}">{{ trans('settings.audit_' . $filterKey) }}</label>
+                    <input id="audit_filter_{{ $filterKey }}"
+                           component="submit-on-change"
+                           type="date"
+                           name="{{ $filterKey }}"
+                           value="{{ $listDetails[$filterKey] ?? '' }}">
+                </form>
+            @endforeach
+        </div>
+
+        <hr class="mt-l mb-s">
+
+        {{ $activities->links() }}
+
+        <table class="table">
+            <tbody>
+            <tr>
+                <th>{{ trans('settings.audit_table_user') }}</th>
+                <th>
+                    <a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'key']) }}">{{ trans('settings.audit_table_event') }}</a>
+                </th>
+                <th>{{ trans('settings.audit_table_related') }}</th>
+                <th>
+                    <a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'created_at']) }}">{{ trans('settings.audit_table_date') }}</a></th>
+            </tr>
+            @foreach($activities as $activity)
+                <tr>
+                    <td>
+                        @include('partials.table-user', ['user' => $activity->user, 'user_id' => $activity->user_id])
+                    </td>
+                    <td>{{ $activity->type }}</td>
+                    <td width="40%">
+                        @if($activity->entity)
+                            <a href="{{ $activity->entity->getUrl() }}" class="table-entity-item">
+                                <span role="presentation" class="icon text-{{$activity->entity->getType()}}">@icon($activity->entity->getType())</span>
+                                <div class="text-{{ $activity->entity->getType() }}">
+                                    {{ $activity->entity->name }}
+                                </div>
+                            </a>
+                        @elseif($activity->detail && $activity->isForEntity())
+                            <div class="px-m">
+                                {{ trans('settings.audit_deleted_item') }} <br>
+                                {{ trans('settings.audit_deleted_item_name', ['name' => $activity->detail]) }}
+                            </div>
+                        @elseif($activity->detail)
+                            <div class="px-m">{{ $activity->detail }}</div>
+                        @endif
+                    </td>
+                    <td>{{ $activity->created_at }}</td>
+                </tr>
+            @endforeach
+            </tbody>
+        </table>
+
+        {{ $activities->links() }}
+    </div>
+
+</div>
+@stop
index a5277418661ea29af619b3f9fb58a0abadd13de8..8adc1045b46d15a8744e15f06d54b3992d07a3e6 100644 (file)
@@ -3,21 +3,13 @@
 @section('body')
     <div class="container small">
 
-        <div class="grid left-focus v-center no-row-gap">
-            <div class="py-m">
-                @include('settings.navbar', ['selected' => 'settings'])
-            </div>
-            <div class="text-right p-m">
-                <a target="_blank" rel="noopener noreferrer" href="https://github.com/BookStackApp/BookStack/releases">
-                    BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
-                </a>
-            </div>
-        </div>
+        @include('settings.navbar-with-version', ['selected' => 'settings'])
 
         <div class="card content-wrap auto-height">
-            <h2 class="list-heading">{{ trans('settings.app_features_security') }}</h2>
+            <h2 id="features" class="list-heading">{{ trans('settings.app_features_security') }}</h2>
             <form action="{{ url("/settings") }}" method="POST">
                 {!! csrf_field() !!}
+                <input type="hidden" name="section" value="features">
 
                 <div class="setting-list">
 
         </div>
 
         <div class="card content-wrap auto-height">
-            <h2 class="list-heading">{{ trans('settings.app_customization') }}</h2>
+            <h2 id="customization" class="list-heading">{{ trans('settings.app_customization') }}</h2>
             <form action="{{ url("/settings") }}" method="POST" enctype="multipart/form-data">
                 {!! csrf_field() !!}
+                <input type="hidden" name="section" value="customization">
 
                 <div class="setting-list">
 
@@ -90,7 +83,7 @@
                             <label for="setting-app-name" class="setting-list-label">{{ trans('settings.app_name') }}</label>
                             <p class="small">{{ trans('settings.app_name_desc') }}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             <input type="text" value="{{ setting('app-name', 'BookStack') }}" name="setting-app-name" id="setting-app-name">
                             @include('components.toggle-switch', [
                                 'name' => 'setting-app-name-header',
                             <label class="setting-list-label">{{ trans('settings.app_editor') }}</label>
                             <p class="small">{{ trans('settings.app_editor_desc') }}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             <select name="setting-app-editor" id="setting-app-editor">
                                 <option @if(setting('app-editor') === 'wysiwyg') selected @endif value="wysiwyg">WYSIWYG</option>
                                 <option @if(setting('app-editor') === 'markdown') selected @endif value="markdown">Markdown</option>
                             <label class="setting-list-label">{{ trans('settings.app_logo') }}</label>
                             <p class="small">{!! trans('settings.app_logo_desc') !!}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             @include('components.image-picker', [
                                      'removeName' => 'setting-app-logo',
                                      'removeValue' => 'none',
                         </div>
                     </div>
 
+                    <!-- Primary Color -->
                     <div class="grid half gap-xl">
                         <div>
                             <label class="setting-list-label">{{ trans('settings.app_primary_color') }}</label>
                             <p class="small">{!! trans('settings.app_primary_color_desc') !!}</p>
                         </div>
-                        <div setting-app-color-picker class="text-m-right">
-                            <input type="color" value="{{ setting('app-color') }}" name="setting-app-color" id="setting-app-color" placeholder="#206ea7">
+                        <div setting-app-color-picker class="text-m-right pt-xs">
+                            <input type="color" data-default="#206ea7" data-current="{{ setting('app-color') }}" value="{{ setting('app-color') }}" name="setting-app-color" id="setting-app-color" placeholder="#206ea7">
                             <input type="hidden" value="{{ setting('app-color-light') }}" name="setting-app-color-light" id="setting-app-color-light">
-                            <br>
-                            <button type="button" class="text-button text-muted mt-s mx-s" setting-app-color-picker-reset>{{ trans('common.reset') }}</button>
+                            <div class="pr-s">
+                                <button type="button" class="text-button text-muted mt-s" setting-app-color-picker-default>{{ trans('common.default') }}</button>
+                                <span class="sep">|</span>
+                                <button type="button" class="text-button text-muted mt-s" setting-app-color-picker-reset>{{ trans('common.reset') }}</button>
+                            </div>
+
+                        </div>
+                    </div>
+
+                    <!-- Entity Color -->
+                    <div class="pb-l">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.content_colors') }}</label>
+                            <p class="small">{!! trans('settings.content_colors_desc') !!}</p>
+                        </div>
+                        <div class="grid half pt-m">
+                            <div>
+                                @include('components.setting-entity-color-picker', ['type' => 'bookshelf'])
+                                @include('components.setting-entity-color-picker', ['type' => 'book'])
+                                @include('components.setting-entity-color-picker', ['type' => 'chapter'])
+                            </div>
+                            <div>
+                                @include('components.setting-entity-color-picker', ['type' => 'page'])
+                                @include('components.setting-entity-color-picker', ['type' => 'page-draft'])
+                            </div>
                         </div>
                     </div>
 
                             <label for="setting-app-homepage" class="setting-list-label">{{ trans('settings.app_homepage') }}</label>
                             <p class="small">{{ trans('settings.app_homepage_desc') }}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             <select name="setting-app-homepage-type" id="setting-app-homepage-type">
                                 <option @if(setting('app-homepage-type') === 'default') selected @endif value="default">{{ trans('common.default') }}</option>
                                 <option @if(setting('app-homepage-type') === 'books') selected @endif value="books">{{ trans('entities.books') }}</option>
         </div>
 
         <div class="card content-wrap auto-height">
-            <h2 class="list-heading">{{ trans('settings.reg_settings') }}</h2>
+            <h2 id="registration" class="list-heading">{{ trans('settings.reg_settings') }}</h2>
             <form action="{{ url("/settings") }}" method="POST">
                 {!! csrf_field() !!}
+                <input type="hidden" name="section" value="registration">
 
                 <div class="setting-list">
                     <div class="grid half gap-xl">
                                 'label' => trans('settings.reg_enable_toggle')
                             ])
 
+                            @if(in_array(config('auth.method'), ['ldap', 'saml2']))
+                                <div class="text-warn text-small mb-l">{{ trans('settings.reg_enable_external_warning') }}</div>
+                            @endif
+
                             <label for="setting-registration-role">{{ trans('settings.reg_default_role') }}</label>
                             <select id="setting-registration-role" name="setting-registration-role" @if($errors->has('setting-registration-role')) class="neg" @endif>
                                 @foreach(\BookStack\Auth\Role::all() as $role)
-                                    <option value="{{$role->id}}" data-role-name="{{ $role->name }}"
+                                    <option value="{{$role->id}}"
+                                            data-system-role-name="{{ $role->system_name ?? '' }}"
                                             @if(setting('registration-role', \BookStack\Auth\Role::first()->id) == $role->id) selected @endif
                                     >
                                         {{ $role->display_name }}
                             <label for="setting-registration-restrict" class="setting-list-label">{{ trans('settings.reg_confirm_restrict_domain') }}</label>
                             <p class="small">{!! trans('settings.reg_confirm_restrict_domain_desc') !!}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             <input type="text" id="setting-registration-restrict" name="setting-registration-restrict" placeholder="{{ trans('settings.reg_confirm_restrict_domain_placeholder') }}" value="{{ setting('registration-restrict', '') }}">
                         </div>
                     </div>
 
     </div>
 
-    @include('components.image-manager', ['imageType' => 'system'])
     @include('components.entity-selector-popup', ['entityTypes' => 'page'])
-@stop
\ No newline at end of file
+@stop
index 7311bbbe20c96ab7ea15fe5d48ed817985a358e8..941a258d84942e32a1c004e4b0b7013f954984f0 100644 (file)
@@ -3,14 +3,23 @@
 @section('body')
 <div class="container small">
 
-    <div class="grid left-focus v-center no-row-gap">
-        <div class="py-m">
-            @include('settings.navbar', ['selected' => 'maintenance'])
-        </div>
-        <div class="text-right p-m">
-            <a target="_blank" rel="noopener noreferrer" href="https://github.com/BookStackApp/BookStack/releases">
-            BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
-            </a>
+    @include('settings.navbar-with-version', ['selected' => 'maintenance'])
+
+    <div class="card content-wrap auto-height pb-xl">
+        <h2 class="list-heading">{{ trans('settings.recycle_bin') }}</h2>
+        <div class="grid half gap-xl">
+            <div>
+                <p class="small text-muted">{{ trans('settings.maint_recycle_bin_desc') }}</p>
+            </div>
+            <div>
+                <div class="grid half no-gap mb-m">
+                    <p class="mb-xs text-bookshelf">@icon('bookshelf'){{ trans('entities.shelves') }}: {{ $recycleStats['bookshelf'] }}</p>
+                    <p class="mb-xs text-book">@icon('book'){{ trans('entities.books') }}: {{ $recycleStats['book'] }}</p>
+                    <p class="mb-xs text-chapter">@icon('chapter'){{ trans('entities.chapters') }}: {{ $recycleStats['chapter'] }}</p>
+                    <p class="mb-xs text-page">@icon('page'){{ trans('entities.pages') }}: {{ $recycleStats['page'] }}</p>
+                </div>
+                <a href="{{ url('/settings/recycle-bin') }}" class="button outline">{{ trans('settings.maint_recycle_bin_open') }}</a>
+            </div>
         </div>
     </div>
 
@@ -24,7 +33,7 @@
                 <form method="POST" action="{{ url('/settings/maintenance/cleanup-images') }}">
                     {!! csrf_field()  !!}
                     <input type="hidden" name="_method" value="DELETE">
-                    <div>
+                    <div class="mb-s">
                         @if(session()->has('cleanup-images-warning'))
                             <p class="text-neg">
                                 {{ session()->get('cleanup-images-warning') }}
@@ -32,9 +41,9 @@
                             <input type="hidden" name="ignore_revisions" value="{{ session()->getOldInput('ignore_revisions', 'false') }}">
                             <input type="hidden" name="confirm" value="true">
                         @else
-                            <label>
-                                <input type="checkbox" name="ignore_revisions" value="true">
-                                {{ trans('settings.maint_image_cleanup_ignore_revisions') }}
+                            <label class="flex-container-row">
+                                <div class="mr-s"><input type="checkbox" name="ignore_revisions" value="true"></div>
+                                <div>{{ trans('settings.maint_delete_images_only_in_revisions') }}</div>
                             </label>
                         @endif
                     </div>
diff --git a/resources/views/settings/navbar-with-version.blade.php b/resources/views/settings/navbar-with-version.blade.php
new file mode 100644 (file)
index 0000000..c02c370
--- /dev/null
@@ -0,0 +1,15 @@
+{{--
+$selected - String name of the selected tab
+$version - Version of bookstack to display
+--}}
+<div class="flex-container-row v-center wrap">
+    <div class="py-m flex fit-content">
+        @include('settings.navbar', ['selected' => $selected])
+    </div>
+    <div class="flex"></div>
+    <div class="text-right p-m flex fit-content">
+        <a target="_blank" rel="noopener noreferrer" href="https://github.com/BookStackApp/BookStack/releases">
+            BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
+        </a>
+    </div>
+</div>
\ No newline at end of file
index 896de9d97477c0c3e510ca806ed7e33cbf12e611..a472196c56e7bded70e893953f7383918257dca0 100644 (file)
@@ -1,13 +1,16 @@
 
 <nav class="active-link-list">
-    @if($currentUser->can('settings-manage'))
+    @if(userCan('settings-manage'))
         <a href="{{ url('/settings') }}" @if($selected == 'settings') class="active" @endif>@icon('settings'){{ trans('settings.settings') }}</a>
         <a href="{{ url('/settings/maintenance') }}" @if($selected == 'maintenance') class="active" @endif>@icon('spanner'){{ trans('settings.maint') }}</a>
     @endif
-    @if($currentUser->can('users-manage'))
+    @if(userCan('settings-manage') && userCan('users-manage'))
+        <a href="{{ url('/settings/audit') }}" @if($selected == 'audit') class="active" @endif>@icon('open-book'){{ trans('settings.audit') }}</a>
+    @endif
+    @if(userCan('users-manage'))
         <a href="{{ url('/settings/users') }}" @if($selected == 'users') class="active" @endif>@icon('users'){{ trans('settings.users') }}</a>
     @endif
-    @if($currentUser->can('user-roles-manage'))
+    @if(userCan('user-roles-manage'))
         <a href="{{ url('/settings/roles') }}" @if($selected == 'roles') class="active" @endif>@icon('lock-open'){{ trans('settings.roles') }}</a>
     @endif
 </nav>
\ No newline at end of file
diff --git a/resources/views/settings/recycle-bin/deletable-entity-list.blade.php b/resources/views/settings/recycle-bin/deletable-entity-list.blade.php
new file mode 100644 (file)
index 0000000..07ad94f
--- /dev/null
@@ -0,0 +1,11 @@
+@include('partials.entity-display-item', ['entity' => $entity])
+@if($entity->isA('book'))
+    @foreach($entity->chapters()->withTrashed()->get() as $chapter)
+        @include('partials.entity-display-item', ['entity' => $chapter])
+    @endforeach
+@endif
+@if($entity->isA('book') || $entity->isA('chapter'))
+    @foreach($entity->pages()->withTrashed()->get() as $page)
+        @include('partials.entity-display-item', ['entity' => $page])
+    @endforeach
+@endif
\ No newline at end of file
diff --git a/resources/views/settings/recycle-bin/destroy.blade.php b/resources/views/settings/recycle-bin/destroy.blade.php
new file mode 100644 (file)
index 0000000..bd5ef79
--- /dev/null
@@ -0,0 +1,29 @@
+@extends('simple-layout')
+
+@section('body')
+    <div class="container small">
+
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'maintenance'])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h2 class="list-heading">{{ trans('settings.recycle_bin_permanently_delete') }}</h2>
+            <p class="text-muted">{{ trans('settings.recycle_bin_destroy_confirm') }}</p>
+            <form action="{{ url('/settings/recycle-bin/' . $deletion->id) }}" method="post">
+                {!! method_field('DELETE') !!}
+                {!! csrf_field() !!}
+                <a href="{{ url('/settings/recycle-bin') }}" class="button outline">{{ trans('common.cancel') }}</a>
+                <button type="submit" class="button">{{ trans('common.delete_confirm') }}</button>
+            </form>
+
+            @if($deletion->deletable instanceof \BookStack\Entities\Models\Entity)
+                <hr class="mt-m">
+                <h5>{{ trans('settings.recycle_bin_destroy_list') }}</h5>
+                @include('settings.recycle-bin.deletable-entity-list', ['entity' => $deletion->deletable])
+            @endif
+
+        </div>
+
+    </div>
+@stop
diff --git a/resources/views/settings/recycle-bin/index.blade.php b/resources/views/settings/recycle-bin/index.blade.php
new file mode 100644 (file)
index 0000000..b5de84e
--- /dev/null
@@ -0,0 +1,101 @@
+@extends('simple-layout')
+
+@section('body')
+    <div class="container">
+
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'maintenance'])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h2 class="list-heading">{{ trans('settings.recycle_bin') }}</h2>
+
+            <div class="grid half left-focus">
+                <div>
+                    <p class="text-muted">{{ trans('settings.recycle_bin_desc') }}</p>
+                </div>
+                <div class="text-right">
+                    <div component="dropdown" class="dropdown-container">
+                        <button refs="dropdown@toggle"
+                                type="button"
+                                class="button outline">{{ trans('settings.recycle_bin_empty') }} </button>
+                        <div refs="dropdown@menu" class="dropdown-menu">
+                            <p class="text-neg small px-m mb-xs">{{ trans('settings.recycle_bin_empty_confirm') }}</p>
+
+                            <form action="{{ url('/settings/recycle-bin/empty') }}" method="POST">
+                                {!! csrf_field() !!}
+                                <button type="submit" class="text-primary small delete">{{ trans('common.confirm') }}</button>
+                            </form>
+                        </div>
+                    </div>
+
+                </div>
+            </div>
+
+
+            <hr class="mt-l mb-s">
+
+            {!! $deletions->links() !!}
+
+            <table class="table">
+                <tr>
+                    <th width="50%">{{ trans('settings.recycle_bin_deleted_item') }}</th>
+                    <th width="20%">{{ trans('settings.recycle_bin_deleted_by') }}</th>
+                    <th width="15%">{{ trans('settings.recycle_bin_deleted_at') }}</th>
+                    <th width="15%"></th>
+                </tr>
+                @if(count($deletions) === 0)
+                    <tr>
+                        <td colspan="4">
+                            <p class="text-muted"><em>{{ trans('settings.recycle_bin_contents_empty') }}</em></p>
+                        </td>
+                    </tr>
+                @endif
+                @foreach($deletions as $deletion)
+                <tr>
+                    <td>
+                        <div class="table-entity-item">
+                            <span role="presentation" class="icon text-{{$deletion->deletable->getType()}}">@icon($deletion->deletable->getType())</span>
+                            <div class="text-{{ $deletion->deletable->getType() }}">
+                                {{ $deletion->deletable->name }}
+                            </div>
+                        </div>
+                        @if($deletion->deletable instanceof \BookStack\Entities\Models\Book || $deletion->deletable instanceof \BookStack\Entities\Models\Chapter)
+                            <div class="mb-m"></div>
+                        @endif
+                        @if($deletion->deletable instanceof \BookStack\Entities\Models\Book)
+                            <div class="pl-xl block inline">
+                                <div class="text-chapter">
+                                    @icon('chapter') {{ trans_choice('entities.x_chapters', $deletion->deletable->chapters()->withTrashed()->count()) }}
+                                </div>
+                            </div>
+                        @endif
+                        @if($deletion->deletable instanceof \BookStack\Entities\Models\Book || $deletion->deletable instanceof \BookStack\Entities\Models\Chapter)
+                        <div class="pl-xl block inline">
+                            <div class="text-page">
+                                @icon('page') {{ trans_choice('entities.x_pages', $deletion->deletable->pages()->withTrashed()->count()) }}
+                            </div>
+                        </div>
+                        @endif
+                    </td>
+                    <td>@include('partials.table-user', ['user' => $deletion->deleter, 'user_id' => $deletion->deleted_by])</td>
+                    <td width="200">{{ $deletion->created_at }}</td>
+                    <td width="150" class="text-right">
+                        <div component="dropdown" class="dropdown-container">
+                            <button type="button" refs="dropdown@toggle" class="button outline">{{ trans('common.actions') }}</button>
+                            <ul refs="dropdown@menu" class="dropdown-menu">
+                                <li><a class="block" href="{{ url('/settings/recycle-bin/'.$deletion->id.'/restore') }}">{{ trans('settings.recycle_bin_restore') }}</a></li>
+                                <li><a class="block" href="{{ url('/settings/recycle-bin/'.$deletion->id.'/destroy') }}">{{ trans('settings.recycle_bin_permanently_delete') }}</a></li>
+                            </ul>
+                        </div>
+                    </td>
+                </tr>
+                @endforeach
+            </table>
+
+            {!! $deletions->links() !!}
+
+        </div>
+
+    </div>
+@stop
diff --git a/resources/views/settings/recycle-bin/restore.blade.php b/resources/views/settings/recycle-bin/restore.blade.php
new file mode 100644 (file)
index 0000000..c888aa8
--- /dev/null
@@ -0,0 +1,31 @@
+@extends('simple-layout')
+
+@section('body')
+    <div class="container small">
+
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'maintenance'])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h2 class="list-heading">{{ trans('settings.recycle_bin_restore') }}</h2>
+            <p class="text-muted">{{ trans('settings.recycle_bin_restore_confirm') }}</p>
+            <form action="{{ url('/settings/recycle-bin/' . $deletion->id . '/restore') }}" method="post">
+                {!! csrf_field() !!}
+                <a href="{{ url('/settings/recycle-bin') }}" class="button outline">{{ trans('common.cancel') }}</a>
+                <button type="submit" class="button">{{ trans('settings.recycle_bin_restore') }}</button>
+            </form>
+
+            @if($deletion->deletable instanceof \BookStack\Entities\Models\Entity)
+                <hr class="mt-m">
+                <h5>{{ trans('settings.recycle_bin_restore_list') }}</h5>
+                @if($deletion->deletable->getParent() && $deletion->deletable->getParent()->trashed())
+                    <p class="text-neg">{{ trans('settings.recycle_bin_restore_deleted_parent') }}</p>
+                @endif
+                @include('settings.recycle-bin.deletable-entity-list', ['entity' => $deletion->deletable])
+            @endif
+
+        </div>
+
+    </div>
+@stop
index 4f40345df99947dec5de11fa2d44cf88dcf382e0..fa7c12b0a2aafab0ec401e4a951a24966a89afa6 100644 (file)
@@ -19,7 +19,7 @@
                 @if($role->users->count() > 0)
                     <div class="form-group">
                         <p>{{ trans('settings.role_delete_users_assigned', ['userCount' => $role->users->count()]) }}</p>
-                        @include('form.role-select', ['options' => $roles, 'name' => 'migration_role_id'])
+                        @include('form.role-select', ['options' => $roles, 'name' => 'migrate_role_id'])
                     </div>
                 @endif
 
index 20b8d65ed9c22fd12a2050d6e2f2ba3a060d04d5..604acbb165021a5f8bc50814106f5447d77dcda0 100644 (file)
@@ -19,7 +19,7 @@
                     @include('form.text', ['name' => 'description'])
                 </div>
 
-                @if(config('auth.method') === 'ldap')
+                @if(config('auth.method') === 'ldap' || config('auth.method') === 'saml2')
                     <div class="form-group">
                         <label for="name">{{ trans('settings.role_external_auth_id') }}</label>
                         @include('form.text', ['name' => 'external_auth_id'])
             </div>
         </div>
 
-        <div class="grid half" permissions-table>
-            <div>
-                <label class="setting-list-label">{{ trans('settings.role_system') }}</label>
-                <a href="#" permissions-table-toggle-all class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
-            </div>
-            <div class="toggle-switch-list">
-                <div>@include('settings.roles.checkbox', ['permission' => 'users-manage', 'label' => trans('settings.role_manage_users')])</div>
-                <div>@include('settings.roles.checkbox', ['permission' => 'user-roles-manage', 'label' => trans('settings.role_manage_roles')])</div>
-                <div>@include('settings.roles.checkbox', ['permission' => 'restrictions-manage-all', 'label' => trans('settings.role_manage_entity_permissions')])</div>
-                <div>@include('settings.roles.checkbox', ['permission' => 'restrictions-manage-own', 'label' => trans('settings.role_manage_own_entity_permissions')])</div>
-                <div>@include('settings.roles.checkbox', ['permission' => 'templates-manage', 'label' => trans('settings.role_manage_page_templates')])</div>
-                <div>@include('settings.roles.checkbox', ['permission' => 'settings-manage', 'label' => trans('settings.role_manage_settings')])</div>
+        <div permissions-table>
+            <label class="setting-list-label">{{ trans('settings.role_system') }}</label>
+            <a href="#" permissions-table-toggle-all class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+
+            <div class="toggle-switch-list grid half mt-m">
+                <div>
+                    <div>@include('settings.roles.checkbox', ['permission' => 'restrictions-manage-all', 'label' => trans('settings.role_manage_entity_permissions')])</div>
+                    <div>@include('settings.roles.checkbox', ['permission' => 'restrictions-manage-own', 'label' => trans('settings.role_manage_own_entity_permissions')])</div>
+                    <div>@include('settings.roles.checkbox', ['permission' => 'templates-manage', 'label' => trans('settings.role_manage_page_templates')])</div>
+                    <div>@include('settings.roles.checkbox', ['permission' => 'access-api', 'label' => trans('settings.role_access_api')])</div>
+                </div>
+                <div>
+                    <div>@include('settings.roles.checkbox', ['permission' => 'settings-manage', 'label' => trans('settings.role_manage_settings')])</div>
+                    <div>@include('settings.roles.checkbox', ['permission' => 'users-manage', 'label' => trans('settings.role_manage_users')])</div>
+                    <div>@include('settings.roles.checkbox', ['permission' => 'user-roles-manage', 'label' => trans('settings.role_manage_roles')])</div>
+                    <p class="text-warn text-small mt-s mb-none">{{ trans('settings.roles_system_warning') }}</p>
+                </div>
             </div>
         </div>
 
                         <img class="avatar small" src="{{ $user->getAvatar(40) }}" alt="{{ $user->name }}">
                     </div>
                     <div>
-                        @if(userCan('users-manage') || $currentUser->id == $user->id)
+                        @if(userCan('users-manage') || user()->id == $user->id)
                             <a href="{{ url("/settings/users/{$user->id}") }}">
                                 @endif
                                 {{ $user->name }}
-                                @if(userCan('users-manage') || $currentUser->id == $user->id)
+                                @if(userCan('users-manage') || user()->id == $user->id)
                             </a>
                         @endif
                     </div>
             {{ trans('settings.role_users_none') }}
         </p>
     @endif
-</div>
\ No newline at end of file
+</div>
index 19c5bbecd69ae95c70bd782703cfe377ddd32b30..e635455bfd93a359ae2d3352b716fd9dc399085d 100644 (file)
@@ -60,7 +60,7 @@
         <label for="tag-manager">{{ trans('entities.shelf_tags') }}</label>
     </button>
     <div class="collapse-content" collapsible-content>
-        @include('components.tag-manager', ['entity' => $shelf ?? null, 'entityType' => 'bookshelf'])
+        @include('components.tag-manager', ['entity' => $shelf ?? null])
     </div>
 </div>
 
diff --git a/resources/views/shelves/grid-item.blade.php b/resources/views/shelves/grid-item.blade.php
deleted file mode 100644 (file)
index 25b35b9..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<a href="{{$shelf->getUrl()}}" class="bookshelf-grid-item grid-card"
-   data-entity-type="bookshelf" data-entity-id="{{$shelf->id}}">
-    <div class="bg-shelf featured-image-container-wrap">
-        <div class="featured-image-container" @if($shelf->cover) style="background-image: url('{{ $shelf->getBookCover() }}')"@endif>
-        </div>
-        @icon('bookshelf')
-    </div>
-    <div class="grid-card-content">
-        <h2>{{$shelf->getShortName(35)}}</h2>
-        @if(isset($shelf->searchSnippet))
-            <p class="text-muted">{!! $shelf->searchSnippet !!}</p>
-        @else
-            <p class="text-muted">{{ $shelf->getExcerpt(130) }}</p>
-        @endif
-    </div>
-    <div class="grid-card-footer text-muted text-small">
-        @icon('star')<span title="{{$shelf->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $shelf->created_at->diffForHumans()]) }}</span>
-        <br>
-        @icon('edit')<span title="{{ $shelf->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $shelf->updated_at->diffForHumans()]) }}</span>
-    </div>
-</a>
\ No newline at end of file
index 98f97f1331b8c9985dd6f869759ac0116571c510..21c33aa9c62d1aba748143b8af0e82ac3d01d351 100644 (file)
@@ -9,13 +9,13 @@
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
         <div class="icon-list text-primary">
-            @if($currentUser->can('bookshelf-create-all'))
+            @if(userCan('bookshelf-create-all'))
                 <a href="{{ url("/create-shelf") }}" class="icon-list-item">
                     <span>@icon('add')</span>
                     <span>{{ trans('entities.shelves_new_action') }}</span>
                 </a>
             @endif
-            @include('partials.view-toggle', ['view' => $view, 'type' => 'shelf'])
+            @include('partials.view-toggle', ['view' => $view, 'type' => 'shelves'])
         </div>
     </div>
 
index c9c9670c5833c84e02b09b919decc73a152cf0e2..00cacfa707c3c28f927695e7cf7fc71c3f629ba1 100644 (file)
@@ -1,5 +1,5 @@
 <a href="{{ $shelf->getUrl() }}" class="shelf entity-list-item" data-entity-type="bookshelf" data-entity-id="{{$shelf->id}}">
-    <div class="entity-list-item-image bg-shelf @if($shelf->image_id) has-image @endif" style="background-image: url('{{ $shelf->getBookCover() }}')">
+    <div class="entity-list-item-image bg-bookshelf @if($shelf->image_id) has-image @endif" style="background-image: url('{{ $shelf->getBookCover() }}')">
         @icon('bookshelf')
     </div>
     <div class="content py-xs">
@@ -10,7 +10,7 @@
     </div>
 </a>
 <div class="entity-shelf-books grid third gap-y-xs entity-list-item-children">
-    @foreach($shelf->books as $book)
+    @foreach($shelf->visibleBooks as $book)
         <div>
             <a href="{{ $book->getUrl('?shelf=' . $shelf->id) }}" class="entity-chip text-book">
                 @icon('book')
index b20b08a2c59e40f8a110394404f63ac198c91f8f..3600a8c795f70303dc464eb73b6fe503ec1bef90 100644 (file)
@@ -21,7 +21,7 @@
         @else
             <div class="grid third">
                 @foreach($shelves as $key => $shelf)
-                    @include('shelves.grid-item', ['shelf' => $shelf])
+                    @include('partials.entity-grid-item', ['entity' => $shelf])
                 @endforeach
             </div>
         @endif
index 2212e1c1e32ae3014b3f839f802d1dc534542c3a..46432c1b92594372b196fdb35fec9951833fffa9 100644 (file)
         <div class="book-content">
             <p class="text-muted">{!! nl2br(e($shelf->description)) !!}</p>
             @if(count($shelf->visibleBooks) > 0)
-                <div class="entity-list">
-                    @foreach($shelf->visibleBooks as $book)
-                        @include('books.list-item', ['book' => $book])
-                    @endforeach
-                </div>
+                @if($view === 'list')
+                    <div class="entity-list">
+                        @foreach($shelf->visibleBooks as $book)
+                            @include('books.list-item', ['book' => $book])
+                        @endforeach
+                    </div>
+                @else
+                    <div class="grid third">
+                        @foreach($shelf->visibleBooks as $key => $book)
+                            @include('partials.entity-grid-item', ['entity' => $book])
+                        @endforeach
+                    </div>
+                @endif
             @else
                 <div class="mt-xl">
                     <hr>
@@ -87,6 +95,8 @@
                 </a>
             @endif
 
+            @include('partials.view-toggle', ['view' => $view, 'type' => 'shelf'])
+
             <hr class="primary-background">
 
             @if(userCan('bookshelf-update', $shelf))
diff --git a/resources/views/users/api-tokens/create.blade.php b/resources/views/users/api-tokens/create.blade.php
new file mode 100644 (file)
index 0000000..46c3e0b
--- /dev/null
@@ -0,0 +1,33 @@
+@extends('simple-layout')
+
+@section('body')
+
+    <div class="container small pt-xl">
+
+        <main class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('settings.user_api_token_create') }}</h1>
+
+            <form action="{{ $user->getEditUrl('/create-api-token') }}" method="post">
+                {!! csrf_field() !!}
+
+                <div class="setting-list">
+                    @include('users.api-tokens.form')
+
+                    <div>
+                        <p class="text-warn italic">
+                            {{ trans('settings.user_api_token_create_secret_message') }}
+                        </p>
+                    </div>
+                </div>
+
+                <div class="form-group text-right">
+                    <a href="{{ $user->getEditUrl('#api_tokens') }}" class="button outline">{{ trans('common.cancel') }}</a>
+                    <button class="button" type="submit">{{ trans('common.save') }}</button>
+                </div>
+
+            </form>
+
+        </main>
+    </div>
+
+@stop
diff --git a/resources/views/users/api-tokens/delete.blade.php b/resources/views/users/api-tokens/delete.blade.php
new file mode 100644 (file)
index 0000000..8fcfcda
--- /dev/null
@@ -0,0 +1,26 @@
+@extends('simple-layout')
+
+@section('body')
+    <div class="container small pt-xl">
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('settings.user_api_token_delete') }}</h1>
+
+            <p>{{ trans('settings.user_api_token_delete_warning', ['tokenName' => $token->name]) }}</p>
+
+            <div class="grid half">
+                <p class="text-neg"><strong>{{ trans('settings.user_api_token_delete_confirm') }}</strong></p>
+                <div>
+                    <form action="{{ $user->getEditUrl('/api-tokens/' . $token->id) }}" method="POST" class="text-right">
+                        {!! csrf_field() !!}
+                        {!! method_field('delete') !!}
+
+                        <a href="{{ $user->getEditUrl('/api-tokens/' . $token->id) }}" class="button outline">{{ trans('common.cancel') }}</a>
+                        <button type="submit" class="button">{{ trans('common.confirm') }}</button>
+                    </form>
+                </div>
+            </div>
+
+        </div>
+    </div>
+@stop
diff --git a/resources/views/users/api-tokens/edit.blade.php b/resources/views/users/api-tokens/edit.blade.php
new file mode 100644 (file)
index 0000000..821a00d
--- /dev/null
@@ -0,0 +1,66 @@
+@extends('simple-layout')
+
+@section('body')
+
+    <div class="container small pt-xl">
+
+        <main class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('settings.user_api_token') }}</h1>
+
+            <form action="{{ $user->getEditUrl('/api-tokens/' . $token->id) }}" method="post">
+                {!! method_field('put') !!}
+                {!! csrf_field() !!}
+
+                <div class="setting-list">
+
+                    <div class="grid half gap-xl v-center">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.user_api_token_id') }}</label>
+                            <p class="small">{{ trans('settings.user_api_token_id_desc') }}</p>
+                        </div>
+                        <div>
+                            @include('form.text', ['name' => 'token_id', 'readonly' => true])
+                        </div>
+                    </div>
+
+
+                    @if( $secret )
+                        <div class="grid half gap-xl v-center">
+                            <div>
+                                <label class="setting-list-label">{{ trans('settings.user_api_token_secret') }}</label>
+                                <p class="small text-warn">{{ trans('settings.user_api_token_secret_desc') }}</p>
+                            </div>
+                            <div>
+                                <input type="text" readonly="readonly" value="{{ $secret }}">
+                            </div>
+                        </div>
+                    @endif
+
+                    @include('users.api-tokens.form', ['model' => $token])
+                </div>
+
+                <div class="grid half gap-xl v-center">
+
+                    <div class="text-muted text-small">
+                        <span title="{{ $token->created_at }}">
+                            {{ trans('settings.user_api_token_created', ['timeAgo' => $token->created_at->diffForHumans()]) }}
+                        </span>
+                        <br>
+                        <span title="{{ $token->updated_at }}">
+                            {{ trans('settings.user_api_token_updated', ['timeAgo' => $token->created_at->diffForHumans()]) }}
+                        </span>
+                    </div>
+
+                    <div class="form-group text-right">
+                        <a href="{{  $user->getEditUrl('#api_tokens') }}" class="button outline">{{ trans('common.back') }}</a>
+                        <a href="{{  $user->getEditUrl('/api-tokens/' . $token->id . '/delete') }}" class="button outline">{{ trans('settings.user_api_token_delete') }}</a>
+                        <button class="button" type="submit">{{ trans('common.save') }}</button>
+                    </div>
+                </div>
+
+            </form>
+
+        </main>
+    </div>
+
+@stop
diff --git a/resources/views/users/api-tokens/form.blade.php b/resources/views/users/api-tokens/form.blade.php
new file mode 100644 (file)
index 0000000..d81a330
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+<div class="grid half gap-xl v-center">
+    <div>
+        <label class="setting-list-label">{{ trans('settings.user_api_token_name') }}</label>
+        <p class="small">{{ trans('settings.user_api_token_name_desc') }}</p>
+    </div>
+    <div>
+        @include('form.text', ['name' => 'name'])
+    </div>
+</div>
+
+<div class="grid half gap-xl v-center">
+    <div>
+        <label class="setting-list-label">{{ trans('settings.user_api_token_expiry') }}</label>
+        <p class="small">{{ trans('settings.user_api_token_expiry_desc') }}</p>
+    </div>
+    <div class="text-right">
+        @include('form.date', ['name' => 'expires_at'])
+    </div>
+</div>
\ No newline at end of file
diff --git a/resources/views/users/api-tokens/list.blade.php b/resources/views/users/api-tokens/list.blade.php
new file mode 100644 (file)
index 0000000..ea18933
--- /dev/null
@@ -0,0 +1,34 @@
+<section class="card content-wrap auto-height" id="api_tokens">
+    <div class="grid half mb-s">
+        <div><h2 class="list-heading">{{ trans('settings.users_api_tokens') }}</h2></div>
+        <div class="text-right pt-xs">
+            @if(userCan('access-api'))
+                <a href="{{ url('/api/docs') }}" class="button outline">{{ trans('settings.users_api_tokens_docs') }}</a>
+                <a href="{{ $user->getEditUrl('/create-api-token') }}" class="button outline">{{ trans('settings.users_api_tokens_create') }}</a>
+            @endif
+        </div>
+    </div>
+    @if (count($user->apiTokens) > 0)
+        <table class="table">
+            <tr>
+                <th>{{ trans('common.name') }}</th>
+                <th>{{ trans('settings.users_api_tokens_expires') }}</th>
+                <th></th>
+            </tr>
+            @foreach($user->apiTokens as $token)
+                <tr>
+                    <td>
+                        {{ $token->name }} <br>
+                        <span class="small text-muted italic">{{ $token->token_id }}</span>
+                    </td>
+                    <td>{{ $token->expires_at->format('Y-m-d') ?? '' }}</td>
+                    <td class="text-right">
+                        <a class="button outline small" href="{{ $user->getEditUrl('/api-tokens/' . $token->id) }}">{{ trans('common.edit') }}</a>
+                    </td>
+                </tr>
+            @endforeach
+        </table>
+    @else
+        <p class="text-muted italic py-m">{{ trans('settings.users_api_tokens_none') }}</p>
+    @endif
+</section>
\ No newline at end of file
index 9971eeeeb54ca63ba42045982f076e6324936989..d953b646afe8c0ba10643863cb61fb384de064b5 100644 (file)
@@ -19,7 +19,7 @@
                 </div>
 
                 <div class="form-group text-right">
-                    <a href="{{  url($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
+                    <a href="{{  url(userCan('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
                     <button class="button" type="submit">{{ trans('common.save') }}</button>
                 </div>
 
index d3349c2f3fc29b93e6b10319e95d0cefc97df7e5..aba6f5cc1f6de6cee8da0beec9b64a51fc563eab 100644 (file)
 
             <p>{{ trans('settings.users_delete_warning', ['userName' => $user->name]) }}</p>
 
+            <hr class="my-l">
+
+            <div class="grid half gap-xl v-center">
+                <div>
+                    <label class="setting-list-label">{{ trans('settings.users_migrate_ownership') }}</label>
+                    <p class="small">{{ trans('settings.users_migrate_ownership_desc') }}</p>
+                </div>
+                <div>
+                    @include('components.user-select', ['name' => 'new_owner_id', 'user' => null])
+                </div>
+            </div>
+
+            <hr class="my-l">
+
             <div class="grid half">
                 <p class="text-neg"><strong>{{ trans('settings.users_delete_confirm') }}</strong></p>
                 <div>
index ff1e7cbe5d51577e2cd807dc4842aee8b268f1c8..7fb12bd757389c0128a0e4f62f36fb03cb6e5808 100644 (file)
@@ -8,7 +8,7 @@
         </div>
 
         <section class="card content-wrap">
-            <h1 class="list-heading">{{ $user->id === $currentUser->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h1>
+            <h1 class="list-heading">{{ $user->id === user()->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h1>
             <form action="{{ url("/settings/users/{$user->id}") }}" method="post" enctype="multipart/form-data">
                 {!! csrf_field() !!}
                 <input type="hidden" name="_method" value="PUT">
@@ -54,7 +54,7 @@
                 </div>
 
                 <div class="text-right">
-                    <a href="{{  url($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
+                    <a href="{{  url(userCan('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
                     @if($authMethod !== 'system')
                         <a href="{{ url("/settings/users/{$user->id}/delete") }}" class="button outline">{{ trans('settings.users_delete') }}</a>
                     @endif
@@ -63,7 +63,7 @@
             </form>
         </section>
 
-        @if($currentUser->id === $user->id && count($activeSocialDrivers) > 0)
+        @if(user()->id === $user->id && count($activeSocialDrivers) > 0)
             <section class="card content-wrap auto-height">
                 <h2 class="list-heading">{{ trans('settings.users_social_accounts') }}</h2>
                 <p class="text-muted">{{ trans('settings.users_social_accounts_info') }}</p>
                 </div>
             </section>
         @endif
+
+        @if((user()->id === $user->id && userCan('access-api')) || userCan('users-manage'))
+            @include('users.api-tokens.list', ['user' => $user])
+        @endif
     </div>
 
 @stop
index 32b717ec8c2ec4db4ef10211f54f0b1aab30f054..df3d06c2f34f232b17fe033472d4078f293285cb 100644 (file)
@@ -25,7 +25,7 @@
     </div>
 </div>
 
-@if($authMethod === 'ldap' && userCan('users-manage'))
+@if(($authMethod === 'ldap' || $authMethod === 'saml2') && userCan('users-manage'))
     <div class="grid half gap-xl v-center">
         <div>
             <label class="setting-list-label">{{ trans('settings.users_external_auth_id') }}</label>
@@ -84,4 +84,4 @@
         </div>
 
     </div>
-@endif
\ No newline at end of file
+@endif
index da373c1618b563fddcc9768644b7d1ac608e29cf..6bc229ec682a1fac2ae1599b4e1bfe0717c406a2 100644 (file)
                             <input type="text" name="search" placeholder="{{ trans('settings.users_search') }}" @if($listDetails['search']) value="{{$listDetails['search']}}" @endif>
                         </form>
                     </div>
-                    @if(userCan('users-manage'))
-                        <a href="{{ url("/settings/users/create") }}" style="margin-top: 0;" class="outline button">{{ trans('settings.users_add_new') }}</a>
-                    @endif
+                    <a href="{{ url("/settings/users/create") }}" class="outline button mt-none">{{ trans('settings.users_add_new') }}</a>
                 </div>
             </div>
 
-            {{--TODO - Add last login--}}
             <table class="table">
                 <tr>
                     <th></th>
                         <a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'email']) }}">{{ trans('auth.email') }}</a>
                     </th>
                     <th>{{ trans('settings.role_user_roles') }}</th>
+                    <th class="text-right">
+                        <a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'last_activity_at']) }}">{{ trans('settings.users_latest_activity') }}</a>
+                    </th>
                 </tr>
                 @foreach($users as $user)
                     <tr>
                         <td class="text-center" style="line-height: 0;"><img class="avatar med" src="{{ $user->getAvatar(40)}}" alt="{{ $user->name }}"></td>
                         <td>
-                            @if(userCan('users-manage') || $currentUser->id == $user->id)
-                                <a href="{{ url("/settings/users/{$user->id}") }}">
-                                    @endif
-                                    {{ $user->name }} <br> <span class="text-muted">{{ $user->email }}</span>
-                                    @if(userCan('users-manage') || $currentUser->id == $user->id)
-                                </a>
-                            @endif
+                            <a href="{{ url("/settings/users/{$user->id}") }}">
+                                {{ $user->name }} <br> <span class="text-muted">{{ $user->email }}</span>
+                            </a>
                         </td>
                         <td>
                             @foreach($user->roles as $index => $role)
                                 <small><a href="{{ url("/settings/roles/{$role->id}") }}">{{$role->display_name}}</a>@if($index !== count($user->roles) -1),@endif</small>
                             @endforeach
                         </td>
+                        <td class="text-right text-muted">
+                            @if($user->last_activity_at)
+                                <small title="{{ $user->last_activity_at->format('Y-m-d H:i:s') }}">{{ $user->last_activity_at->diffForHumans() }}</small>
+                            @endif
+                        </td>
                     </tr>
                 @endforeach
             </table>
diff --git a/routes/api.php b/routes/api.php
new file mode 100644 (file)
index 0000000..44643d6
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * Routes for the BookStack API.
+ * Routes have a uri prefix of /api/.
+ * Controllers are all within app/Http/Controllers/Api
+ */
+
+Route::get('docs', 'ApiDocsController@display');
+Route::get('docs.json', 'ApiDocsController@json');
+
+Route::get('books', 'BookApiController@list');
+Route::post('books', 'BookApiController@create');
+Route::get('books/{id}', 'BookApiController@read');
+Route::put('books/{id}', 'BookApiController@update');
+Route::delete('books/{id}', 'BookApiController@delete');
+
+Route::get('books/{id}/export/html', 'BookExportApiController@exportHtml');
+Route::get('books/{id}/export/pdf', 'BookExportApiController@exportPdf');
+Route::get('books/{id}/export/plaintext', 'BookExportApiController@exportPlainText');
+
+Route::get('chapters', 'ChapterApiController@list');
+Route::post('chapters', 'ChapterApiController@create');
+Route::get('chapters/{id}', 'ChapterApiController@read');
+Route::put('chapters/{id}', 'ChapterApiController@update');
+Route::delete('chapters/{id}', 'ChapterApiController@delete');
+
+Route::get('chapters/{id}/export/html', 'ChapterExportApiController@exportHtml');
+Route::get('chapters/{id}/export/pdf', 'ChapterExportApiController@exportPdf');
+Route::get('chapters/{id}/export/plaintext', 'ChapterExportApiController@exportPlainText');
+
+Route::get('pages', 'PageApiController@list');
+Route::post('pages', 'PageApiController@create');
+Route::get('pages/{id}', 'PageApiController@read');
+Route::put('pages/{id}', 'PageApiController@update');
+Route::delete('pages/{id}', 'PageApiController@delete');
+
+Route::get('pages/{id}/export/html', 'PageExportApiController@exportHtml');
+Route::get('pages/{id}/export/pdf', 'PageExportApiController@exportPdf');
+Route::get('pages/{id}/export/plaintext', 'PageExportApiController@exportPlainText');
+
+Route::get('shelves', 'BookshelfApiController@list');
+Route::post('shelves', 'BookshelfApiController@create');
+Route::get('shelves/{id}', 'BookshelfApiController@read');
+Route::put('shelves/{id}', 'BookshelfApiController@update');
+Route::delete('shelves/{id}', 'BookshelfApiController@delete');
index eafb6a45cbc6f91d1881f453439d53902437d218..64d08e165a9659c0bcd09111a8db6fd37a33269a 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+Route::get('/status', 'StatusController@show');
 Route::get('/robots.txt', 'HomeController@getRobots');
 
 // Authenticated routes...
@@ -101,22 +102,14 @@ Route::group(['middleware' => 'auth'], function () {
     Route::get('/user/{userId}', 'UserController@showProfilePage');
 
     // Image routes
-    Route::group(['prefix' => 'images'], function () {
-
-        // Gallery
-        Route::get('/gallery', 'Images\GalleryImageController@list');
-        Route::post('/gallery', 'Images\GalleryImageController@create');
-
-        // Drawio
-        Route::get('/drawio', 'Images\DrawioImageController@list');
-        Route::get('/drawio/base64/{id}', 'Images\DrawioImageController@getAsBase64');
-        Route::post('/drawio', 'Images\DrawioImageController@create');
-
-        // Shared gallery & draw.io endpoint
-        Route::get('/usage/{id}', 'Images\ImageController@usage');
-        Route::put('/{id}', 'Images\ImageController@update');
-        Route::delete('/{id}', 'Images\ImageController@destroy');
-    });
+    Route::get('/images/gallery', 'Images\GalleryImageController@list');
+    Route::post('/images/gallery', 'Images\GalleryImageController@create');
+    Route::get('/images/drawio', 'Images\DrawioImageController@list');
+    Route::get('/images/drawio/base64/{id}', 'Images\DrawioImageController@getAsBase64');
+    Route::post('/images/drawio', 'Images\DrawioImageController@create');
+    Route::get('/images/edit/{id}', 'Images\ImageController@edit');
+    Route::put('/images/{id}', 'Images\ImageController@update');
+    Route::delete('/images/{id}', 'Images\ImageController@destroy');
 
     // Attachments routes
     Route::get('/attachments/{id}', 'AttachmentController@get');
@@ -124,6 +117,7 @@ Route::group(['middleware' => 'auth'], function () {
     Route::post('/attachments/upload/{id}', 'AttachmentController@uploadUpdate');
     Route::post('/attachments/link', 'AttachmentController@attachLink');
     Route::put('/attachments/{id}', 'AttachmentController@update');
+    Route::get('/attachments/edit/{id}', 'AttachmentController@getUpdateForm');
     Route::get('/attachments/get/page/{pageId}', 'AttachmentController@listForPage');
     Route::put('/attachments/sort/page/{pageId}', 'AttachmentController@sortForPage');
     Route::delete('/attachments/{id}', 'AttachmentController@delete');
@@ -134,8 +128,7 @@ Route::group(['middleware' => 'auth'], function () {
     Route::delete('/ajax/page/{id}', 'PageController@ajaxDestroy');
 
     // Tag routes (AJAX)
-    Route::group(['prefix' => 'ajax/tags'], function() {
-        Route::get('/get/{entityType}/{entityId}', 'TagController@getForEntity');
+    Route::group(['prefix' => 'ajax/tags'], function () {
         Route::get('/suggest/names', 'TagController@getNameSuggestions');
         Route::get('/suggest/values', 'TagController@getValueSuggestions');
     });
@@ -143,9 +136,9 @@ Route::group(['middleware' => 'auth'], function () {
     Route::get('/ajax/search/entities', 'SearchController@searchEntitiesAjax');
 
     // Comments
-    Route::post('/ajax/page/{pageId}/comment', 'CommentController@savePageComment');
-    Route::put('/ajax/comment/{id}', 'CommentController@update');
-    Route::delete('/ajax/comment/{id}', 'CommentController@destroy');
+    Route::post('/comment/{pageId}', 'CommentController@savePageComment');
+    Route::put('/comment/{id}', 'CommentController@update');
+    Route::delete('/comment/{id}', 'CommentController@destroy');
 
     // Links
     Route::get('/link/{id}', 'PageController@redirectFromLink');
@@ -156,6 +149,9 @@ Route::group(['middleware' => 'auth'], function () {
     Route::get('/search/chapter/{bookId}', 'SearchController@searchChapter');
     Route::get('/search/entity/siblings', 'SearchController@searchSiblings');
 
+    // User Search
+    Route::get('/search/users/select', 'UserSearchController@forSelect');
+
     Route::get('/templates', 'PageTemplateController@list');
     Route::get('/templates/{templateId}', 'PageTemplateController@get');
 
@@ -170,40 +166,63 @@ Route::group(['middleware' => 'auth'], function () {
         Route::post('/', 'SettingController@update');
 
         // Maintenance
-        Route::get('/maintenance', 'SettingController@showMaintenance');
-        Route::delete('/maintenance/cleanup-images', 'SettingController@cleanupImages');
-        Route::post('/maintenance/send-test-email', 'SettingController@sendTestEmail');
+        Route::get('/maintenance', 'MaintenanceController@index');
+        Route::delete('/maintenance/cleanup-images', 'MaintenanceController@cleanupImages');
+        Route::post('/maintenance/send-test-email', 'MaintenanceController@sendTestEmail');
+
+        // Recycle Bin
+        Route::get('/recycle-bin', 'RecycleBinController@index');
+        Route::post('/recycle-bin/empty', 'RecycleBinController@empty');
+        Route::get('/recycle-bin/{id}/destroy', 'RecycleBinController@showDestroy');
+        Route::delete('/recycle-bin/{id}', 'RecycleBinController@destroy');
+        Route::get('/recycle-bin/{id}/restore', 'RecycleBinController@showRestore');
+        Route::post('/recycle-bin/{id}/restore', 'RecycleBinController@restore');
+
+        // Audit Log
+        Route::get('/audit', 'AuditLogController@index');
 
         // Users
         Route::get('/users', 'UserController@index');
         Route::get('/users/create', 'UserController@create');
         Route::get('/users/{id}/delete', 'UserController@delete');
-        Route::patch('/users/{id}/switch-book-view', 'UserController@switchBookView');
+        Route::patch('/users/{id}/switch-books-view', 'UserController@switchBooksView');
+        Route::patch('/users/{id}/switch-shelves-view', 'UserController@switchShelvesView');
         Route::patch('/users/{id}/switch-shelf-view', 'UserController@switchShelfView');
         Route::patch('/users/{id}/change-sort/{type}', 'UserController@changeSort');
         Route::patch('/users/{id}/update-expansion-preference/{key}', 'UserController@updateExpansionPreference');
+        Route::patch('/users/toggle-dark-mode', 'UserController@toggleDarkMode');
         Route::post('/users/create', 'UserController@store');
         Route::get('/users/{id}', 'UserController@edit');
         Route::put('/users/{id}', 'UserController@update');
         Route::delete('/users/{id}', 'UserController@destroy');
 
+        // User API Tokens
+        Route::get('/users/{userId}/create-api-token', 'UserApiTokenController@create');
+        Route::post('/users/{userId}/create-api-token', 'UserApiTokenController@store');
+        Route::get('/users/{userId}/api-tokens/{tokenId}', 'UserApiTokenController@edit');
+        Route::put('/users/{userId}/api-tokens/{tokenId}', 'UserApiTokenController@update');
+        Route::get('/users/{userId}/api-tokens/{tokenId}/delete', 'UserApiTokenController@delete');
+        Route::delete('/users/{userId}/api-tokens/{tokenId}', 'UserApiTokenController@destroy');
+
         // Roles
-        Route::get('/roles', 'PermissionController@listRoles');
-        Route::get('/roles/new', 'PermissionController@createRole');
-        Route::post('/roles/new', 'PermissionController@storeRole');
-        Route::get('/roles/delete/{id}', 'PermissionController@showDeleteRole');
-        Route::delete('/roles/delete/{id}', 'PermissionController@deleteRole');
-        Route::get('/roles/{id}', 'PermissionController@editRole');
-        Route::put('/roles/{id}', 'PermissionController@updateRole');
+        Route::get('/roles', 'RoleController@list');
+        Route::get('/roles/new', 'RoleController@create');
+        Route::post('/roles/new', 'RoleController@store');
+        Route::get('/roles/delete/{id}', 'RoleController@showDelete');
+        Route::delete('/roles/delete/{id}', 'RoleController@delete');
+        Route::get('/roles/{id}', 'RoleController@edit');
+        Route::put('/roles/{id}', 'RoleController@update');
     });
 
 });
 
 // Social auth routes
-Route::get('/login/service/{socialDriver}', 'Auth\LoginController@getSocialLogin');
-Route::get('/login/service/{socialDriver}/callback', 'Auth\RegisterController@socialCallback');
-Route::get('/login/service/{socialDriver}/detach', 'Auth\RegisterController@detachSocialAccount');
-Route::get('/register/service/{socialDriver}', 'Auth\RegisterController@socialRegister');
+Route::get('/login/service/{socialDriver}', 'Auth\SocialController@getSocialLogin');
+Route::get('/login/service/{socialDriver}/callback', 'Auth\SocialController@socialCallback');
+Route::group(['middleware' => 'auth'], function () {
+    Route::get('/login/service/{socialDriver}/detach', 'Auth\SocialController@detachSocialAccount');
+});
+Route::get('/register/service/{socialDriver}', 'Auth\SocialController@socialRegister');
 
 // Login/Logout routes
 Route::get('/login', 'Auth\LoginController@getLogin');
@@ -216,6 +235,13 @@ Route::post('/register/confirm/resend', 'Auth\ConfirmEmailController@resend');
 Route::get('/register/confirm/{token}', 'Auth\ConfirmEmailController@confirm');
 Route::post('/register', 'Auth\RegisterController@postRegister');
 
+// SAML routes
+Route::post('/saml2/login', 'Auth\Saml2Controller@login');
+Route::get('/saml2/logout', 'Auth\Saml2Controller@logout');
+Route::get('/saml2/metadata', 'Auth\Saml2Controller@metadata');
+Route::get('/saml2/sls', 'Auth\Saml2Controller@sls');
+Route::post('/saml2/acs', 'Auth\Saml2Controller@acs');
+
 // User invitation routes
 Route::get('/register/invite/{token}', 'Auth\UserInviteController@showSetPassword');
 Route::post('/register/invite/{token}', 'Auth\UserInviteController@setPassword');
index f47bc44a30d7b01d419178dc0e9f83cc414887b6..9c3fe273c1cbf034ab8a7246f943cbabbe0d1e2c 100644 (file)
@@ -1,7 +1,7 @@
 <?php namespace Tests;
 
 
-use BookStack\Entities\Book;
+use BookStack\Entities\Models\Book;
 
 class ActivityTrackingTest extends BrowserKitTest
 {
diff --git a/tests/Api/ApiAuthTest.php b/tests/Api/ApiAuthTest.php
new file mode 100644 (file)
index 0000000..3020939
--- /dev/null
@@ -0,0 +1,147 @@
+<?php namespace Tests\Api;
+
+use BookStack\Auth\Permissions\RolePermission;
+use BookStack\Auth\User;
+use Carbon\Carbon;
+use Tests\TestCase;
+
+class ApiAuthTest extends TestCase
+{
+    use TestsApi;
+
+    protected $endpoint = '/api/books';
+
+    public function test_requests_succeed_with_default_auth()
+    {
+        $viewer = $this->getViewer();
+        $this->giveUserPermissions($viewer, ['access-api']);
+
+        $resp = $this->get($this->endpoint);
+        $resp->assertStatus(401);
+
+        $this->actingAs($viewer, 'standard');
+
+        $resp = $this->get($this->endpoint);
+        $resp->assertStatus(200);
+    }
+
+    public function test_no_token_throws_error()
+    {
+        $resp = $this->get($this->endpoint);
+        $resp->assertStatus(401);
+        $resp->assertJson($this->errorResponse("No authorization token found on the request", 401));
+    }
+
+    public function test_bad_token_format_throws_error()
+    {
+        $resp = $this->get($this->endpoint, ['Authorization' => "Token abc123"]);
+        $resp->assertStatus(401);
+        $resp->assertJson($this->errorResponse("An authorization token was found on the request but the format appeared incorrect", 401));
+    }
+
+    public function test_token_with_non_existing_id_throws_error()
+    {
+        $resp = $this->get($this->endpoint, ['Authorization' => "Token abc:123"]);
+        $resp->assertStatus(401);
+        $resp->assertJson($this->errorResponse("No matching API token was found for the provided authorization token", 401));
+    }
+
+    public function test_token_with_bad_secret_value_throws_error()
+    {
+        $resp = $this->get($this->endpoint, ['Authorization' => "Token {$this->apiTokenId}:123"]);
+        $resp->assertStatus(401);
+        $resp->assertJson($this->errorResponse("The secret provided for the given used API token is incorrect", 401));
+    }
+
+    public function test_api_access_permission_required_to_access_api()
+    {
+        $resp = $this->get($this->endpoint, $this->apiAuthHeader());
+        $resp->assertStatus(200);
+        auth()->logout();
+
+        $accessApiPermission = RolePermission::getByName('access-api');
+        $editorRole = $this->getEditor()->roles()->first();
+        $editorRole->detachPermission($accessApiPermission);
+
+        $resp = $this->get($this->endpoint, $this->apiAuthHeader());
+        $resp->assertStatus(403);
+        $resp->assertJson($this->errorResponse("The owner of the used API token does not have permission to make API calls", 403));
+    }
+
+    public function test_api_access_permission_required_to_access_api_with_session_auth()
+    {
+        $editor = $this->getEditor();
+        $this->actingAs($editor, 'standard');
+
+        $resp = $this->get($this->endpoint);
+        $resp->assertStatus(200);
+        auth('standard')->logout();
+
+        $accessApiPermission = RolePermission::getByName('access-api');
+        $editorRole = $this->getEditor()->roles()->first();
+        $editorRole->detachPermission($accessApiPermission);
+
+        $editor = User::query()->where('id', '=', $editor->id)->first();
+
+        $this->actingAs($editor, 'standard');
+        $resp = $this->get($this->endpoint);
+        $resp->assertStatus(403);
+        $resp->assertJson($this->errorResponse("The owner of the used API token does not have permission to make API calls", 403));
+    }
+
+    public function test_token_expiry_checked()
+    {
+        $editor = $this->getEditor();
+        $token = $editor->apiTokens()->first();
+
+        $resp = $this->get($this->endpoint, $this->apiAuthHeader());
+        $resp->assertStatus(200);
+        auth()->logout();
+
+        $token->expires_at = Carbon::now()->subDay()->format('Y-m-d');
+        $token->save();
+
+        $resp = $this->get($this->endpoint, $this->apiAuthHeader());
+        $resp->assertJson($this->errorResponse("The authorization token used has expired", 403));
+    }
+
+    public function test_email_confirmation_checked_using_api_auth()
+    {
+        $editor = $this->getEditor();
+        $editor->email_confirmed = false;
+        $editor->save();
+
+        // Set settings and get user instance
+        $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']);
+
+        $resp = $this->get($this->endpoint, $this->apiAuthHeader());
+        $resp->assertStatus(401);
+        $resp->assertJson($this->errorResponse("The email address for the account in use needs to be confirmed", 401));
+    }
+
+    public function test_rate_limit_headers_active_on_requests()
+    {
+        $resp = $this->actingAsApiEditor()->get($this->endpoint);
+        $resp->assertHeader('x-ratelimit-limit', 180);
+        $resp->assertHeader('x-ratelimit-remaining', 179);
+        $resp = $this->actingAsApiEditor()->get($this->endpoint);
+        $resp->assertHeader('x-ratelimit-remaining', 178);
+    }
+
+    public function test_rate_limit_hit_gives_json_error()
+    {
+        config()->set(['api.requests_per_minute' => 1]);
+        $resp = $this->actingAsApiEditor()->get($this->endpoint);
+        $resp->assertStatus(200);
+
+        $resp = $this->actingAsApiEditor()->get($this->endpoint);
+        $resp->assertStatus(429);
+        $resp->assertHeader('x-ratelimit-remaining', 0);
+        $resp->assertHeader('retry-after');
+        $resp->assertJson([
+            'error' => [
+                'code' => 429,
+            ]
+        ]);
+    }
+}
\ No newline at end of file
diff --git a/tests/Api/ApiConfigTest.php b/tests/Api/ApiConfigTest.php
new file mode 100644 (file)
index 0000000..def62c9
--- /dev/null
@@ -0,0 +1,55 @@
+<?php namespace Tests\Api;
+
+use Tests\TestCase;
+
+class ApiConfigTest extends TestCase
+{
+    use TestsApi;
+
+    protected $endpoint = '/api/books';
+
+    public function test_default_item_count_reflected_in_listing_requests()
+    {
+        $this->actingAsApiEditor();
+
+        config()->set(['api.default_item_count' => 5]);
+        $resp = $this->get($this->endpoint);
+        $resp->assertJsonCount(5, 'data');
+
+        config()->set(['api.default_item_count' => 1]);
+        $resp = $this->get($this->endpoint);
+        $resp->assertJsonCount(1, 'data');
+    }
+
+    public function test_default_item_count_does_not_limit_count_param()
+    {
+        $this->actingAsApiEditor();
+        config()->set(['api.default_item_count' => 1]);
+        $resp = $this->get($this->endpoint . '?count=5');
+        $resp->assertJsonCount(5, 'data');
+    }
+
+    public function test_max_item_count_limits_listing_requests()
+    {
+        $this->actingAsApiEditor();
+
+        config()->set(['api.max_item_count' => 2]);
+        $resp = $this->get($this->endpoint);
+        $resp->assertJsonCount(2, 'data');
+
+        $resp = $this->get($this->endpoint . '?count=5');
+        $resp->assertJsonCount(2, 'data');
+    }
+
+    public function test_requests_per_min_alters_rate_limit()
+    {
+        $resp = $this->actingAsApiEditor()->get($this->endpoint);
+        $resp->assertHeader('x-ratelimit-limit', 180);
+
+        config()->set(['api.requests_per_minute' => 10]);
+
+        $resp = $this->actingAsApiEditor()->get($this->endpoint);
+        $resp->assertHeader('x-ratelimit-limit', 10);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/Api/ApiDocsTest.php b/tests/Api/ApiDocsTest.php
new file mode 100644 (file)
index 0000000..1687c64
--- /dev/null
@@ -0,0 +1,58 @@
+<?php namespace Tests\Api;
+
+use BookStack\Auth\User;
+use Tests\TestCase;
+
+class ApiDocsTest extends TestCase
+{
+    use TestsApi;
+
+    protected $endpoint = '/api/docs';
+
+    public function test_docs_page_not_visible_to_normal_viewers()
+    {
+        $viewer = $this->getViewer();
+        $resp = $this->actingAs($viewer)->get($this->endpoint);
+        $resp->assertStatus(403);
+
+        $resp = $this->actingAsApiEditor()->get($this->endpoint);
+        $resp->assertStatus(200);
+    }
+
+    public function test_docs_page_returns_view_with_docs_content()
+    {
+        $resp = $this->actingAsApiEditor()->get($this->endpoint);
+        $resp->assertStatus(200);
+        $resp->assertSee(url('/api/docs.json'));
+        $resp->assertSee('Show a JSON view of the API docs data.');
+        $resp->assertHeader('Content-Type', 'text/html; charset=UTF-8');
+    }
+
+    public function test_docs_json_endpoint_returns_json()
+    {
+        $resp = $this->actingAsApiEditor()->get($this->endpoint . '.json');
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Type', 'application/json');
+        $resp->assertJson([
+            'docs' => [ [
+                'name' => 'docs-display',
+                'uri' => 'api/docs'
+            ] ]
+        ]);
+    }
+
+    public function test_docs_page_visible_by_public_user_if_given_permission()
+    {
+        $this->setSettings(['app-public' => true]);
+        $guest = User::getDefault();
+
+        $this->startSession();
+        $resp = $this->get('/api/docs');
+        $resp->assertStatus(403);
+
+        $this->giveUserPermissions($guest, ['access-api']);
+
+        $resp = $this->get('/api/docs');
+        $resp->assertStatus(200);
+    }
+}
\ No newline at end of file
diff --git a/tests/Api/ApiListingTest.php b/tests/Api/ApiListingTest.php
new file mode 100644 (file)
index 0000000..c3d9bc1
--- /dev/null
@@ -0,0 +1,100 @@
+<?php namespace Tests\Api;
+
+use BookStack\Entities\Models\Book;
+use Tests\TestCase;
+
+class ApiListingTest extends TestCase
+{
+    use TestsApi;
+
+    protected $endpoint = '/api/books';
+
+    public function test_count_parameter_limits_responses()
+    {
+        $this->actingAsApiEditor();
+        $bookCount = min(Book::visible()->count(), 100);
+
+        $resp = $this->get($this->endpoint);
+        $resp->assertJsonCount($bookCount, 'data');
+
+        $resp = $this->get($this->endpoint . '?count=1');
+        $resp->assertJsonCount(1, 'data');
+    }
+
+    public function test_offset_parameter()
+    {
+        $this->actingAsApiEditor();
+        $books = Book::visible()->orderBy('id')->take(3)->get();
+
+        $resp = $this->get($this->endpoint . '?count=1');
+        $resp->assertJsonMissing(['name' => $books[1]->name ]);
+
+        $resp = $this->get($this->endpoint . '?count=1&offset=1000');
+        $resp->assertJsonCount(0, 'data');
+    }
+
+    public function test_sort_parameter()
+    {
+        $this->actingAsApiEditor();
+
+        $sortChecks = [
+            '-id' => Book::visible()->orderBy('id', 'desc')->first(),
+            '+name' => Book::visible()->orderBy('name', 'asc')->first(),
+            'name' => Book::visible()->orderBy('name', 'asc')->first(),
+            '-name' => Book::visible()->orderBy('name', 'desc')->first()
+        ];
+
+        foreach ($sortChecks as $sortOption => $result) {
+            $resp = $this->get($this->endpoint . '?count=1&sort=' . $sortOption);
+            $resp->assertJson(['data' => [
+                [
+                    'id' => $result->id,
+                    'name' => $result->name,
+                ]
+            ]]);
+        }
+    }
+
+    public function test_filter_parameter()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->first();
+        $nameSubstr = substr($book->name, 0, 4);
+        $encodedNameSubstr = rawurlencode($nameSubstr);
+
+        $filterChecks = [
+            // Test different types of filter
+            "filter[id]={$book->id}" => 1,
+            "filter[id:ne]={$book->id}" => Book::visible()->where('id', '!=', $book->id)->count(),
+            "filter[id:gt]={$book->id}" => Book::visible()->where('id', '>', $book->id)->count(),
+            "filter[id:gte]={$book->id}" => Book::visible()->where('id', '>=', $book->id)->count(),
+            "filter[id:lt]={$book->id}" => Book::visible()->where('id', '<', $book->id)->count(),
+            "filter[name:like]={$encodedNameSubstr}%" => Book::visible()->where('name', 'like', $nameSubstr . '%')->count(),
+
+            // Test mulitple filters 'and' together
+            "filter[id]={$book->id}&filter[name]=random_non_existing_string" => 0,
+        ];
+
+        foreach ($filterChecks as $filterOption => $resultCount) {
+            $resp = $this->get($this->endpoint . '?count=1&' . $filterOption);
+            $resp->assertJson(['total' => $resultCount]);
+        }
+    }
+
+    public function test_total_on_results_shows_correctly()
+    {
+        $this->actingAsApiEditor();
+        $bookCount = Book::query()->count();
+        $resp = $this->get($this->endpoint . '?count=1');
+        $resp->assertJson(['total' => $bookCount ]);
+    }
+
+    public function test_total_on_results_shows_correctly_when_offset_provided()
+    {
+        $this->actingAsApiEditor();
+        $bookCount = Book::query()->count();
+        $resp = $this->get($this->endpoint . '?count=1&offset=1');
+        $resp->assertJson(['total' => $bookCount ]);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/Api/BooksApiTest.php b/tests/Api/BooksApiTest.php
new file mode 100644 (file)
index 0000000..a36acdd
--- /dev/null
@@ -0,0 +1,143 @@
+<?php namespace Tests\Api;
+
+use BookStack\Entities\Models\Book;
+use Tests\TestCase;
+
+class BooksApiTest extends TestCase
+{
+    use TestsApi;
+
+    protected $baseEndpoint = '/api/books';
+
+    public function test_index_endpoint_returns_expected_book()
+    {
+        $this->actingAsApiEditor();
+        $firstBook = Book::query()->orderBy('id', 'asc')->first();
+
+        $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
+        $resp->assertJson(['data' => [
+            [
+                'id' => $firstBook->id,
+                'name' => $firstBook->name,
+                'slug' => $firstBook->slug,
+            ]
+        ]]);
+    }
+
+    public function test_create_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $details = [
+            'name' => 'My API book',
+            'description' => 'A book created via the API',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(200);
+        $newItem = Book::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
+        $resp->assertJson(array_merge($details, ['id' => $newItem->id, 'slug' => $newItem->slug]));
+        $this->assertActivityExists('book_create', $newItem);
+    }
+
+    public function test_book_name_needed_to_create()
+    {
+        $this->actingAsApiEditor();
+        $details = [
+            'description' => 'A book created via the API',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(422);
+        $resp->assertJson([
+            "error" => [
+                "message" => "The given data was invalid.",
+                "validation" => [
+                    "name" => ["The name field is required."]
+                ],
+                "code" => 422,
+            ],
+        ]);
+    }
+
+    public function test_read_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->first();
+
+        $resp = $this->getJson($this->baseEndpoint . "/{$book->id}");
+
+        $resp->assertStatus(200);
+        $resp->assertJson([
+            'id' => $book->id,
+            'slug' => $book->slug,
+            'created_by' => [
+                'name' => $book->createdBy->name,
+            ],
+            'updated_by' => [
+                'name' => $book->createdBy->name,
+            ],
+            'owned_by' => [
+                'name' => $book->ownedBy->name
+            ],
+        ]);
+    }
+
+    public function test_update_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->first();
+        $details = [
+            'name' => 'My updated API book',
+            'description' => 'A book created via the API',
+        ];
+
+        $resp = $this->putJson($this->baseEndpoint . "/{$book->id}", $details);
+        $book->refresh();
+
+        $resp->assertStatus(200);
+        $resp->assertJson(array_merge($details, ['id' => $book->id, 'slug' => $book->slug]));
+        $this->assertActivityExists('book_update', $book);
+    }
+
+    public function test_delete_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->first();
+        $resp = $this->deleteJson($this->baseEndpoint . "/{$book->id}");
+
+        $resp->assertStatus(204);
+        $this->assertActivityExists('book_delete');
+    }
+
+    public function test_export_html_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/html");
+        $resp->assertStatus(200);
+        $resp->assertSee($book->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html"');
+    }
+
+    public function test_export_plain_text_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/plaintext");
+        $resp->assertStatus(200);
+        $resp->assertSee($book->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.txt"');
+    }
+
+    public function test_export_pdf_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/pdf");
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"');
+    }
+}
\ No newline at end of file
diff --git a/tests/Api/ChaptersApiTest.php b/tests/Api/ChaptersApiTest.php
new file mode 100644 (file)
index 0000000..c7368ea
--- /dev/null
@@ -0,0 +1,189 @@
+<?php namespace Tests\Api;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use Tests\TestCase;
+
+class ChaptersApiTest extends TestCase
+{
+    use TestsApi;
+
+    protected $baseEndpoint = '/api/chapters';
+
+    public function test_index_endpoint_returns_expected_chapter()
+    {
+        $this->actingAsApiEditor();
+        $firstChapter = Chapter::query()->orderBy('id', 'asc')->first();
+
+        $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
+        $resp->assertJson(['data' => [
+            [
+                'id' => $firstChapter->id,
+                'name' => $firstChapter->name,
+                'slug' => $firstChapter->slug,
+                'book_id' => $firstChapter->book->id,
+                'priority' => $firstChapter->priority,
+            ]
+        ]]);
+    }
+
+    public function test_create_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::query()->first();
+        $details = [
+            'name' => 'My API chapter',
+            'description' => 'A chapter created via the API',
+            'book_id' => $book->id,
+            'tags' => [
+                [
+                    'name' => 'tagname',
+                    'value' => 'tagvalue',
+                ]
+            ]
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(200);
+        $newItem = Chapter::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
+        $resp->assertJson(array_merge($details, ['id' => $newItem->id, 'slug' => $newItem->slug]));
+        $this->assertDatabaseHas('tags', [
+            'entity_id' => $newItem->id,
+            'entity_type' => $newItem->getMorphClass(),
+            'name' => 'tagname',
+            'value' => 'tagvalue',
+        ]);
+        $resp->assertJsonMissing(['pages' => []]);
+        $this->assertActivityExists('chapter_create', $newItem);
+    }
+
+    public function test_chapter_name_needed_to_create()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::query()->first();
+        $details = [
+            'book_id' => $book->id,
+            'description' => 'A chapter created via the API',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(422);
+        $resp->assertJson($this->validationResponse([
+            "name" => ["The name field is required."]
+        ]));
+    }
+
+    public function test_chapter_book_id_needed_to_create()
+    {
+        $this->actingAsApiEditor();
+        $details = [
+            'name' => 'My api chapter',
+            'description' => 'A chapter created via the API',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(422);
+        $resp->assertJson($this->validationResponse([
+            "book_id" => ["The book id field is required."]
+        ]));
+    }
+
+    public function test_read_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+        $page = $chapter->pages()->first();
+
+        $resp = $this->getJson($this->baseEndpoint . "/{$chapter->id}");
+        $resp->assertStatus(200);
+        $resp->assertJson([
+            'id' => $chapter->id,
+            'slug' => $chapter->slug,
+            'created_by' => [
+                'name' => $chapter->createdBy->name,
+            ],
+            'book_id' => $chapter->book_id,
+            'updated_by' => [
+                'name' => $chapter->createdBy->name,
+            ],
+            'owned_by' => [
+                'name' => $chapter->ownedBy->name
+            ],
+            'pages' => [
+                [
+                    'id' => $page->id,
+                    'slug' => $page->slug,
+                    'name' => $page->name,
+                ]
+            ],
+        ]);
+        $resp->assertJsonCount($chapter->pages()->count(), 'pages');
+    }
+
+    public function test_update_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+        $details = [
+            'name' => 'My updated API chapter',
+            'description' => 'A chapter created via the API',
+            'tags' => [
+                [
+                    'name' => 'freshtag',
+                    'value' => 'freshtagval',
+                ]
+            ],
+        ];
+
+        $resp = $this->putJson($this->baseEndpoint . "/{$chapter->id}", $details);
+        $chapter->refresh();
+
+        $resp->assertStatus(200);
+        $resp->assertJson(array_merge($details, [
+            'id' => $chapter->id, 'slug' => $chapter->slug, 'book_id' => $chapter->book_id
+        ]));
+        $this->assertActivityExists('chapter_update', $chapter);
+    }
+
+    public function test_delete_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+        $resp = $this->deleteJson($this->baseEndpoint . "/{$chapter->id}");
+
+        $resp->assertStatus(204);
+        $this->assertActivityExists('chapter_delete');
+    }
+
+    public function test_export_html_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/html");
+        $resp->assertStatus(200);
+        $resp->assertSee($chapter->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html"');
+    }
+
+    public function test_export_plain_text_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/plaintext");
+        $resp->assertStatus(200);
+        $resp->assertSee($chapter->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.txt"');
+    }
+
+    public function test_export_pdf_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/pdf");
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"');
+    }
+}
\ No newline at end of file
diff --git a/tests/Api/PagesApiTest.php b/tests/Api/PagesApiTest.php
new file mode 100644 (file)
index 0000000..e08e9b1
--- /dev/null
@@ -0,0 +1,261 @@
+<?php namespace Tests\Api;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Tests\TestCase;
+
+class PagesApiTest extends TestCase
+{
+    use TestsApi;
+
+    protected $baseEndpoint = '/api/pages';
+
+    public function test_index_endpoint_returns_expected_page()
+    {
+        $this->actingAsApiEditor();
+        $firstPage = Page::query()->orderBy('id', 'asc')->first();
+
+        $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
+        $resp->assertJson(['data' => [
+            [
+                'id' => $firstPage->id,
+                'name' => $firstPage->name,
+                'slug' => $firstPage->slug,
+                'book_id' => $firstPage->book->id,
+                'priority' => $firstPage->priority,
+            ]
+        ]]);
+    }
+
+    public function test_create_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::query()->first();
+        $details = [
+            'name' => 'My API page',
+            'book_id' => $book->id,
+            'html' => '<p>My new page content</p>',
+            'tags' => [
+                [
+                    'name' => 'tagname',
+                    'value' => 'tagvalue',
+                ]
+            ]
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        unset($details['html']);
+        $resp->assertStatus(200);
+        $newItem = Page::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
+        $resp->assertJson(array_merge($details, ['id' => $newItem->id, 'slug' => $newItem->slug]));
+        $this->assertDatabaseHas('tags', [
+            'entity_id' => $newItem->id,
+            'entity_type' => $newItem->getMorphClass(),
+            'name' => 'tagname',
+            'value' => 'tagvalue',
+        ]);
+        $resp->assertSeeText('My new page content');
+        $resp->assertJsonMissing(['book' => []]);
+        $this->assertActivityExists('page_create', $newItem);
+    }
+
+    public function test_page_name_needed_to_create()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::query()->first();
+        $details = [
+            'book_id' => $book->id,
+            'html' => '<p>A page created via the API</p>',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(422);
+        $resp->assertJson($this->validationResponse([
+            "name" => ["The name field is required."]
+        ]));
+    }
+
+    public function test_book_id_or_chapter_id_needed_to_create()
+    {
+        $this->actingAsApiEditor();
+        $details = [
+            'name' => 'My api page',
+            'html' => '<p>A page created via the API</p>',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(422);
+        $resp->assertJson($this->validationResponse([
+            "book_id" => ["The book id field is required when chapter id is not present."],
+            "chapter_id" => ["The chapter id field is required when book id is not present."]
+        ]));
+
+        $chapter = Chapter::visible()->first();
+        $resp = $this->postJson($this->baseEndpoint, array_merge($details, ['chapter_id' => $chapter->id]));
+        $resp->assertStatus(200);
+
+        $book = Book::visible()->first();
+        $resp = $this->postJson($this->baseEndpoint, array_merge($details, ['book_id' => $book->id]));
+        $resp->assertStatus(200);
+    }
+
+    public function test_markdown_can_be_provided_for_create()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->first();
+        $details = [
+            'book_id' => $book->id,
+            'name' => 'My api page',
+            'markdown' => "# A new API page \n[link](https://example.com)",
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertJson(['markdown' => $details['markdown']]);
+
+        $respHtml = $resp->json('html');
+        $this->assertStringContainsString('new API page</h1>', $respHtml);
+        $this->assertStringContainsString('link</a>', $respHtml);
+        $this->assertStringContainsString('href="https://example.com"', $respHtml);
+    }
+
+    public function test_read_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+
+        $resp = $this->getJson($this->baseEndpoint . "/{$page->id}");
+        $resp->assertStatus(200);
+        $resp->assertJson([
+            'id' => $page->id,
+            'slug' => $page->slug,
+            'created_by' => [
+                'name' => $page->createdBy->name,
+            ],
+            'book_id' => $page->book_id,
+            'updated_by' => [
+                'name' => $page->createdBy->name,
+            ],
+            'owned_by' => [
+                'name' => $page->ownedBy->name
+            ],
+        ]);
+    }
+
+    public function test_read_endpoint_provides_rendered_html()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+        $page->html = "<p>testing</p><script>alert('danger')</script><h1>Hello</h1>";
+        $page->save();
+
+        $resp = $this->getJson($this->baseEndpoint . "/{$page->id}");
+        $html = $resp->json('html');
+        $this->assertStringNotContainsString('script', $html);
+        $this->assertStringContainsString('Hello', $html);
+        $this->assertStringContainsString('testing', $html);
+    }
+
+    public function test_update_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+        $details = [
+            'name' => 'My updated API page',
+            'html' => '<p>A page created via the API</p>',
+            'tags' => [
+                [
+                    'name' => 'freshtag',
+                    'value' => 'freshtagval',
+                ]
+            ],
+        ];
+
+        $resp = $this->putJson($this->baseEndpoint . "/{$page->id}", $details);
+        $page->refresh();
+
+        $resp->assertStatus(200);
+        unset($details['html']);
+        $resp->assertJson(array_merge($details, [
+            'id' => $page->id, 'slug' => $page->slug, 'book_id' => $page->book_id
+        ]));
+        $this->assertActivityExists('page_update', $page);
+    }
+
+    public function test_providing_new_chapter_id_on_update_will_move_page()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+        $chapter = Chapter::visible()->where('book_id', '!=', $page->book_id)->first();
+        $details = [
+            'name' => 'My updated API page',
+            'chapter_id' => $chapter->id,
+            'html' => '<p>A page created via the API</p>',
+        ];
+
+        $resp = $this->putJson($this->baseEndpoint . "/{$page->id}", $details);
+        $resp->assertStatus(200);
+        $resp->assertJson([
+            'chapter_id' => $chapter->id,
+            'book_id' => $chapter->book_id,
+        ]);
+    }
+
+    public function test_providing_move_via_update_requires_page_create_permission_on_new_parent()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+        $chapter = Chapter::visible()->where('book_id', '!=', $page->book_id)->first();
+        $this->setEntityRestrictions($chapter, ['view'], [$this->getEditor()->roles()->first()]);
+        $details = [
+            'name' => 'My updated API page',
+            'chapter_id' => $chapter->id,
+            'html' => '<p>A page created via the API</p>',
+        ];
+
+        $resp = $this->putJson($this->baseEndpoint . "/{$page->id}", $details);
+        $resp->assertStatus(403);
+    }
+
+    public function test_delete_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+        $resp = $this->deleteJson($this->baseEndpoint . "/{$page->id}");
+
+        $resp->assertStatus(204);
+        $this->assertActivityExists('page_delete', $page);
+    }
+
+    public function test_export_html_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/html");
+        $resp->assertStatus(200);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.html"');
+    }
+
+    public function test_export_plain_text_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/plaintext");
+        $resp->assertStatus(200);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.txt"');
+    }
+
+    public function test_export_pdf_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/pdf");
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"');
+    }
+}
\ No newline at end of file
diff --git a/tests/Api/ShelvesApiTest.php b/tests/Api/ShelvesApiTest.php
new file mode 100644 (file)
index 0000000..32715dd
--- /dev/null
@@ -0,0 +1,139 @@
+<?php namespace Tests\Api;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use Tests\TestCase;
+
+class ShelvesApiTest extends TestCase
+{
+    use TestsApi;
+
+    protected $baseEndpoint = '/api/shelves';
+
+    public function test_index_endpoint_returns_expected_shelf()
+    {
+        $this->actingAsApiEditor();
+        $firstBookshelf = Bookshelf::query()->orderBy('id', 'asc')->first();
+
+        $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
+        $resp->assertJson(['data' => [
+            [
+                'id' => $firstBookshelf->id,
+                'name' => $firstBookshelf->name,
+                'slug' => $firstBookshelf->slug,
+            ]
+        ]]);
+    }
+
+    public function test_create_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $books = Book::query()->take(2)->get();
+
+        $details = [
+            'name' => 'My API shelf',
+            'description' => 'A shelf created via the API',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, array_merge($details, ['books' => [$books[0]->id, $books[1]->id]]));
+        $resp->assertStatus(200);
+        $newItem = Bookshelf::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
+        $resp->assertJson(array_merge($details, ['id' => $newItem->id, 'slug' => $newItem->slug]));
+        $this->assertActivityExists('bookshelf_create', $newItem);
+        foreach ($books as $index => $book) {
+            $this->assertDatabaseHas('bookshelves_books', [
+                'bookshelf_id' => $newItem->id,
+                'book_id' => $book->id,
+                'order' => $index,
+            ]);
+        }
+    }
+
+    public function test_shelf_name_needed_to_create()
+    {
+        $this->actingAsApiEditor();
+        $details = [
+            'description' => 'A shelf created via the API',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(422);
+        $resp->assertJson([
+            "error" => [
+                "message" => "The given data was invalid.",
+                "validation" => [
+                    "name" => ["The name field is required."]
+                ],
+                "code" => 422,
+            ],
+        ]);
+    }
+
+    public function test_read_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $shelf = Bookshelf::visible()->first();
+
+        $resp = $this->getJson($this->baseEndpoint . "/{$shelf->id}");
+
+        $resp->assertStatus(200);
+        $resp->assertJson([
+            'id' => $shelf->id,
+            'slug' => $shelf->slug,
+            'created_by' => [
+                'name' => $shelf->createdBy->name,
+            ],
+            'updated_by' => [
+                'name' => $shelf->createdBy->name,
+            ],
+            'owned_by' => [
+                'name' => $shelf->ownedBy->name
+            ],
+        ]);
+    }
+
+    public function test_update_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $shelf = Bookshelf::visible()->first();
+        $details = [
+            'name' => 'My updated API shelf',
+            'description' => 'A shelf created via the API',
+        ];
+
+        $resp = $this->putJson($this->baseEndpoint . "/{$shelf->id}", $details);
+        $shelf->refresh();
+
+        $resp->assertStatus(200);
+        $resp->assertJson(array_merge($details, ['id' => $shelf->id, 'slug' => $shelf->slug]));
+        $this->assertActivityExists('bookshelf_update', $shelf);
+    }
+
+    public function test_update_only_assigns_books_if_param_provided()
+    {
+        $this->actingAsApiEditor();
+        $shelf = Bookshelf::visible()->first();
+        $this->assertTrue($shelf->books()->count() > 0);
+        $details = [
+            'name' => 'My updated API shelf',
+        ];
+
+        $resp = $this->putJson($this->baseEndpoint . "/{$shelf->id}", $details);
+        $resp->assertStatus(200);
+        $this->assertTrue($shelf->books()->count() > 0);
+
+        $resp = $this->putJson($this->baseEndpoint . "/{$shelf->id}", ['books' => []]);
+        $resp->assertStatus(200);
+        $this->assertTrue($shelf->books()->count() === 0);
+    }
+
+    public function test_delete_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $shelf = Bookshelf::visible()->first();
+        $resp = $this->deleteJson($this->baseEndpoint . "/{$shelf->id}");
+
+        $resp->assertStatus(204);
+        $this->assertActivityExists('bookshelf_delete');
+    }
+}
\ No newline at end of file
diff --git a/tests/Api/TestsApi.php b/tests/Api/TestsApi.php
new file mode 100644 (file)
index 0000000..1ad4d14
--- /dev/null
@@ -0,0 +1,46 @@
+<?php namespace Tests\Api;
+
+trait TestsApi
+{
+
+    protected $apiTokenId = 'apitoken';
+    protected $apiTokenSecret = 'password';
+
+    /**
+     * Set the API editor role as the current user via the API driver.
+     */
+    protected function actingAsApiEditor()
+    {
+        $this->actingAs($this->getEditor(), 'api');
+        return $this;
+    }
+
+    /**
+     * Format the given items into a standardised error format.
+     */
+    protected function errorResponse(string $message, int $code): array
+    {
+        return ["error" => ["code" => $code, "message" => $message]];
+    }
+
+    /**
+     * Format the given (field_name => ["messages"]) array
+     * into a standard validation response format.
+     */
+    protected function validationResponse(array $messages): array
+    {
+        $err = $this->errorResponse("The given data was invalid.", 422);
+        $err['error']['validation'] = $messages;
+        return $err;
+    }
+    /**
+     * Get an approved API auth header.
+     */
+    protected function apiAuthHeader(): array
+    {
+        return [
+            "Authorization" => "Token {$this->apiTokenId}:{$this->apiTokenSecret}"
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/tests/AuditLogTest.php b/tests/AuditLogTest.php
new file mode 100644 (file)
index 0000000..3dc6fd7
--- /dev/null
@@ -0,0 +1,120 @@
+<?php namespace Tests;
+
+use BookStack\Actions\Activity;
+use BookStack\Actions\ActivityService;
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\UserRepo;
+use BookStack\Entities\Tools\TrashCan;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Repos\PageRepo;
+use Carbon\Carbon;
+
+class AuditLogTest extends TestCase
+{
+    /** @var ActivityService  */
+    protected $activityService;
+
+    public function setUp(): void
+    {
+        parent::setUp();
+        $this->activityService = app(ActivityService::class);
+    }
+
+    public function test_only_accessible_with_right_permissions()
+    {
+        $viewer = $this->getViewer();
+        $this->actingAs($viewer);
+
+        $resp = $this->get('/settings/audit');
+        $this->assertPermissionError($resp);
+
+        $this->giveUserPermissions($viewer, ['settings-manage']);
+        $resp = $this->get('/settings/audit');
+        $this->assertPermissionError($resp);
+
+        $this->giveUserPermissions($viewer, ['users-manage']);
+        $resp = $this->get('/settings/audit');
+        $resp->assertStatus(200);
+        $resp->assertSeeText('Audit Log');
+    }
+
+    public function test_shows_activity()
+    {
+        $admin = $this->getAdmin();
+        $this->actingAs($admin);
+        $page = Page::query()->first();
+        $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
+        $activity = Activity::query()->orderBy('id', 'desc')->first();
+
+        $resp = $this->get('settings/audit');
+        $resp->assertSeeText($page->name);
+        $resp->assertSeeText('page_create');
+        $resp->assertSeeText($activity->created_at->toDateTimeString());
+        $resp->assertElementContains('.table-user-item', $admin->name);
+    }
+
+    public function test_shows_name_for_deleted_items()
+    {
+        $this->actingAs( $this->getAdmin());
+        $page = Page::query()->first();
+        $pageName = $page->name;
+        $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
+
+        app(PageRepo::class)->destroy($page);
+        app(TrashCan::class)->empty();
+
+        $resp = $this->get('settings/audit');
+        $resp->assertSeeText('Deleted Item');
+        $resp->assertSeeText('Name: ' . $pageName);
+    }
+
+    public function test_shows_activity_for_deleted_users()
+    {
+        $viewer = $this->getViewer();
+        $this->actingAs($viewer);
+        $page = Page::query()->first();
+        $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
+
+        $this->actingAs($this->getAdmin());
+        app(UserRepo::class)->destroy($viewer);
+
+        $resp = $this->get('settings/audit');
+        $resp->assertSeeText("[ID: {$viewer->id}] Deleted User");
+    }
+
+    public function test_filters_by_key()
+    {
+        $this->actingAs($this->getAdmin());
+        $page = Page::query()->first();
+        $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
+
+        $resp = $this->get('settings/audit');
+        $resp->assertSeeText($page->name);
+
+        $resp = $this->get('settings/audit?event=page_delete');
+        $resp->assertDontSeeText($page->name);
+    }
+
+    public function test_date_filters()
+    {
+        $this->actingAs($this->getAdmin());
+        $page = Page::query()->first();
+        $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
+
+        $yesterday = (Carbon::now()->subDay()->format('Y-m-d'));
+        $tomorrow = (Carbon::now()->addDay()->format('Y-m-d'));
+
+        $resp = $this->get('settings/audit?date_from=' . $yesterday);
+        $resp->assertSeeText($page->name);
+
+        $resp = $this->get('settings/audit?date_from=' . $tomorrow);
+        $resp->assertDontSeeText($page->name);
+
+        $resp = $this->get('settings/audit?date_to=' . $tomorrow);
+        $resp->assertSeeText($page->name);
+
+        $resp = $this->get('settings/audit?date_to=' . $yesterday);
+        $resp->assertDontSeeText($page->name);
+    }
+
+}
\ No newline at end of file
index eb83faded44edc67d721e595d1a11d3861e12255..a0de7f803505860647964c68a917c2d49833cf0d 100644 (file)
@@ -1,10 +1,15 @@
-<?php namespace Tests;
+<?php namespace Tests\Auth;
 
+use BookStack\Auth\Role;
 use BookStack\Auth\User;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
 use BookStack\Notifications\ConfirmEmail;
+use BookStack\Notifications\ResetPassword;
 use BookStack\Settings\SettingService;
+use DB;
+use Hash;
 use Illuminate\Support\Facades\Notification;
+use Tests\BrowserKitTest;
 
 class AuthTest extends BrowserKitTest
 {
@@ -128,7 +133,7 @@ class AuthTest extends BrowserKitTest
             ->press('Resend Confirmation Email');
 
         // Get confirmation and confirm notification matches
-        $emailConfirmation = \DB::table('email_confirmations')->where('user_id', '=', $dbUser->id)->first();
+        $emailConfirmation = DB::table('email_confirmations')->where('user_id', '=', $dbUser->id)->first();
         Notification::assertSentTo($dbUser, ConfirmEmail::class, function($notification, $channels) use ($emailConfirmation) {
             return $notification->token === $emailConfirmation->token;
         });
@@ -165,6 +170,11 @@ class AuthTest extends BrowserKitTest
             ->seePageIs('/register/confirm')
             ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]);
 
+        $this->visit('/')
+            ->seePageIs('/register/confirm/awaiting');
+
+        auth()->logout();
+
         $this->visit('/')->seePageIs('/login')
             ->type($user->email, '#email')
             ->type($user->password, '#password')
@@ -197,6 +207,10 @@ class AuthTest extends BrowserKitTest
             ->seePageIs('/register/confirm')
             ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]);
 
+        $this->visit('/')
+            ->seePageIs('/register/confirm/awaiting');
+
+        auth()->logout();
         $this->visit('/')->seePageIs('/login')
             ->type($user->email, '#email')
             ->type($user->password, '#password')
@@ -208,13 +222,14 @@ class AuthTest extends BrowserKitTest
     public function test_user_creation()
     {
         $user = factory(User::class)->make();
+        $adminRole = Role::getRole('admin');
 
         $this->asAdmin()
             ->visit('/settings/users')
             ->click('Add New User')
             ->type($user->name, '#name')
             ->type($user->email, '#email')
-            ->check('roles[admin]')
+            ->check("roles[{$adminRole->id}]")
             ->type($user->password, '#password')
             ->type($user->password, '#password-confirm')
             ->press('Save')
@@ -256,7 +271,7 @@ class AuthTest extends BrowserKitTest
             ->seePageIs('/settings/users');
 
             $userPassword = User::find($user->id)->password;
-            $this->assertTrue(\Hash::check('newpassword', $userPassword));
+            $this->assertTrue(Hash::check('newpassword', $userPassword));
     }
 
     public function test_user_deletion()
@@ -275,7 +290,7 @@ class AuthTest extends BrowserKitTest
 
     public function test_user_cannot_be_deleted_if_last_admin()
     {
-        $adminRole = \BookStack\Auth\Role::getRole('admin');
+        $adminRole = Role::getRole('admin');
 
         // Delete all but one admin user if there are more than one
         $adminUsers = $adminRole->users;
@@ -308,14 +323,13 @@ class AuthTest extends BrowserKitTest
 
     public function test_reset_password_flow()
     {
-
         Notification::fake();
 
         $this->visit('/login')->click('Forgot Password?')
             ->seePageIs('/password/email')
             ->type('[email protected]', 'email')
             ->press('Send Reset Link')
-            ->see('A password reset link has been sent to [email protected]');
+            ->see('A password reset link will be sent to [email protected] if that email address is found in the system.');
 
         $this->seeInDatabase('password_resets', [
             'email' => '[email protected]'
@@ -323,8 +337,8 @@ class AuthTest extends BrowserKitTest
 
         $user = User::where('email', '=', '[email protected]')->first();
 
-        Notification::assertSentTo($user, \BookStack\Notifications\ResetPassword::class);
-        $n = Notification::sent($user, \BookStack\Notifications\ResetPassword::class);
+        Notification::assertSentTo($user, ResetPassword::class);
+        $n = Notification::sent($user, ResetPassword::class);
 
         $this->visit('/password/reset/' . $n->first()->token)
             ->see('Reset Password')
@@ -336,6 +350,28 @@ class AuthTest extends BrowserKitTest
             ->see('Your password has been successfully reset');
     }
 
+    public function test_reset_password_flow_shows_success_message_even_if_wrong_password_to_prevent_user_discovery()
+    {
+        $this->visit('/login')->click('Forgot Password?')
+            ->seePageIs('/password/email')
+            ->type('[email protected]', 'email')
+            ->press('Send Reset Link')
+            ->see('A password reset link will be sent to [email protected] if that email address is found in the system.')
+            ->dontSee('We can\'t find a user');
+
+
+        $this->visit('/password/reset/arandometokenvalue')
+            ->see('Reset Password')
+            ->submitForm('Reset Password', [
+                'email' => '[email protected]',
+                'password' => 'randompass',
+                'password_confirmation' => 'randompass'
+            ])->followRedirects()
+            ->seePageIs('/password/reset/arandometokenvalue')
+            ->dontSee('We can\'t find a user')
+            ->see('The password reset token is invalid for this email address.');
+    }
+
     public function test_reset_password_page_shows_sign_links()
     {
         $this->setSettings(['registration-enabled' => 'true']);
@@ -355,13 +391,53 @@ class AuthTest extends BrowserKitTest
             ->seePageUrlIs($page->getUrl());
     }
 
+    public function test_login_intended_redirect_does_not_redirect_to_external_pages()
+    {
+        config()->set('app.url', 'http://localhost');
+        $this->setSettings(['app-public' => true]);
+
+        $this->get('/login', ['referer' => 'https://example.com']);
+        $login = $this->post('/login', ['email' => '[email protected]', 'password' => 'password']);
+
+        $login->assertRedirectedTo('http://localhost');
+    }
+
+    public function test_login_authenticates_admins_on_all_guards()
+    {
+        $this->post('/login', ['email' => '[email protected]', 'password' => 'password']);
+        $this->assertTrue(auth()->check());
+        $this->assertTrue(auth('ldap')->check());
+        $this->assertTrue(auth('saml2')->check());
+    }
+
+    public function test_login_authenticates_nonadmins_on_default_guard_only()
+    {
+        $editor = $this->getEditor();
+        $editor->password = bcrypt('password');
+        $editor->save();
+
+        $this->post('/login', ['email' => $editor->email, 'password' => 'password']);
+        $this->assertTrue(auth()->check());
+        $this->assertFalse(auth('ldap')->check());
+        $this->assertFalse(auth('saml2')->check());
+    }
+
+    public function test_failed_logins_are_logged_when_message_configured()
+    {
+        $log = $this->withTestLogger();
+        config()->set(['logging.failed_login.message' => 'Failed login for %u']);
+
+        $this->post('/login', ['email' => '[email protected]', 'password' => 'cattreedog']);
+        $this->assertTrue($log->hasWarningThatContains('Failed login for [email protected]'));
+
+        $this->post('/login', ['email' => '[email protected]', 'password' => 'password']);
+        $this->assertFalse($log->hasWarningThatContains('Failed login for [email protected]'));
+    }
+
     /**
      * Perform a login
-     * @param string $email
-     * @param string $password
-     * @return $this
      */
-    protected function login($email, $password)
+    protected function login(string $email, string $password): AuthTest
     {
         return $this->visit('/login')
             ->type($email, '#email')
index fe28698dfac6b79013020c43fb82a93f1bdcb8fe..3cb39ca2c59c63a8315a76c44ebbaed1881bbfe2 100644 (file)
@@ -1,8 +1,11 @@
-<?php namespace Tests;
+<?php namespace Tests\Auth;
+
+use BookStack\Auth\Access\LdapService;
 use BookStack\Auth\Role;
 use BookStack\Auth\Access\Ldap;
 use BookStack\Auth\User;
 use Mockery\MockInterface;
+use Tests\BrowserKitTest;
 
 class LdapTest extends BrowserKitTest
 {
@@ -19,13 +22,18 @@ class LdapTest extends BrowserKitTest
     {
         parent::setUp();
         if (!defined('LDAP_OPT_REFERRALS')) define('LDAP_OPT_REFERRALS', 1);
-        app('config')->set([
+        config()->set([
             'auth.method' => 'ldap',
+            'auth.defaults.guard' => 'ldap',
             'services.ldap.base_dn' => 'dc=ldap,dc=local',
             'services.ldap.email_attribute' => 'mail',
             'services.ldap.display_name_attribute' => 'cn',
+            'services.ldap.id_attribute' => 'uid',
             'services.ldap.user_to_groups' => false,
-            'auth.providers.users.driver' => 'ldap',
+            'services.ldap.version' => '3',
+            'services.ldap.user_filter' => '(&(uid=${user}))',
+            'services.ldap.follow_referrals' => false,
+            'services.ldap.tls_insecure' => false,
         ]);
         $this->mockLdap = \Mockery::mock(Ldap::class);
         $this->app[Ldap::class] = $this->mockLdap;
@@ -55,20 +63,29 @@ class LdapTest extends BrowserKitTest
             ->press('Log In');
     }
 
+    /**
+     * Set LDAP method mocks for things we commonly call without altering.
+     */
+    protected function commonLdapMocks(int $connects = 1, int $versions = 1, int $options = 2, int $binds = 4, int $escapes = 2, int $explodes = 0)
+    {
+        $this->mockLdap->shouldReceive('connect')->times($connects)->andReturn($this->resourceId);
+        $this->mockLdap->shouldReceive('setVersion')->times($versions);
+        $this->mockLdap->shouldReceive('setOption')->times($options);
+        $this->mockLdap->shouldReceive('bind')->times($binds)->andReturn(true);
+        $this->mockEscapes($escapes);
+        $this->mockExplodes($explodes);
+    }
+
     public function test_login()
     {
-        $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
-        $this->mockLdap->shouldReceive('setVersion')->once();
-        $this->mockLdap->shouldReceive('setOption')->times(4);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
+        $this->commonLdapMocks(1, 1, 2, 4, 2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
                 'cn' => [$this->mockUser->name],
                 'dn' => ['dc=test' . config('services.ldap.base_dn')]
             ]]);
-        $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
-        $this->mockEscapes(4);
 
         $this->mockUserLogin()
             ->seePageIs('/login')->see('Please enter an email to use for this account.');
@@ -80,21 +97,46 @@ class LdapTest extends BrowserKitTest
             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name]);
     }
 
+    public function test_email_domain_restriction_active_on_new_ldap_login()
+    {
+        $this->setSettings([
+            'registration-restrict' => 'testing.com'
+        ]);
+
+        $this->commonLdapMocks(1, 1, 2, 4, 2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
+            ->andReturn(['count' => 1, 0 => [
+                'uid' => [$this->mockUser->name],
+                'cn' => [$this->mockUser->name],
+                'dn' => ['dc=test' . config('services.ldap.base_dn')]
+            ]]);
+
+        $this->mockUserLogin()
+            ->seePageIs('/login')
+            ->see('Please enter an email to use for this account.');
+
+        $email = '[email protected]';
+
+        $this->type($email, '#email')
+            ->press('Log In')
+            ->seePageIs('/login')
+            ->see('That email domain does not have access to this application')
+            ->dontSeeInDatabase('users', ['email' => $email]);
+    }
+
     public function test_login_works_when_no_uid_provided_by_ldap_server()
     {
-        $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
-        $this->mockLdap->shouldReceive('setVersion')->once();
         $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
-        $this->mockLdap->shouldReceive('setOption')->times(2);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
+
+        $this->commonLdapMocks(1, 1, 1, 2, 1);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'cn' => [$this->mockUser->name],
                 'dn' => $ldapDn,
                 'mail' => [$this->mockUser->email]
             ]]);
-        $this->mockLdap->shouldReceive('bind')->times(3)->andReturn(true);
-        $this->mockEscapes(2);
 
         $this->mockUserLogin()
             ->seePageIs('/')
@@ -102,26 +144,58 @@ class LdapTest extends BrowserKitTest
             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
     }
 
-    public function test_initial_incorrect_details()
+    public function test_a_custom_uid_attribute_can_be_specified_and_is_used_properly()
     {
-        $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
-        $this->mockLdap->shouldReceive('setVersion')->once();
-        $this->mockLdap->shouldReceive('setOption')->times(2);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
+        config()->set(['services.ldap.id_attribute' => 'my_custom_id']);
+
+        $this->commonLdapMocks(1, 1, 1, 2, 1);
+        $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
+            ->andReturn(['count' => 1, 0 => [
+                'cn' => [$this->mockUser->name],
+                'dn' => $ldapDn,
+                'my_custom_id' => ['cooluser456'],
+                'mail' => [$this->mockUser->email]
+            ]]);
+
+
+        $this->mockUserLogin()
+            ->seePageIs('/')
+            ->see($this->mockUser->name)
+            ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']);
+    }
+
+    public function test_initial_incorrect_credentials()
+    {
+        $this->commonLdapMocks(1, 1, 1, 0, 1);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
                 'cn' => [$this->mockUser->name],
                 'dn' => ['dc=test' . config('services.ldap.base_dn')]
             ]]);
-        $this->mockLdap->shouldReceive('bind')->times(3)->andReturn(true, true, false);
-        $this->mockEscapes(2);
+        $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true, false);
+
+        $this->mockUserLogin()
+            ->seePageIs('/login')->see('These credentials do not match our records.')
+            ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
+    }
+
+    public function test_login_not_found_username()
+    {
+        $this->commonLdapMocks(1, 1, 1, 1, 1);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
+            ->andReturn(['count' => 0]);
 
         $this->mockUserLogin()
             ->seePageIs('/login')->see('These credentials do not match our records.')
             ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
     }
 
+
     public function test_create_user_form()
     {
         $this->asAdmin()->visit('/settings/users/create')
@@ -163,9 +237,9 @@ class LdapTest extends BrowserKitTest
 
     public function test_login_maps_roles_and_retains_existing_roles()
     {
-        $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
-        $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
-        $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
+        $roleToReceive2 = factory(Role::class)->create(['display_name' => 'LdapTester Second']);
+        $existingRole = factory(Role::class)->create(['display_name' => 'ldaptester-existing']);
         $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
         $this->mockUser->attachRole($existingRole);
 
@@ -174,10 +248,9 @@ class LdapTest extends BrowserKitTest
             'services.ldap.group_attribute' => 'memberOf',
             'services.ldap.remove_from_groups' => false,
         ]);
-        $this->mockLdap->shouldReceive('connect')->times(2)->andReturn($this->resourceId);
-        $this->mockLdap->shouldReceive('setVersion')->times(2);
-        $this->mockLdap->shouldReceive('setOption')->times(5);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(5)
+
+        $this->commonLdapMocks(1, 1, 4, 5, 4, 6);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
@@ -190,9 +263,6 @@ class LdapTest extends BrowserKitTest
                     1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
                 ]
             ]]);
-        $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
-        $this->mockEscapes(5);
-        $this->mockExplodes(6);
 
         $this->mockUserLogin()->seePageIs('/');
 
@@ -213,8 +283,8 @@ class LdapTest extends BrowserKitTest
 
     public function test_login_maps_roles_and_removes_old_roles_if_set()
     {
-        $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
-        $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
+        $existingRole = factory(Role::class)->create(['display_name' => 'ldaptester-existing']);
         $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
         $this->mockUser->attachRole($existingRole);
 
@@ -223,10 +293,9 @@ class LdapTest extends BrowserKitTest
             'services.ldap.group_attribute' => 'memberOf',
             'services.ldap.remove_from_groups' => true,
         ]);
-        $this->mockLdap->shouldReceive('connect')->times(2)->andReturn($this->resourceId);
-        $this->mockLdap->shouldReceive('setVersion')->times(2);
-        $this->mockLdap->shouldReceive('setOption')->times(4);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
+
+        $this->commonLdapMocks(1, 1, 3, 4, 3, 2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
@@ -238,9 +307,6 @@ class LdapTest extends BrowserKitTest
                     0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
                 ]
             ]]);
-        $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
-        $this->mockEscapes(4);
-        $this->mockExplodes(2);
 
         $this->mockUserLogin()->seePageIs('/');
 
@@ -257,25 +323,24 @@ class LdapTest extends BrowserKitTest
 
     public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
     {
-        $role = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
+        $role = factory(Role::class)->create(['display_name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
         $this->asAdmin()->visit('/settings/roles/' . $role->id)
             ->see('ex-auth-a');
     }
 
     public function test_login_maps_roles_using_external_auth_ids_if_set()
     {
-        $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
-        $roleToNotReceive = factory(Role::class)->create(['name' => 'ldaptester-not-receive', 'display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
+        $roleToNotReceive = factory(Role::class)->create(['display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
 
         app('config')->set([
             'services.ldap.user_to_groups' => true,
             'services.ldap.group_attribute' => 'memberOf',
             'services.ldap.remove_from_groups' => true,
         ]);
-        $this->mockLdap->shouldReceive('connect')->times(2)->andReturn($this->resourceId);
-        $this->mockLdap->shouldReceive('setVersion')->times(2);
-        $this->mockLdap->shouldReceive('setOption')->times(4);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
+
+        $this->commonLdapMocks(1, 1, 3, 4, 3, 2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3)
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
@@ -287,9 +352,6 @@ class LdapTest extends BrowserKitTest
                     0 => "cn=ex-auth-a,ou=groups,dc=example,dc=com",
                 ]
             ]]);
-        $this->mockLdap->shouldReceive('bind')->times(5)->andReturn(true);
-        $this->mockEscapes(4);
-        $this->mockExplodes(2);
 
         $this->mockUserLogin()->seePageIs('/');
 
@@ -306,8 +368,8 @@ class LdapTest extends BrowserKitTest
 
     public function test_login_group_mapping_does_not_conflict_with_default_role()
     {
-        $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
-        $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
+        $roleToReceive2 = factory(Role::class)->create(['display_name' => 'LdapTester Second']);
         $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
 
         setting()->put('registration-role', $roleToReceive->id);
@@ -317,10 +379,9 @@ class LdapTest extends BrowserKitTest
             'services.ldap.group_attribute' => 'memberOf',
             'services.ldap.remove_from_groups' => true,
         ]);
-        $this->mockLdap->shouldReceive('connect')->times(2)->andReturn($this->resourceId);
-        $this->mockLdap->shouldReceive('setVersion')->times(2);
-        $this->mockLdap->shouldReceive('setOption')->times(5);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(5)
+
+        $this->commonLdapMocks(1, 1, 4, 5, 4, 6);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
@@ -333,9 +394,6 @@ class LdapTest extends BrowserKitTest
                     1 => "cn=ldaptester-second,ou=groups,dc=example,dc=com",
                 ]
             ]]);
-        $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
-        $this->mockEscapes(5);
-        $this->mockExplodes(6);
 
         $this->mockUserLogin()->seePageIs('/');
 
@@ -356,19 +414,15 @@ class LdapTest extends BrowserKitTest
             'services.ldap.display_name_attribute' => 'displayName'
         ]);
 
-        $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
-        $this->mockLdap->shouldReceive('setVersion')->once();
-        $this->mockLdap->shouldReceive('setOption')->times(4);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
+        $this->commonLdapMocks(1, 1, 2, 4, 2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
                 'cn' => [$this->mockUser->name],
                 'dn' => ['dc=test' . config('services.ldap.base_dn')],
-                'displayName' => 'displayNameAttribute'
+                'displayname' => 'displayNameAttribute'
             ]]);
-        $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
-        $this->mockEscapes(4);
 
         $this->mockUserLogin()
             ->seePageIs('/login')->see('Please enter an email to use for this account.');
@@ -386,18 +440,14 @@ class LdapTest extends BrowserKitTest
             'services.ldap.display_name_attribute' => 'displayName'
         ]);
 
-        $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
-        $this->mockLdap->shouldReceive('setVersion')->once();
-        $this->mockLdap->shouldReceive('setOption')->times(4);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
+        $this->commonLdapMocks(1, 1, 2, 4, 2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
                 'cn' => [$this->mockUser->name],
                 'dn' => ['dc=test' . config('services.ldap.base_dn')]
             ]]);
-        $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
-        $this->mockEscapes(4);
 
         $this->mockUserLogin()
             ->seePageIs('/login')->see('Please enter an email to use for this account.');
@@ -416,15 +466,12 @@ class LdapTest extends BrowserKitTest
         ]);
 
         // Standard mocks
-        $this->mockLdap->shouldReceive('setVersion')->once();
-        $this->mockLdap->shouldReceive('setOption')->times(2);
-        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)->andReturn(['count' => 1, 0 => [
+        $this->commonLdapMocks(0, 1, 1, 2, 1);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)->andReturn(['count' => 1, 0 => [
             'uid' => [$this->mockUser->name],
             'cn' => [$this->mockUser->name],
             'dn' => ['dc=test' . config('services.ldap.base_dn')]
         ]]);
-        $this->mockLdap->shouldReceive('bind')->times(3)->andReturn(true);
-        $this->mockEscapes(2);
 
         $this->mockLdap->shouldReceive('connect')->once()
             ->with($expectedHost, $expectedPort)->andReturn($this->resourceId);
@@ -446,4 +493,159 @@ class LdapTest extends BrowserKitTest
     {
         $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com', 'ldap.bookstack.com', 389);
     }
+
+    public function test_forgot_password_routes_inaccessible()
+    {
+        $resp = $this->get('/password/email');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->post('/password/email');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->get('/password/reset/abc123');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->post('/password/reset');
+        $this->assertPermissionError($resp);
+    }
+
+    public function test_user_invite_routes_inaccessible()
+    {
+        $resp = $this->get('/register/invite/abc123');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->post('/register/invite/abc123');
+        $this->assertPermissionError($resp);
+    }
+
+    public function test_user_register_routes_inaccessible()
+    {
+        $resp = $this->get('/register');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->post('/register');
+        $this->assertPermissionError($resp);
+    }
+
+    public function test_dump_user_details_option_works()
+    {
+        config()->set(['services.ldap.dump_user_details' => true]);
+
+        $this->commonLdapMocks(1, 1, 1, 1, 1);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
+            ->andReturn(['count' => 1, 0 => [
+                'uid' => [$this->mockUser->name],
+                'cn' => [$this->mockUser->name],
+                'dn' => ['dc=test' . config('services.ldap.base_dn')]
+            ]]);
+
+        $this->post('/login', [
+            'username' => $this->mockUser->name,
+            'password' => $this->mockUser->password,
+        ]);
+        $this->seeJsonStructure([
+            'details_from_ldap' => [],
+            'details_bookstack_parsed' => [],
+        ]);
+    }
+
+    public function test_ldap_attributes_can_be_binary_decoded_if_marked()
+    {
+        config()->set(['services.ldap.id_attribute' => 'BIN;uid']);
+        $ldapService = app()->make(LdapService::class);
+        $this->commonLdapMocks(1, 1, 1, 1, 1);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), ['cn', 'dn', 'uid', 'mail', 'cn'])
+            ->andReturn(['count' => 1, 0 => [
+                'uid' => [hex2bin('FFF8F7')],
+                'cn' => [$this->mockUser->name],
+                'dn' => ['dc=test' . config('services.ldap.base_dn')]
+            ]]);
+
+        $details = $ldapService->getUserDetails('test');
+        $this->assertEquals('fff8f7', $details['uid']);
+    }
+
+    public function test_new_ldap_user_login_with_already_used_email_address_shows_error_message_to_user()
+    {
+        $this->commonLdapMocks(1, 1, 2, 4, 2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
+            ->andReturn(['count' => 1, 0 => [
+                'uid' => [$this->mockUser->name],
+                'cn' => [$this->mockUser->name],
+                'dn' => ['dc=test' . config('services.ldap.base_dn')],
+                'mail' => '[email protected]',
+            ]], ['count' => 1, 0 => [
+                'uid' => ['Barry'],
+                'cn' => ['Scott'],
+                'dn' => ['dc=bscott' . config('services.ldap.base_dn')],
+                'mail' => '[email protected]',
+            ]]);
+
+        // First user login
+        $this->mockUserLogin()->seePageIs('/');
+
+        // Second user login
+        auth()->logout();
+        $this->post('/login', ['username' => 'bscott', 'password' => 'pass'])->followRedirects();
+
+        $this->see('A user with the email [email protected] already exists but with different credentials');
+    }
+
+    public function test_login_with_email_confirmation_required_maps_groups_but_shows_confirmation_screen()
+    {
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
+        $user = factory(User::class)->make();
+        setting()->put('registration-confirmation', 'true');
+
+        app('config')->set([
+            'services.ldap.user_to_groups' => true,
+            'services.ldap.group_attribute' => 'memberOf',
+            'services.ldap.remove_from_groups' => true,
+        ]);
+
+        $this->commonLdapMocks(1, 1, 3, 4, 3, 2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')
+            ->times(3)
+            ->andReturn(['count' => 1, 0 => [
+                'uid' => [$user->name],
+                'cn' => [$user->name],
+                'dn' => ['dc=test' . config('services.ldap.base_dn')],
+                'mail' => [$user->email],
+                'memberof' => [
+                    'count' => 1,
+                    0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
+                ]
+            ]]);
+
+        $this->mockUserLogin()->seePageIs('/register/confirm');
+        $this->seeInDatabase('users', [
+            'email' => $user->email,
+            'email_confirmed' => false,
+        ]);
+
+        $user  = User::query()->where('email', '=', $user->email)->first();
+        $this->seeInDatabase('role_user', [
+            'user_id' => $user->id,
+            'role_id' => $roleToReceive->id
+        ]);
+
+        $homePage = $this->get('/');
+        $homePage->assertRedirectedTo('/register/confirm/awaiting');
+    }
+
+    public function test_failed_logins_are_logged_when_message_configured()
+    {
+        $log = $this->withTestLogger();
+        config()->set(['logging.failed_login.message' => 'Failed login for %u']);
+
+        $this->commonLdapMocks(1, 1, 1, 1, 1);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
+            ->andReturn(['count' => 0]);
+
+        $this->post('/login', ['username' => 'timmyjenkins', 'password' => 'cattreedog']);
+        $this->assertTrue($log->hasWarningThatContains('Failed login for timmyjenkins'));
+    }
 }
diff --git a/tests/Auth/Saml2Test.php b/tests/Auth/Saml2Test.php
new file mode 100644 (file)
index 0000000..58c02b4
--- /dev/null
@@ -0,0 +1,388 @@
+<?php namespace Tests\Auth;
+
+use BookStack\Auth\Role;
+use BookStack\Auth\User;
+use Tests\TestCase;
+
+class Saml2Test extends TestCase
+{
+
+    public function setUp(): void
+    {
+        parent::setUp();
+        // Set default config for SAML2
+        config()->set([
+            'auth.method' => 'saml2',
+            'auth.defaults.guard' => 'saml2',
+            'saml2.name' => 'SingleSignOn-Testing',
+            'saml2.email_attribute' => 'email',
+            'saml2.display_name_attributes' => ['first_name', 'last_name'],
+            'saml2.external_id_attribute' => 'uid',
+            'saml2.user_to_groups' => false,
+            'saml2.group_attribute' => 'user_groups',
+            'saml2.remove_from_groups' => false,
+            'saml2.onelogin_overrides' => null,
+            'saml2.onelogin.idp.entityId' => 'http://saml.local/saml2/idp/metadata.php',
+            'saml2.onelogin.idp.singleSignOnService.url' => 'http://saml.local/saml2/idp/SSOService.php',
+            'saml2.onelogin.idp.singleLogoutService.url' => 'http://saml.local/saml2/idp/SingleLogoutService.php',
+            'saml2.autoload_from_metadata' => false,
+            'saml2.onelogin.idp.x509cert' => $this->testCert,
+            'saml2.onelogin.debug' => false,
+        ]);
+    }
+
+    public function test_metadata_endpoint_displays_xml_as_expected()
+    {
+        $req = $this->get('/saml2/metadata');
+        $req->assertHeader('Content-Type', 'text/xml; charset=UTF-8');
+        $req->assertSee('md:EntityDescriptor');
+        $req->assertSee(url('/saml2/acs'));
+    }
+
+    public function test_onelogin_overrides_functions_as_expected()
+    {
+        $json = '{"sp": {"assertionConsumerService": {"url": "https://example.com/super-cats"}}, "contactPerson": {"technical": {"givenName": "Barry Scott", "emailAddress": "[email protected]"}}}';
+        config()->set(['saml2.onelogin_overrides' => $json]);
+
+        $req = $this->get('/saml2/metadata');
+        $req->assertSee('https://example.com/super-cats');
+        $req->assertSee('md:ContactPerson');
+        $req->assertSee('<md:GivenName>Barry Scott</md:GivenName>');
+    }
+
+    public function test_login_option_shows_on_login_page()
+    {
+        $req = $this->get('/login');
+        $req->assertSeeText('SingleSignOn-Testing');
+        $req->assertElementExists('form[action$="/saml2/login"][method=POST] button');
+    }
+
+    public function test_login()
+    {
+        $req = $this->post('/saml2/login');
+        $redirect = $req->headers->get('location');
+        $this->assertStringStartsWith('http://saml.local/saml2/idp/SSOService.php', $redirect, 'Login redirects to SSO location');
+
+        config()->set(['saml2.onelogin.strict' => false]);
+        $this->assertFalse($this->isAuthenticated());
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+
+            $acsPost = $this->post('/saml2/acs');
+            $acsPost->assertRedirect('/');
+            $this->assertTrue($this->isAuthenticated());
+            $this->assertDatabaseHas('users', [
+                'email' => '[email protected]',
+                'external_auth_id' => 'user',
+                'email_confirmed' => false,
+                'name' => 'Barry Scott'
+            ]);
+
+        });
+    }
+
+    public function test_group_role_sync_on_login()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.user_to_groups' => true,
+            'saml2.remove_from_groups' => false,
+        ]);
+
+        $memberRole = factory(Role::class)->create(['external_auth_id' => 'member']);
+        $adminRole = Role::getSystemRole('admin');
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () use ($memberRole, $adminRole) {
+            $acsPost = $this->post('/saml2/acs');
+            $user = User::query()->where('external_auth_id', '=', 'user')->first();
+
+            $userRoleIds = $user->roles()->pluck('id');
+            $this->assertContains($memberRole->id, $userRoleIds, 'User was assigned to member role');
+            $this->assertContains($adminRole->id, $userRoleIds, 'User was assigned to admin role');
+        });
+    }
+
+    public function test_group_role_sync_removal_option_works_as_expected()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.user_to_groups' => true,
+            'saml2.remove_from_groups' => true,
+        ]);
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+            $acsPost = $this->post('/saml2/acs');
+            $user = User::query()->where('external_auth_id', '=', 'user')->first();
+
+            $randomRole = factory(Role::class)->create(['external_auth_id' => 'random']);
+            $user->attachRole($randomRole);
+            $this->assertContains($randomRole->id, $user->roles()->pluck('id'));
+
+            auth()->logout();
+            $acsPost = $this->post('/saml2/acs');
+            $this->assertNotContains($randomRole->id, $user->roles()->pluck('id'));
+        });
+    }
+
+    public function test_logout_link_directs_to_saml_path()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+        ]);
+
+        $resp = $this->actingAs($this->getEditor())->get('/');
+        $resp->assertElementExists('a[href$="/saml2/logout"]');
+        $resp->assertElementContains('a[href$="/saml2/logout"]', 'Logout');
+    }
+
+    public function test_logout_sls_flow()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+        ]);
+
+        $handleLogoutResponse = function () {
+            $this->assertTrue($this->isAuthenticated());
+
+            $req = $this->get('/saml2/sls');
+            $req->assertRedirect('/');
+            $this->assertFalse($this->isAuthenticated());
+        };
+
+        $loginAndStartLogout = function () use ($handleLogoutResponse) {
+            $this->post('/saml2/acs');
+
+            $req = $this->get('/saml2/logout');
+            $redirect = $req->headers->get('location');
+            $this->assertStringStartsWith('http://saml.local/saml2/idp/SingleLogoutService.php', $redirect);
+            $this->withGet(['SAMLResponse' => $this->sloResponseData], $handleLogoutResponse);
+        };
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], $loginAndStartLogout);
+    }
+
+    public function test_logout_sls_flow_when_sls_not_configured()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.onelogin.idp.singleLogoutService.url' => null,
+        ]);
+
+        $loginAndStartLogout = function () {
+            $this->post('/saml2/acs');
+
+            $req = $this->get('/saml2/logout');
+            $req->assertRedirect('/');
+            $this->assertFalse($this->isAuthenticated());
+        };
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], $loginAndStartLogout);
+    }
+
+    public function test_dump_user_details_option_works()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.dump_user_details' => true,
+        ]);
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+            $acsPost = $this->post('/saml2/acs');
+            $acsPost->assertJsonStructure([
+                'id_from_idp',
+                'attrs_from_idp' => [],
+                'attrs_after_parsing' => [],
+            ]);
+        });
+    }
+
+    public function test_saml_routes_are_only_active_if_saml_enabled()
+    {
+        config()->set(['auth.method' => 'standard']);
+        $getRoutes = ['/logout', '/metadata', '/sls'];
+        foreach ($getRoutes as $route) {
+            $req = $this->get('/saml2' . $route);
+            $this->assertPermissionError($req);
+        }
+
+        $postRoutes = ['/login', '/acs'];
+        foreach ($postRoutes as $route) {
+            $req = $this->post('/saml2' . $route);
+            $this->assertPermissionError($req);
+        }
+    }
+
+    public function test_forgot_password_routes_inaccessible()
+    {
+        $resp = $this->get('/password/email');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->post('/password/email');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->get('/password/reset/abc123');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->post('/password/reset');
+        $this->assertPermissionError($resp);
+    }
+
+    public function test_standard_login_routes_inaccessible()
+    {
+        $resp = $this->post('/login');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->get('/logout');
+        $this->assertPermissionError($resp);
+    }
+
+    public function test_user_invite_routes_inaccessible()
+    {
+        $resp = $this->get('/register/invite/abc123');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->post('/register/invite/abc123');
+        $this->assertPermissionError($resp);
+    }
+
+    public function test_user_register_routes_inaccessible()
+    {
+        $resp = $this->get('/register');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->post('/register');
+        $this->assertPermissionError($resp);
+    }
+
+    public function test_email_domain_restriction_active_on_new_saml_login()
+    {
+        $this->setSettings([
+            'registration-restrict' => 'testing.com'
+        ]);
+        config()->set([
+            'saml2.onelogin.strict' => false,
+        ]);
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+            $acsPost = $this->post('/saml2/acs');
+            $acsPost->assertRedirect('/login');
+            $errorMessage = session()->get('error');
+            $this->assertStringContainsString('That email domain does not have access to this application', $errorMessage);
+            $this->assertDatabaseMissing('users', ['email' => '[email protected]']);
+        });
+    }
+
+    public function test_group_sync_functions_when_email_confirmation_required()
+    {
+        setting()->put('registration-confirmation', 'true');
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.user_to_groups' => true,
+            'saml2.remove_from_groups' => false,
+        ]);
+
+        $memberRole = factory(Role::class)->create(['external_auth_id' => 'member']);
+        $adminRole = Role::getSystemRole('admin');
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () use ($memberRole, $adminRole) {
+            $acsPost = $this->followingRedirects()->post('/saml2/acs');
+
+            $this->assertEquals('http://localhost/register/confirm', url()->current());
+            $acsPost->assertSee('Please check your email and click the confirmation button to access BookStack.');
+            $user = User::query()->where('external_auth_id', '=', 'user')->first();
+
+            $userRoleIds = $user->roles()->pluck('id');
+            $this->assertContains($memberRole->id, $userRoleIds, 'User was assigned to member role');
+            $this->assertContains($adminRole->id, $userRoleIds, 'User was assigned to admin role');
+            $this->assertTrue($user->email_confirmed == false, 'User email remains unconfirmed');
+        });
+
+        $homeGet = $this->get('/');
+        $homeGet->assertRedirect('/register/confirm/awaiting');
+    }
+
+    public function test_login_where_existing_non_saml_user_shows_warning()
+    {
+        $this->post('/saml2/login');
+        config()->set(['saml2.onelogin.strict' => false]);
+
+        // Make the user pre-existing in DB with different auth_id
+        User::query()->forceCreate([
+            'email' => '[email protected]',
+            'external_auth_id' => 'old_system_user_id',
+            'email_confirmed' => false,
+            'name' => 'Barry Scott'
+        ]);
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+            $acsPost = $this->post('/saml2/acs');
+            $acsPost->assertRedirect('/login');
+            $this->assertFalse($this->isAuthenticated());
+            $this->assertDatabaseHas('users', [
+                'email' => '[email protected]',
+                'external_auth_id' => 'old_system_user_id',
+            ]);
+
+            $loginGet = $this->get('/login');
+            $loginGet->assertSee("A user with the email [email protected] already exists but with different credentials");
+        });
+    }
+
+    protected function withGet(array $options, callable $callback)
+    {
+        return $this->withGlobal($_GET, $options, $callback);
+    }
+
+    protected function withPost(array $options, callable $callback)
+    {
+        return $this->withGlobal($_POST, $options, $callback);
+    }
+
+    protected function withGlobal(array &$global, array $options, callable $callback)
+    {
+        $original = [];
+        foreach ($options as $key => $val) {
+            $original[$key] = $global[$key] ?? null;
+            $global[$key] = $val;
+        }
+
+        $callback();
+
+        foreach ($options as $key => $val) {
+            $val = $original[$key];
+            if ($val) {
+                $global[$key] = $val;
+            } else {
+                unset($global[$key]);
+            }
+        }
+    }
+
+    /**
+     * The post data for a callback for single-sign-in.
+     * Provides the following attributes:
+     * array:5 [
+        "uid" => array:1 [
+            0 => "user"
+        ]
+        "first_name" => array:1 [
+            0 => "Barry"
+        ]
+        "last_name" => array:1 [
+            0 => "Scott"
+        ]
+        "email" => array:1 [
+            0 => "[email protected]"
+        ]
+        "user_groups" => array:2 [
+            0 => "member"
+            1 => "admin"
+        ]
+    ]
+     */
+    protected $acsPostData = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfNGRkNDU2NGRjNzk0MDYxZWYxYmFhMDQ2N2Q3OTAyOGNlZDNjZTU0YmVlIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxOS0xMS0xN1QxNzo1MzozOVoiIERlc3RpbmF0aW9uPSJodHRwOi8vYm9va3N0YWNrLmxvY2FsL3NhbWwyL2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl82YTBmNGYzOTkzMDQwZjE5ODdmZDM3MDY4YjUyOTYyMjlhZDUzNjFjIj48c2FtbDpJc3N1ZXI+aHR0cDovL3NhbWwubG9jYWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfNGRkNDU2NGRjNzk0MDYxZWYxYmFhMDQ2N2Q3OTAyOGNlZDNjZTU0YmVlIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+dm1oL1M3NU5mK2crZWNESkN6QWJaV0tKVmx1ZzdCZnNDKzlhV05lSXJlUT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+dnJhZ0tKWHNjVm5UNjJFaEk3bGk4MERUWHNOTGJOc3lwNWZ2QnU4WjFYSEtFUVA3QWpPNkcxcVBwaGpWQ2dRMzd6TldVVTZvUytQeFA3UDlHeG5xL3hKejRUT3lHcHJ5N1RoK2pIcHc0YWVzQTdrTmp6VU51UmU2c1ltWTlrRXh2VjMvTmJRZjROMlM2Y2RhRHIzWFRodllVVDcxYzQwNVVHOFJpQjJaY3liWHIxZU1yWCtXUDBnU2Qrc0F2RExqTjBJc3pVWlVUNThadFpEVE1ya1ZGL0pIbFBFQ04vVW1sYVBBeitTcUJ4c25xTndZK1oxYUt3MnlqeFRlNnUxM09Kb29OOVN1REowNE0rK2F3RlY3NkI4cXEyTzMxa3FBbDJibm1wTGxtTWdRNFEraUlnL3dCc09abTV1clphOWJObDNLVEhtTVBXbFpkbWhsLzgvMy9IT1RxN2thWGs3cnlWRHRLcFlsZ3FUajNhRUpuL0dwM2o4SFp5MUVialRiOTRRT1ZQMG5IQzB1V2hCaE13TjdzVjFrUSsxU2NjUlpUZXJKSGlSVUQvR0srTVg3M0YrbzJVTFRIL1Z6Tm9SM2o4N2hOLzZ1UC9JeG5aM1RudGR1MFZPZS9ucEdVWjBSMG9SWFhwa2JTL2poNWk1ZjU0RXN4eXZ1VEM5NHdKaEM8L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFYXpDQ0F0T2dBd0lCQWdJVWU3YTA4OENucjRpem1ybkJFbng1cTNIVE12WXdEUVlKS29aSWh2Y05BUUVMQlFBd1JURUxNQWtHQTFVRUJoTUNSMEl4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTUdFbHVkR1Z5Ym1WMElGZHBaR2RwZEhNZ1VIUjVJRXgwWkRBZUZ3MHhPVEV4TVRZeE1qRTNNVFZhRncweU9URXhNVFV4TWpFM01UVmFNRVV4Q3pBSkJnTlZCQVlUQWtkQ01STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdnZ0dpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCandBd2dnR0tBb0lCZ1FEekxlOUZmZHlwbFR4SHA0U3VROWdRdFpUM3QrU0RmdkVMNzJwcENmRlp3NytCNXM1Qi9UNzNhWHBvUTNTNTNwR0kxUklXQ2dlMmlDVVEydHptMjdhU05IMGl1OWFKWWNVUVovUklUcWQwYXl5RGtzMU5BMlBUM1RXNnQzbTdLVjVyZTRQME5iK1lEZXV5SGRreitqY010cG44Q21Cb1QwSCtza2hhMGhpcUlOa2prUlBpSHZMSFZHcCt0SFVFQS9JNm1ONGFCL1VFeFNUTHM3OU5zTFVmdGVxcXhlOSt0dmRVYVRveURQcmhQRmpPTnMrOU5LQ2t6SUM2dmN2N0o2QXR1S0c2bkVUK3pCOXlPV2d0R1lRaWZYcVFBMnk1ZEw4MUJCMHE1dU1hQkxTMnBxM2FQUGp6VTJGMytFeXNqeVNXVG5Da2ZrN0M1U3NDWFJ1OFErVTk1dHVucE5md2Y1b2xFNldhczQ4Tk1NK1B3VjdpQ05NUGtOemxscTZQQ2lNK1A4RHJNU2N6elVaWlFVU3Y2ZFN3UENvK1lTVmltRU0wT2czWEpUaU5oUTVBTmxhSW42Nkt3NWdmb0JmdWlYbXlJS2lTRHlBaURZbUZhZjQzOTV3V3dMa1RSK2N3OFdmamFIc3dLWlRvbW4xTVIzT0pzWTJVSjBlUkJZTStZU3NDQXdFQUFhTlRNRkV3SFFZRFZSME9CQllFRkltcDJDWUNHZmNiN3c5MUgvY1NoVENrWHdSL01COEdBMVVkSXdRWU1CYUFGSW1wMkNZQ0dmY2I3dzkxSC9jU2hUQ2tYd1IvTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3RFFZSktvWklodmNOQVFFTEJRQURnZ0dCQUErZy9DN3VMOWxuK1crcUJrbkxXODFrb2pZZmxnUEsxSTFNSEl3bk12bC9aVEhYNGRSWEtEcms3S2NVcTFLanFhak5WNjZmMWNha3AwM0lpakJpTzBYaTFnWFVaWUxvQ2lOR1V5eXA5WGxvaUl5OVh3MlBpV25ydzAreVp5dlZzc2JlaFhYWUpsNFJpaEJqQld1bDlSNHdNWUxPVVNKRGUyV3hjVUJoSm54eU5ScytQMHhMU1FYNkIybjZueG9Ea280cDA3czhaS1hRa2VpWjJpd0ZkVHh6UmtHanRoTVV2NzA0bnpzVkdCVDBEQ1B0ZlNhTzVLSlpXMXJDczN5aU10aG5CeHE0cUVET1FKRklsKy9MRDcxS2JCOXZaY1c1SnVhdnpCRm1rS0dOcm8vNkcxSTdlbDQ2SVI0d2lqVHlORkNZVXVEOWR0aWduTm1wV3ROOE9XK3B0aUwvanRUeVNXdWtqeXMwcyt2TG44M0NWdmpCMGRKdFZBSVlPZ1hGZEl1aWk2Nmdjend3TS9MR2lPRXhKbjBkVE56c0ovSVlocHhMNEZCRXVQMHBza1kwbzBhVWxKMkxTMmord1NRVFJLc0JnTWp5clVyZWtsZTJPRFN0U3RuM2VhYmpJeDAvRkhscEZyMGpOSW0vb01QN2t3anRVWDR6YU5lNDdRSTRHZz09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9Il82ODQyZGY5YzY1OWYxM2ZlNTE5NmNkOWVmNmMyZjAyODM2NGFlOTQzYjEiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE5LTExLTE3VDE3OjUzOjM5WiI+PHNhbWw6SXNzdWVyPmh0dHA6Ly9zYW1sLmxvY2FsL3NhbWwyL2lkcC9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPgogIDxkczpSZWZlcmVuY2UgVVJJPSIjXzY4NDJkZjljNjU5ZjEzZmU1MTk2Y2Q5ZWY2YzJmMDI4MzY0YWU5NDNiMSI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8+PGRzOkRpZ2VzdFZhbHVlPmtyYjV3NlM4dG9YYy9lU3daUFVPQnZRem4zb3M0SkFDdXh4ckpreHBnRnc9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPjJxcW1Ba3hucXhOa3N5eXh5dnFTVDUxTDg5VS9ZdHpja2t1ekF4ci9hQ1JTK1NPRzg1YkFNWm8vU3puc3d0TVlBYlFRQ0VGb0R1amdNdlpzSFl3NlR2dmFHanlXWUpRNVZyYWhlemZaSWlCVUU0NHBtWGFrOCswV0l0WTVndnBGSXhxWFZaRmdFUkt2VExmZVFCMzhkMVZQc0ZVZ0RYdXQ4VS9Qdm43dXZwdXZjVXorMUUyOUVKR2FZL0dndnhUN0tyWU9SQTh3SitNdVRzUVZtanNlUnhveVJTejA4TmJ3ZTJIOGpXQnpFWWNxWWwyK0ZnK2hwNWd0S216VmhLRnBkNXZBNjdBSXo1NXN0QmNHNSswNHJVaWpFSzRzci9xa0x5QmtKQjdLdkwzanZKcG8zQjhxYkxYeXhLb1dSSmRnazhKNHMvTVp1QWk3QWUxUXNTTjl2Z3ZTdVRlc0VCUjVpSHJuS1lrbEpRWXNrbUQzbSsremE4U1NRbnBlM0UzYUZBY3p6cElUdUQ4YkFCWmRqcUk2TkhrSmFRQXBmb0hWNVQrZ244ejdUTWsrSStUU2JlQURubUxCS3lnMHRabW10L0ZKbDV6eWowVmxwc1dzTVM2OVE2bUZJVStqcEhSanpOb2FLMVM1dlQ3ZW1HbUhKSUp0cWlOdXJRN0tkQlBJPC9kczpTaWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRWF6Q0NBdE9nQXdJQkFnSVVlN2EwODhDbnI0aXptcm5CRW54NXEzSFRNdll3RFFZSktvWklodmNOQVFFTEJRQXdSVEVMTUFrR0ExVUVCaE1DUjBJeEV6QVJCZ05WQkFnTUNsTnZiV1V0VTNSaGRHVXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpEQWVGdzB4T1RFeE1UWXhNakUzTVRWYUZ3MHlPVEV4TVRVeE1qRTNNVFZhTUVVeEN6QUpCZ05WQkFZVEFrZENNUk13RVFZRFZRUUlEQXBUYjIxbExWTjBZWFJsTVNFd0h3WURWUVFLREJoSmJuUmxjbTVsZENCWGFXUm5hWFJ6SUZCMGVTQk1kR1F3Z2dHaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQmp3QXdnZ0dLQW9JQmdRRHpMZTlGZmR5cGxUeEhwNFN1UTlnUXRaVDN0K1NEZnZFTDcycHBDZkZadzcrQjVzNUIvVDczYVhwb1EzUzUzcEdJMVJJV0NnZTJpQ1VRMnR6bTI3YVNOSDBpdTlhSlljVVFaL1JJVHFkMGF5eURrczFOQTJQVDNUVzZ0M203S1Y1cmU0UDBOYitZRGV1eUhka3oramNNdHBuOENtQm9UMEgrc2toYTBoaXFJTmtqa1JQaUh2TEhWR3ArdEhVRUEvSTZtTjRhQi9VRXhTVExzNzlOc0xVZnRlcXF4ZTkrdHZkVWFUb3lEUHJoUEZqT05zKzlOS0NreklDNnZjdjdKNkF0dUtHNm5FVCt6Qjl5T1dndEdZUWlmWHFRQTJ5NWRMODFCQjBxNXVNYUJMUzJwcTNhUFBqelUyRjMrRXlzanlTV1RuQ2tmazdDNVNzQ1hSdThRK1U5NXR1bnBOZndmNW9sRTZXYXM0OE5NTStQd1Y3aUNOTVBrTnpsbHE2UENpTStQOERyTVNjenpVWlpRVVN2NmRTd1BDbytZU1ZpbUVNME9nM1hKVGlOaFE1QU5sYUluNjZLdzVnZm9CZnVpWG15SUtpU0R5QWlEWW1GYWY0Mzk1d1d3TGtUUitjdzhXZmphSHN3S1pUb21uMU1SM09Kc1kyVUowZVJCWU0rWVNzQ0F3RUFBYU5UTUZFd0hRWURWUjBPQkJZRUZJbXAyQ1lDR2ZjYjd3OTFIL2NTaFRDa1h3Ui9NQjhHQTFVZEl3UVlNQmFBRkltcDJDWUNHZmNiN3c5MUgvY1NoVENrWHdSL01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBK2cvQzd1TDlsbitXK3FCa25MVzgxa29qWWZsZ1BLMUkxTUhJd25NdmwvWlRIWDRkUlhLRHJrN0tjVXExS2pxYWpOVjY2ZjFjYWtwMDNJaWpCaU8wWGkxZ1hVWllMb0NpTkdVeXlwOVhsb2lJeTlYdzJQaVducncwK3laeXZWc3NiZWhYWFlKbDRSaWhCakJXdWw5UjR3TVlMT1VTSkRlMld4Y1VCaEpueHlOUnMrUDB4TFNRWDZCMm42bnhvRGtvNHAwN3M4WktYUWtlaVoyaXdGZFR4elJrR2p0aE1VdjcwNG56c1ZHQlQwRENQdGZTYU81S0paVzFyQ3MzeWlNdGhuQnhxNHFFRE9RSkZJbCsvTEQ3MUtiQjl2WmNXNUp1YXZ6QkZta0tHTnJvLzZHMUk3ZWw0NklSNHdpalR5TkZDWVV1RDlkdGlnbk5tcFd0TjhPVytwdGlML2p0VHlTV3VranlzMHMrdkxuODNDVnZqQjBkSnRWQUlZT2dYRmRJdWlpNjZnY3p3d00vTEdpT0V4Sm4wZFROenNKL0lZaHB4TDRGQkV1UDBwc2tZMG8wYVVsSjJMUzJqK3dTUVRSS3NCZ01qeXJVcmVrbGUyT0RTdFN0bjNlYWJqSXgwL0ZIbHBGcjBqTkltL29NUDdrd2p0VVg0emFOZTQ3UUk0R2c9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaHR0cDovL2Jvb2tzdGFjay5sb2NhbC9zYW1sMi9tZXRhZGF0YSIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDp0cmFuc2llbnQiPl8yYzdhYjg2ZWI4ZjFkMTA2MzQ0M2YyMTljYzU4NjhmZjY2NzA4OTEyZTM8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTktMTEtMTdUMTc6NTg6MzlaIiBSZWNpcGllbnQ9Imh0dHA6Ly9ib29rc3RhY2subG9jYWwvc2FtbDIvYWNzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzZhMGY0ZjM5OTMwNDBmMTk4N2ZkMzcwNjhiNTI5NjIyOWFkNTM2MWMiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxOS0xMS0xN1QxNzo1MzowOVoiIE5vdE9uT3JBZnRlcj0iMjAxOS0xMS0xN1QxNzo1ODozOVoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL2Jvb2tzdGFjay5sb2NhbC9zYW1sMi9tZXRhZGF0YTwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTktMTEtMTdUMTc6NTM6MzlaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDE5LTExLTE4VDAxOjUzOjM5WiIgU2Vzc2lvbkluZGV4PSJfNGZlN2MwZDE1NzJkNjRiMjdmOTMwYWE2ZjIzNmE2ZjQyZTkzMDkwMWNjIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnVzZXI8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZmlyc3RfbmFtZSIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+QmFycnk8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibGFzdF9uYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5TY290dDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlbWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlckBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1c2VyX2dyb3VwcyIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+bWVtYmVyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmFkbWluPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+';
+
+    protected $sloResponseData = 'fZHRa8IwEMb/lZJ3bdJa04a2MOYYglOY4sNe5JKms9gmpZfC/vxF3ZjC8OXgLvl938ddjtC1vVjZTzu6d429NaiDr641KC5PBRkHIyxgg8JAp1E4JbZPbysRTanoB+ussi25QR4TgKgH11hDguWiIIeawTxOaK1iPYt5XcczHUlJeVRlMklBJjOuM1qDVCTY6wE9WRAv5HHEUS8NOjDOjyjLJoxNGN+xVESpSNgHCRYaXWPAXaijc70IQ2ntyUPqNG2tgjY8Z45CbNFLmt8V7GxBNuuX1eZ1uT7EcZJKAE4TJhXPaMxlVlFffPKKJnXE5ryusoiU+VlMXJIN5Y/feXRn1VR92GkHFTiY9sc+D2+p/HqRrQM34n33bCsd7KEd9eMd4+W32I5KaUQSlleHP9Hwv6uX3w==';
+
+    protected $testCert = 'MIIEazCCAtOgAwIBAgIUe7a088Cnr4izmrnBEnx5q3HTMvYwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xOTExMTYxMjE3MTVaFw0yOTExMTUxMjE3MTVaMEUxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDzLe9FfdyplTxHp4SuQ9gQtZT3t+SDfvEL72ppCfFZw7+B5s5B/T73aXpoQ3S53pGI1RIWCge2iCUQ2tzm27aSNH0iu9aJYcUQZ/RITqd0ayyDks1NA2PT3TW6t3m7KV5re4P0Nb+YDeuyHdkz+jcMtpn8CmBoT0H+skha0hiqINkjkRPiHvLHVGp+tHUEA/I6mN4aB/UExSTLs79NsLUfteqqxe9+tvdUaToyDPrhPFjONs+9NKCkzIC6vcv7J6AtuKG6nET+zB9yOWgtGYQifXqQA2y5dL81BB0q5uMaBLS2pq3aPPjzU2F3+EysjySWTnCkfk7C5SsCXRu8Q+U95tunpNfwf5olE6Was48NMM+PwV7iCNMPkNzllq6PCiM+P8DrMSczzUZZQUSv6dSwPCo+YSVimEM0Og3XJTiNhQ5ANlaIn66Kw5gfoBfuiXmyIKiSDyAiDYmFaf4395wWwLkTR+cw8WfjaHswKZTomn1MR3OJsY2UJ0eRBYM+YSsCAwEAAaNTMFEwHQYDVR0OBBYEFImp2CYCGfcb7w91H/cShTCkXwR/MB8GA1UdIwQYMBaAFImp2CYCGfcb7w91H/cShTCkXwR/MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggGBAA+g/C7uL9ln+W+qBknLW81kojYflgPK1I1MHIwnMvl/ZTHX4dRXKDrk7KcUq1KjqajNV66f1cakp03IijBiO0Xi1gXUZYLoCiNGUyyp9XloiIy9Xw2PiWnrw0+yZyvVssbehXXYJl4RihBjBWul9R4wMYLOUSJDe2WxcUBhJnxyNRs+P0xLSQX6B2n6nxoDko4p07s8ZKXQkeiZ2iwFdTxzRkGjthMUv704nzsVGBT0DCPtfSaO5KJZW1rCs3yiMthnBxq4qEDOQJFIl+/LD71KbB9vZcW5JuavzBFmkKGNro/6G1I7el46IR4wijTyNFCYUuD9dtignNmpWtN8OW+ptiL/jtTySWukjys0s+vLn83CVvjB0dJtVAIYOgXFdIuii66gczwwM/LGiOExJn0dTNzsJ/IYhpxL4FBEuP0pskY0o0aUlJ2LS2j+wSQTRKsBgMjyrUrekle2ODStStn3eabjIx0/FHlpFr0jNIm/oMP7kwjtUX4zaNe47QI4Gg==';
+}
index 526c0e199a184cf7ee0a7f74c49fcea0bf42cbba..d448b567e5ef1462bf168500bc2dc6b8ed1df2f2 100644 (file)
@@ -1,20 +1,26 @@
-<?php namespace Tests;
+<?php namespace Tests\Auth;
+
+use BookStack\Auth\User;
+use DB;
+use Laravel\Socialite\Contracts\Factory;
+use Laravel\Socialite\Contracts\Provider;
+use Mockery;
+use Tests\TestCase;
 
 class SocialAuthTest extends TestCase
 {
 
     public function test_social_registration()
     {
-        // http://docs.mockery.io/en/latest/reference/startup_methods.html
-        $user = factory(\BookStack\Auth\User::class)->make();
+        $user = factory(User::class)->make();
 
         $this->setSettings(['registration-enabled' => 'true']);
         config(['GOOGLE_APP_ID' => 'abc123', 'GOOGLE_APP_SECRET' => '123abc', 'APP_URL' => 'http://localhost']);
 
-        $mockSocialite = \Mockery::mock('Laravel\Socialite\Contracts\Factory');
-        $this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite;
-        $mockSocialDriver = \Mockery::mock('Laravel\Socialite\Contracts\Provider');
-        $mockSocialUser = \Mockery::mock('\Laravel\Socialite\Contracts\User');
+        $mockSocialite = Mockery::mock(Factory::class);
+        $this->app[Factory::class] = $mockSocialite;
+        $mockSocialDriver = Mockery::mock(Provider::class);
+        $mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);
 
         $mockSocialite->shouldReceive('driver')->twice()->with('google')->andReturn($mockSocialDriver);
         $mockSocialDriver->shouldReceive('redirect')->once()->andReturn(redirect('/'));
@@ -40,10 +46,10 @@ class SocialAuthTest extends TestCase
             'APP_URL' => 'http://localhost'
         ]);
 
-        $mockSocialite = \Mockery::mock('Laravel\Socialite\Contracts\Factory');
-        $this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite;
-        $mockSocialDriver = \Mockery::mock('Laravel\Socialite\Contracts\Provider');
-        $mockSocialUser = \Mockery::mock('\Laravel\Socialite\Contracts\User');
+        $mockSocialite = Mockery::mock(Factory::class);
+        $this->app[Factory::class] = $mockSocialite;
+        $mockSocialDriver = Mockery::mock(Provider::class);
+        $mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);
 
         $mockSocialUser->shouldReceive('getId')->twice()->andReturn('logintest123');
 
@@ -70,7 +76,7 @@ class SocialAuthTest extends TestCase
 
 
         // Test social callback with matching social account
-        \DB::table('social_accounts')->insert([
+        DB::table('social_accounts')->insert([
             'user_id' => $this->getAdmin()->id,
             'driver' => 'github',
             'driver_id' => 'logintest123'
@@ -86,11 +92,11 @@ class SocialAuthTest extends TestCase
             'APP_URL' => 'http://localhost'
         ]);
 
-        $user = factory(\BookStack\Auth\User::class)->make();
-        $mockSocialite = \Mockery::mock('Laravel\Socialite\Contracts\Factory');
-        $this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite;
-        $mockSocialDriver = \Mockery::mock('Laravel\Socialite\Contracts\Provider');
-        $mockSocialUser = \Mockery::mock('\Laravel\Socialite\Contracts\User');
+        $user = factory(User::class)->make();
+        $mockSocialite = Mockery::mock(Factory::class);
+        $this->app[Factory::class] = $mockSocialite;
+        $mockSocialDriver = Mockery::mock(Provider::class);
+        $mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);
 
         $mockSocialUser->shouldReceive('getId')->times(4)->andReturn(1);
         $mockSocialUser->shouldReceive('getEmail')->times(2)->andReturn($user->email);
@@ -125,11 +131,11 @@ class SocialAuthTest extends TestCase
             'APP_URL' => 'http://localhost', 'services.google.auto_register' => true, 'services.google.auto_confirm' => true
         ]);
 
-        $user = factory(\BookStack\Auth\User::class)->make();
-        $mockSocialite = \Mockery::mock('Laravel\Socialite\Contracts\Factory');
-        $this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite;
-        $mockSocialDriver = \Mockery::mock('Laravel\Socialite\Contracts\Provider');
-        $mockSocialUser = \Mockery::mock('\Laravel\Socialite\Contracts\User');
+        $user = factory(User::class)->make();
+        $mockSocialite = Mockery::mock(Factory::class);
+        $this->app[Factory::class] = $mockSocialite;
+        $mockSocialDriver = Mockery::mock(Provider::class);
+        $mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);
 
         $mockSocialUser->shouldReceive('getId')->times(3)->andReturn(1);
         $mockSocialUser->shouldReceive('getEmail')->times(2)->andReturn($user->email);
@@ -156,4 +162,32 @@ class SocialAuthTest extends TestCase
         $this->assertStringContainsString('prompt=select_account', $resp->headers->get('Location'));
     }
 
+    public function test_social_registration_with_no_name_uses_email_as_name()
+    {
+        $user = factory(User::class)->make(['email' => '[email protected]']);
+
+        $this->setSettings(['registration-enabled' => 'true']);
+        config(['GITHUB_APP_ID' => 'abc123', 'GITHUB_APP_SECRET' => '123abc', 'APP_URL' => 'http://localhost']);
+
+        $mockSocialite = Mockery::mock(Factory::class);
+        $this->app[Factory::class] = $mockSocialite;
+        $mockSocialDriver = Mockery::mock(Provider::class);
+        $mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);
+
+        $mockSocialite->shouldReceive('driver')->twice()->with('github')->andReturn($mockSocialDriver);
+        $mockSocialDriver->shouldReceive('redirect')->once()->andReturn(redirect('/'));
+        $mockSocialDriver->shouldReceive('user')->once()->andReturn($mockSocialUser);
+
+        $mockSocialUser->shouldReceive('getId')->twice()->andReturn(1);
+        $mockSocialUser->shouldReceive('getEmail')->twice()->andReturn($user->email);
+        $mockSocialUser->shouldReceive('getName')->once()->andReturn('');
+        $mockSocialUser->shouldReceive('getAvatar')->once()->andReturn('avatar_placeholder');
+
+        $this->get('/register/service/github');
+        $this->get('/login/service/github/callback');
+        $this->assertDatabaseHas('users', ['name' => 'nonameuser', 'email' => $user->email]);
+        $user = $user->whereEmail($user->email)->first();
+        $this->assertDatabaseHas('social_accounts', ['user_id' => $user->id]);
+    }
+
 }
index d200134a5548707b52966d445d13dff1ef096316..f2a1d0e78177e94330515b37eb924e19770c9c95 100644 (file)
@@ -1,4 +1,4 @@
-<?php namespace Tests;
+<?php namespace Tests\Auth;
 
 
 use BookStack\Auth\Access\UserInviteService;
@@ -8,6 +8,7 @@ use Carbon\Carbon;
 use DB;
 use Illuminate\Support\Str;
 use Notification;
+use Tests\TestCase;
 
 class UserInviteTest extends TestCase
 {
index b81afe31106025352a3d5c50a23b2162b1ae6d5f..6c332a98469c4f5141972ee0b7e562b2f74b4ab2 100644 (file)
@@ -1,10 +1,16 @@
 <?php namespace Tests;
 
-use BookStack\Entities\Entity;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
 use BookStack\Auth\Role;
 use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Entities\Models\Page;
 use BookStack\Settings\SettingService;
+use DB;
 use Illuminate\Contracts\Console\Kernel;
+use Illuminate\Foundation\Application;
 use Illuminate\Foundation\Testing\DatabaseTransactions;
 use Laravel\BrowserKitTesting\TestCase;
 use Symfony\Component\DomCrawler\Crawler;
@@ -23,14 +29,14 @@ abstract class BrowserKitTest extends TestCase
 
     public function tearDown() : void
     {
-        \DB::disconnect();
+        DB::disconnect();
         parent::tearDown();
     }
 
     /**
      * Creates the application.
      *
-     * @return \Illuminate\Foundation\Application
+     * @return Application
      */
     public function createApplication()
     {
@@ -47,7 +53,7 @@ abstract class BrowserKitTest extends TestCase
      */
     public function getNormalUser()
     {
-        return \BookStack\Auth\User::where('system_name', '=', null)->get()->last();
+        return User::where('system_name', '=', null)->get()->last();
     }
 
     /**
@@ -64,23 +70,21 @@ abstract class BrowserKitTest extends TestCase
 
     /**
      * Create a group of entities that belong to a specific user.
-     * @param $creatorUser
-     * @param $updaterUser
-     * @return array
      */
-    protected function createEntityChainBelongingToUser($creatorUser, $updaterUser = false)
+    protected function createEntityChainBelongingToUser(User $creatorUser, ?User $updaterUser = null): array
     {
-        if ($updaterUser === false) $updaterUser = $creatorUser;
-        $book = factory(\BookStack\Entities\Book::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]);
-        $chapter = factory(\BookStack\Entities\Chapter::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]);
-        $page = factory(\BookStack\Entities\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id, 'chapter_id' => $chapter->id]);
+        if (empty($updaterUser)) {
+            $updaterUser = $creatorUser;
+        }
+
+        $userAttrs = ['created_by' => $creatorUser->id, 'owned_by' => $creatorUser->id, 'updated_by' => $updaterUser->id];
+        $book = factory(Book::class)->create($userAttrs);
+        $chapter = factory(Chapter::class)->create(array_merge(['book_id' => $book->id], $userAttrs));
+        $page = factory(Page::class)->create(array_merge(['book_id' => $book->id, 'chapter_id' => $chapter->id], $userAttrs));
         $restrictionService = $this->app[PermissionService::class];
         $restrictionService->buildJointPermissionsForEntity($book);
-        return [
-            'book' => $book,
-            'chapter' => $chapter,
-            'page' => $page
-        ];
+
+        return compact('book', 'chapter', 'page');
     }
 
     /**
@@ -101,7 +105,7 @@ abstract class BrowserKitTest extends TestCase
      */
     protected function getNewBlankUser($attributes = [])
     {
-        $user = factory(\BookStack\Auth\User::class)->create($attributes);
+        $user = factory(User::class)->create($attributes);
         return $user;
     }
 
index 4aef0ed2658861b2fe8a6f326c12ea131e3888ee..8c6ea84bf8524e2ec61d1b2284a560147ab760aa 100644 (file)
@@ -1,9 +1,14 @@
 <?php namespace Tests;
 
+use BookStack\Actions\ActivityType;
+use BookStack\Actions\Comment;
+use BookStack\Actions\CommentRepo;
 use BookStack\Auth\Permissions\JointPermission;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Page;
 use BookStack\Auth\User;
 use BookStack\Entities\Repos\PageRepo;
+use Symfony\Component\Console\Exception\RuntimeException;
 
 class CommandsTest extends TestCase
 {
@@ -33,10 +38,10 @@ class CommandsTest extends TestCase
     {
         $this->asEditor();
         $page = Page::first();
-        \Activity::add($page, 'page_update', $page->book->id);
+        \Activity::addForEntity($page, ActivityType::PAGE_UPDATE);
 
         $this->assertDatabaseHas('activities', [
-            'key' => 'page_update',
+            'type' => 'page_update',
             'entity_id' => $page->id,
             'user_id' => $this->getEditor()->id
         ]);
@@ -46,7 +51,7 @@ class CommandsTest extends TestCase
 
 
         $this->assertDatabaseMissing('activities', [
-            'key' => 'page_update'
+            'type' => 'page_update'
         ]);
     }
 
@@ -118,4 +123,100 @@ class CommandsTest extends TestCase
         $this->assertTrue(User::where('email', '=', '[email protected]')->first()->hasSystemRole('admin'), 'User has admin role as expected');
         $this->assertTrue(\Auth::attempt(['email' => '[email protected]', 'password' => 'testing-4']), 'Password stored as expected');
     }
+
+    public function test_copy_shelf_permissions_command_shows_error_when_no_required_option_given()
+    {
+        $this->artisan('bookstack:copy-shelf-permissions')
+            ->expectsOutput('Either a --slug or --all option must be provided.')
+            ->assertExitCode(0);
+    }
+
+    public function test_copy_shelf_permissions_command_using_slug()
+    {
+        $shelf = Bookshelf::first();
+        $child = $shelf->books()->first();
+        $editorRole = $this->getEditor()->roles()->first();
+        $this->assertFalse(boolval($child->restricted), "Child book should not be restricted by default");
+        $this->assertTrue($child->permissions()->count() === 0, "Child book should have no permissions by default");
+
+        $this->setEntityRestrictions($shelf, ['view', 'update'], [$editorRole]);
+        $this->artisan('bookstack:copy-shelf-permissions', [
+            '--slug' => $shelf->slug,
+        ]);
+        $child = $shelf->books()->first();
+
+        $this->assertTrue(boolval($child->restricted), "Child book should now be restricted");
+        $this->assertTrue($child->permissions()->count() === 2, "Child book should have copied permissions");
+        $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'view', 'role_id' => $editorRole->id]);
+        $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'update', 'role_id' => $editorRole->id]);
+    }
+
+    public function test_copy_shelf_permissions_command_using_all()
+    {
+        $shelf = Bookshelf::query()->first();
+        Bookshelf::query()->where('id', '!=', $shelf->id)->delete();
+        $child = $shelf->books()->first();
+        $editorRole = $this->getEditor()->roles()->first();
+        $this->assertFalse(boolval($child->restricted), "Child book should not be restricted by default");
+        $this->assertTrue($child->permissions()->count() === 0, "Child book should have no permissions by default");
+
+        $this->setEntityRestrictions($shelf, ['view', 'update'], [$editorRole]);
+        $this->artisan('bookstack:copy-shelf-permissions --all')
+            ->expectsQuestion('Permission settings for all shelves will be cascaded. Books assigned to multiple shelves will receive only the permissions of it\'s last processed shelf. Are you sure you want to proceed?', 'y');
+        $child = $shelf->books()->first();
+
+        $this->assertTrue(boolval($child->restricted), "Child book should now be restricted");
+        $this->assertTrue($child->permissions()->count() === 2, "Child book should have copied permissions");
+        $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'view', 'role_id' => $editorRole->id]);
+        $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'update', 'role_id' => $editorRole->id]);
+    }
+
+    public function test_update_url_command_updates_page_content()
+    {
+        $page = Page::query()->first();
+        $page->html = '<a href="https://example.com/donkeys"></a>';
+        $page->save();
+
+        $this->artisan('bookstack:update-url https://example.com https://cats.example.com')
+            ->expectsQuestion("This will search for \"https://example.com\" in your database and replace it with  \"https://cats.example.com\".\nAre you sure you want to proceed?", 'y')
+            ->expectsQuestion("This operation could cause issues if used incorrectly. Have you made a backup of your existing database?", 'y');
+
+        $this->assertDatabaseHas('pages', [
+            'id' => $page->id,
+            'html' => '<a href="https://cats.example.com/donkeys"></a>'
+        ]);
+    }
+
+    public function test_update_url_command_requires_valid_url()
+    {
+        $badUrlMessage = "The given urls are expected to be full urls starting with http:// or https://";
+        $this->artisan('bookstack:update-url //example.com https://cats.example.com')->expectsOutput($badUrlMessage);
+        $this->artisan('bookstack:update-url https://example.com htts://cats.example.com')->expectsOutput($badUrlMessage);
+        $this->artisan('bookstack:update-url example.com https://cats.example.com')->expectsOutput($badUrlMessage);
+
+        $this->expectException(RuntimeException::class);
+        $this->artisan('bookstack:update-url https://cats.example.com');
+    }
+
+    public function test_regenerate_comment_content_command()
+    {
+        Comment::query()->forceCreate([
+            'html' => 'some_old_content',
+            'text' => 'some_fresh_content',
+        ]);
+
+        $this->assertDatabaseHas('comments', [
+            'html' => 'some_old_content',
+        ]);
+
+        $exitCode = \Artisan::call('bookstack:regenerate-comment-content');
+        $this->assertTrue($exitCode === 0, 'Command executed successfully');
+
+        $this->assertDatabaseMissing('comments', [
+            'html' => 'some_old_content',
+        ]);
+        $this->assertDatabaseHas('comments', [
+            'html' => "<p>some_fresh_content</p>\n",
+        ]);
+    }
 }
index 5c7673847140eb71d284e14d94d92921f6b706c6..9b3290370c197a14bd1d558a9728e1c96cae89e6 100644 (file)
@@ -1,14 +1,18 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
-use BookStack\Auth\Role;
 use BookStack\Auth\User;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Uploads\Image;
 use Illuminate\Support\Str;
+use Tests\TestCase;
+use Tests\Uploads\UsesImages;
 
 class BookShelfTest extends TestCase
 {
 
+    use UsesImages;
+
     public function test_shelves_shows_in_header_if_have_view_permissions()
     {
         $viewer = $this->getViewer();
@@ -52,6 +56,25 @@ class BookShelfTest extends TestCase
         $resp->assertElementContains('a', 'New Shelf');
     }
 
+    public function test_book_not_visible_in_shelf_list_view_if_user_cant_view_shelf()
+    {
+        config()->set([
+            'app.views.bookshelves' => 'list',
+        ]);
+        $shelf = Bookshelf::query()->first();
+        $book = $shelf->books()->first();
+
+        $resp = $this->asEditor()->get('/shelves');
+        $resp->assertSee($book->name);
+        $resp->assertSee($book->getUrl());
+
+        $this->setEntityRestrictions($book, []);
+
+        $resp = $this->asEditor()->get('/shelves');
+        $resp->assertDontSee($book->name);
+        $resp->assertDontSee($book->getUrl());
+    }
+
     public function test_shelves_create()
     {
         $booksToInclude = Book::take(2)->get();
@@ -83,6 +106,26 @@ class BookShelfTest extends TestCase
         $this->assertDatabaseHas('bookshelves_books', ['bookshelf_id' => $shelf->id, 'book_id' => $booksToInclude[1]->id]);
     }
 
+    public function test_shelves_create_sets_cover_image()
+    {
+        $shelfInfo = [
+            'name' => 'My test book' . Str::random(4),
+            'description' => 'Test book description ' . Str::random(10)
+        ];
+
+        $imageFile = $this->getTestImage('shelf-test.png');
+        $resp = $this->asEditor()->call('POST', '/shelves', $shelfInfo, [], ['image' => $imageFile]);
+        $resp->assertRedirect();
+
+        $lastImage = Image::query()->orderByDesc('id')->firstOrFail();
+        $shelf = Bookshelf::query()->where('name', '=', $shelfInfo['name'])->first();
+        $this->assertDatabaseHas('bookshelves', [
+            'id' => $shelf->id,
+            'image_id' => $lastImage->id,
+        ]);
+        $this->assertEquals($lastImage->id, $shelf->cover->id);
+    }
+
     public function test_shelf_view()
     {
         $shelf = Bookshelf::first();
@@ -179,16 +222,25 @@ class BookShelfTest extends TestCase
 
     public function test_shelf_delete()
     {
-        $shelf = Bookshelf::first();
-        $resp = $this->asEditor()->get($shelf->getUrl('/delete'));
-        $resp->assertSeeText('Delete Bookshelf');
-        $resp->assertSee("action=\"{$shelf->getUrl()}\"");
-
-        $resp = $this->delete($shelf->getUrl());
-        $resp->assertRedirect('/shelves');
-        $this->assertDatabaseMissing('bookshelves', ['id' => $shelf->id]);
-        $this->assertDatabaseMissing('bookshelves_books', ['bookshelf_id' => $shelf->id]);
-        $this->assertSessionHas('success');
+        $shelf = Bookshelf::query()->whereHas('books')->first();
+        $this->assertNull($shelf->deleted_at);
+        $bookCount = $shelf->books()->count();
+
+        $deleteViewReq = $this->asEditor()->get($shelf->getUrl('/delete'));
+        $deleteViewReq->assertSeeText('Are you sure you want to delete this bookshelf?');
+
+        $deleteReq = $this->delete($shelf->getUrl());
+        $deleteReq->assertRedirect(url('/shelves'));
+        $this->assertActivityExists('bookshelf_delete', $shelf);
+
+        $shelf->refresh();
+        $this->assertNotNull($shelf->deleted_at);
+
+        $this->assertTrue($shelf->books()->count() === $bookCount);
+        $this->assertTrue($shelf->deletions()->count() === 1);
+
+        $redirectReq = $this->get($deleteReq->baseResponse->headers->get('location'));
+        $redirectReq->assertNotificationContains('Bookshelf Successfully Deleted');
     }
 
     public function test_shelf_copy_permissions()
@@ -240,4 +292,32 @@ class BookShelfTest extends TestCase
         $pageVisit->assertElementNotContains('.breadcrumbs', $shelf->getShortName());
     }
 
+    public function test_bookshelves_show_on_book()
+    {
+        // Create shelf
+        $shelfInfo = [
+            'name' => 'My test shelf' . Str::random(4),
+            'description' => 'Test shelf description ' . Str::random(10)
+        ];
+
+        $this->asEditor()->post('/shelves', $shelfInfo);
+        $shelf = Bookshelf::where('name', '=', $shelfInfo['name'])->first();
+
+        // Create book and add to shelf
+        $this->asEditor()->post($shelf->getUrl('/create-book'), [
+            'name' => 'Test book name',
+            'description' => 'Book in shelf description'
+        ]);
+
+        $newBook = Book::query()->orderBy('id', 'desc')->first();
+
+        $resp = $this->asEditor()->get($newBook->getUrl());
+        $resp->assertElementContains('.tri-layout-left-contents', $shelfInfo['name']);
+
+        // Remove shelf
+        $this->delete($shelf->getUrl());
+
+        $resp = $this->asEditor()->get($newBook->getUrl());
+        $resp->assertDontSee($shelfInfo['name']);
+    }
 }
diff --git a/tests/Entity/BookTest.php b/tests/Entity/BookTest.php
new file mode 100644 (file)
index 0000000..6c2cf30
--- /dev/null
@@ -0,0 +1,34 @@
+<?php namespace Tests\Entity;
+
+use BookStack\Entities\Models\Book;
+use Tests\TestCase;
+
+class BookTest extends TestCase
+{
+    public function test_book_delete()
+    {
+        $book = Book::query()->whereHas('pages')->whereHas('chapters')->first();
+        $this->assertNull($book->deleted_at);
+        $pageCount = $book->pages()->count();
+        $chapterCount = $book->chapters()->count();
+
+        $deleteViewReq = $this->asEditor()->get($book->getUrl('/delete'));
+        $deleteViewReq->assertSeeText('Are you sure you want to delete this book?');
+
+        $deleteReq = $this->delete($book->getUrl());
+        $deleteReq->assertRedirect(url('/books'));
+        $this->assertActivityExists('book_delete', $book);
+
+        $book->refresh();
+        $this->assertNotNull($book->deleted_at);
+
+        $this->assertTrue($book->pages()->count() === 0);
+        $this->assertTrue($book->chapters()->count() === 0);
+        $this->assertTrue($book->pages()->withTrashed()->count() === $pageCount);
+        $this->assertTrue($book->chapters()->withTrashed()->count() === $chapterCount);
+        $this->assertTrue($book->deletions()->count() === 1);
+
+        $redirectReq = $this->get($deleteReq->baseResponse->headers->get('location'));
+        $redirectReq->assertNotificationContains('Book Successfully Deleted');
+    }
+}
\ No newline at end of file
diff --git a/tests/Entity/ChapterTest.php b/tests/Entity/ChapterTest.php
new file mode 100644 (file)
index 0000000..e9350a3
--- /dev/null
@@ -0,0 +1,31 @@
+<?php namespace Tests\Entity;
+
+use BookStack\Entities\Models\Chapter;
+use Tests\TestCase;
+
+class ChapterTest extends TestCase
+{
+    public function test_chapter_delete()
+    {
+        $chapter = Chapter::query()->whereHas('pages')->first();
+        $this->assertNull($chapter->deleted_at);
+        $pageCount = $chapter->pages()->count();
+
+        $deleteViewReq = $this->asEditor()->get($chapter->getUrl('/delete'));
+        $deleteViewReq->assertSeeText('Are you sure you want to delete this chapter?');
+
+        $deleteReq = $this->delete($chapter->getUrl());
+        $deleteReq->assertRedirect($chapter->getParent()->getUrl());
+        $this->assertActivityExists('chapter_delete', $chapter);
+
+        $chapter->refresh();
+        $this->assertNotNull($chapter->deleted_at);
+
+        $this->assertTrue($chapter->pages()->count() === 0);
+        $this->assertTrue($chapter->pages()->withTrashed()->count() === $pageCount);
+        $this->assertTrue($chapter->deletions()->count() === 1);
+
+        $redirectReq = $this->get($deleteReq->baseResponse->headers->get('location'));
+        $redirectReq->assertNotificationContains('Chapter Successfully Deleted');
+    }
+}
\ No newline at end of file
index 967e550a735e6294faf970bcfc1a605b8a57c2f7..49ceede9f3edd23207b5f62906c14fe677fcd043 100644 (file)
@@ -1,28 +1,35 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
-class CommentSettingTest extends BrowserKitTest {
-  protected $page;
+use BookStack\Entities\Models\Page;
+use Tests\BrowserKitTest;
 
-  public function setUp(): void {
-      parent::setUp();
-      $this->page = \BookStack\Entities\Page::first();
-  }
+class CommentSettingTest extends BrowserKitTest
+{
+    protected $page;
 
-  public function test_comment_disable () {
-    $this->asAdmin();
+    public function setUp(): void
+    {
+        parent::setUp();
+        $this->page = Page::first();
+    }
 
-    $this->setSettings(['app-disable-comments' => 'true']);
+    public function test_comment_disable()
+    {
+        $this->asAdmin();
 
-    $this->asAdmin()->visit($this->page->getUrl())
-    ->pageNotHasElement('.comments-list');
-  }
+        $this->setSettings(['app-disable-comments' => 'true']);
 
-  public function test_comment_enable () {
-    $this->asAdmin();
+        $this->asAdmin()->visit($this->page->getUrl())
+            ->pageNotHasElement('.comments-list');
+    }
 
-    $this->setSettings(['app-disable-comments' => 'false']);
+    public function test_comment_enable()
+    {
+        $this->asAdmin();
 
-    $this->asAdmin()->visit($this->page->getUrl())
-    ->pageHasElement('.comments-list');
-  }
+        $this->setSettings(['app-disable-comments' => 'false']);
+
+        $this->asAdmin()->visit($this->page->getUrl())
+            ->pageHasElement('.comments-list');
+    }
 }
\ No newline at end of file
index 2b943f96f18c853dfd36f805110f7eb0715b0666..63d1a29a29ac656bf5d53ec38a00fad8102af35c 100644 (file)
@@ -1,7 +1,8 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
 use BookStack\Actions\Comment;
+use Tests\TestCase;
 
 class CommentTest extends TestCase
 {
@@ -12,7 +13,7 @@ class CommentTest extends TestCase
         $page = Page::first();
 
         $comment = factory(Comment::class)->make(['parent_id' => 2]);
-        $resp = $this->postJson("/ajax/page/$page->id/comment", $comment->getAttributes());
+        $resp = $this->postJson("/comment/$page->id", $comment->getAttributes());
 
         $resp->assertStatus(200);
         $resp->assertSee($comment->text);
@@ -35,13 +36,12 @@ class CommentTest extends TestCase
         $page = Page::first();
 
         $comment = factory(Comment::class)->make();
-        $this->postJson("/ajax/page/$page->id/comment", $comment->getAttributes());
+        $this->postJson("/comment/$page->id", $comment->getAttributes());
 
         $comment = $page->comments()->first();
         $newText = 'updated text content';
-        $resp = $this->putJson("/ajax/comment/$comment->id", [
+        $resp = $this->putJson("/comment/$comment->id", [
             'text' => $newText,
-            'html' => '<p>'.$newText.'</p>',
         ]);
 
         $resp->assertStatus(200);
@@ -60,15 +60,57 @@ class CommentTest extends TestCase
         $page = Page::first();
 
         $comment = factory(Comment::class)->make();
-        $this->postJson("/ajax/page/$page->id/comment", $comment->getAttributes());
+        $this->postJson("/comment/$page->id", $comment->getAttributes());
 
         $comment = $page->comments()->first();
 
-        $resp = $this->delete("/ajax/comment/$comment->id");
+        $resp = $this->delete("/comment/$comment->id");
         $resp->assertStatus(200);
 
         $this->assertDatabaseMissing('comments', [
             'id' => $comment->id
         ]);
     }
+
+    public function test_comments_converts_markdown_input_to_html()
+    {
+        $page = Page::first();
+        $this->asAdmin()->postJson("/comment/$page->id", [
+            'text' => '# My Title',
+        ]);
+
+        $this->assertDatabaseHas('comments', [
+            'entity_id' => $page->id,
+            'entity_type' => $page->getMorphClass(),
+            'text' => '# My Title',
+            'html' => "<h1>My Title</h1>\n",
+        ]);
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertSee('<h1>My Title</h1>');
+    }
+
+    public function test_html_cannot_be_injected_via_comment_content()
+    {
+        $this->asAdmin();
+        $page = Page::first();
+
+        $script = '<script>const a = "script";</script>\n\n# sometextinthecomment';
+        $this->postJson("/comment/$page->id", [
+            'text' => $script,
+        ]);
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertDontSee($script);
+        $pageView->assertSee('sometextinthecomment');
+
+        $comment = $page->comments()->first();
+        $this->putJson("/comment/$comment->id", [
+            'text' => $script . 'updated',
+        ]);
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertDontSee($script);
+        $pageView->assertSee('sometextinthecommentupdated');
+    }
 }
index 3eb50a41279fa6cf56783f3d108a961efed93438..2b5dc6d749cdbf40e24ae9f93c5f5d446c0fc051 100644 (file)
@@ -1,16 +1,18 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
-
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Actions\Tag;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Tests\TestCase;
 
 class EntitySearchTest extends TestCase
 {
 
     public function test_page_search()
     {
-        $book = \BookStack\Entities\Book::all()->first();
+        $book = Book::all()->first();
         $page = $book->pages->first();
 
         $search = $this->asEditor()->get('/search?term=' . urlencode($page->name));
@@ -54,7 +56,7 @@ class EntitySearchTest extends TestCase
 
     public function test_book_search()
     {
-        $book = \BookStack\Entities\Book::first();
+        $book = Book::first();
         $page = $book->pages->last();
         $chapter = $book->chapters->last();
 
@@ -67,7 +69,7 @@ class EntitySearchTest extends TestCase
 
     public function test_chapter_search()
     {
-        $chapter = \BookStack\Entities\Chapter::has('pages')->first();
+        $chapter = Chapter::has('pages')->first();
         $page = $chapter->pages[0];
 
         $pageTestResp = $this->asEditor()->get('/search/chapter/' . $chapter->id . '?term=' . urlencode($page->name));
@@ -77,11 +79,11 @@ class EntitySearchTest extends TestCase
     public function test_tag_search()
     {
         $newTags = [
-            new \BookStack\Actions\Tag([
+            new Tag([
                 'name' => 'animal',
                 'value' => 'cat'
             ]),
-            new \BookStack\Actions\Tag([
+            new Tag([
                 'name' => 'color',
                 'value' => 'red'
             ])
@@ -204,4 +206,91 @@ class EntitySearchTest extends TestCase
         $chapterSearch->assertSee($chapter->name);
         $chapterSearch->assertSee($chapter->book->getShortName(42));
     }
+
+    public function test_sibling_search_for_pages()
+    {
+        $chapter = Chapter::query()->with('pages')->first();
+        $this->assertGreaterThan(2, count($chapter->pages), 'Ensure we\'re testing with at least 1 sibling');
+        $page = $chapter->pages->first();
+
+        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page");
+        $search->assertSuccessful();
+        foreach ($chapter->pages as $page) {
+            $search->assertSee($page->name);
+        }
+
+        $search->assertDontSee($chapter->name);
+    }
+
+    public function test_sibling_search_for_pages_without_chapter()
+    {
+        $page = Page::query()->where('chapter_id', '=', 0)->firstOrFail();
+        $bookChildren = $page->book->getDirectChildren();
+        $this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling');
+
+        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page");
+        $search->assertSuccessful();
+        foreach ($bookChildren as $child) {
+            $search->assertSee($child->name);
+        }
+
+        $search->assertDontSee($page->book->name);
+    }
+
+    public function test_sibling_search_for_chapters()
+    {
+        $chapter = Chapter::query()->firstOrFail();
+        $bookChildren = $chapter->book->getDirectChildren();
+        $this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling');
+
+        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$chapter->id}&entity_type=chapter");
+        $search->assertSuccessful();
+        foreach ($bookChildren as $child) {
+            $search->assertSee($child->name);
+        }
+
+        $search->assertDontSee($chapter->book->name);
+    }
+
+    public function test_sibling_search_for_books()
+    {
+        $books = Book::query()->take(10)->get();
+        $book = $books->first();
+        $this->assertGreaterThan(2, count($books), 'Ensure we\'re testing with at least 1 sibling');
+
+        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$book->id}&entity_type=book");
+        $search->assertSuccessful();
+        foreach ($books as $expectedBook) {
+            $search->assertSee($expectedBook->name);
+        }
+    }
+
+    public function test_sibling_search_for_shelves()
+    {
+        $shelves = Bookshelf::query()->take(10)->get();
+        $shelf = $shelves->first();
+        $this->assertGreaterThan(2, count($shelves), 'Ensure we\'re testing with at least 1 sibling');
+
+        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$shelf->id}&entity_type=bookshelf");
+        $search->assertSuccessful();
+        foreach ($shelves as $expectedShelf) {
+            $search->assertSee($expectedShelf->name);
+        }
+    }
+
+    public function test_search_works_on_updated_page_content()
+    {
+        $page = Page::query()->first();
+        $this->asEditor();
+
+        $update = $this->put($page->getUrl(), [
+            'name' => $page->name,
+            'html' => '<p>dog pandabearmonster spaghetti</p>',
+        ]);
+
+        $search = $this->asEditor()->get('/search?term=pandabearmonster');
+        $search->assertStatus(200);
+        $search->assertSeeText($page->name);
+        $search->assertSee($page->getUrl());
+    }
 }
index 3d12ed7495943dcc75ef979d87d845a57c41ee47..3a363e2b87bfeaaa7424946f49acff2556554c7d 100644 (file)
@@ -1,12 +1,13 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
 use BookStack\Auth\UserRepo;
 use BookStack\Entities\Repos\PageRepo;
 use Carbon\Carbon;
+use Tests\BrowserKitTest;
 
 class EntityTest extends BrowserKitTest
 {
@@ -16,27 +17,10 @@ class EntityTest extends BrowserKitTest
         // Test Creation
         $book = $this->bookCreation();
         $chapter = $this->chapterCreation($book);
-        $page = $this->pageCreation($chapter);
+        $this->pageCreation($chapter);
 
         // Test Updating
-        $book = $this->bookUpdate($book);
-
-        // Test Deletion
-        $this->bookDelete($book);
-    }
-
-    public function bookDelete(Book $book)
-    {
-        $this->asAdmin()
-            ->visit($book->getUrl())
-            // Check link works correctly
-            ->click('Delete')
-            ->seePageIs($book->getUrl() . '/delete')
-            // Ensure the book name is show to user
-            ->see($book->name)
-            ->press('Confirm')
-            ->seePageIs('/books')
-            ->notSeeInDatabase('books', ['id' => $book->id]);
+        $this->bookUpdate($book);
     }
 
     public function bookUpdate(Book $book)
@@ -271,15 +255,20 @@ class EntityTest extends BrowserKitTest
             ->seeInElement('#recently-updated-pages', $page->name);
     }
 
-    public function test_slug_multi_byte_lower_casing()
+    public function test_slug_multi_byte_url_safe()
     {
         $book = $this->newBook([
-            'name' => 'Ð\9aÐ\9dÐ\98Ð\93Ð\90'
+            'name' => 'инÑ\84оÑ\80маÑ\86иÑ\8f'
         ]);
 
-        $this->assertEquals('книга', $book->slug);
-    }
+        $this->assertEquals('informatsiya', $book->slug);
 
+        $book = $this->newBook([
+            'name' => '¿Qué?'
+        ]);
+
+        $this->assertEquals('que', $book->slug);
+    }
 
     public function test_slug_format()
     {
@@ -315,4 +304,14 @@ class EntityTest extends BrowserKitTest
             ->seePageIs($book->getUrl());
     }
 
+    public function test_page_within_chapter_deletion_returns_to_chapter()
+    {
+        $chapter = Chapter::query()->first();
+        $page = $chapter->pages()->first();
+
+        $this->asEditor()->visit($page->getUrl('/delete'))
+            ->submitForm('Confirm')
+            ->seePageIs($chapter->getUrl());
+    }
+
 }
index 9a2d32028e4b26887c7d231b68df2b1887668e3a..1e44f015a5a0b69f8520c9227b971e79f17c0b63 100644 (file)
@@ -1,10 +1,11 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
 
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
-use BookStack\Uploads\HttpFetcher;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Illuminate\Support\Facades\Storage;
 use Illuminate\Support\Str;
+use Tests\TestCase;
 
 class ExportTest extends TestCase
 {
@@ -153,14 +154,55 @@ class ExportTest extends TestCase
     public function test_page_export_sets_right_data_type_for_svg_embeds()
     {
         $page = Page::first();
-        $page->html = '<img src="http://example.com/image.svg">';
+        Storage::disk('local')->makeDirectory('uploads/images/gallery');
+        Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', '<svg></svg>');
+        $page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg">';
         $page->save();
 
         $this->asEditor();
-        $this->mockHttpFetch('<svg></svg>');
         $resp = $this->get($page->getUrl('/export/html'));
+        Storage::disk('local')->delete('uploads/images/gallery/svg_test.svg');
+
         $resp->assertStatus(200);
         $resp->assertSee('<img src="data:image/svg+xml;base64');
     }
 
-}
\ No newline at end of file
+    public function test_page_image_containment_works_on_multiple_images_within_a_single_line()
+    {
+        $page = Page::first();
+        Storage::disk('local')->makeDirectory('uploads/images/gallery');
+        Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', '<svg></svg>');
+        Storage::disk('local')->put('uploads/images/gallery/svg_test2.svg', '<svg></svg>');
+        $page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg" class="a"><img src="http://localhost/uploads/images/gallery/svg_test2.svg" class="b">';
+        $page->save();
+
+        $resp = $this->asEditor()->get($page->getUrl('/export/html'));
+        Storage::disk('local')->delete('uploads/images/gallery/svg_test.svg');
+        Storage::disk('local')->delete('uploads/images/gallery/svg_test2.svg');
+
+        $resp->assertDontSee('http://localhost/uploads/images/gallery/svg_test');
+    }
+
+    public function test_page_export_contained_html_image_fetches_only_run_when_url_points_to_image_upload_folder()
+    {
+        $page = Page::first();
+        $page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg"/>'
+            .'<img src="http://localhost/uploads/svg_test.svg"/>'
+            .'<img src="/uploads/svg_test.svg"/>';
+        $storageDisk = Storage::disk('local');
+        $storageDisk->makeDirectory('uploads/images/gallery');
+        $storageDisk->put('uploads/images/gallery/svg_test.svg', '<svg>good</svg>');
+        $storageDisk->put('uploads/svg_test.svg', '<svg>bad</svg>');
+        $page->save();
+
+        $resp = $this->asEditor()->get($page->getUrl('/export/html'));
+
+        $storageDisk->delete('uploads/images/gallery/svg_test.svg');
+        $storageDisk->delete('uploads/svg_test.svg');
+
+        $resp->assertDontSee('http://localhost/uploads/images/gallery/svg_test.svg');
+        $resp->assertSee('http://localhost/uploads/svg_test.svg');
+        $resp->assertSee('src="/uploads/svg_test.svg"');
+    }
+
+}
index 5d3af4f6e26705b8fed3e617a78d9104fe3fb99d..5e5fa8a0c2f7b52427784e295bef3b935e250294 100644 (file)
@@ -1,4 +1,6 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
+
+use Tests\BrowserKitTest;
 
 class MarkdownTest extends BrowserKitTest
 {
@@ -7,7 +9,7 @@ class MarkdownTest extends BrowserKitTest
     public function setUp(): void
     {
         parent::setUp();
-        $this->page = \BookStack\Entities\Page::first();
+        $this->page = \BookStack\Entities\Models\Page::first();
     }
 
     protected function setMarkdownEditor()
index 8a78c8ac019fd08de3cc37d50e7df50954c83f6e..6d5200794bc79354bd8243d80711867323f9f0c6 100644 (file)
@@ -1,7 +1,8 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
-use BookStack\Entities\Managers\PageContent;
-use BookStack\Entities\Page;
+use BookStack\Entities\Tools\PageContent;
+use BookStack\Entities\Models\Page;
+use Tests\TestCase;
 
 class PageContentTest extends TestCase
 {
@@ -70,6 +71,25 @@ class PageContentTest extends TestCase
         $pageResp->assertSee($content);
     }
 
+    public function test_page_includes_rendered_on_book_export()
+    {
+        $page = Page::query()->first();
+        $secondPage = Page::query()
+            ->where('book_id', '!=', $page->book_id)
+            ->first();
+
+        $content = '<p id="bkmrk-meow">my cat is awesome and scratchy</p>';
+        $secondPage->html = $content;
+        $secondPage->save();
+
+        $page->html = "{{@{$secondPage->id}#bkmrk-meow}}";
+        $page->save();
+
+        $this->asEditor();
+        $htmlContent = $this->get($page->book->getUrl('/export/html'));
+        $htmlContent->assertSee('my cat is awesome and scratchy');
+    }
+
     public function test_page_content_scripts_removed_by_default()
     {
         $this->asEditor();
@@ -139,6 +159,72 @@ class PageContentTest extends TestCase
 
     }
 
+    public function test_javascript_uri_links_are_removed()
+    {
+        $checks = [
+            '<a id="xss" href="javascript:alert(document.cookie)>Click me</a>',
+            '<a id="xss" href="javascript: alert(document.cookie)>Click me</a>'
+        ];
+
+        $this->asEditor();
+        $page = Page::first();
+
+        foreach ($checks as $check) {
+            $page->html = $check;
+            $page->save();
+
+            $pageView = $this->get($page->getUrl());
+            $pageView->assertStatus(200);
+            $pageView->assertElementNotContains('.page-content', '<a id="xss">');
+            $pageView->assertElementNotContains('.page-content', 'href=javascript:');
+        }
+    }
+    public function test_form_actions_with_javascript_are_removed()
+    {
+        $checks = [
+            '<form><input id="xss" type=submit formaction=javascript:alert(document.domain) value=Submit><input></form>',
+            '<form ><button id="xss" formaction=javascript:alert(document.domain)>Click me</button></form>',
+            '<form id="xss" action=javascript:alert(document.domain)><input type=submit value=Submit></form>'
+        ];
+
+        $this->asEditor();
+        $page = Page::first();
+
+        foreach ($checks as $check) {
+            $page->html = $check;
+            $page->save();
+
+            $pageView = $this->get($page->getUrl());
+            $pageView->assertStatus(200);
+            $pageView->assertElementNotContains('.page-content', '<button id="xss"');
+            $pageView->assertElementNotContains('.page-content', '<input id="xss"');
+            $pageView->assertElementNotContains('.page-content', '<form id="xss"');
+            $pageView->assertElementNotContains('.page-content', 'action=javascript:');
+            $pageView->assertElementNotContains('.page-content', 'formaction=javascript:');
+        }
+    }
+    
+    public function test_metadata_redirects_are_removed()
+    {
+        $checks = [
+            '<meta http-equiv="refresh" content="0; url=//external_url">',
+        ];
+
+        $this->asEditor();
+        $page = Page::first();
+
+        foreach ($checks as $check) {
+            $page->html = $check;
+            $page->save();
+
+            $pageView = $this->get($page->getUrl());
+            $pageView->assertStatus(200);
+            $pageView->assertElementNotContains('.page-content', '<meta>');
+            $pageView->assertElementNotContains('.page-content', '</meta>');
+            $pageView->assertElementNotContains('.page-content', 'content=');
+            $pageView->assertElementNotContains('.page-content', 'external_url');
+        }
+    }
     public function test_page_inline_on_attributes_removed_by_default()
     {
         $this->asEditor();
@@ -242,6 +328,23 @@ class PageContentTest extends TestCase
         $this->assertEquals(substr_count($updatedPage->html, "bkmrk-test\""), 1);
     }
 
+    public function test_anchors_referencing_non_bkmrk_ids_rewritten_after_save()
+    {
+        $this->asEditor();
+        $page = Page::first();
+
+        $content = '<h1 id="non-standard-id">test</h1><p><a href="#non-standard-id">link</a></p>';
+        $this->put($page->getUrl(), [
+            'name' => $page->name,
+            'html' => $content,
+            'summary' => ''
+        ]);
+
+        $updatedPage = Page::where('id', '=', $page->id)->first();
+        $this->assertStringContainsString('id="bkmrk-test"', $updatedPage->html);
+        $this->assertStringContainsString('href="#bkmrk-test"', $updatedPage->html);
+    }
+
     public function test_get_page_nav_sets_correct_properties()
     {
         $content = '<h1 id="testa">Hello</h1><h2 id="testb">There</h2><h3 id="testc">Donkey</h3>';
@@ -303,4 +406,77 @@ class PageContentTest extends TestCase
             'level' => 3,
         ], $navMap[2]);
     }
+
+    public function test_page_text_decodes_html_entities()
+    {
+        $page = Page::query()->first();
+
+        $this->actingAs($this->getAdmin())
+            ->put($page->getUrl(''), [
+                'name' => 'Testing',
+                'html' => '<p>&quot;Hello &amp; welcome&quot;</p>',
+            ]);
+
+        $page->refresh();
+        $this->assertEquals('"Hello & welcome"', $page->text);
+    }
+
+    public function test_page_markdown_table_rendering()
+    {
+        $this->asEditor();
+        $page = Page::query()->first();
+
+        $content = '| Syntax      | Description |
+| ----------- | ----------- |
+| Header      | Title       |
+| Paragraph   | Text        |';
+        $this->put($page->getUrl(), [
+            'name' => $page->name,  'markdown' => $content,
+            'html' => '', 'summary' => ''
+        ]);
+
+        $page->refresh();
+        $this->assertStringContainsString('</tbody>', $page->html);
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertElementExists('.page-content table tbody td');
+    }
+
+    public function test_page_markdown_task_list_rendering()
+    {
+        $this->asEditor();
+        $page = Page::query()->first();
+
+        $content = '- [ ] Item a
+- [x] Item b';
+        $this->put($page->getUrl(), [
+            'name' => $page->name,  'markdown' => $content,
+            'html' => '', 'summary' => ''
+        ]);
+
+        $page->refresh();
+        $this->assertStringContainsString('input', $page->html);
+        $this->assertStringContainsString('type="checkbox"', $page->html);
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertElementExists('.page-content input[type=checkbox]');
+    }
+
+    public function test_page_markdown_strikethrough_rendering()
+    {
+        $this->asEditor();
+        $page = Page::query()->first();
+
+        $content = '~~some crossed out text~~';
+        $this->put($page->getUrl(), [
+            'name' => $page->name,  'markdown' => $content,
+            'html' => '', 'summary' => ''
+        ]);
+
+        $page->refresh();
+        $this->assertStringMatchesFormat('%A<s%A>some crossed out text</s>%A', $page->html);
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertElementExists('.page-content p > s');
+    }
 }
index e83f78a10a8a8f2d566ece9586a115dd24d4c323..0e3980c6702217cd10043748a9d8ad89dbb18bcc 100644 (file)
@@ -1,6 +1,8 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
+use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\PageRepo;
+use Tests\BrowserKitTest;
 
 class PageDraftTest extends BrowserKitTest
 {
@@ -14,7 +16,7 @@ class PageDraftTest extends BrowserKitTest
     public function setUp(): void
     {
         parent::setUp();
-        $this->page = \BookStack\Entities\Page::first();
+        $this->page = \BookStack\Entities\Models\Page::first();
         $this->pageRepo = app(PageRepo::class);
     }
 
@@ -54,7 +56,7 @@ class PageDraftTest extends BrowserKitTest
 
     public function test_alert_message_shows_if_someone_else_editing()
     {
-        $nonEditedPage = \BookStack\Entities\Page::take(10)->get()->last();
+        $nonEditedPage = \BookStack\Entities\Models\Page::take(10)->get()->last();
         $addedContent = '<p>test message content</p>';
         $this->asAdmin()->visit($this->page->getUrl('/edit'))
             ->dontSeeInField('html', $addedContent);
@@ -73,7 +75,7 @@ class PageDraftTest extends BrowserKitTest
 
     public function test_draft_pages_show_on_homepage()
     {
-        $book = \BookStack\Entities\Book::first();
+        $book = \BookStack\Entities\Models\Book::first();
         $this->asAdmin()->visit('/')
             ->dontSeeInElement('#recent-drafts', 'New Page')
             ->visit($book->getUrl() . '/create-page')
@@ -83,7 +85,7 @@ class PageDraftTest extends BrowserKitTest
 
     public function test_draft_pages_not_visible_by_others()
     {
-        $book = \BookStack\Entities\Book::first();
+        $book = \BookStack\Entities\Models\Book::first();
         $chapter = $book->chapters->first();
         $newUser = $this->getEditor();
 
@@ -100,4 +102,15 @@ class PageDraftTest extends BrowserKitTest
             ->dontSeeInElement('.book-contents', 'New Page');
     }
 
+    public function test_page_html_in_ajax_fetch_response()
+    {
+        $this->asAdmin();
+        $page = Page::query()->first();
+
+        $this->getJson('/ajax/page/' . $page->id);
+        $this->seeJson([
+            'html' => $page->html,
+        ]);
+    }
+
 }
index 38193ec1a6b7bd09cadbe04e6a8bcb3ea8765f98..68a8f01a9ef347a362b545b7db633435bc26b33f 100644 (file)
@@ -1,6 +1,6 @@
-<?php namespace Entity;
+<?php namespace Tests\Entity;
 
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\PageRepo;
 use Tests\TestCase;
 
@@ -24,6 +24,21 @@ class PageRevisionTest extends TestCase
         $revisionView->assertSee('new content');
     }
 
+    public function test_page_revision_preview_shows_content_of_revision()
+    {
+        $this->asEditor();
+
+        $pageRepo = app(PageRepo::class);
+        $page = Page::first();
+        $pageRepo->update($page, ['name' => 'updated page', 'html' => '<p>new revision content</p>', 'summary' => 'page revision testing']);
+        $pageRevision = $page->revisions->last();
+        $pageRepo->update($page, ['name' => 'updated page', 'html' => '<p>Updated content</p>', 'summary' => 'page revision testing 2']);
+
+        $revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id);
+        $revisionView->assertStatus(200);
+        $revisionView->assertSee('new revision content');
+    }
+
     public function test_page_revision_restore_updates_content()
     {
         $this->asEditor();
@@ -51,6 +66,28 @@ class PageRevisionTest extends TestCase
         $pageView->assertSee('def456');
     }
 
+    public function test_page_revision_restore_sets_new_revision_with_summary()
+    {
+        $this->asEditor();
+
+        $pageRepo = app(PageRepo::class);
+        $page = Page::first();
+        $pageRepo->update($page, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'My first update']);
+        $pageRepo->update($page, ['name' => 'updated page again', 'html' => '<p>new content</p>', 'summary' => '']);
+        $page->refresh();
+
+        $revToRestore = $page->revisions()->where('name', 'like', '%abc123')->first();
+        $this->put($page->getUrl() . '/revisions/' . $revToRestore->id . '/restore');
+        $page->refresh();
+
+        $this->assertDatabaseHas('page_revisions', [
+            'page_id' => $page->id,
+            'text' => 'new contente def456',
+            'type' => 'version',
+            'summary' => "Restored from #{$revToRestore->id}; My first update",
+        ]);
+    }
+
     public function test_page_revision_count_increments_on_update()
     {
         $page = Page::first();
index 883de4a9f91f98f1b67dfcb83676c06d2a5489ae..a5594e8b8df06ececf95ae1df6d8256590738e91 100644 (file)
@@ -1,6 +1,6 @@
-<?php namespace Entity;
+<?php namespace Tests\Entity;
 
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
 use Tests\TestCase;
 
 class PageTemplateTest extends TestCase
diff --git a/tests/Entity/PageTest.php b/tests/Entity/PageTest.php
new file mode 100644 (file)
index 0000000..4fc6b9c
--- /dev/null
@@ -0,0 +1,148 @@
+<?php namespace Tests\Entity;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Page;
+use Tests\TestCase;
+
+class PageTest extends TestCase
+{
+    public function test_page_creation_with_markdown_content()
+    {
+        $this->setSettings(['app-editor' => 'markdown']);
+        $book = Book::query()->first();
+
+        $this->asEditor()->get($book->getUrl('/create-page'));
+        $draft = Page::query()->where('book_id', '=', $book->id)
+            ->where('draft', '=', true)->first();
+
+        $details = [
+            'markdown' => '# a title',
+            'html' => '<h1>a title</h1>',
+            'name' => 'my page',
+        ];
+        $resp = $this->post($book->getUrl("/draft/{$draft->id}"), $details);
+        $resp->assertRedirect();
+
+        $this->assertDatabaseHas('pages', [
+            'markdown' => $details['markdown'],
+            'name' => $details['name'],
+            'id' => $draft->id,
+            'draft' => false
+        ]);
+
+        $draft->refresh();
+        $resp = $this->get($draft->getUrl("/edit"));
+        $resp->assertSee("# a title");
+    }
+
+    public function test_page_delete()
+    {
+        $page = Page::query()->first();
+        $this->assertNull($page->deleted_at);
+
+        $deleteViewReq = $this->asEditor()->get($page->getUrl('/delete'));
+        $deleteViewReq->assertSeeText('Are you sure you want to delete this page?');
+
+        $deleteReq = $this->delete($page->getUrl());
+        $deleteReq->assertRedirect($page->getParent()->getUrl());
+        $this->assertActivityExists('page_delete', $page);
+
+        $page->refresh();
+        $this->assertNotNull($page->deleted_at);
+        $this->assertTrue($page->deletions()->count() === 1);
+
+        $redirectReq = $this->get($deleteReq->baseResponse->headers->get('location'));
+        $redirectReq->assertNotificationContains('Page Successfully Deleted');
+    }
+
+    public function test_page_copy()
+    {
+        $page = Page::first();
+        $page->html = '<p>This is some test content</p>';
+        $page->save();
+
+        $currentBook = $page->book;
+        $newBook = Book::where('id', '!=', $currentBook->id)->first();
+
+        $resp = $this->asEditor()->get($page->getUrl('/copy'));
+        $resp->assertSee('Copy Page');
+
+        $movePageResp = $this->post($page->getUrl('/copy'), [
+            'entity_selection' => 'book:' . $newBook->id,
+            'name' => 'My copied test page'
+        ]);
+        $pageCopy = Page::where('name', '=', 'My copied test page')->first();
+
+        $movePageResp->assertRedirect($pageCopy->getUrl());
+        $this->assertTrue($pageCopy->book->id == $newBook->id, 'Page was copied to correct book');
+        $this->assertStringContainsString('This is some test content', $pageCopy->html);
+    }
+
+    public function test_page_copy_with_markdown_has_both_html_and_markdown()
+    {
+        $page = Page::first();
+        $page->html = '<h1>This is some test content</h1>';
+        $page->markdown = '# This is some test content';
+        $page->save();
+        $newBook = Book::where('id', '!=', $page->book->id)->first();
+
+        $this->asEditor()->post($page->getUrl('/copy'), [
+            'entity_selection' => 'book:' . $newBook->id,
+            'name' => 'My copied test page'
+        ]);
+        $pageCopy = Page::where('name', '=', 'My copied test page')->first();
+
+        $this->assertStringContainsString('This is some test content', $pageCopy->html);
+        $this->assertEquals('# This is some test content', $pageCopy->markdown);
+    }
+
+    public function test_page_copy_with_no_destination()
+    {
+        $page = Page::first();
+        $currentBook = $page->book;
+
+        $resp = $this->asEditor()->get($page->getUrl('/copy'));
+        $resp->assertSee('Copy Page');
+
+        $movePageResp = $this->post($page->getUrl('/copy'), [
+            'name' => 'My copied test page'
+        ]);
+
+        $pageCopy = Page::where('name', '=', 'My copied test page')->first();
+
+        $movePageResp->assertRedirect($pageCopy->getUrl());
+        $this->assertTrue($pageCopy->book->id == $currentBook->id, 'Page was copied to correct book');
+        $this->assertTrue($pageCopy->id !== $page->id, 'Page copy is not the same instance');
+    }
+
+    public function test_page_can_be_copied_without_edit_permission()
+    {
+        $page = Page::first();
+        $currentBook = $page->book;
+        $newBook = Book::where('id', '!=', $currentBook->id)->first();
+        $viewer = $this->getViewer();
+
+        $resp = $this->actingAs($viewer)->get($page->getUrl());
+        $resp->assertDontSee($page->getUrl('/copy'));
+
+        $newBook->owned_by = $viewer->id;
+        $newBook->save();
+        $this->giveUserPermissions($viewer, ['page-create-own']);
+        $this->regenEntityPermissions($newBook);
+
+        $resp = $this->actingAs($viewer)->get($page->getUrl());
+        $resp->assertSee($page->getUrl('/copy'));
+
+        $movePageResp = $this->post($page->getUrl('/copy'), [
+            'entity_selection' => 'book:' . $newBook->id,
+            'name' => 'My copied test page'
+        ]);
+        $movePageResp->assertRedirect();
+
+        $this->assertDatabaseHas('pages', [
+            'name' => 'My copied test page',
+            'created_by' => $viewer->id,
+            'book_id' => $newBook->id,
+        ]);
+    }
+}
\ No newline at end of file
diff --git a/tests/Entity/SearchOptionsTest.php b/tests/Entity/SearchOptionsTest.php
new file mode 100644 (file)
index 0000000..c9e1165
--- /dev/null
@@ -0,0 +1,43 @@
+<?php namespace Tests\Entity;
+
+use BookStack\Entities\Tools\SearchOptions;
+use Tests\TestCase;
+
+class SearchOptionsTest extends TestCase
+{
+    public function test_from_string_parses_a_search_string_properly()
+    {
+        $options = SearchOptions::fromString('cat "dog" [tag=good] {is_tree}');
+
+        $this->assertEquals(['cat'], $options->searches);
+        $this->assertEquals(['dog'], $options->exacts);
+        $this->assertEquals(['tag=good'], $options->tags);
+        $this->assertEquals(['is_tree' => ''], $options->filters);
+    }
+
+    public function test_to_string_includes_all_items_in_the_correct_format()
+    {
+        $expected = 'cat "dog" [tag=good] {is_tree}';
+        $options = new SearchOptions;
+        $options->searches = ['cat'];
+        $options->exacts = ['dog'];
+        $options->tags = ['tag=good'];
+        $options->filters = ['is_tree' => ''];
+
+        $output = $options->toString();
+        foreach (explode(' ', $expected) as $term) {
+            $this->assertStringContainsString($term, $output);
+        }
+    }
+
+    public function test_correct_filter_values_are_set_from_string()
+    {
+        $opts = SearchOptions::fromString('{is_tree} {name:dan} {cat:happy}');
+
+        $this->assertEquals([
+            'is_tree' => '',
+            'name' => 'dan',
+            'cat' => 'happy',
+        ], $opts->filters);
+    }
+}
index 7d67f05c6d6b0b77be7dde916d3c497c512d0482..d75a134ea5c298bac5d0f6d826436b24e6dbbd70 100644 (file)
@@ -1,9 +1,10 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\PageRepo;
+use Tests\TestCase;
 
 class SortTest extends TestCase
 {
@@ -78,7 +79,7 @@ class SortTest extends TestCase
         $movePageResp = $this->actingAs($this->getEditor())->put($page->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id
         ]);
-        $page = Page::find($page->id);
+        $page->refresh();
 
         $movePageResp->assertRedirect($page->getUrl());
         $this->assertTrue($page->book->id == $newBook->id, 'Page parent is now the new book');
@@ -238,73 +239,4 @@ class SortTest extends TestCase
         $checkResp->assertSee($newBook->name);
     }
 
-    public function test_page_copy()
-    {
-        $page = Page::first();
-        $currentBook = $page->book;
-        $newBook = Book::where('id', '!=', $currentBook->id)->first();
-
-        $resp = $this->asEditor()->get($page->getUrl('/copy'));
-        $resp->assertSee('Copy Page');
-
-        $movePageResp = $this->post($page->getUrl('/copy'), [
-            'entity_selection' => 'book:' . $newBook->id,
-            'name' => 'My copied test page'
-        ]);
-        $pageCopy = Page::where('name', '=', 'My copied test page')->first();
-
-        $movePageResp->assertRedirect($pageCopy->getUrl());
-        $this->assertTrue($pageCopy->book->id == $newBook->id, 'Page was copied to correct book');
-    }
-
-    public function test_page_copy_with_no_destination()
-    {
-        $page = Page::first();
-        $currentBook = $page->book;
-
-        $resp = $this->asEditor()->get($page->getUrl('/copy'));
-        $resp->assertSee('Copy Page');
-
-        $movePageResp = $this->post($page->getUrl('/copy'), [
-            'name' => 'My copied test page'
-        ]);
-
-        $pageCopy = Page::where('name', '=', 'My copied test page')->first();
-
-        $movePageResp->assertRedirect($pageCopy->getUrl());
-        $this->assertTrue($pageCopy->book->id == $currentBook->id, 'Page was copied to correct book');
-        $this->assertTrue($pageCopy->id !== $page->id, 'Page copy is not the same instance');
-    }
-
-    public function test_page_can_be_copied_without_edit_permission()
-    {
-        $page = Page::first();
-        $currentBook = $page->book;
-        $newBook = Book::where('id', '!=', $currentBook->id)->first();
-        $viewer = $this->getViewer();
-
-        $resp = $this->actingAs($viewer)->get($page->getUrl());
-        $resp->assertDontSee($page->getUrl('/copy'));
-
-        $newBook->created_by = $viewer->id;
-        $newBook->save();
-        $this->giveUserPermissions($viewer, ['page-create-own']);
-        $this->regenEntityPermissions($newBook);
-
-        $resp = $this->actingAs($viewer)->get($page->getUrl());
-        $resp->assertSee($page->getUrl('/copy'));
-
-        $movePageResp = $this->post($page->getUrl('/copy'), [
-            'entity_selection' => 'book:' . $newBook->id,
-            'name' => 'My copied test page'
-        ]);
-        $movePageResp->assertRedirect();
-
-        $this->assertDatabaseHas('pages', [
-            'name' => 'My copied test page',
-            'created_by' => $viewer->id,
-            'book_id' => $newBook->id,
-        ]);
-    }
-
 }
\ No newline at end of file
index 13876410a0cddccaf4e3d04783e5735ccfd78697..3ad10641ef3d0196ce15800d18d0ed149e3ed50a 100644 (file)
@@ -1,11 +1,12 @@
-<?php namespace Tests;
+<?php namespace Tests\Entity;
 
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
 use BookStack\Actions\Tag;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
 use BookStack\Auth\Permissions\PermissionService;
+use Tests\BrowserKitTest;
 
 class TagTest extends BrowserKitTest
 {
@@ -29,48 +30,6 @@ class TagTest extends BrowserKitTest
         return $entity;
     }
 
-    public function test_get_page_tags()
-    {
-        $page = $this->getEntityWithTags(Page::class);
-
-        // Add some other tags to check they don't interfere
-        factory(Tag::class, $this->defaultTagCount)->create();
-
-        $this->asAdmin()->get("/ajax/tags/get/page/" . $page->id)
-            ->shouldReturnJson();
-
-        $json = json_decode($this->response->getContent());
-        $this->assertTrue(count($json) === $this->defaultTagCount, "Returned JSON item count is not as expected");
-    }
-
-    public function test_get_chapter_tags()
-    {
-        $chapter = $this->getEntityWithTags(Chapter::class);
-
-        // Add some other tags to check they don't interfere
-        factory(Tag::class, $this->defaultTagCount)->create();
-
-        $this->asAdmin()->get("/ajax/tags/get/chapter/" . $chapter->id)
-            ->shouldReturnJson();
-
-        $json = json_decode($this->response->getContent());
-        $this->assertTrue(count($json) === $this->defaultTagCount, "Returned JSON item count is not as expected");
-    }
-
-    public function test_get_book_tags()
-    {
-        $book = $this->getEntityWithTags(Book::class);
-
-        // Add some other tags to check they don't interfere
-        factory(Tag::class, $this->defaultTagCount)->create();
-
-        $this->asAdmin()->get("/ajax/tags/get/book/" . $book->id)
-            ->shouldReturnJson();
-
-        $json = json_decode($this->response->getContent());
-        $this->assertTrue(count($json) === $this->defaultTagCount, "Returned JSON item count is not as expected");
-    }
-
     public function test_tag_name_suggestions()
     {
         // Create some tags with similar names to test with
index a5e4a4a5e58b9d1e237fa37240cca6d7079154de..1558df78d1c200d59b29d559ceef38f687b81e4f 100644 (file)
@@ -1,5 +1,8 @@
 <?php namespace Tests;
 
+use BookStack\Entities\Models\Book;
+use Illuminate\Support\Facades\Log;
+
 class ErrorTest extends TestCase
 {
 
@@ -18,4 +21,21 @@ class ErrorTest extends TestCase
         $notFound->assertDontSeeText('Log in');
         $notFound->assertSeeText('tester');
     }
+
+    public function test_item_not_found_does_not_get_logged_to_file()
+    {
+        $this->actingAs($this->getViewer());
+        $handler = $this->withTestLogger();
+        $book = Book::query()->first();
+
+        // Ensure we're seeing errors
+        Log::error('cat');
+        $this->assertTrue($handler->hasErrorThatContains('cat'));
+
+        $this->get('/books/arandomnotfouindbook');
+        $this->get($book->getUrl('/chapter/arandomnotfouindchapter'));
+        $this->get($book->getUrl('/chapter/arandomnotfouindpages'));
+
+        $this->assertCount(1, $handler->getRecords());
+    }
 }
\ No newline at end of file
index ada1f5aafde22b4929d31166be3d156a7c98e053..943a3160a1915af2a803ed88cd82411e67f2336d 100644 (file)
@@ -1,6 +1,6 @@
 <?php namespace Tests;
 
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Bookshelf;
 
 class HomepageTest extends TestCase
 {
@@ -98,16 +98,16 @@ class HomepageTest extends TestCase
     {
         $editor = $this->getEditor();
         setting()->putUser($editor, 'bookshelves_view_type', 'grid');
+        $shelf = Bookshelf::query()->firstOrFail();
 
         $this->setSettings(['app-homepage-type' => 'bookshelves']);
 
         $this->asEditor();
         $homeVisit = $this->get('/');
         $homeVisit->assertSee('Shelves');
-        $homeVisit->assertSee('bookshelf-grid-item grid-card');
         $homeVisit->assertSee('grid-card-content');
-        $homeVisit->assertSee('grid-card-footer');
         $homeVisit->assertSee('featured-image-container');
+        $homeVisit->assertElementContains('.grid-card', $shelf->name);
 
         $this->setSettings(['app-homepage-type' => false]);
         $this->test_default_homepage_visible();
index cd68756ae4365ad27ef4499f6a363b96570b474a..d5c6e453238ead0b3d82f0d805d23f7983fdf676 100644 (file)
@@ -11,7 +11,7 @@ class LanguageTest extends TestCase
     public function setUp(): void
     {
         parent::setUp();
-        $this->langs = array_diff(scandir(resource_path('lang')), ['..', '.', 'check.php', 'format.php']);
+        $this->langs = array_diff(scandir(resource_path('lang')), ['..', '.']);
     }
 
     public function test_locales_config_key_set_properly()
@@ -19,7 +19,21 @@ class LanguageTest extends TestCase
         $configLocales = config('app.locales');
         sort($configLocales);
         sort($this->langs);
-        $this->assertTrue(implode(':', $this->langs) === implode(':', $configLocales), 'app.locales configuration variable matches found lang files');
+        $this->assertEquals(implode(':', $configLocales), implode(':', $this->langs), 'app.locales configuration variable does not match those found in lang files');
+    }
+
+    // Not part of standard phpunit test runs since we sometimes expect non-added langs.
+    public function do_test_locales_all_have_language_dropdown_entry()
+    {
+        $dropdownLocales = array_keys(trans('settings.language_select', [], 'en'));
+        sort($dropdownLocales);
+        sort($this->langs);
+        $diffs = array_diff($this->langs, $dropdownLocales);
+        if (count($diffs) > 0) {
+            $diffText = implode(',', $diffs);
+            $this->addWarning("Languages: {$diffText} found in files but not in language select dropdown.");
+        }
+        $this->assertTrue(true);
     }
 
     public function test_correct_language_if_not_logged_in()
diff --git a/tests/Permissions/EntityOwnerChangeTest.php b/tests/Permissions/EntityOwnerChangeTest.php
new file mode 100644 (file)
index 0000000..2f06bff
--- /dev/null
@@ -0,0 +1,50 @@
+<?php namespace Tests\Permissions;
+
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Illuminate\Support\Str;
+use Tests\TestCase;
+
+class EntityOwnerChangeTest extends TestCase
+{
+
+    public function test_changing_page_owner()
+    {
+        $page = Page::query()->first();
+        $user = User::query()->where('id', '!=', $page->owned_by)->first();
+
+        $this->asAdmin()->put($page->getUrl('permissions'), ['owned_by' => $user->id]);
+        $this->assertDatabaseHas('pages', ['owned_by' => $user->id, 'id' => $page->id]);
+    }
+
+    public function test_changing_chapter_owner()
+    {
+        $chapter = Chapter::query()->first();
+        $user = User::query()->where('id', '!=', $chapter->owned_by)->first();
+
+        $this->asAdmin()->put($chapter->getUrl('permissions'), ['owned_by' => $user->id]);
+        $this->assertDatabaseHas('chapters', ['owned_by' => $user->id, 'id' => $chapter->id]);
+    }
+
+    public function test_changing_book_owner()
+    {
+        $book = Book::query()->first();
+        $user = User::query()->where('id', '!=', $book->owned_by)->first();
+
+        $this->asAdmin()->put($book->getUrl('permissions'), ['owned_by' => $user->id]);
+        $this->assertDatabaseHas('books', ['owned_by' => $user->id, 'id' => $book->id]);
+    }
+
+    public function test_changing_shelf_owner()
+    {
+        $shelf = Bookshelf::query()->first();
+        $user = User::query()->where('id', '!=', $shelf->owned_by)->first();
+
+        $this->asAdmin()->put($shelf->getUrl('permissions'), ['owned_by' => $user->id]);
+        $this->assertDatabaseHas('bookshelves', ['owned_by' => $user->id, 'id' => $shelf->id]);
+    }
+
+}
\ No newline at end of file
similarity index 95%
rename from tests/Permissions/RestrictionsTest.php
rename to tests/Permissions/EntityPermissionsTest.php
index d899c6396041a9d8a46d695efff0599ce0159a83..1e6d1cc325e6bff83ff55322dc05e0c99e0c22d1 100644 (file)
@@ -1,13 +1,15 @@
-<?php namespace Tests;
+<?php namespace Tests\Permissions;
 
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
 use BookStack\Auth\User;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
+use Illuminate\Support\Str;
+use Tests\BrowserKitTest;
 
-class RestrictionsTest extends BrowserKitTest
+class EntityPermissionsTest extends BrowserKitTest
 {
 
     /**
@@ -57,7 +59,7 @@ class RestrictionsTest extends BrowserKitTest
 
     public function test_bookshelf_update_restriction()
     {
-        $shelf = BookShelf::first();
+        $shelf = Bookshelf::first();
 
         $this->actingAs($this->user)
             ->visit($shelf->getUrl('/edit'))
@@ -489,6 +491,22 @@ class RestrictionsTest extends BrowserKitTest
             ->dontSee($page->name);
     }
 
+    public function test_restricted_chapter_pages_not_visible_on_book_page()
+    {
+        $chapter = Chapter::query()->first();
+        $this->actingAs($this->user)
+            ->visit($chapter->book->getUrl())
+            ->see($chapter->pages->first()->name);
+
+        foreach ($chapter->pages as $page) {
+            $this->setEntityRestrictions($page, []);
+        }
+
+        $this->actingAs($this->user)
+            ->visit($chapter->book->getUrl())
+            ->dontSee($chapter->pages->first()->name);
+    }
+
     public function test_bookshelf_update_restriction_override()
     {
         $shelf = Bookshelf::first();
@@ -625,11 +643,15 @@ class RestrictionsTest extends BrowserKitTest
     public function test_page_visible_if_has_permissions_when_book_not_visible()
     {
         $book = Book::first();
-
-        $this->setEntityRestrictions($book, []);
-
         $bookChapter = $book->chapters->first();
         $bookPage = $bookChapter->pages->first();
+
+        foreach ([$book, $bookChapter, $bookPage] as $entity) {
+            $entity->name = Str::random(24);
+            $entity->save();
+        }
+
+        $this->setEntityRestrictions($book, []);
         $this->setEntityRestrictions($bookPage, ['view']);
 
         $this->actingAs($this->viewer);
diff --git a/tests/Permissions/ExportPermissionsTest.php b/tests/Permissions/ExportPermissionsTest.php
new file mode 100644 (file)
index 0000000..e5a1146
--- /dev/null
@@ -0,0 +1,67 @@
+<?php namespace Tests\Permissions;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use Illuminate\Support\Str;
+use Tests\TestCase;
+
+class ExportPermissionsTest extends TestCase
+{
+
+    public function test_page_content_without_view_access_hidden_on_chapter_export()
+    {
+        $chapter = Chapter::query()->first();
+        $page = $chapter->pages()->firstOrFail();
+        $pageContent = Str::random(48);
+        $page->html = '<p>' . $pageContent . '</p>';
+        $page->save();
+        $viewer = $this->getViewer();
+        $this->actingAs($viewer);
+        $formats = ['html', 'plaintext'];
+
+        foreach ($formats as $format) {
+            $resp = $this->get($chapter->getUrl("export/{$format}"));
+            $resp->assertStatus(200);
+            $resp->assertSee($page->name);
+            $resp->assertSee($pageContent);
+        }
+
+        $this->setEntityRestrictions($page, []);
+
+        foreach ($formats as $format) {
+            $resp = $this->get($chapter->getUrl("export/{$format}"));
+            $resp->assertStatus(200);
+            $resp->assertDontSee($page->name);
+            $resp->assertDontSee($pageContent);
+        }
+    }
+
+    public function test_page_content_without_view_access_hidden_on_book_export()
+    {
+        $book = Book::query()->first();
+        $page = $book->pages()->firstOrFail();
+        $pageContent = Str::random(48);
+        $page->html = '<p>' . $pageContent . '</p>';
+        $page->save();
+        $viewer = $this->getViewer();
+        $this->actingAs($viewer);
+        $formats = ['html', 'plaintext'];
+
+        foreach ($formats as $format) {
+            $resp = $this->get($book->getUrl("export/{$format}"));
+            $resp->assertStatus(200);
+            $resp->assertSee($page->name);
+            $resp->assertSee($pageContent);
+        }
+
+        $this->setEntityRestrictions($page, []);
+
+        foreach ($formats as $format) {
+            $resp = $this->get($book->getUrl("export/{$format}"));
+            $resp->assertStatus(200);
+            $resp->assertDontSee($page->name);
+            $resp->assertDontSee($pageContent);
+        }
+    }
+
+}
\ No newline at end of file
index 371cffc0f3bd1be95a263c2bffd4c5f4277bb8f2..8398d08281a12d4e0239ae1531f4817d86b0c1a5 100644 (file)
@@ -1,11 +1,15 @@
-<?php namespace Tests;
-
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Page;
-use BookStack\Auth\Permissions\PermissionsRepo;
+<?php namespace Tests\Permissions;
+
+use BookStack\Actions\Comment;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
 use BookStack\Auth\Role;
+use BookStack\Uploads\Image;
 use Laravel\BrowserKitTesting\HttpException;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Tests\BrowserKitTest;
 
 class RolesTest extends BrowserKitTest
 {
@@ -24,7 +28,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_cannot_delete_admin_role()
     {
-        $adminRole = \BookStack\Auth\Role::getRole('admin');
+        $adminRole = Role::getRole('admin');
         $deletePageUrl = '/settings/roles/delete/' . $adminRole->id;
         $this->asAdmin()->visit($deletePageUrl)
             ->press('Confirm')
@@ -58,7 +62,7 @@ class RolesTest extends BrowserKitTest
             ->type('Test Role', 'display_name')
             ->type('A little test description', 'description')
             ->press('Save Role')
-            ->seeInDatabase('roles', ['display_name' => $testRoleName, 'name' => 'test-role', 'description' => $testRoleDesc])
+            ->seeInDatabase('roles', ['display_name' => $testRoleName, 'description' => $testRoleDesc])
             ->seePageIs('/settings/roles');
         // Updating
         $this->asAdmin()->visit('/settings/roles')
@@ -66,7 +70,7 @@ class RolesTest extends BrowserKitTest
             ->click($testRoleName)
             ->type($testRoleUpdateName, '#display_name')
             ->press('Save Role')
-            ->seeInDatabase('roles', ['display_name' => $testRoleUpdateName, 'name' => 'test-role', 'description' => $testRoleDesc])
+            ->seeInDatabase('roles', ['display_name' => $testRoleUpdateName, 'description' => $testRoleDesc])
             ->seePageIs('/settings/roles');
         // Deleting
         $this->asAdmin()->visit('/settings/roles')
@@ -100,6 +104,25 @@ class RolesTest extends BrowserKitTest
         $this->see('This user is the only user assigned to the administrator role');
     }
 
+    public function test_migrate_users_on_delete_works()
+    {
+        $roleA = Role::query()->create(['display_name' => 'Delete Test A']);
+        $roleB = Role::query()->create(['display_name' => 'Delete Test B']);
+        $this->user->attachRole($roleB);
+
+        $this->assertCount(0, $roleA->users()->get());
+        $this->assertCount(1, $roleB->users()->get());
+
+        $deletePage = $this->asAdmin()->get("/settings/roles/delete/{$roleB->id}");
+        $deletePage->seeElement('select[name=migrate_role_id]');
+        $this->asAdmin()->delete("/settings/roles/delete/{$roleB->id}", [
+            'migrate_role_id' => $roleA->id,
+        ]);
+
+        $this->assertCount(1, $roleA->users()->get());
+        $this->assertEquals($this->user->id, $roleA->users()->first()->id);
+    }
+
     public function test_manage_user_permission()
     {
         $this->actingAs($this->user)->visit('/settings/users')
@@ -177,7 +200,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_restrictions_manage_all_permission()
     {
-        $page = \BookStack\Entities\Page::take(1)->get()->first();
+        $page = Page::take(1)->get()->first();
         $this->actingAs($this->user)->visit($page->getUrl())
             ->dontSee('Permissions')
             ->visit($page->getUrl() . '/permissions')
@@ -191,17 +214,25 @@ class RolesTest extends BrowserKitTest
 
     public function test_restrictions_manage_own_permission()
     {
-        $otherUsersPage = \BookStack\Entities\Page::first();
+        $otherUsersPage = Page::first();
         $content = $this->createEntityChainBelongingToUser($this->user);
+
+        // Set a different creator on the page we're checking to ensure
+        // that the owner fields are checked
+        $page = $content['page']; /** @var Page $page */
+        $page->created_by = $otherUsersPage->id;
+        $page->owned_by = $this->user->id;
+        $page->save();
+
         // Check can't restrict other's content
         $this->actingAs($this->user)->visit($otherUsersPage->getUrl())
             ->dontSee('Permissions')
             ->visit($otherUsersPage->getUrl() . '/permissions')
             ->seePageIs('/');
         // Check can't restrict own content
-        $this->actingAs($this->user)->visit($content['page']->getUrl())
+        $this->actingAs($this->user)->visit($page->getUrl())
             ->dontSee('Permissions')
-            ->visit($content['page']->getUrl() . '/permissions')
+            ->visit($page->getUrl() . '/permissions')
             ->seePageIs('/');
 
         $this->giveUserPermissions($this->user, ['restrictions-manage-own']);
@@ -212,10 +243,10 @@ class RolesTest extends BrowserKitTest
             ->visit($otherUsersPage->getUrl() . '/permissions')
             ->seePageIs('/');
         // Check can restrict own content
-        $this->actingAs($this->user)->visit($content['page']->getUrl())
+        $this->actingAs($this->user)->visit($page->getUrl())
             ->see('Permissions')
             ->click('Permissions')
-            ->seePageIs($content['page']->getUrl() . '/permissions');
+            ->seePageIs($page->getUrl() . '/permissions');
     }
 
     /**
@@ -266,7 +297,7 @@ class RolesTest extends BrowserKitTest
     {
         $otherShelf = Bookshelf::first();
         $ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
-        $ownShelf->forceFill(['created_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
+        $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
         $this->regenEntityPermissions($ownShelf);
 
         $this->checkAccessPermission('bookshelf-update-own', [
@@ -283,7 +314,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_bookshelves_edit_all_permission()
     {
-        $otherShelf = \BookStack\Entities\Bookshelf::first();
+        $otherShelf = Bookshelf::first();
         $this->checkAccessPermission('bookshelf-update-all', [
             $otherShelf->getUrl('/edit')
         ], [
@@ -294,9 +325,9 @@ class RolesTest extends BrowserKitTest
     public function test_bookshelves_delete_own_permission()
     {
         $this->giveUserPermissions($this->user, ['bookshelf-update-all']);
-        $otherShelf = \BookStack\Entities\Bookshelf::first();
+        $otherShelf = Bookshelf::first();
         $ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
-        $ownShelf->forceFill(['created_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
+        $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
         $this->regenEntityPermissions($ownShelf);
 
         $this->checkAccessPermission('bookshelf-delete-own', [
@@ -318,7 +349,7 @@ class RolesTest extends BrowserKitTest
     public function test_bookshelves_delete_all_permission()
     {
         $this->giveUserPermissions($this->user, ['bookshelf-update-all']);
-        $otherShelf = \BookStack\Entities\Bookshelf::first();
+        $otherShelf = Bookshelf::first();
         $this->checkAccessPermission('bookshelf-delete-all', [
             $otherShelf->getUrl('/delete')
         ], [
@@ -348,7 +379,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_books_edit_own_permission()
     {
-        $otherBook = \BookStack\Entities\Book::take(1)->get()->first();
+        $otherBook = Book::take(1)->get()->first();
         $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
         $this->checkAccessPermission('book-update-own', [
             $ownBook->getUrl() . '/edit'
@@ -364,7 +395,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_books_edit_all_permission()
     {
-        $otherBook = \BookStack\Entities\Book::take(1)->get()->first();
+        $otherBook = Book::take(1)->get()->first();
         $this->checkAccessPermission('book-update-all', [
             $otherBook->getUrl() . '/edit'
         ], [
@@ -375,7 +406,7 @@ class RolesTest extends BrowserKitTest
     public function test_books_delete_own_permission()
     {
         $this->giveUserPermissions($this->user, ['book-update-all']);
-        $otherBook = \BookStack\Entities\Book::take(1)->get()->first();
+        $otherBook = Book::take(1)->get()->first();
         $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
         $this->checkAccessPermission('book-delete-own', [
             $ownBook->getUrl() . '/delete'
@@ -396,7 +427,7 @@ class RolesTest extends BrowserKitTest
     public function test_books_delete_all_permission()
     {
         $this->giveUserPermissions($this->user, ['book-update-all']);
-        $otherBook = \BookStack\Entities\Book::take(1)->get()->first();
+        $otherBook = Book::take(1)->get()->first();
         $this->checkAccessPermission('book-delete-all', [
             $otherBook->getUrl() . '/delete'
         ], [
@@ -411,7 +442,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_chapter_create_own_permissions()
     {
-        $book = \BookStack\Entities\Book::take(1)->get()->first();
+        $book = Book::take(1)->get()->first();
         $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
         $this->checkAccessPermission('chapter-create-own', [
             $ownBook->getUrl('/create-chapter')
@@ -433,7 +464,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_chapter_create_all_permissions()
     {
-        $book = \BookStack\Entities\Book::take(1)->get()->first();
+        $book = Book::take(1)->get()->first();
         $this->checkAccessPermission('chapter-create-all', [
             $book->getUrl('/create-chapter')
         ], [
@@ -449,7 +480,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_chapter_edit_own_permission()
     {
-        $otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+        $otherChapter = Chapter::take(1)->get()->first();
         $ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
         $this->checkAccessPermission('chapter-update-own', [
             $ownChapter->getUrl() . '/edit'
@@ -465,7 +496,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_chapter_edit_all_permission()
     {
-        $otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+        $otherChapter = Chapter::take(1)->get()->first();
         $this->checkAccessPermission('chapter-update-all', [
             $otherChapter->getUrl() . '/edit'
         ], [
@@ -476,7 +507,7 @@ class RolesTest extends BrowserKitTest
     public function test_chapter_delete_own_permission()
     {
         $this->giveUserPermissions($this->user, ['chapter-update-all']);
-        $otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+        $otherChapter = Chapter::take(1)->get()->first();
         $ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
         $this->checkAccessPermission('chapter-delete-own', [
             $ownChapter->getUrl() . '/delete'
@@ -498,7 +529,7 @@ class RolesTest extends BrowserKitTest
     public function test_chapter_delete_all_permission()
     {
         $this->giveUserPermissions($this->user, ['chapter-update-all']);
-        $otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+        $otherChapter = Chapter::take(1)->get()->first();
         $this->checkAccessPermission('chapter-delete-all', [
             $otherChapter->getUrl() . '/delete'
         ], [
@@ -514,8 +545,8 @@ class RolesTest extends BrowserKitTest
 
     public function test_page_create_own_permissions()
     {
-        $book = \BookStack\Entities\Book::first();
-        $chapter = \BookStack\Entities\Chapter::first();
+        $book = Book::first();
+        $chapter = Chapter::first();
 
         $entities = $this->createEntityChainBelongingToUser($this->user);
         $ownBook = $entities['book'];
@@ -539,7 +570,7 @@ class RolesTest extends BrowserKitTest
 
         foreach ($accessUrls as $index => $url) {
             $this->actingAs($this->user)->visit($url);
-            $expectedUrl = \BookStack\Entities\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
+            $expectedUrl = Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
             $this->seePageIs($expectedUrl);
         }
 
@@ -561,8 +592,8 @@ class RolesTest extends BrowserKitTest
 
     public function test_page_create_all_permissions()
     {
-        $book = \BookStack\Entities\Book::take(1)->get()->first();
-        $chapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+        $book = Book::take(1)->get()->first();
+        $chapter = Chapter::take(1)->get()->first();
         $baseUrl = $book->getUrl() . '/page';
         $createUrl = $book->getUrl('/create-page');
 
@@ -583,7 +614,7 @@ class RolesTest extends BrowserKitTest
 
         foreach ($accessUrls as $index => $url) {
             $this->actingAs($this->user)->visit($url);
-            $expectedUrl = \BookStack\Entities\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
+            $expectedUrl = Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
             $this->seePageIs($expectedUrl);
         }
 
@@ -602,7 +633,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_page_edit_own_permission()
     {
-        $otherPage = \BookStack\Entities\Page::take(1)->get()->first();
+        $otherPage = Page::take(1)->get()->first();
         $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
         $this->checkAccessPermission('page-update-own', [
             $ownPage->getUrl() . '/edit'
@@ -618,7 +649,7 @@ class RolesTest extends BrowserKitTest
 
     public function test_page_edit_all_permission()
     {
-        $otherPage = \BookStack\Entities\Page::take(1)->get()->first();
+        $otherPage = Page::take(1)->get()->first();
         $this->checkAccessPermission('page-update-all', [
             $otherPage->getUrl() . '/edit'
         ], [
@@ -629,7 +660,7 @@ class RolesTest extends BrowserKitTest
     public function test_page_delete_own_permission()
     {
         $this->giveUserPermissions($this->user, ['page-update-all']);
-        $otherPage = \BookStack\Entities\Page::take(1)->get()->first();
+        $otherPage = Page::take(1)->get()->first();
         $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
         $this->checkAccessPermission('page-delete-own', [
             $ownPage->getUrl() . '/delete'
@@ -637,40 +668,42 @@ class RolesTest extends BrowserKitTest
             $ownPage->getUrl() => 'Delete'
         ]);
 
-        $bookUrl = $ownPage->book->getUrl();
+        $parent = $ownPage->chapter ?? $ownPage->book;
         $this->visit($otherPage->getUrl())
             ->dontSeeInElement('.action-buttons', 'Delete')
             ->visit($otherPage->getUrl() . '/delete')
             ->seePageIs('/');
         $this->visit($ownPage->getUrl())->visit($ownPage->getUrl() . '/delete')
             ->press('Confirm')
-            ->seePageIs($bookUrl)
+            ->seePageIs($parent->getUrl())
             ->dontSeeInElement('.book-content', $ownPage->name);
     }
 
     public function test_page_delete_all_permission()
     {
         $this->giveUserPermissions($this->user, ['page-update-all']);
-        $otherPage = \BookStack\Entities\Page::take(1)->get()->first();
+        $otherPage = Page::take(1)->get()->first();
         $this->checkAccessPermission('page-delete-all', [
             $otherPage->getUrl() . '/delete'
         ], [
             $otherPage->getUrl() => 'Delete'
         ]);
 
-        $bookUrl = $otherPage->book->getUrl();
+        $parent = $otherPage->chapter ?? $otherPage->book;
         $this->visit($otherPage->getUrl())->visit($otherPage->getUrl() . '/delete')
             ->press('Confirm')
-            ->seePageIs($bookUrl)
+            ->seePageIs($parent->getUrl())
             ->dontSeeInElement('.book-content', $otherPage->name);
     }
 
     public function test_public_role_visible_in_user_edit_screen()
     {
-        $user = \BookStack\Auth\User::first();
+        $user = User::first();
+        $adminRole = Role::getSystemRole('admin');
+        $publicRole = Role::getSystemRole('public');
         $this->asAdmin()->visit('/settings/users/' . $user->id)
-            ->seeElement('[name="roles[admin]"]')
-            ->seeElement('[name="roles[public]"]');
+            ->seeElement('[name="roles['.$adminRole->id.']"]')
+            ->seeElement('[name="roles['.$publicRole->id.']"]');
     }
 
     public function test_public_role_visible_in_role_listing()
@@ -683,9 +716,8 @@ class RolesTest extends BrowserKitTest
     public function test_public_role_visible_in_default_role_setting()
     {
         $this->asAdmin()->visit('/settings')
-            ->seeElement('[data-role-name="admin"]')
-            ->seeElement('[data-role-name="public"]');
-
+            ->seeElement('[data-system-role-name="admin"]')
+            ->seeElement('[data-system-role-name="public"]');
     }
 
     public function test_public_role_not_deleteable()
@@ -702,8 +734,8 @@ class RolesTest extends BrowserKitTest
     public function test_image_delete_own_permission()
     {
         $this->giveUserPermissions($this->user, ['image-update-all']);
-        $page = \BookStack\Entities\Page::first();
-        $image = factory(\BookStack\Uploads\Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $this->user->id, 'updated_by' => $this->user->id]);
+        $page = Page::first();
+        $image = factory(Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $this->user->id, 'updated_by' => $this->user->id]);
 
         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)
             ->seeStatusCode(403);
@@ -719,8 +751,8 @@ class RolesTest extends BrowserKitTest
     {
         $this->giveUserPermissions($this->user, ['image-update-all']);
         $admin = $this->getAdmin();
-        $page = \BookStack\Entities\Page::first();
-        $image = factory(\BookStack\Uploads\Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $admin->id, 'updated_by' => $admin->id]);
+        $page = Page::first();
+        $image = factory(Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $admin->id, 'updated_by' => $admin->id]);
 
         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)
             ->seeStatusCode(403);
@@ -741,7 +773,7 @@ class RolesTest extends BrowserKitTest
     {
         // To cover issue fixed in f99c8ff99aee9beb8c692f36d4b84dc6e651e50a.
         $page = Page::first();
-        $viewerRole = \BookStack\Auth\Role::getRole('viewer');
+        $viewerRole = Role::getRole('viewer');
         $viewer = $this->getViewer();
         $this->actingAs($viewer)->visit($page->getUrl())->assertResponseStatus(200);
 
@@ -759,14 +791,14 @@ class RolesTest extends BrowserKitTest
     {
         $admin = $this->getAdmin();
         // Book links
-        $book = factory(\BookStack\Entities\Book::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id]);
+        $book = factory(Book::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id]);
         $this->updateEntityPermissions($book);
         $this->actingAs($this->getViewer())->visit($book->getUrl())
             ->dontSee('Create a new page')
             ->dontSee('Add a chapter');
 
         // Chapter links
-        $chapter = factory(\BookStack\Entities\Chapter::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id, 'book_id' => $book->id]);
+        $chapter = factory(Chapter::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id, 'book_id' => $book->id]);
         $this->updateEntityPermissions($chapter);
         $this->actingAs($this->getViewer())->visit($chapter->getUrl())
             ->dontSee('Create a new page')
@@ -850,8 +882,8 @@ class RolesTest extends BrowserKitTest
     }
 
     private function addComment($page) {
-        $comment = factory(\BookStack\Actions\Comment::class)->make();
-        $url = "/ajax/page/$page->id/comment";
+        $comment = factory(Comment::class)->make();
+        $url = "/comment/$page->id";
         $request = [
             'text' => $comment->text,
             'html' => $comment->html
@@ -863,8 +895,8 @@ class RolesTest extends BrowserKitTest
     }
 
     private function updateComment($commentId) {
-        $comment = factory(\BookStack\Actions\Comment::class)->make();
-        $url = "/ajax/comment/$commentId";
+        $comment = factory(Comment::class)->make();
+        $url = "/comment/$commentId";
         $request = [
             'text' => $comment->text,
             'html' => $comment->html
@@ -874,7 +906,7 @@ class RolesTest extends BrowserKitTest
     }
 
     private function deleteComment($commentId) {
-         $url = '/ajax/comment/' . $commentId;
+         $url = '/comment/' . $commentId;
          return $this->json('DELETE', $url);
     }
 
index 27b4822fa2bce01f5379b50e3558333c01846003..1941901240b5288d628a8264c5bd83a1bef0b074 100644 (file)
@@ -1,16 +1,25 @@
 <?php namespace Tests;
 
+use Auth;
+use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\RolePermission;
+use BookStack\Auth\Role;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+
 class PublicActionTest extends BrowserKitTest
 {
 
     public function test_app_not_public()
     {
         $this->setSettings(['app-public' => 'false']);
-        $book = \BookStack\Entities\Book::orderBy('name', 'asc')->first();
+        $book = Book::orderBy('name', 'asc')->first();
         $this->visit('/books')->seePageIs('/login');
         $this->visit($book->getUrl())->seePageIs('/login');
 
-        $page = \BookStack\Entities\Page::first();
+        $page = Page::first();
         $this->visit($page->getUrl())->seePageIs('/login');
     }
 
@@ -35,7 +44,7 @@ class PublicActionTest extends BrowserKitTest
     public function test_books_viewable()
     {
         $this->setSettings(['app-public' => 'true']);
-        $books = \BookStack\Entities\Book::orderBy('name', 'asc')->take(10)->get();
+        $books = Book::orderBy('name', 'asc')->take(10)->get();
         $bookToVisit = $books[1];
 
         // Check books index page is showing
@@ -52,7 +61,7 @@ class PublicActionTest extends BrowserKitTest
     public function test_chapters_viewable()
     {
         $this->setSettings(['app-public' => 'true']);
-        $chapterToVisit = \BookStack\Entities\Chapter::first();
+        $chapterToVisit = Chapter::first();
         $pageToVisit = $chapterToVisit->pages()->first();
 
         // Check chapters index page is showing
@@ -70,15 +79,15 @@ class PublicActionTest extends BrowserKitTest
     public function test_public_page_creation()
     {
         $this->setSettings(['app-public' => 'true']);
-        $publicRole = \BookStack\Auth\Role::getSystemRole('public');
+        $publicRole = Role::getSystemRole('public');
         // Grant all permissions to public
         $publicRole->permissions()->detach();
-        foreach (\BookStack\Auth\Permissions\RolePermission::all() as $perm) {
+        foreach (RolePermission::all() as $perm) {
             $publicRole->attachPermission($perm);
         }
-        $this->app[\BookStack\Auth\Permissions\PermissionService::class]->buildJointPermissionForRole($publicRole);
+        $this->app[PermissionService::class]->buildJointPermissionForRole($publicRole);
 
-        $chapter = \BookStack\Entities\Chapter::first();
+        $chapter = Chapter::first();
         $this->visit($chapter->book->getUrl());
         $this->visit($chapter->getUrl())
             ->click('New Page')
@@ -89,7 +98,7 @@ class PublicActionTest extends BrowserKitTest
             'name' => 'My guest page'
         ])->seePageIs($chapter->book->getUrl('/page/my-guest-page/edit'));
 
-        $user = \BookStack\Auth\User::getDefault();
+        $user = User::getDefault();
         $this->seeInDatabase('pages', [
             'name' => 'My guest page',
             'chapter_id' => $chapter->id,
@@ -100,9 +109,9 @@ class PublicActionTest extends BrowserKitTest
 
     public function test_content_not_listed_on_404_for_public_users()
     {
-        $page = \BookStack\Entities\Page::first();
+        $page = Page::first();
         $this->asAdmin()->visit($page->getUrl());
-        \Auth::logout();
+        Auth::logout();
         view()->share('pageTitle', '');
         $this->forceVisit('/cats/dogs/hippos');
         $this->dontSee($page->name);
@@ -139,4 +148,36 @@ class PublicActionTest extends BrowserKitTest
         $this->seeText("User-agent: *\nDisallow: /");
     }
 
+    public function test_public_view_then_login_redirects_to_previous_content()
+    {
+        $this->setSettings(['app-public' => 'true']);
+        $book = Book::query()->first();
+        $this->visit($book->getUrl())
+            ->see($book->name)
+            ->visit('/login')
+            ->type('[email protected]', '#email')
+            ->type('password', '#password')
+            ->press('Log In')
+            ->seePageUrlIs($book->getUrl());
+    }
+
+    public function test_access_hidden_content_then_login_redirects_to_intended_content()
+    {
+        $this->setSettings(['app-public' => 'true']);
+        $book = Book::query()->first();
+        $this->setEntityRestrictions($book);
+
+        try {
+            $this->visit($book->getUrl());
+        } catch (\Exception $exception) {}
+
+        $this->see('Book not found')
+            ->dontSee($book->name)
+            ->visit('/login')
+            ->type('[email protected]', '#email')
+            ->type('password', '#password')
+            ->press('Log In')
+            ->seePageUrlIs($book->getUrl())
+            ->see($book->name);
+    }
 }
\ No newline at end of file
diff --git a/tests/RecycleBinTest.php b/tests/RecycleBinTest.php
new file mode 100644 (file)
index 0000000..60f06cf
--- /dev/null
@@ -0,0 +1,232 @@
+<?php namespace Tests;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Deletion;
+use BookStack\Entities\Models\Page;
+use DB;
+use Illuminate\Support\Carbon;
+
+class RecycleBinTest extends TestCase
+{
+    public function test_recycle_bin_routes_permissions()
+    {
+        $page = Page::query()->first();
+        $editor = $this->getEditor();
+        $this->actingAs($editor)->delete($page->getUrl());
+        $deletion = Deletion::query()->firstOrFail();
+
+        $routes = [
+            'GET:/settings/recycle-bin',
+            'POST:/settings/recycle-bin/empty',
+            "GET:/settings/recycle-bin/{$deletion->id}/destroy",
+            "GET:/settings/recycle-bin/{$deletion->id}/restore",
+            "POST:/settings/recycle-bin/{$deletion->id}/restore",
+            "DELETE:/settings/recycle-bin/{$deletion->id}",
+        ];
+
+        foreach($routes as $route) {
+            [$method, $url] = explode(':', $route);
+            $resp = $this->call($method, $url);
+            $this->assertPermissionError($resp);
+        }
+
+        $this->giveUserPermissions($editor, ['restrictions-manage-all']);
+
+        foreach($routes as $route) {
+            [$method, $url] = explode(':', $route);
+            $resp = $this->call($method, $url);
+            $this->assertPermissionError($resp);
+        }
+
+        $this->giveUserPermissions($editor, ['settings-manage']);
+
+        foreach($routes as $route) {
+            DB::beginTransaction();
+            [$method, $url] = explode(':', $route);
+            $resp = $this->call($method, $url);
+            $this->assertNotPermissionError($resp);
+            DB::rollBack();
+        }
+
+    }
+
+    public function test_recycle_bin_view()
+    {
+        $page = Page::query()->first();
+        $book = Book::query()->whereHas('pages')->whereHas('chapters')->withCount(['pages', 'chapters'])->first();
+        $editor = $this->getEditor();
+        $this->actingAs($editor)->delete($page->getUrl());
+        $this->actingAs($editor)->delete($book->getUrl());
+
+        $viewReq = $this->asAdmin()->get('/settings/recycle-bin');
+        $viewReq->assertElementContains('table.table', $page->name);
+        $viewReq->assertElementContains('table.table', $editor->name);
+        $viewReq->assertElementContains('table.table', $book->name);
+        $viewReq->assertElementContains('table.table', $book->pages_count . ' Pages');
+        $viewReq->assertElementContains('table.table', $book->chapters_count . ' Chapters');
+    }
+
+    public function test_recycle_bin_empty()
+    {
+        $page = Page::query()->first();
+        $book = Book::query()->where('id' , '!=', $page->book_id)->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail();
+        $editor = $this->getEditor();
+        $this->actingAs($editor)->delete($page->getUrl());
+        $this->actingAs($editor)->delete($book->getUrl());
+
+        $this->assertTrue(Deletion::query()->count() === 2);
+        $emptyReq = $this->asAdmin()->post('/settings/recycle-bin/empty');
+        $emptyReq->assertRedirect('/settings/recycle-bin');
+
+        $this->assertTrue(Deletion::query()->count() === 0);
+        $this->assertDatabaseMissing('books', ['id' => $book->id]);
+        $this->assertDatabaseMissing('pages', ['id' => $page->id]);
+        $this->assertDatabaseMissing('pages', ['id' => $book->pages->first()->id]);
+        $this->assertDatabaseMissing('chapters', ['id' => $book->chapters->first()->id]);
+
+        $itemCount = 2 + $book->pages->count() + $book->chapters->count();
+        $redirectReq = $this->get('/settings/recycle-bin');
+        $redirectReq->assertNotificationContains('Deleted '.$itemCount.' total items from the recycle bin');
+    }
+
+    public function test_entity_restore()
+    {
+        $book = Book::query()->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail();
+        $this->asEditor()->delete($book->getUrl());
+        $deletion = Deletion::query()->firstOrFail();
+
+        $this->assertEquals($book->pages->count(), DB::table('pages')->where('book_id', '=', $book->id)->whereNotNull('deleted_at')->count());
+        $this->assertEquals($book->chapters->count(), DB::table('chapters')->where('book_id', '=', $book->id)->whereNotNull('deleted_at')->count());
+
+        $restoreReq = $this->asAdmin()->post("/settings/recycle-bin/{$deletion->id}/restore");
+        $restoreReq->assertRedirect('/settings/recycle-bin');
+        $this->assertTrue(Deletion::query()->count() === 0);
+
+        $this->assertEquals($book->pages->count(), DB::table('pages')->where('book_id', '=', $book->id)->whereNull('deleted_at')->count());
+        $this->assertEquals($book->chapters->count(), DB::table('chapters')->where('book_id', '=', $book->id)->whereNull('deleted_at')->count());
+
+        $itemCount = 1 + $book->pages->count() + $book->chapters->count();
+        $redirectReq = $this->get('/settings/recycle-bin');
+        $redirectReq->assertNotificationContains('Restored '.$itemCount.' total items from the recycle bin');
+    }
+
+    public function test_permanent_delete()
+    {
+        $book = Book::query()->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail();
+        $this->asEditor()->delete($book->getUrl());
+        $deletion = Deletion::query()->firstOrFail();
+
+        $deleteReq = $this->asAdmin()->delete("/settings/recycle-bin/{$deletion->id}");
+        $deleteReq->assertRedirect('/settings/recycle-bin');
+        $this->assertTrue(Deletion::query()->count() === 0);
+
+        $this->assertDatabaseMissing('books', ['id' => $book->id]);
+        $this->assertDatabaseMissing('pages', ['id' => $book->pages->first()->id]);
+        $this->assertDatabaseMissing('chapters', ['id' => $book->chapters->first()->id]);
+
+        $itemCount = 1 + $book->pages->count() + $book->chapters->count();
+        $redirectReq = $this->get('/settings/recycle-bin');
+        $redirectReq->assertNotificationContains('Deleted '.$itemCount.' total items from the recycle bin');
+    }
+
+    public function test_permanent_entity_delete_updates_existing_activity_with_entity_name()
+    {
+        $page = Page::query()->firstOrFail();
+        $this->asEditor()->delete($page->getUrl());
+        $deletion = $page->deletions()->firstOrFail();
+
+        $this->assertDatabaseHas('activities', [
+            'type' => 'page_delete',
+            'entity_id' => $page->id,
+            'entity_type' => $page->getMorphClass(),
+        ]);
+
+        $this->asAdmin()->delete("/settings/recycle-bin/{$deletion->id}");
+
+        $this->assertDatabaseMissing('activities', [
+            'type' => 'page_delete',
+            'entity_id' => $page->id,
+            'entity_type' => $page->getMorphClass(),
+        ]);
+
+        $this->assertDatabaseHas('activities', [
+            'type' => 'page_delete',
+            'entity_id' => null,
+            'entity_type' => null,
+            'detail' => $page->name,
+        ]);
+    }
+
+    public function test_auto_clear_functionality_works()
+    {
+        config()->set('app.recycle_bin_lifetime', 5);
+        $page = Page::query()->firstOrFail();
+        $otherPage = Page::query()->where('id', '!=', $page->id)->firstOrFail();
+
+        $this->asEditor()->delete($page->getUrl());
+        $this->assertDatabaseHas('pages', ['id' => $page->id]);
+        $this->assertEquals(1, Deletion::query()->count());
+
+        Carbon::setTestNow(Carbon::now()->addDays(6));
+        $this->asEditor()->delete($otherPage->getUrl());
+        $this->assertEquals(1, Deletion::query()->count());
+
+        $this->assertDatabaseMissing('pages', ['id' => $page->id]);
+    }
+
+    public function test_auto_clear_functionality_with_negative_time_keeps_forever()
+    {
+        config()->set('app.recycle_bin_lifetime', -1);
+        $page = Page::query()->firstOrFail();
+        $otherPage = Page::query()->where('id', '!=', $page->id)->firstOrFail();
+
+        $this->asEditor()->delete($page->getUrl());
+        $this->assertEquals(1, Deletion::query()->count());
+
+        Carbon::setTestNow(Carbon::now()->addDays(6000));
+        $this->asEditor()->delete($otherPage->getUrl());
+        $this->assertEquals(2, Deletion::query()->count());
+
+        $this->assertDatabaseHas('pages', ['id' => $page->id]);
+    }
+
+    public function test_auto_clear_functionality_with_zero_time_deletes_instantly()
+    {
+        config()->set('app.recycle_bin_lifetime', 0);
+        $page = Page::query()->firstOrFail();
+
+        $this->asEditor()->delete($page->getUrl());
+        $this->assertDatabaseMissing('pages', ['id' => $page->id]);
+        $this->assertEquals(0, Deletion::query()->count());
+    }
+
+    public function test_restore_flow_when_restoring_nested_delete_first()
+    {
+        $book = Book::query()->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail();
+        $chapter = $book->chapters->first();
+        $this->asEditor()->delete($chapter->getUrl());
+        $this->asEditor()->delete($book->getUrl());
+
+        $bookDeletion = $book->deletions()->first();
+        $chapterDeletion = $chapter->deletions()->first();
+
+        $chapterRestoreView = $this->asAdmin()->get("/settings/recycle-bin/{$chapterDeletion->id}/restore");
+        $chapterRestoreView->assertStatus(200);
+        $chapterRestoreView->assertSeeText($chapter->name);
+
+        $chapterRestore = $this->post("/settings/recycle-bin/{$chapterDeletion->id}/restore");
+        $chapterRestore->assertRedirect("/settings/recycle-bin");
+        $this->assertDatabaseMissing("deletions", ["id" => $chapterDeletion->id]);
+
+        $chapter->refresh();
+        $this->assertNotNull($chapter->deleted_at);
+
+        $bookRestoreView = $this->asAdmin()->get("/settings/recycle-bin/{$bookDeletion->id}/restore");
+        $bookRestoreView->assertStatus(200);
+        $bookRestoreView->assertSeeText($chapter->name);
+
+        $this->post("/settings/recycle-bin/{$bookDeletion->id}/restore");
+        $chapter->refresh();
+        $this->assertNull($chapter->deleted_at);
+    }
+}
\ No newline at end of file
diff --git a/tests/SecurityHeaderTest.php b/tests/SecurityHeaderTest.php
new file mode 100644 (file)
index 0000000..db095ff
--- /dev/null
@@ -0,0 +1,71 @@
+<?php namespace Tests;
+
+
+use Illuminate\Support\Str;
+
+class SecurityHeaderTest extends TestCase
+{
+
+    public function test_cookies_samesite_lax_by_default()
+    {
+        $resp = $this->get("/");
+        foreach ($resp->headers->getCookies() as $cookie) {
+            $this->assertEquals("lax", $cookie->getSameSite());
+        }
+    }
+
+    public function test_cookies_samesite_none_when_iframe_hosts_set()
+    {
+        $this->runWithEnv("ALLOWED_IFRAME_HOSTS", "http://example.com", function() {
+            $resp = $this->get("/");
+            foreach ($resp->headers->getCookies() as $cookie) {
+                $this->assertEquals("none", $cookie->getSameSite());
+            }
+        });
+    }
+
+    public function test_secure_cookies_controlled_by_app_url()
+    {
+        $this->runWithEnv("APP_URL", "http://example.com", function() {
+            $resp = $this->get("/");
+            foreach ($resp->headers->getCookies() as $cookie) {
+                $this->assertFalse($cookie->isSecure());
+            }
+        });
+
+        $this->runWithEnv("APP_URL", "https://example.com", function() {
+            $resp = $this->get("/");
+            foreach ($resp->headers->getCookies() as $cookie) {
+                $this->assertTrue($cookie->isSecure());
+            }
+        });
+    }
+
+    public function test_iframe_csp_self_only_by_default()
+    {
+        $resp = $this->get("/");
+        $cspHeaders = collect($resp->headers->get('Content-Security-Policy'));
+        $frameHeaders = $cspHeaders->filter(function ($val) {
+            return Str::startsWith($val, 'frame-ancestors');
+        });
+
+        $this->assertTrue($frameHeaders->count() === 1);
+        $this->assertEquals('frame-ancestors \'self\'', $frameHeaders->first());
+    }
+
+    public function test_iframe_csp_includes_extra_hosts_if_configured()
+    {
+        $this->runWithEnv("ALLOWED_IFRAME_HOSTS", "https://a.example.com https://b.example.com", function() {
+            $resp = $this->get("/");
+            $cspHeaders = collect($resp->headers->get('Content-Security-Policy'));
+            $frameHeaders = $cspHeaders->filter(function($val) {
+                return Str::startsWith($val, 'frame-ancestors');
+            });
+
+            $this->assertTrue($frameHeaders->count() === 1);
+            $this->assertEquals('frame-ancestors \'self\' https://a.example.com https://b.example.com', $frameHeaders->first());
+        });
+
+    }
+
+}
\ No newline at end of file
index 3433f3b832f86d61411729ea23983905596d57d0..02f7caae1cb859b4c14d105eab864a78eb4ec0fc 100644 (file)
@@ -1,11 +1,11 @@
 <?php namespace Tests;
 
 use BookStack\Auth\User;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Entities\Repos\BookshelfRepo;
 use BookStack\Entities\Repos\ChapterRepo;
@@ -15,9 +15,14 @@ use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Settings\SettingService;
 use BookStack\Uploads\HttpFetcher;
+use Illuminate\Http\Response;
 use Illuminate\Support\Env;
+use Illuminate\Support\Facades\Log;
 use Mockery;
+use Monolog\Handler\TestHandler;
+use Monolog\Logger;
 use Throwable;
+use Illuminate\Foundation\Testing\Assert as PHPUnit;
 
 trait SharedTestHelpers
 {
@@ -69,14 +74,14 @@ trait SharedTestHelpers
     }
 
     /**
-     * Get an instance of a user with 'viewer' permissions
-     * @param $attributes
-     * @return mixed
+     * Get an instance of a user with 'viewer' permissions.
      */
-    protected function getViewer($attributes = [])
+    protected function getViewer(array $attributes = []): User
     {
         $user = Role::getRole('viewer')->users()->first();
-        if (!empty($attributes)) $user->forceFill($attributes)->save();
+        if (!empty($attributes)) {
+            $user->forceFill($attributes)->save();
+        }
         return $user;
     }
 
@@ -174,15 +179,13 @@ trait SharedTestHelpers
 
     /**
      * Give the given user some permissions.
-     * @param User $user
-     * @param array $permissions
      */
-    protected function giveUserPermissions(User $user, $permissions = [])
+    protected function giveUserPermissions(User $user, array $permissions = [])
     {
         $newRole = $this->createNewRole($permissions);
         $user->attachRole($newRole);
         $user->load('roles');
-        $user->permissions(false);
+        $user->clearPermissionCache();
     }
 
     /**
@@ -262,4 +265,48 @@ trait SharedTestHelpers
         self::assertThat($passed, self::isTrue(), "Failed asserting that given map:\n\n{$toCheckStr}\n\nincludes:\n\n{$toIncludeStr}");
     }
 
+    /**
+     * Assert a permission error has occurred.
+     */
+    protected function assertPermissionError($response)
+    {
+        PHPUnit::assertTrue($this->isPermissionError($response->baseResponse ?? $response->response), "Failed asserting the response contains a permission error.");
+    }
+
+    /**
+     * Assert a permission error has occurred.
+     */
+    protected function assertNotPermissionError($response)
+    {
+        PHPUnit::assertFalse($this->isPermissionError($response->baseResponse ?? $response->response), "Failed asserting the response does not contain a permission error.");
+    }
+
+    /**
+     * Check if the given response is a permission error.
+     */
+    private function isPermissionError($response): bool
+    {
+        return $response->status() === 302
+            && $response->headers->get('Location') === url('/')
+            && strpos(session()->pull('error', ''), 'You do not have permission to access') === 0;
+    }
+
+    /**
+     * Set a test handler as the logging interface for the application.
+     * Allows capture of logs for checking against during tests.
+     */
+    protected function withTestLogger(): TestHandler
+    {
+        $monolog = new Logger('testing');
+        $testHandler = new TestHandler();
+        $monolog->pushHandler($testHandler);
+
+        Log::extend('testing', function() use ($monolog) {
+            return $monolog;
+        });
+        Log::setDefaultDriver('testing');
+
+        return $testHandler;
+    }
+
 }
\ No newline at end of file
diff --git a/tests/StatusTest.php b/tests/StatusTest.php
new file mode 100644 (file)
index 0000000..b4c35cf
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+use Illuminate\Cache\ArrayStore;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Session;
+use Tests\TestCase;
+
+class StatusTest extends TestCase
+{
+    public function test_returns_json_with_expected_results()
+    {
+        $resp = $this->get("/status");
+        $resp->assertStatus(200);
+        $resp->assertJson([
+            'database' => true,
+            'cache' => true,
+            'session' => true,
+        ]);
+    }
+
+    public function test_returns_500_status_and_false_on_db_error()
+    {
+        DB::shouldReceive('table')->andThrow(new Exception());
+
+        $resp = $this->get("/status");
+        $resp->assertStatus(500);
+        $resp->assertJson([
+            'database' => false,
+        ]);
+    }
+
+    public function test_returns_500_status_and_false_on_wrong_cache_return()
+    {
+        $mockStore = Mockery::mock(new ArrayStore())->makePartial();
+        Cache::swap($mockStore);
+        $mockStore->shouldReceive('get')->andReturn('cat');
+
+        $resp = $this->get("/status");
+        $resp->assertStatus(500);
+        $resp->assertJson([
+            'cache' => false,
+        ]);
+    }
+
+    public function test_returns_500_status_and_false_on_wrong_session_return()
+    {
+        $session = Session::getFacadeRoot();
+        $mockSession = Mockery::mock($session)->makePartial();
+        Session::swap($mockSession);
+        $mockSession->shouldReceive('get')->andReturn('cat');
+
+        $resp = $this->get("/status");
+        $resp->assertStatus(500);
+        $resp->assertJson([
+            'session' => false,
+        ]);
+    }
+}
\ No newline at end of file
index 939a1a91e8e8adf51a3a131196b8defda0e28fa9..2c901981af53cb9e22909fdcc89673cb3cf5fece 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace Tests;
 
+use BookStack\Entities\Models\Entity;
 use Illuminate\Foundation\Testing\DatabaseTransactions;
 use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
 
@@ -15,19 +16,6 @@ abstract class TestCase extends BaseTestCase
      */
     protected $baseUrl = 'http://localhost';
 
-    /**
-     * Assert a permission error has occurred.
-     * @param TestResponse $response
-     * @return TestCase
-     */
-    protected function assertPermissionError(TestResponse $response)
-    {
-        $response->assertRedirect('/');
-        $this->assertSessionHas('error');
-        session()->remove('error');
-        return $this;
-    }
-
     /**
      * Assert the session contains a specific entry.
      * @param string $key
@@ -60,4 +48,20 @@ abstract class TestCase extends BaseTestCase
     {
         return TestResponse::fromBaseResponse($response);
     }
+
+    /**
+     * Assert that an activity entry exists of the given key.
+     * Checks the activity belongs to the given entity if provided.
+     */
+    protected function assertActivityExists(string $type, Entity $entity = null)
+    {
+        $detailsToCheck = ['type' => $type];
+
+        if ($entity) {
+            $detailsToCheck['entity_type'] = $entity->getMorphClass();
+            $detailsToCheck['entity_id'] = $entity->id;
+        }
+
+        $this->assertDatabaseHas('activities', $detailsToCheck);
+    }
 }
\ No newline at end of file
index c06311d84089d6997d06f8b00d9bf1ec7b667eb7..76ff322fff6e0dae8ede4d175ef3e4b46ffa3c6b 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace Tests;
 
 use BookStack\Notifications\TestEmail;
+use Illuminate\Contracts\Notifications\Dispatcher;
 use Illuminate\Support\Facades\Notification;
 
 class TestEmailTest extends TestCase
@@ -26,6 +27,24 @@ class TestEmailTest extends TestCase
         Notification::assertSentTo($admin, TestEmail::class);
     }
 
+    public function test_send_test_email_failure_displays_error_notification()
+    {
+        $mockDispatcher = $this->mock(Dispatcher::class);
+        $this->app[Dispatcher::class] = $mockDispatcher;
+
+        $exception = new \Exception('A random error occurred when testing an email');
+        $mockDispatcher->shouldReceive('send')->andThrow($exception);
+
+        $admin = $this->getAdmin();
+        $sendReq = $this->actingAs($admin)->post('/settings/maintenance/send-test-email');
+        $sendReq->assertRedirect('/settings/maintenance#image-cleanup');
+        $this->assertSessionHas('error');
+
+        $message = session()->get('error');
+        $this->assertStringContainsString('Error thrown when sending a test email:', $message);
+        $this->assertStringContainsString('A random error occurred when testing an email', $message);
+    }
+
     public function test_send_test_email_requires_settings_manage_permission()
     {
         Notification::fake();
index a68a5783fa044c881bfbf8fa39b66355128ae8be..9c6b78782b4c91bb8576bfb535666559a588d772 100644 (file)
@@ -15,9 +15,8 @@ class TestResponse extends BaseTestResponse {
 
     /**
      * Get the DOM Crawler for the response content.
-     * @return Crawler
      */
-    protected function crawler()
+    protected function crawler(): Crawler
     {
         if (!is_object($this->crawlerInstance)) {
             $this->crawlerInstance = new Crawler($this->getContent());
@@ -27,7 +26,6 @@ class TestResponse extends BaseTestResponse {
 
     /**
      * Assert the response contains the specified element.
-     * @param string $selector
      * @return $this
      */
     public function assertElementExists(string $selector)
@@ -45,7 +43,6 @@ class TestResponse extends BaseTestResponse {
 
     /**
      * Assert the response does not contain the specified element.
-     * @param string $selector
      * @return $this
      */
     public function assertElementNotExists(string $selector)
@@ -63,8 +60,6 @@ class TestResponse extends BaseTestResponse {
 
     /**
      * Assert the response includes a specific element containing the given text.
-     * @param string $selector
-     * @param string $text
      * @return $this
      */
     public function assertElementContains(string $selector, string $text)
@@ -95,8 +90,6 @@ class TestResponse extends BaseTestResponse {
 
     /**
      * Assert the response does not include a specific element containing the given text.
-     * @param string $selector
-     * @param string $text
      * @return $this
      */
     public function assertElementNotContains(string $selector, string $text)
@@ -125,12 +118,20 @@ class TestResponse extends BaseTestResponse {
         return $this;
     }
 
+    /**
+     * Assert there's a notification within the view containing the given text.
+     * @return $this
+     */
+    public function assertNotificationContains(string $text)
+    {
+        return $this->assertElementContains('[notification]', $text);
+    }
+
     /**
      * Get the escaped text pattern for the constraint.
-     * @param  string  $text
      * @return string
      */
-    protected function getEscapedPattern($text)
+    protected function getEscapedPattern(string $text)
     {
         $rawPattern = preg_quote($text, '/');
         $escapedPattern = preg_quote(e($text), '/');
index c84305ad88112354a418f6e9c01a58cded82b9eb..1374b3aa9e288b3405c61fe2afe5834d3f40c481 100644 (file)
@@ -1,4 +1,7 @@
-<?php namespace Tests;
+<?php namespace Tests\Unit;
+
+use Illuminate\Support\Facades\Log;
+use Tests\TestCase;
 
 /**
  * Class ConfigTest
@@ -34,6 +37,28 @@ class ConfigTest extends TestCase
         $this->checkEnvConfigResult('APP_URL', $oldDefault, 'app.url', '');
     }
 
+    public function test_errorlog_plain_webserver_channel()
+    {
+        // We can't full test this due to it being targeted for the SAPI logging handler
+        // so we just overwrite that component so we can capture the error log output.
+        config()->set([
+            'logging.channels.errorlog_plain_webserver.handler_with' => [0],
+        ]);
+
+        $temp = tempnam(sys_get_temp_dir(), 'bs-test');
+        $original = ini_set( 'error_log', $temp);
+
+        Log::channel('errorlog_plain_webserver')->info('Aww, look, a cute puppy');
+
+        ini_set( 'error_log', $original);
+
+        $output = file_get_contents($temp);
+        $this->assertStringContainsString('Aww, look, a cute puppy', $output);
+        $this->assertStringNotContainsString('INFO', $output);
+        $this->assertStringNotContainsString('info', $output);
+        $this->assertStringNotContainsString('testing', $output);
+    }
+
     /**
      * Set an environment variable of the given name and value
      * then check the given config key to see if it matches the given result.
index c2386443c6fdacd9260cc8b5bfb7777c74a5c208..b9f485da13f0743178049d2e314db04fccd18536 100644 (file)
@@ -1,19 +1,10 @@
-<?php namespace Tests;
+<?php namespace Tests\Unit;
+
+use Tests\TestCase;
 
 class UrlTest extends TestCase
 {
 
-    public function test_request_url_takes_custom_url_into_account()
-    {
-        config()->set('app.url', 'http://example.com/bookstack');
-        $this->get('/');
-        $this->assertEquals('http://example.com/bookstack', request()->getUri());
-
-        config()->set('app.url', 'http://example.com/docs/content');
-        $this->get('/');
-        $this->assertEquals('http://example.com/docs/content', request()->getUri());
-    }
-
     public function test_url_helper_takes_custom_url_into_account()
     {
         $this->runWithEnv('APP_URL', 'http://example.com/bookstack', function() {
index 12b254d00a4c84ac87877d8e3e49917163f7c071..1ca9ea23b17d5d04101c2203173d394b3455b379 100644 (file)
@@ -1,41 +1,56 @@
-<?php namespace Tests;
+<?php namespace Tests\Uploads;
 
+use BookStack\Entities\Tools\TrashCan;
+use BookStack\Entities\Repos\PageRepo;
 use BookStack\Uploads\Attachment;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
 use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Uploads\AttachmentService;
+use Illuminate\Http\UploadedFile;
+use Tests\TestCase;
+use Tests\TestResponse;
 
 class AttachmentTest extends TestCase
 {
     /**
      * Get a test file that can be uploaded
-     * @param $fileName
-     * @return \Illuminate\Http\UploadedFile
      */
-    protected function getTestFile($fileName)
+    protected function getTestFile(string $fileName): UploadedFile
     {
-        return new \Illuminate\Http\UploadedFile(base_path('tests/test-data/test-file.txt'), $fileName, 'text/plain', 55, null, true);
+        return new UploadedFile(base_path('tests/test-data/test-file.txt'), $fileName, 'text/plain', 55, null, true);
     }
 
     /**
      * Uploads a file with the given name.
-     * @param $name
-     * @param int $uploadedTo
-     * @return \Illuminate\Foundation\Testing\TestResponse
      */
-    protected function uploadFile($name, $uploadedTo = 0)
+    protected function uploadFile(string $name, int $uploadedTo = 0): \Illuminate\Foundation\Testing\TestResponse
     {
         $file = $this->getTestFile($name);
         return $this->call('POST', '/attachments/upload', ['uploaded_to' => $uploadedTo], [], ['file' => $file], []);
     }
 
+    /**
+     * Create a new attachment
+     */
+    protected function createAttachment(Page $page): Attachment
+    {
+        $this->post('attachments/link', [
+            'attachment_link_url' => 'https://example.com',
+            'attachment_link_name' => 'Example Attachment Link',
+            'attachment_link_uploaded_to' => $page->id,
+        ]);
+
+        return Attachment::query()->latest()->first();
+    }
+
     /**
      * Delete all uploaded files.
      * To assist with cleanup.
      */
     protected function deleteUploads()
     {
-        $fileService = $this->app->make(\BookStack\Uploads\AttachmentService::class);
-        foreach (\BookStack\Uploads\Attachment::all() as $file) {
+        $fileService = $this->app->make(AttachmentService::class);
+        foreach (Attachment::all() as $file) {
             $fileService->deleteFile($file);
         }
     }
@@ -109,12 +124,12 @@ class AttachmentTest extends TestCase
         $this->asAdmin();
 
         $linkReq = $this->call('POST', 'attachments/link', [
-            'link' => 'https://example.com',
-            'name' => 'Example Attachment Link',
-            'uploaded_to' => $page->id,
+            'attachment_link_url' => 'https://example.com',
+            'attachment_link_name' => 'Example Attachment Link',
+            'attachment_link_uploaded_to' => $page->id,
         ]);
 
-        $expectedResp = [
+        $expectedData = [
             'path' => 'https://example.com',
             'name' => 'Example Attachment Link',
             'uploaded_to' => $page->id,
@@ -126,8 +141,7 @@ class AttachmentTest extends TestCase
         ];
 
         $linkReq->assertStatus(200);
-        $linkReq->assertJson($expectedResp);
-        $this->assertDatabaseHas('attachments', $expectedResp);
+        $this->assertDatabaseHas('attachments', $expectedData);
         $attachment = Attachment::orderBy('id', 'desc')->take(1)->first();
 
         $pageGet = $this->get($page->getUrl());
@@ -145,29 +159,21 @@ class AttachmentTest extends TestCase
         $page = Page::first();
         $this->asAdmin();
 
-        $this->call('POST', 'attachments/link', [
-            'link' => 'https://example.com',
-            'name' => 'Example Attachment Link',
-            'uploaded_to' => $page->id,
+        $attachment = $this->createAttachment($page);
+        $update = $this->call('PUT', 'attachments/' . $attachment->id, [
+            'attachment_edit_name' => 'My new attachment name',
+            'attachment_edit_url' => 'https://test.example.com'
         ]);
 
-        $attachmentId = \BookStack\Uploads\Attachment::first()->id;
-
-        $update = $this->call('PUT', 'attachments/' . $attachmentId, [
-            'uploaded_to' => $page->id,
-            'name' => 'My new attachment name',
-            'link' => 'https://test.example.com'
-        ]);
-
-        $expectedResp = [
+        $expectedData = [
+            'id' => $attachment->id,
             'path' => 'https://test.example.com',
             'name' => 'My new attachment name',
             'uploaded_to' => $page->id
         ];
 
         $update->assertStatus(200);
-        $update->assertJson($expectedResp);
-        $this->assertDatabaseHas('attachments', $expectedResp);
+        $this->assertDatabaseHas('attachments', $expectedData);
 
         $this->deleteUploads();
     }
@@ -183,7 +189,7 @@ class AttachmentTest extends TestCase
         $filePath = storage_path($attachment->path);
         $this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist');
 
-        $attachment = \BookStack\Uploads\Attachment::first();
+        $attachment = Attachment::first();
         $this->delete($attachment->getUrl());
 
         $this->assertDatabaseMissing('attachments', [
@@ -209,7 +215,8 @@ class AttachmentTest extends TestCase
             'name' => $fileName
         ]);
 
-        $this->call('DELETE', $page->getUrl());
+        app(PageRepo::class)->destroy($page);
+        app(TrashCan::class)->empty();
 
         $this->assertDatabaseMissing('attachments', [
             'name' => $fileName
@@ -243,4 +250,45 @@ class AttachmentTest extends TestCase
 
         $this->deleteUploads();
     }
+
+    public function test_data_and_js_links_cannot_be_attached_to_a_page()
+    {
+        $page = Page::first();
+        $this->asAdmin();
+
+        $badLinks = [
+            'javascript:alert("bunny")',
+            ' javascript:alert("bunny")',
+            'JavaScript:alert("bunny")',
+            "\t\n\t\nJavaScript:alert(\"bunny\")",
+            "data:text/html;<a></a>",
+            "Data:text/html;<a></a>",
+            "Data:text/html;<a></a>",
+        ];
+
+        foreach ($badLinks as $badLink) {
+            $linkReq = $this->post('attachments/link', [
+                'attachment_link_url' => $badLink,
+                'attachment_link_name' => 'Example Attachment Link',
+                'attachment_link_uploaded_to' => $page->id,
+            ]);
+            $linkReq->assertStatus(422);
+            $this->assertDatabaseMissing('attachments', [
+                'path' => $badLink,
+            ]);
+        }
+
+        $attachment = $this->createAttachment($page);
+
+        foreach ($badLinks as $badLink) {
+            $linkReq = $this->put('attachments/' . $attachment->id, [
+                'attachment_edit_url' => $badLink,
+                'attachment_edit_name' => 'Example Attachment Link',
+            ]);
+            $linkReq->assertStatus(422);
+            $this->assertDatabaseMissing('attachments', [
+                'path' => $badLink,
+            ]);
+        }
+    }
 }
index ecf7037a9ffe281c1d2778ee49b128da2c3e261d..efaa016ddc6660ead3ea67d9a300ccd54cd75e4f 100644 (file)
@@ -1,7 +1,9 @@
 <?php namespace Tests\Uploads;
 
 use BookStack\Auth\User;
+use BookStack\Exceptions\HttpFetchException;
 use BookStack\Uploads\HttpFetcher;
+use Illuminate\Support\Facades\Log;
 use Tests\TestCase;
 
 class AvatarTest extends TestCase
@@ -11,7 +13,7 @@ class AvatarTest extends TestCase
 
     protected function createUserRequest($user)
     {
-        $resp = $this->asAdmin()->post('/settings/users/create', [
+        $this->asAdmin()->post('/settings/users/create', [
             'name' => $user->name,
             'email' => $user->email,
             'password' => 'testing',
@@ -22,8 +24,7 @@ class AvatarTest extends TestCase
 
     protected function assertImageFetchFrom(string $url)
     {
-        $http = \Mockery::mock(HttpFetcher::class);
-        $this->app->instance(HttpFetcher::class, $http);
+        $http = $this->mock(HttpFetcher::class);
 
         $http->shouldReceive('fetch')
             ->once()->with($url)
@@ -55,6 +56,7 @@ class AvatarTest extends TestCase
     public function test_custom_url_used_if_set()
     {
         config()->set([
+            'services.disable_services' => false,
             'services.avatar_url' => 'https://example.com/${email}/${hash}/${size}',
         ]);
 
@@ -74,11 +76,26 @@ class AvatarTest extends TestCase
 
         $user = factory(User::class)->make();
 
-        $http = \Mockery::mock(HttpFetcher::class);
-        $this->app->instance(HttpFetcher::class, $http);
+        $http = $this->mock(HttpFetcher::class);
         $http->shouldNotReceive('fetch');
 
         $this->createUserRequest($user);
     }
 
+    public function test_no_failure_but_error_logged_on_failed_avatar_fetch()
+    {
+        config()->set([
+            'services.disable_services' => false,
+        ]);
+
+        $http = $this->mock(HttpFetcher::class);
+        $http->shouldReceive('fetch')->andThrow(new HttpFetchException());
+
+        $logger = $this->withTestLogger();
+
+        $user = factory(User::class)->make();
+        $this->createUserRequest($user);
+        $this->assertTrue($logger->hasError('Failed to save user avatar image'));
+    }
+
 }
diff --git a/tests/Uploads/DrawioTest.php b/tests/Uploads/DrawioTest.php
new file mode 100644 (file)
index 0000000..d134135
--- /dev/null
@@ -0,0 +1,79 @@
+<?php namespace Tests\Uploads;
+
+use BookStack\Entities\Models\Page;
+use BookStack\Uploads\Image;
+use Tests\TestCase;
+
+class DrawioTest extends TestCase
+{
+    use UsesImages;
+
+    public function test_get_image_as_base64()
+    {
+        $page = Page::first();
+        $this->asAdmin();
+        $imageName = 'first-image.png';
+
+        $this->uploadImage($imageName, $page->id);
+        $image = Image::first();
+        $image->type = 'drawio';
+        $image->save();
+
+        $imageGet = $this->getJson("/images/drawio/base64/{$image->id}");
+        $imageGet->assertJson([
+            'content' => 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEcDCo5iYNs+gAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFElEQVQI12O0jN/KgASYGFABqXwAZtoBV6Sl3hIAAAAASUVORK5CYII='
+        ]);
+    }
+
+    public function test_drawing_base64_upload()
+    {
+        $page = Page::first();
+        $editor = $this->getEditor();
+        $this->actingAs($editor);
+
+        $upload = $this->postJson('images/drawio', [
+            'uploaded_to' => $page->id,
+            'image' => 'image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEcDCo5iYNs+gAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFElEQVQI12O0jN/KgASYGFABqXwAZtoBV6Sl3hIAAAAASUVORK5CYII='
+        ]);
+
+        $upload->assertStatus(200);
+        $upload->assertJson([
+            'type' => 'drawio',
+            'uploaded_to' => $page->id,
+            'created_by' => $editor->id,
+            'updated_by' => $editor->id,
+        ]);
+
+        $image = Image::where('type', '=', 'drawio')->first();
+        $this->assertTrue(file_exists(public_path($image->path)), 'Uploaded image not found at path: '. public_path($image->path));
+
+        $testImageData = file_get_contents($this->getTestImageFilePath());
+        $uploadedImageData = file_get_contents(public_path($image->path));
+        $this->assertTrue($testImageData === $uploadedImageData, "Uploaded image file data does not match our test image as expected");
+    }
+
+    public function test_drawio_url_can_be_configured()
+    {
+        config()->set('services.drawio', 'http://cats.com?dog=tree');
+        $page = Page::first();
+        $editor = $this->getEditor();
+
+        $resp = $this->actingAs($editor)->get($page->getUrl('/edit'));
+        $resp->assertSee('drawio-url="http://cats.com?dog=tree"');
+    }
+
+    public function test_drawio_url_can_be_disabled()
+    {
+        config()->set('services.drawio', true);
+        $page = Page::first();
+        $editor = $this->getEditor();
+
+        $resp = $this->actingAs($editor)->get($page->getUrl('/edit'));
+        $resp->assertSee('drawio-url="https://embed.diagrams.net/?embed=1&amp;proto=json&amp;spin=1"');
+
+        config()->set('services.drawio', false);
+        $resp = $this->actingAs($editor)->get($page->getUrl('/edit'));
+        $resp->assertDontSee('drawio-url');
+    }
+
+}
\ No newline at end of file
index 0615a95ce52641cff442ce913d59bb65f812d2b3..1c736d672d977b8727c8ee5cf08b9f9ee5ba1538 100644 (file)
@@ -2,7 +2,7 @@
 
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Uploads\Image;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
 use BookStack\Uploads\ImageService;
 use Illuminate\Support\Str;
 use Tests\TestCase;
@@ -36,6 +36,30 @@ class ImageTest extends TestCase
         ]);
     }
 
+    public function test_image_display_thumbnail_generation_does_not_increase_image_size()
+    {
+        $page = Page::first();
+        $admin = $this->getAdmin();
+        $this->actingAs($admin);
+
+        $originalFile = $this->getTestImageFilePath('compressed.png');
+        $originalFileSize = filesize($originalFile);
+        $imgDetails = $this->uploadGalleryImage($page, 'compressed.png');
+        $relPath = $imgDetails['path'];
+
+        $this->assertTrue(file_exists(public_path($relPath)), 'Uploaded image found at path: '. public_path($relPath));
+        $displayImage = $imgDetails['response']->thumbs->display;
+
+        $displayImageRelPath = implode('/', array_slice(explode('/', $displayImage), 3));
+        $displayImagePath = public_path($displayImageRelPath);
+        $displayFileSize = filesize($displayImagePath);
+
+        $this->deleteImage($relPath);
+        $this->deleteImage($displayImageRelPath);
+
+        $this->assertEquals($originalFileSize, $displayFileSize, 'Display thumbnail generation should not increase image size');
+    }
+
     public function test_image_edit()
     {
         $editor = $this->getEditor();
@@ -47,11 +71,7 @@ class ImageTest extends TestCase
         $newName = Str::random();
         $update = $this->put('/images/' . $image->id, ['name' => $newName]);
         $update->assertSuccessful();
-        $update->assertJson([
-            'id' => $image->id,
-            'name' => $newName,
-            'type' => 'gallery',
-        ]);
+        $update->assertSee($newName);
 
         $this->deleteImage($imgDetails['path']);
 
@@ -68,31 +88,22 @@ class ImageTest extends TestCase
         $imgDetails = $this->uploadGalleryImage();
         $image = Image::query()->first();
 
-        $emptyJson = ['images' => [], 'has_more' => false];
-        $resultJson = [
-            'images' => [
-                [
-                    'id' => $image->id,
-                    'name' => $imgDetails['name'],
-                ]
-            ],
-            'has_more' => false,
-        ];
-
         $pageId = $imgDetails['page']->id;
         $firstPageRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}");
-        $firstPageRequest->assertSuccessful()->assertJson($resultJson);
+        $firstPageRequest->assertSuccessful()->assertElementExists('div');
+        $firstPageRequest->assertSuccessful()->assertSeeText($image->name);
 
         $secondPageRequest = $this->get("/images/gallery?page=2&uploaded_to={$pageId}");
-        $secondPageRequest->assertSuccessful()->assertExactJson($emptyJson);
+        $secondPageRequest->assertSuccessful()->assertElementNotExists('div');
 
         $namePartial = substr($imgDetails['name'], 0, 3);
         $searchHitRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}&search={$namePartial}");
-        $searchHitRequest->assertSuccessful()->assertJson($resultJson);
+        $searchHitRequest->assertSuccessful()->assertSee($imgDetails['name']);
 
         $namePartial = Str::random(16);
-        $searchHitRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}&search={$namePartial}");
-        $searchHitRequest->assertSuccessful()->assertExactJson($emptyJson);
+        $searchFailRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}&search={$namePartial}");
+        $searchFailRequest->assertSuccessful()->assertDontSee($imgDetails['name']);
+        $searchFailRequest->assertSuccessful()->assertElementNotExists('div');
     }
 
     public function test_image_usage()
@@ -107,14 +118,10 @@ class ImageTest extends TestCase
         $page->html = '<img src="'.$image->url.'">';
         $page->save();
 
-        $usage = $this->get('/images/usage/' . $image->id);
+        $usage = $this->get('/images/edit/' . $image->id . '?delete=true');
         $usage->assertSuccessful();
-        $usage->assertJson([
-            [
-                'id' => $page->id,
-                'name' => $page->name
-            ]
-        ]);
+        $usage->assertSeeText($page->name);
+        $usage->assertSee($page->getUrl());
 
         $this->deleteImage($imgDetails['path']);
     }
@@ -175,6 +182,38 @@ class ImageTest extends TestCase
         $this->assertFalse(file_exists(public_path($relPath)), 'Uploaded double extension file was uploaded but should have been stopped');
     }
 
+    public function test_url_entities_removed_from_filenames()
+    {
+        $this->asEditor();
+        $badNames = [
+            "bad-char-#-image.png",
+            "bad-char-?-image.png",
+            "?#.png",
+            "?.png",
+            "#.png",
+        ];
+        foreach ($badNames as $name) {
+            $galleryFile = $this->getTestImage($name);
+            $page = Page::first();
+            $badPath = $this->getTestImagePath('gallery', $name);
+            $this->deleteImage($badPath);
+
+            $upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
+            $upload->assertStatus(200);
+
+            $lastImage = Image::query()->latest('id')->first();
+            $newFileName = explode('.', basename($lastImage->path))[0];
+
+            $this->assertEquals($lastImage->name, $name);
+            $this->assertFalse(strpos($lastImage->path, $name), 'Path contains original image name');
+            $this->assertFalse(file_exists(public_path($badPath)), 'Uploaded image file name was not stripped of url entities');
+
+            $this->assertTrue(strlen($newFileName) > 0, 'File name was reduced to nothing');
+
+            $this->deleteImage($lastImage->path);
+        }
+    }
+
     public function test_secure_images_uploads_to_correct_place()
     {
         config()->set('filesystems.images', 'local_secure');
@@ -238,10 +277,11 @@ class ImageTest extends TestCase
         $page = Page::first();
         $this->asAdmin();
         $imageName = 'first-image.png';
+        $relPath = $this->getTestImagePath('gallery', $imageName);
+        $this->deleteImage($relPath);
 
         $this->uploadImage($imageName, $page->id);
         $image = Image::first();
-        $relPath = $this->getTestImagePath('gallery', $imageName);
 
         $delete = $this->delete( '/images/' . $image->id);
         $delete->assertStatus(200);
@@ -254,48 +294,29 @@ class ImageTest extends TestCase
         $this->assertFalse(file_exists(public_path($relPath)), 'Uploaded image has not been deleted as expected');
     }
 
-    public function testBase64Get()
+    public function test_image_delete_does_not_delete_similar_images()
     {
         $page = Page::first();
         $this->asAdmin();
         $imageName = 'first-image.png';
 
-        $this->uploadImage($imageName, $page->id);
-        $image = Image::first();
-        $image->type = 'drawio';
-        $image->save();
-
-        $imageGet = $this->getJson("/images/drawio/base64/{$image->id}");
-        $imageGet->assertJson([
-            'content' => 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEcDCo5iYNs+gAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFElEQVQI12O0jN/KgASYGFABqXwAZtoBV6Sl3hIAAAAASUVORK5CYII='
-        ]);
-    }
-
-    public function test_drawing_base64_upload()
-    {
-        $page = Page::first();
-        $editor = $this->getEditor();
-        $this->actingAs($editor);
+        $relPath = $this->getTestImagePath('gallery', $imageName);
+        $this->deleteImage($relPath);
 
-        $upload = $this->postJson('images/drawio', [
-            'uploaded_to' => $page->id,
-            'image' => 'image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEcDCo5iYNs+gAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFElEQVQI12O0jN/KgASYGFABqXwAZtoBV6Sl3hIAAAAASUVORK5CYII='
-        ]);
+        $this->uploadImage($imageName, $page->id);
+        $this->uploadImage($imageName, $page->id);
+        $this->uploadImage($imageName, $page->id);
 
-        $upload->assertStatus(200);
-        $upload->assertJson([
-            'type' => 'drawio',
-            'uploaded_to' => $page->id,
-            'created_by' => $editor->id,
-            'updated_by' => $editor->id,
-        ]);
+        $image = Image::first();
+        $folder = public_path(dirname($relPath));
+        $imageCount = count(glob($folder . '/*'));
 
-        $image = Image::where('type', '=', 'drawio')->first();
-        $this->assertTrue(file_exists(public_path($image->path)), 'Uploaded image not found at path: '. public_path($image->path));
+        $delete = $this->delete( '/images/' . $image->id);
+        $delete->assertStatus(200);
 
-        $testImageData = file_get_contents($this->getTestImageFilePath());
-        $uploadedImageData = file_get_contents(public_path($image->path));
-        $this->assertTrue($testImageData === $uploadedImageData, "Uploaded image file data does not match our test image as expected");
+        $newCount = count(glob($folder . '/*'));
+        $this->assertEquals($imageCount - 1, $newCount, 'More files than expected have been deleted');
+        $this->assertFalse(file_exists(public_path($relPath)), 'Uploaded image has not been deleted as expected');
     }
 
     protected function getTestProfileImage()
index aa5ffe4c78250fb41f3a96c1319970a06eae3344..64f26dea8a9be7c847909d0d192a780a2ed25f02 100644 (file)
@@ -1,7 +1,6 @@
 <?php namespace Tests\Uploads;
 
-
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
 use Illuminate\Http\UploadedFile;
 
 trait UsesImages
@@ -10,9 +9,13 @@ trait UsesImages
      * Get the path to our basic test image.
      * @return string
      */
-    protected function getTestImageFilePath()
+    protected function getTestImageFilePath(?string $fileName = null)
     {
-        return base_path('tests/test-data/test-image.png');
+        if (is_null($fileName)) {
+            $fileName = 'test-image.png';
+        }
+
+        return base_path('tests/test-data/' . $fileName);
     }
 
     /**
@@ -20,9 +23,9 @@ trait UsesImages
      * @param $fileName
      * @return UploadedFile
      */
-    protected function getTestImage($fileName)
+    protected function getTestImage($fileName, ?string $testDataFileName = null)
     {
-        return new UploadedFile($this->getTestImageFilePath(), $fileName, 'image/png', 5238, null, true);
+        return new UploadedFile($this->getTestImageFilePath($testDataFileName), $fileName, 'image/png', 5238, null, true);
     }
 
     /**
@@ -36,11 +39,8 @@ trait UsesImages
 
     /**
      * Get the path for a test image.
-     * @param $type
-     * @param $fileName
-     * @return string
      */
-    protected function getTestImagePath($type, $fileName)
+    protected function getTestImagePath(string $type, string $fileName): string
     {
         return '/uploads/images/' . $type . '/' . Date('Y-m') . '/' . $fileName;
     }
@@ -52,9 +52,9 @@ trait UsesImages
      * @param string $contentType
      * @return \Illuminate\Foundation\Testing\TestResponse
      */
-    protected function uploadImage($name, $uploadedTo = 0, $contentType = 'image/png')
+    protected function uploadImage($name, $uploadedTo = 0, $contentType = 'image/png', ?string $testDataFileName = null)
     {
-        $file = $this->getTestImage($name);
+        $file = $this->getTestImage($name, $testDataFileName);
         return $this->withHeader('Content-Type', $contentType)
             ->call('POST', '/images/gallery', ['uploaded_to' => $uploadedTo], [], ['file' => $file], []);
     }
@@ -66,22 +66,23 @@ trait UsesImages
      * @param Page|null $page
      * @return array
      */
-    protected function uploadGalleryImage(Page $page = null)
+    protected function uploadGalleryImage(Page $page = null, ?string $testDataFileName = null)
     {
         if ($page === null) {
             $page = Page::query()->first();
         }
 
-        $imageName = 'first-image.png';
+        $imageName = $testDataFileName ?? 'first-image.png';
         $relPath = $this->getTestImagePath('gallery', $imageName);
         $this->deleteImage($relPath);
 
-        $upload = $this->uploadImage($imageName, $page->id);
+        $upload = $this->uploadImage($imageName, $page->id, 'image/png', $testDataFileName);
         $upload->assertStatus(200);
         return [
             'name' => $imageName,
             'path' => $relPath,
-            'page' => $page
+            'page' => $page,
+            'response' => json_decode($upload->getContent()),
         ];
     }
 
diff --git a/tests/User/UserApiTokenTest.php b/tests/User/UserApiTokenTest.php
new file mode 100644 (file)
index 0000000..df686dd
--- /dev/null
@@ -0,0 +1,189 @@
+<?php namespace Tests\User;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Api\ApiToken;
+use Carbon\Carbon;
+use Tests\TestCase;
+
+class UserApiTokenTest extends TestCase
+{
+
+    protected $testTokenData = [
+        'name' => 'My test API token',
+        'expires_at' => '2050-04-01',
+    ];
+
+    public function test_tokens_section_not_visible_without_access_api_permission()
+    {
+        $user = $this->getViewer();
+
+        $resp = $this->actingAs($user)->get($user->getEditUrl());
+        $resp->assertDontSeeText('API Tokens');
+
+        $this->giveUserPermissions($user, ['access-api']);
+
+        $resp = $this->actingAs($user)->get($user->getEditUrl());
+        $resp->assertSeeText('API Tokens');
+        $resp->assertSeeText('Create Token');
+    }
+
+    public function test_those_with_manage_users_can_view_other_user_tokens_but_not_create()
+    {
+        $viewer = $this->getViewer();
+        $editor = $this->getEditor();
+        $this->giveUserPermissions($viewer, ['users-manage']);
+
+        $resp = $this->actingAs($viewer)->get($editor->getEditUrl());
+        $resp->assertSeeText('API Tokens');
+        $resp->assertDontSeeText('Create Token');
+    }
+
+    public function test_create_api_token()
+    {
+        $editor = $this->getEditor();
+
+        $resp = $this->asAdmin()->get($editor->getEditUrl('/create-api-token'));
+        $resp->assertStatus(200);
+        $resp->assertSee('Create API Token');
+        $resp->assertSee('Token Secret');
+
+        $resp = $this->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
+        $token = ApiToken::query()->latest()->first();
+        $resp->assertRedirect($editor->getEditUrl('/api-tokens/' . $token->id));
+        $this->assertDatabaseHas('api_tokens', [
+            'user_id' => $editor->id,
+            'name' => $this->testTokenData['name'],
+            'expires_at' => $this->testTokenData['expires_at'],
+        ]);
+
+        // Check secret token
+        $this->assertSessionHas('api-token-secret:' . $token->id);
+        $secret = session('api-token-secret:' . $token->id);
+        $this->assertDatabaseMissing('api_tokens', [
+            'secret' => $secret,
+        ]);
+        $this->assertTrue(\Hash::check($secret, $token->secret));
+
+        $this->assertTrue(strlen($token->token_id) === 32);
+        $this->assertTrue(strlen($secret) === 32);
+
+        $this->assertSessionHas('success');
+        $this->assertActivityExists(ActivityType::API_TOKEN_CREATE);
+    }
+
+    public function test_create_with_no_expiry_sets_expiry_hundred_years_away()
+    {
+        $editor = $this->getEditor();
+        $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), ['name' => 'No expiry token', 'expires_at' => '']);
+        $token = ApiToken::query()->latest()->first();
+
+        $over = Carbon::now()->addYears(101);
+        $under = Carbon::now()->addYears(99);
+        $this->assertTrue(
+            ($token->expires_at < $over && $token->expires_at > $under),
+            "Token expiry set at 100 years in future"
+        );
+    }
+
+    public function test_created_token_displays_on_profile_page()
+    {
+        $editor = $this->getEditor();
+        $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
+        $token = ApiToken::query()->latest()->first();
+
+        $resp = $this->get($editor->getEditUrl());
+        $resp->assertElementExists('#api_tokens');
+        $resp->assertElementContains('#api_tokens', $token->name);
+        $resp->assertElementContains('#api_tokens', $token->token_id);
+        $resp->assertElementContains('#api_tokens', $token->expires_at->format('Y-m-d'));
+    }
+
+    public function test_secret_shown_once_after_creation()
+    {
+        $editor = $this->getEditor();
+        $resp = $this->asAdmin()->followingRedirects()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
+        $resp->assertSeeText('Token Secret');
+
+        $token = ApiToken::query()->latest()->first();
+        $this->assertNull(session('api-token-secret:' . $token->id));
+
+        $resp = $this->get($editor->getEditUrl('/api-tokens/' . $token->id));
+        $resp->assertDontSeeText('Client Secret');
+    }
+
+    public function test_token_update()
+    {
+        $editor = $this->getEditor();
+        $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
+        $token = ApiToken::query()->latest()->first();
+        $updateData = [
+            'name' => 'My updated token',
+            'expires_at' => '2011-01-01',
+        ];
+
+        $resp = $this->put($editor->getEditUrl('/api-tokens/' . $token->id), $updateData);
+        $resp->assertRedirect($editor->getEditUrl('/api-tokens/' . $token->id));
+
+        $this->assertDatabaseHas('api_tokens', array_merge($updateData, ['id' => $token->id]));
+        $this->assertSessionHas('success');
+        $this->assertActivityExists(ActivityType::API_TOKEN_UPDATE);
+    }
+
+    public function test_token_update_with_blank_expiry_sets_to_hundred_years_away()
+    {
+        $editor = $this->getEditor();
+        $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
+        $token = ApiToken::query()->latest()->first();
+
+        $resp = $this->put($editor->getEditUrl('/api-tokens/' . $token->id), [
+            'name' => 'My updated token',
+            'expires_at' => '',
+        ]);
+        $token->refresh();
+
+        $over = Carbon::now()->addYears(101);
+        $under = Carbon::now()->addYears(99);
+        $this->assertTrue(
+            ($token->expires_at < $over && $token->expires_at > $under),
+            "Token expiry set at 100 years in future"
+        );
+    }
+
+    public function test_token_delete()
+    {
+        $editor = $this->getEditor();
+        $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
+        $token = ApiToken::query()->latest()->first();
+
+        $tokenUrl = $editor->getEditUrl('/api-tokens/' . $token->id);
+
+        $resp = $this->get($tokenUrl . '/delete');
+        $resp->assertSeeText('Delete Token');
+        $resp->assertSeeText($token->name);
+        $resp->assertElementExists('form[action="'.$tokenUrl.'"]');
+
+        $resp = $this->delete($tokenUrl);
+        $resp->assertRedirect($editor->getEditUrl('#api_tokens'));
+        $this->assertDatabaseMissing('api_tokens', ['id' => $token->id]);
+        $this->assertActivityExists(ActivityType::API_TOKEN_DELETE);
+    }
+
+    public function test_user_manage_can_delete_token_without_api_permission_themselves()
+    {
+        $viewer = $this->getViewer();
+        $editor = $this->getEditor();
+        $this->giveUserPermissions($editor, ['users-manage']);
+
+        $this->asAdmin()->post($viewer->getEditUrl('/create-api-token'), $this->testTokenData);
+        $token = ApiToken::query()->latest()->first();
+
+        $resp = $this->actingAs($editor)->get($viewer->getEditUrl('/api-tokens/' . $token->id));
+        $resp->assertStatus(200);
+        $resp->assertSeeText('Delete Token');
+
+        $resp = $this->actingAs($editor)->delete($viewer->getEditUrl('/api-tokens/' . $token->id));
+        $resp->assertRedirect($viewer->getEditUrl('#api_tokens'));
+        $this->assertDatabaseMissing('api_tokens', ['id' => $token->id]);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/User/UserManagementTest.php b/tests/User/UserManagementTest.php
new file mode 100644 (file)
index 0000000..d99d614
--- /dev/null
@@ -0,0 +1,44 @@
+<?php namespace Tests\User;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Page;
+use Tests\TestCase;
+
+class UserManagementTest extends TestCase
+{
+
+    public function test_delete()
+    {
+        $editor = $this->getEditor();
+        $resp = $this->asAdmin()->delete("settings/users/{$editor->id}");
+        $resp->assertRedirect("/settings/users");
+        $resp = $this->followRedirects($resp);
+
+        $resp->assertSee("User successfully removed");
+        $this->assertActivityExists(ActivityType::USER_DELETE);
+
+        $this->assertDatabaseMissing('users', ['id' => $editor->id]);
+    }
+
+    public function test_delete_offers_migrate_option()
+    {
+        $editor = $this->getEditor();
+        $resp = $this->asAdmin()->get("settings/users/{$editor->id}/delete");
+        $resp->assertSee("Migrate Ownership");
+        $resp->assertSee("new_owner_id");
+    }
+
+    public function test_delete_with_new_owner_id_changes_ownership()
+    {
+        $page = Page::query()->first();
+        $owner = $page->ownedBy;
+        $newOwner = User::query()->where('id', '!=' , $owner->id)->first();
+
+        $this->asAdmin()->delete("settings/users/{$owner->id}", ['new_owner_id' => $newOwner->id]);
+        $this->assertDatabaseHas('pages', [
+            'id' => $page->id,
+            'owned_by' => $newOwner->id,
+        ]);
+    }
+}
\ No newline at end of file
similarity index 78%
rename from tests/UserPreferencesTest.php
rename to tests/User/UserPreferencesTest.php
index b8166427525dcf74006f3fdfa0b60dd2779eeea0..7ffc8f9db7085958b9ef5e5a80bc4bfed22a3022 100644 (file)
@@ -1,4 +1,6 @@
-<?php namespace Tests;
+<?php namespace Tests\User;
+
+use Tests\TestCase;
 
 class UserPreferencesTest extends TestCase
 {
@@ -73,4 +75,21 @@ class UserPreferencesTest extends TestCase
         $invalidKeyRequest = $this->patch('/settings/users/' . $editor->id.'/update-expansion-preference/my-home-details', ['expand' => 'true']);
         $invalidKeyRequest->assertStatus(500);
     }
+
+    public function test_toggle_dark_mode()
+    {
+        $home = $this->actingAs($this->getEditor())->get('/');
+        $home->assertElementNotExists('.dark-mode');
+        $home->assertSee('Dark Mode');
+
+        $this->assertEquals(false, setting()->getForCurrentUser('dark-mode-enabled', false));
+        $prefChange = $this->patch('/settings/users/toggle-dark-mode');
+        $prefChange->assertRedirect();
+        $this->assertEquals(true, setting()->getForCurrentUser('dark-mode-enabled'));
+
+        $home = $this->actingAs($this->getEditor())->get('/');
+        $home->assertElementExists('.dark-mode');
+        $home->assertDontSee('Dark Mode');
+        $home->assertSee('Light Mode');
+    }
 }
\ No newline at end of file
similarity index 72%
rename from tests/UserProfileTest.php
rename to tests/User/UserProfileTest.php
index fc1a529ae43076f59d38d2d642c534c0ff816cd7..27d97381e54393a8f469fcce6f47038cd3690dae 100644 (file)
@@ -1,4 +1,10 @@
-<?php namespace Tests;
+<?php namespace Tests\User;
+
+use Activity;
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Bookshelf;
+use Tests\BrowserKitTest;
 
 class UserProfileTest extends BrowserKitTest
 {
@@ -7,7 +13,7 @@ class UserProfileTest extends BrowserKitTest
     public function setUp(): void
     {
         parent::setUp();
-        $this->user = \BookStack\Auth\User::all()->last();
+        $this->user = User::all()->last();
     }
 
     public function test_profile_page_shows_name()
@@ -55,8 +61,8 @@ class UserProfileTest extends BrowserKitTest
         $newUser = $this->getNewBlankUser();
         $this->actingAs($newUser);
         $entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
-        \Activity::add($entities['book'], 'book_update', $entities['book']->id);
-        \Activity::add($entities['page'], 'page_create', $entities['book']->id);
+        Activity::addForEntity($entities['book'], ActivityType::BOOK_UPDATE);
+        Activity::addForEntity($entities['page'], ActivityType::PAGE_CREATE);
 
         $this->asAdmin()->visit('/user/' . $newUser->id)
             ->seeInElement('#recent-user-activity', 'updated book')
@@ -69,8 +75,8 @@ class UserProfileTest extends BrowserKitTest
         $newUser = $this->getNewBlankUser();
         $this->actingAs($newUser);
         $entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
-        \Activity::add($entities['book'], 'book_update', $entities['book']->id);
-        \Activity::add($entities['page'], 'page_create', $entities['book']->id);
+        Activity::addForEntity($entities['book'], ActivityType::BOOK_UPDATE);
+        Activity::addForEntity($entities['page'], ActivityType::PAGE_CREATE);
 
         $this->asAdmin()->visit('/')->clickInElement('#recent-activity', $newUser->name)
             ->seePageIs('/user/' . $newUser->id)
@@ -87,7 +93,7 @@ class UserProfileTest extends BrowserKitTest
 
     public function test_guest_profile_cannot_be_deleted()
     {
-        $guestUser = \BookStack\Auth\User::getDefault();
+        $guestUser = User::getDefault();
         $this->asAdmin()->visit('/settings/users/' . $guestUser->id . '/delete')
             ->see('Delete User')->see('Guest')
             ->press('Confirm')
@@ -116,4 +122,24 @@ class UserProfileTest extends BrowserKitTest
             ->pageHasElement('.featured-image-container');
     }
 
+    public function test_shelf_view_type_change()
+    {
+        $editor = $this->getEditor();
+        $shelf = Bookshelf::query()->first();
+        setting()->putUser($editor, 'bookshelf_view_type', 'list');
+
+        $this->actingAs($editor)->visit($shelf->getUrl())
+            ->pageNotHasElement('.featured-image-container')
+            ->pageHasElement('.content-wrap .entity-list-item')
+            ->see('Grid View');
+
+        $req = $this->patch("/settings/users/{$editor->id}/switch-shelf-view", ['view_type' => 'grid']);
+        $req->assertRedirectedTo($shelf->getUrl());
+
+        $this->actingAs($editor)->visit($shelf->getUrl())
+            ->pageHasElement('.featured-image-container')
+            ->pageNotHasElement('.content-wrap .entity-list-item')
+            ->see('List View');
+    }
+
 }
diff --git a/tests/test-data/compressed.png b/tests/test-data/compressed.png
new file mode 100644 (file)
index 0000000..e42ef58
Binary files /dev/null and b/tests/test-data/compressed.png differ
diff --git a/version b/version
index d400403770b07fdcd3eb850c6b28c62b7c1a5d83..92d9faea7781a62c65cccb97e9dd1292d1184271 100644 (file)
--- a/version
+++ b/version
@@ -1 +1 @@
-v0.28-dev
+v0.32-dev
diff --git a/webpack.config.js b/webpack.config.js
deleted file mode 100644 (file)
index e496340..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-const path = require('path');
-const dev = process.env.NODE_ENV !== 'production';
-
-const MiniCssExtractPlugin = require("mini-css-extract-plugin");
-
-const config = {
-    target: 'web',
-    mode: dev? 'development' : 'production',
-    entry: {
-        app: './resources/js/index.js',
-        styles: './resources/sass/styles.scss',
-        "export-styles": './resources/sass/export-styles.scss',
-        "print-styles": './resources/sass/print-styles.scss',
-    },
-    output: {
-        filename: '[name].js',
-        path: path.resolve(__dirname, 'public/dist')
-    },
-    module: {
-        rules: [
-            {
-                test: /\.scss$/,
-                use: [
-                    {
-                        loader: MiniCssExtractPlugin.loader,
-                        options: {}
-                    },
-                    {
-                        loader: "css-loader", options: {
-                        sourceMap: dev
-                    }
-                    }, {
-                        loader: "sass-loader", options: {
-                            sourceMap: dev
-                        }
-                    }
-                ]
-            }
-        ]
-    },
-    plugins: [
-        new MiniCssExtractPlugin({
-            filename: "[name].css",
-        }),
-    ]
-};
-
-if (dev) {
-    config['devtool'] = 'inline-source-map';
-}
-
-module.exports = config;
\ No newline at end of file