{"version":3,"file":"app-a85fc8ab.f584a1446b2fc034d810.bundle.js","mappings":"AAGA","sources":["webpack://lernplattform-admin-frontend/./src/resources/elements/uce-ias-editor/uce-ias-editor-navigation-settings/uce-ias-editor-navigation-settings.html","webpack://lernplattform-admin-frontend/./src/resources/elements/uce-ias-editor/uce-ias-editor.html","webpack://lernplattform-admin-frontend/./src/resources/elements/uce-slide-editor/uce-slide-editor.html","webpack://lernplattform-admin-frontend/./src/resources/elements/user-administration-table/user-administration-table.html","webpack://lernplattform-admin-frontend/./src/resources/elements/user-select/user-select.html","webpack://lernplattform-admin-frontend/./src/resources/elements/vimeo-thumbnail/vimeo-thumbnail.html","webpack://lernplattform-admin-frontend/./src/resources/elements/x-select/x-select.html","webpack://lernplattform-admin-frontend/./src/resources/elements/x-tooltip/x-tooltip.html","webpack://lernplattform-admin-frontend/./src/resources/elements/uce-ias-editor/index.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/uce-ias-editor/uce-ias-editor-navigation-settings/uce-ias-editor-navigation-settings.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/uce-ias-editor/uce-ias-editor.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/uce-slide-editor/uce-slide-editor.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/user-administration-table/user-administration-table.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/user-select/user-select.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/vimeo-thumbnail/vimeo-thumbnail.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/x-select/index.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/x-select/x-select.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/x-tooltip/x-tooltip.ts"],"sourcesContent":["// Module\nvar code = \"<template class=\\\"uce-ias-editor-navigation-settings\\\"> <x-select if.to-view=\\\"targetType != null && targetType !== TargetType.AnswerTargets\\\" t=\\\"[label]${successSlideTk}\\\" value.to-view=\\\"block.successSlideId\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onSuccessSlideIdChanged($event)\\\"></x-select> <x-select if.to-view=\\\"targetType === TargetType.TrueFalseTargets\\\" t=\\\"[label]pages.content-editor.iasErrorSlide\\\" value.to-view=\\\"block.errorSlideId\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onErrorSlideIdChanged($event)\\\"></x-select> <x-select if.to-view=\\\"targetType === TargetType.AnswerTargets\\\" t=\\\"[label]pages.content-editor.iasAnswerXSlide\\\" t-params.to-view=\\\"{ x: 1 }\\\" value.to-view=\\\"block.jumpSlide1Id\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onJumpSlide1IdChanged($event)\\\"></x-select> <x-select if.to-view=\\\"targetType === TargetType.AnswerTargets\\\" t=\\\"[label]pages.content-editor.iasAnswerXSlide\\\" t-params.to-view=\\\"{ x: 2 }\\\" value.to-view=\\\"block.jumpSlide2Id\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onJumpSlide2IdChanged($event)\\\"></x-select> <x-select if.to-view=\\\"targetType === TargetType.AnswerTargets\\\" t=\\\"[label]pages.content-editor.iasAnswerXSlide\\\" t-params.to-view=\\\"{ x: 3 }\\\" value.to-view=\\\"block.jumpSlide3Id\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onJumpSlide3IdChanged($event)\\\"></x-select> <x-select if.to-view=\\\"targetType === TargetType.AnswerTargets\\\" t=\\\"[label]pages.content-editor.iasAnswerXSlide\\\" t-params.to-view=\\\"{ x: 4 }\\\" value.to-view=\\\"block.jumpSlide4Id\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onJumpSlide4IdChanged($event)\\\"></x-select> <x-select if.to-view=\\\"targetType === TargetType.MultiTargets\\\" class=\\\"uce-ias-editor-navigation-settings__first-multi-taget\\\" t=\\\"[label]pages.content-editor.iasTargetSlide\\\" t-params.to-view=\\\"{ count: 1 }\\\" value.to-view=\\\"block.jumpSlide1Id\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onJumpSlide1IdChanged($event)\\\"></x-select> <x-select if.to-view=\\\"targetType === TargetType.MultiTargets\\\" t=\\\"[label]pages.content-editor.iasTargetSlide\\\" t-params.to-view=\\\"{ count: 2 }\\\" value.to-view=\\\"block.jumpSlide2Id\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onJumpSlide2IdChanged($event)\\\"></x-select> <x-select if.to-view=\\\"targetType === TargetType.MultiTargets\\\" t=\\\"[label]pages.content-editor.iasTargetSlide\\\" t-params.to-view=\\\"{ count: 3 }\\\" value.to-view=\\\"block.jumpSlide3Id\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onJumpSlide3IdChanged($event)\\\"></x-select> <x-select if.to-view=\\\"targetType === TargetType.MultiTargets\\\" t=\\\"[label]pages.content-editor.iasTargetSlide\\\" t-params.to-view=\\\"{ count: 4 }\\\" value.to-view=\\\"block.jumpSlide4Id\\\" items.to-view=\\\"selectItems\\\" selection-changed.trigger=\\\"onJumpSlide4IdChanged($event)\\\"></x-select> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"uce-ias-editor\\\"> <compose if.to-view=\\\"pageId != null\\\" view-model.to-view=\\\"headerViewModel\\\" model.bind=\\\"{ pageId }\\\"></compose> <div class=\\\"uce-ias-editor__content\\\"> <div class=\\\"uce-ias-editor__title-wrapper\\\"> <inline-edit-field class=\\\"uce-ias-editor__title\\\" value.to-view=\\\"block.text\\\" mode=\\\"click\\\" show-edit-border.to-view=\\\"false\\\" placeholder-tk=\\\"pages.content-editor.headingPlaceholder\\\" save-event.trigger=\\\"onSaveIasTitle($event)\\\"></inline-edit-field> </div> <ias-slide class=\\\"uce-ias-editor__slide\\\" context-identifier.to-view=\\\"contextIdentifier\\\" layout-container-view-model.to-view=\\\"layoutContainerViewModel\\\" slide-id.to-view=\\\"slideId\\\"> <drop-zone if.to-view=\\\"isSlideEmpty && !dndPreventApplyTemplate\\\" slot=\\\"additional\\\" class=\\\"uce-ias-editor__empty-slide\\\" context.to-view=\\\"dndContext\\\" dnd-active.to-view=\\\"dndActive\\\" config.to-view=\\\"firstDropzoneConfig\\\" provide-dropzone-identifiers.to-view=\\\"true\\\"> <div if.to-view=\\\"dndEmptySlideTargeted\\\" class=\\\"uce-ias-editor__add-first\\\"> <safe-icon class=\\\"uce-ias-editor__add-first-icon\\\" icon-name=\\\"System/Tabler/Plus\\\"></safe-icon> </div> </drop-zone> </ias-slide> <div if.to-view=\\\"slideBlock.type === BlockType.VIDEO\\\"> <label class=\\\"form-label\\\" t=\\\"pages.content-editor.videoHelpTextLabel\\\"> </label> <textarea class=\\\"form-control uce-ias-editor__video-text\\\" value.to-view=\\\"slideBlock.text\\\" change.trigger=\\\"handleVideoHelpTextChanged($event)\\\" rows=\\\"2\\\"></textarea> </div> <uce-ias-editor-navigation-settings class=\\\"uce-ias-editor__navigation-settings\\\" context-identifier.to-view=\\\"contextIdentifier\\\" slide-id.to-view=\\\"slideId\\\"></uce-ias-editor-navigation-settings> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"uce-slide-editor\\\"> <div class=\\\"uce-slide-editor__slide-options\\\"> <label class=\\\"uce-slide-editor__toggle custom-control custom-checkbox\\\"> <input class=\\\"custom-control-input\\\" type=\\\"checkbox\\\" checked.two-way=\\\"showHeader\\\"> <span class=\\\"uce-slide-editor__toggle-text custom-control-label\\\" t=\\\"pages.content-editor.header\\\"></span> </label> <label class=\\\"uce-slide-editor__toggle custom-control custom-checkbox\\\"> <input class=\\\"custom-control-input\\\" type=\\\"checkbox\\\" checked.two-way=\\\"showFooter\\\"> <span class=\\\"uce-slide-editor__toggle-text custom-control-label\\\" t=\\\"pages.content-editor.footer\\\"></span> </label> <label class=\\\"uce-slide-editor__toggle custom-control custom-checkbox\\\"> <input class=\\\"custom-control-input\\\" type=\\\"checkbox\\\" checked.two-way=\\\"showBackgroundImage\\\"> <span class=\\\"uce-slide-editor__toggle-text custom-control-label\\\" t=\\\"pages.content-editor.backgroundImage\\\"></span> <button-standard if.to-view=\\\"showBackgroundImage\\\" icon-name.to-view=\\\"slide.backgroundImage ? 'Uce/Photo' : 'Uce/PhotoEmpty'\\\" btn-size=\\\"icon\\\" tooltip-tk=\\\"pages.content-editor.backgroundImage\\\" btn-color=\\\"secondary\\\" click.delegate=\\\"handleChangeBackgroundImageClicked()\\\"></button-standard> </label> <content-editor-enum-options if.to-view=\\\"showBackgroundImage\\\" class=\\\"uce-slide-editor__enum-options\\\" items.to-view=\\\"backgroundImageSizingOptions\\\" tooltip-tk=\\\"pages.content-editor.imageSizingTooltip\\\" selected-option-update.to-view=\\\"backgroundImageSizingSelectedOption\\\" selected-enum-option-changed.trigger=\\\"backgroundImageSizingChanged($event)\\\"></content-editor-enum-options> <content-editor-enum-options if.to-view=\\\"showBackgroundImage\\\" class=\\\"uce-slide-editor__enum-options\\\" items.to-view=\\\"backgroundImageAlignmentOptions\\\" tooltip-tk=\\\"pages.content-editor.imageAlignmentTooltip\\\" selected-option-update.to-view=\\\"backgroundImageAlignmentSelectedOption\\\" selected-enum-option-changed.trigger=\\\"backgroundImageAlignmentChanged($event)\\\"></content-editor-enum-options> <label class=\\\"uce-slide-editor__color-select-wrapper\\\"> <span class=\\\"uce-slide-editor__color-select-text\\\" t=\\\"pages.content-editor.slideBackgroundColor\\\"></span> <content-editor-color-select-preview click.delegate=\\\"toggleColorSelect()\\\" color.to-view=\\\"slide.backgroundColor\\\"></content-editor-color-select-preview> <content-editor-color-select if.to-view=\\\"showColorSelect\\\" current-color.to-view=\\\"slide.backgroundColor\\\" color-selected.trigger=\\\"handleColorSelected($event)\\\" class=\\\"uce-slide-editor__color-select\\\"></content-editor-color-select> </label> </div> <div ref=\\\"contentWrapperElement\\\" class=\\\"uce-slide-editor__content-wrapper\\\"> <content-editor-presentation-slide if.to-view=\\\"contextIdentifier != null && slide != null\\\" class=\\\"uce-slide-editor__slide\\\" context-identifier.to-view=\\\"contextIdentifier\\\" slide-id.to-view=\\\"slide.id\\\" layout-container-view-model.to-view=\\\"layoutContainerViewModel\\\" hide-slide-content.to-view=\\\"dndTemplateActive\\\" force-show-header.to-view=\\\"showHeader\\\" force-show-footer.to-view=\\\"showFooter\\\" force-show-background-image.to-view=\\\"showBackgroundImage\\\" is-editable-slide.to-view=\\\"true\\\"> <inline-edit-field view-model.ref=\\\"headingInlineEditField\\\" slot=\\\"heading\\\" value.to-view=\\\"slide.heading\\\" mode=\\\"click\\\" show-edit-border.to-view=\\\"false\\\" placeholder-tk=\\\"pages.content-editor.headingPlaceholder\\\" max-length.to-view=\\\"60\\\" save-event.trigger=\\\"onSaveSlideHeading($event)\\\"></inline-edit-field> <inline-edit-field view-model.ref=\\\"subheadingInlineEditField\\\" slot=\\\"subheading\\\" value.to-view=\\\"slide.subheading\\\" mode=\\\"click\\\" show-edit-border.to-view=\\\"false\\\" max-length.to-view=\\\"34\\\" placeholder-tk=\\\"pages.content-editor.subheadingPlaceholder\\\" save-event.trigger=\\\"onSaveSlideSubheading($event)\\\"></inline-edit-field> <inline-edit-field view-model.ref=\\\"footerInlineEditField\\\" slot=\\\"footer\\\" value.to-view=\\\"slide.footer\\\" mode=\\\"click\\\" show-edit-border.to-view=\\\"false\\\" max-length.to-view=\\\"34\\\" placeholder-tk=\\\"pages.content-editor.footerPlaceholder\\\" save-event.trigger=\\\"onSaveSlideFooter($event)\\\"></inline-edit-field> <drop-zone if.to-view=\\\"isSlideEmpty || dndTemplateActive\\\" slot=\\\"additional\\\" class=\\\"uce-slide-editor__empty-slide ${ dndTemplateActive ? 'uce-slide-editor__empty-slide--to-front' : ''}\\\" context.to-view=\\\"dndContext\\\" dnd-active.to-view=\\\"dndActive\\\" config.to-view=\\\"firstDropzoneConfig\\\" provide-dropzone-identifiers.to-view=\\\"true\\\"> <div if.to-view=\\\"dndEmptySlideTargeted\\\" class=\\\"uce-slide-editor__add-first\\\"> <safe-icon class=\\\"uce-slide-editor__add-first-icon\\\" icon-name=\\\"System/Tabler/Plus\\\"></safe-icon> </div> </drop-zone> </content-editor-presentation-slide> <rich-text-editor-toolbar class=\\\"uce-slide-editor__markdown-toolbar\\\" context-identifier.to-view=\\\"contextIdentifier\\\"></rich-text-editor-toolbar> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <custom-table columns.to-view=\\\"tableColumns\\\" extendable.to-view=\\\"true\\\" table-id=\\\"user-administration\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\" get-shown-entries-data.call=\\\"fetchTableData(limit, offset)\\\"></custom-table> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"elements.userSelect.selectUsers\\\"></label> <text-tags-widget table-columns.to-view=\\\"userTableColumns\\\" fixed-height.to-view=\\\"true\\\" table-data.to-view=\\\"foundUsers\\\" tags.to-view=\\\"selectedUsersTags\\\" is-selected-callback.call=\\\"isUserSelected($event)\\\" tags-edited.trigger=\\\"handleUserEntryRemoved($event)\\\" entry-selected.trigger=\\\"handleUserEntrySelected($event)\\\" search-string-changed.trigger=\\\"handleUserSearchStringChanged($event)\\\" should-list-stay-open.to-view=\\\"true\\\" mark-selected.to-view=\\\"true\\\"></text-tags-widget> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"vimeo-thumbnail\\\"> <aspect-ratio-container class=\\\"video-thumbnail__img-wrapper\\\"> <img if.to-view=\\\"src\\\" class=\\\"video-thumnail__img\\\" src.to-view=\\\"src\\\"/> </aspect-ratio-container> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"x-select\\\"> <label class=\\\"form-label\\\" for.to-view=\\\"name\\\">${label}</label> <select class=\\\"custom-select\\\" name.to-view=\\\"name\\\" value.bind=\\\"value\\\" change.trigger=\\\"selectedChanged()\\\" attach-focus> <option repeat.for=\\\"item of items\\\" disabled.to-view=\\\"!item.value\\\" model.to-view=\\\"item.value\\\" t.to-view=\\\"item.label\\\"></option> </select> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"x-tooltip dropdown-menu popper-tooltip show x-tooltip ${ isShown ? '' : 'popper-tooltip--hidden' }\\\" role=\\\"dialog\\\" set-false-on-outside-click.two-way=\\\"isShown\\\" click.delegate=\\\"toggle()\\\"> <div class=\\\"popper-arrow\\\" data-popper-arrow></div> <div class=\\\"x-tooltip__text-wrapper\\\"> <small class=\\\"x-tooltip__text\\\"> <slot></slot> </small> </div> </template> \";\n// Exports\nexport default code;","import { FrameworkConfiguration, PLATFORM } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('resources/elements/uce-ias-editor/uce-ias-editor'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/uce-ias-editor/uce-ias-editor-navigation-settings/uce-ias-editor-navigation-settings'));\n}\n","import { autoinject, bindable, computedFrom } from 'aurelia-framework';\nimport { selectState } from 'common/aurelia-decorators/select-state';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { ICourseBlockDto } from 'common/dto/course/content/block.interface';\nimport { BlockType, QuestionType } from 'common/dto/course/content/course.types';\nimport { iasSlideBlockSelectorCreator, iasSlideIdsOrderedSelectorCreator } from 'common/store/selectors/course.context.selectors';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { ISelectItem, SelectedEvent } from 'resources/elements/x-select/x-select';\nimport { ContentEditorApiService } from 'services/api/ContentEditorApiService';\nimport { IState } from 'store/state';\nimport * as ContentContextMutations from 'common/store/mutations/content.context.mutations';\nimport { I18N } from 'aurelia-i18n';\n\nenum TargetType {\n  AnswerTargets = 'answer-targets',\n  OneTarget = 'one-target',\n  TrueFalseTargets = 'true-false-targets',\n  MultiTargets = 'multi-targets',\n}\n\nconst NO_SLIDE = -1;\n\n@autoinject()\nexport class UceIasEditorNavigationSettings {\n  @bindable() public contextIdentifier: string | null = null;\n  @bindable() public slideId: number | null = null;\n\n  @selectState(\n    UceIasEditorNavigationSettings,\n    iasSlideBlockSelectorCreator,\n    ['contextIdentifier', 'slideId'],\n  ) private block?: ICourseBlockDto;\n\n  @selectState(\n    UceIasEditorNavigationSettings,\n    iasSlideIdsOrderedSelectorCreator,\n    ['contextIdentifier']\n  ) private slideIds?: Array<number>;\n\n  private TargetType = TargetType;\n\n  constructor(\n    private storeService: StoreService<IState>,\n    private contentEditorApi: ContentEditorApiService,\n    private i18N: I18N,\n  ) { }\n\n  @computedFrom('block.type', 'block.mpQuestionType')\n  private get targetType(): TargetType | null {\n    if(this.block == null) return null;\n    const { type: blockType, mpQuestionType } = this.block;\n    if(blockType === BlockType.MP_QUESTION && mpQuestionType == null) return null;\n\n    switch(blockType) {\n      case BlockType.MP_INTERACTIVE_TEXT:\n        return TargetType.TrueFalseTargets;\n      case BlockType.MP_QUESTION:\n        return mpQuestionType === QuestionType.SC || mpQuestionType === QuestionType.SCP\n          ? TargetType.AnswerTargets\n          : TargetType.TrueFalseTargets;\n      default:\n        return TargetType.OneTarget;\n    }\n  }\n\n  @computedFrom('targetType')\n  private get successSlideTk(): string | null {\n    if(this.targetType == null) return null;\n\n    return (this.targetType === TargetType.OneTarget || this.targetType === TargetType.MultiTargets)\n      ? 'pages.content-editor.iasNextSlide'\n      : 'pages.content-editor.iasSuccessSlide';\n  }\n\n  @computedFrom('slideIds', 'slideId')\n  private get selectItems(): Array<ISelectItem<number>> {\n    // allow unselect targetSlide by providing a value of NO_SLIDE (-1) instead of null here:\n    const items: Array<ISelectItem<number>> = [{ value: NO_SLIDE, label: this.i18N.tr('pages.content-editor.iasSelectNextSlide') }];\n\n    if(this.slideId != null && this.slideIds != null) {\n      for(const [idx, id] of this.slideIds.entries()) {\n        if(id === this.slideId) continue;\n        items.push({ value: id, label: `${idx + 1}` });\n      }\n    }\n\n    return items;\n  }\n\n  private async onSuccessSlideIdChanged(event: SelectedEvent<number>): Promise<void> {\n    assertNotNullOrUndefined(this.contextIdentifier, 'contextIdentifier cannot be null or undefined');\n    assertNotNullOrUndefined(this.block, 'block cannot be null or undefined');\n    const successSlideId = event.detail.value === NO_SLIDE ? null : event.detail.value;\n    const updatedBlock = await this.contentEditorApi.updateBlock<ICourseBlockDto>(this.block.id, { successSlideId });\n    await this.storeService.dispatch(ContentContextMutations.updateBlock, this.contextIdentifier, updatedBlock);\n  }\n\n  private async onErrorSlideIdChanged(event: SelectedEvent<number>): Promise<void> {\n    assertNotNullOrUndefined(this.contextIdentifier, 'contextIdentifier cannot be null or undefined');\n    assertNotNullOrUndefined(this.block, 'block cannot be null or undefined');\n    const errorSlideId = event.detail.value === NO_SLIDE ? null : event.detail.value;\n    const updatedBlock = await this.contentEditorApi.updateBlock<ICourseBlockDto>(this.block.id, { errorSlideId });\n    await this.storeService.dispatch(ContentContextMutations.updateBlock, this.contextIdentifier, updatedBlock);\n  }\n\n  private async onJumpSlide1IdChanged(event: SelectedEvent<number>): Promise<void> {\n    assertNotNullOrUndefined(this.contextIdentifier, 'contextIdentifier cannot be null or undefined');\n    assertNotNullOrUndefined(this.block, 'block cannot be null or undefined');\n    const jumpSlideId = event.detail.value === NO_SLIDE ? null : event.detail.value;\n    const updatedBlock = await this.contentEditorApi.updateBlock<ICourseBlockDto>(this.block.id, { jumpSlide1Id: jumpSlideId });\n    await this.storeService.dispatch(ContentContextMutations.updateBlock, this.contextIdentifier, updatedBlock);\n  }\n\n  private async onJumpSlide2IdChanged(event: SelectedEvent<number>): Promise<void> {\n    assertNotNullOrUndefined(this.contextIdentifier, 'contextIdentifier cannot be null or undefined');\n    assertNotNullOrUndefined(this.block, 'block cannot be null or undefined');\n    const jumpSlideId = event.detail.value === NO_SLIDE ? null : event.detail.value;\n    const updatedBlock = await this.contentEditorApi.updateBlock<ICourseBlockDto>(this.block.id, { jumpSlide2Id: jumpSlideId });\n    await this.storeService.dispatch(ContentContextMutations.updateBlock, this.contextIdentifier, updatedBlock);\n  }\n\n  private async onJumpSlide3IdChanged(event: SelectedEvent<number>): Promise<void> {\n    assertNotNullOrUndefined(this.contextIdentifier, 'contextIdentifier cannot be null or undefined');\n    assertNotNullOrUndefined(this.block, 'block cannot be null or undefined');\n    const jumpSlideId = event.detail.value === NO_SLIDE ? null : event.detail.value;\n    const updatedBlock = await this.contentEditorApi.updateBlock<ICourseBlockDto>(this.block.id, { jumpSlide3Id: jumpSlideId });\n    await this.storeService.dispatch(ContentContextMutations.updateBlock, this.contextIdentifier, updatedBlock);\n  }\n\n  private async onJumpSlide4IdChanged(event: SelectedEvent<number>): Promise<void> {\n    assertNotNullOrUndefined(this.contextIdentifier, 'contextIdentifier cannot be null or undefined');\n    assertNotNullOrUndefined(this.block, 'block cannot be null or undefined');\n    const jumpSlideId = event.detail.value === NO_SLIDE ? null : event.detail.value;\n    const updatedBlock = await this.contentEditorApi.updateBlock<ICourseBlockDto>(this.block.id, { jumpSlide4Id: jumpSlideId });\n    await this.storeService.dispatch(ContentContextMutations.updateBlock, this.contextIdentifier, updatedBlock);\n  }\n}\n","import { autoinject, PLATFORM, useView } from 'aurelia-framework';\nimport { selectState } from 'common/aurelia-decorators/select-state';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { ContentEditingInsertModeSelector } from 'common/constants/content.editing.constants';\nimport { CONTENT_EDITOR_COURSE_CONTEXT } from 'common/constants/content.editor.constants';\nimport { UniversalBlock } from 'common/dto/content-editor/content.editor.interface';\nimport { ContentElementType, NonBlockElementType } from 'common/dto/content-editor/content.editor.types';\nimport { ICourseBlockDto } from 'common/dto/course/content/block.interface';\nimport { BlockType } from 'common/dto/course/content/course.types';\nimport * as ContentContextMutations from 'common/store/mutations/content.context.mutations';\nimport { iasSlideIdSelectorCreator, isIasSlideEmptySelectorCreator, singlePageBlockSelectorCreator, pageIdSelectorCreator, isIasSlideBlockSelectorCreator } from 'common/store/selectors/course.context.selectors';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { ContentEditorApiService } from '../../../services/api/ContentEditorApiService';\nimport { ContentEditorService } from '../../../services/ContentEditorService';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport {\n  DndActivityEventData,\n  DndContext,\n  DndDropEventData,\n  DndDropzoneIdentifier,\n  DndManager,\n  DndManagerService,\n  DndPayloadUniversal,\n  DndSubContext,\n  DND_EMPTY_IAS_SLIDE_ID,\n  IDndDropzoneConfig,\n} from '../../../services/DndManagerService';\nimport { IState } from '../../../store/state';\nimport { BlockUtils } from '../../../utils/BlockUtils';\nimport { InlineEditSaveEvent } from '../inline-edit-field/inline-edit-field';\nimport { UceComposableLayoutContainer } from '../uce-composable-layout-container/uce-composable-layout-container';\nimport { UceCourseLearnPageHeader } from '../uce-course-learn-page-header/uce-course-learn-page-header';\n\n@useView(PLATFORM.moduleName('./uce-ias-editor.html'))\n@autoinject()\nexport class UceIasEditor {\n  public contextIdentifier = CONTENT_EDITOR_COURSE_CONTEXT;\n\n  private subscriptionManager: SubscriptionManager;\n  private headerViewModel = UceCourseLearnPageHeader;\n  private layoutContainerViewModel = UceComposableLayoutContainer;\n  @selectState(UceIasEditor, pageIdSelectorCreator, ['contextIdentifier']) private pageId?: number | null = null;\n  @selectState(UceIasEditor, singlePageBlockSelectorCreator, ['contextIdentifier']) private block: ICourseBlockDto | null = null;\n  @selectState(UceIasEditor, iasSlideIdSelectorCreator, ['contextIdentifier']) private slideId?: number | null = null;\n  @selectState(UceIasEditor, isIasSlideEmptySelectorCreator, ['contextIdentifier']) private isSlideEmpty?: boolean;\n  @selectState(UceIasEditor, isIasSlideBlockSelectorCreator, ['contextIdentifier']) private slideBlock?: ICourseBlockDto;\n\n  private BlockType = BlockType;\n\n  private dndActive = false;\n  private dndContext: DndContext = DndContext.ContentEditorLayout;\n\n  private dndManager: DndManager<ContentElementType> | null = null;\n  private dndEmptySlideTargeted = false;\n  private dndPreventApplyTemplate = false;\n  private dndEmptySlideIdentifier: DndDropzoneIdentifier = {\n    superId: DND_EMPTY_IAS_SLIDE_ID,\n    subId: DND_EMPTY_IAS_SLIDE_ID,\n  };\n  private firstDropzoneConfig: IDndDropzoneConfig = {\n    identifier: this.dndEmptySlideIdentifier,\n    insertModeSelector: ContentEditingInsertModeSelector.Plain,\n  };\n\n  constructor(\n    private storeService: StoreService<IState>,\n    private dndManagerService: DndManagerService,\n    private customDialogService: CustomDialogService,\n    private contentEditorApi: ContentEditorApiService,\n    private contentEditorService: ContentEditorService,\n    subscriptionManagerService: SubscriptionManagerService,\n  ) {\n    this.subscriptionManager = subscriptionManagerService.createSubscriptionManager();\n  }\n\n  protected attached(): void {\n    this.dndManager = this.dndManagerService.getDndManager<ContentElementType>(this.dndContext);\n    this.dndManager.subscribeDrop(this, this.dndOnDropHandler.bind(this), [this.dndEmptySlideIdentifier]);\n    this.dndManager.subscribePayloadChanged(this, this.dndPayloadChangedHandler.bind(this));\n    this.dndManager.subscribeToDragActivity(this, this.dndDragActivityHandler.bind(this), [this.dndEmptySlideIdentifier]);\n  }\n\n  protected detached(): void {\n    this.dndManager?.disposeSubscriptions(this);\n    this.subscriptionManager.disposeSubscriptions();\n  }\n\n  private async onSaveIasTitle(event: InlineEditSaveEvent): Promise<void> {\n    const text = event.detail.value;\n    assertNotNullOrUndefined(this.block, 'block cannot be null or undefined');\n    const updatedBlock = await this.contentEditorApi.updateBlock<UniversalBlock>(this.block.id, {\n      text\n    });\n    await this.storeService.dispatch(ContentContextMutations.updateBlock, this.contextIdentifier, updatedBlock);\n  }\n\n  private dndPayloadChangedHandler(payload: DndPayloadUniversal<ContentElementType>): void {\n    this.dndPreventApplyTemplate = payload != null\n      && payload.source === DndSubContext.Template;\n  }\n\n  private async dndOnDropHandler(dropInfo: DndDropEventData<ContentElementType>): Promise<void> {\n    if(this.slideId == null\n      || this.contextIdentifier == null\n      || !dropInfo.dndPayload?.createNewType\n      || dropInfo.dndPayload.createNewType === BlockType.EMPTY_CARD\n      || dropInfo.dndPayload.existingPayloadType === NonBlockElementType.Card) return;\n\n    const createBlockDto = (dropInfo.dndPayload.createNewType === BlockType.TEXT || dropInfo.dndPayload.createNewType === BlockType.MP_INTERACTIVE_TEXT)\n      ? {\n        type: dropInfo.dndPayload.createNewType,\n        text: '',\n        questionIds: [],\n      }\n      : await this.customDialogService.showContentEditorBlockEditorDialog({\n        contextIdentifier: this.contextIdentifier,\n        blockId: null,\n        block: { type: dropInfo.dndPayload.createNewType as BlockType }, // must ensure earlier that NonBlockElementType doesn't land in here\n        isIas: true,\n      });\n    if(createBlockDto == null) return;\n\n    const changes = await this.contentEditorApi.addFirstBlockOnIasSlide(this.slideId, BlockUtils.convertToCreateUniversalBlock(createBlockDto));\n    await this.storeService.dispatch(ContentContextMutations.applyLayoutChanges, this.contextIdentifier, changes);\n\n    await this.contentEditorService.focusNewTextBlock(changes);\n  }\n  private dndDragActivityHandler(dndInfo: DndActivityEventData<IDndDropzoneConfig>): void {\n    this.dndEmptySlideTargeted = dndInfo.enterEvent;\n  }\n\n  private async handleVideoHelpTextChanged(event: InputEvent): Promise<void> {\n    assertNotNullOrUndefined(this.slideBlock, 'block cannot be null or undefined');\n    const updatedBlock = await this.contentEditorApi.updateBlock<UniversalBlock>(this.slideBlock.id, {\n      text: (event.target as HTMLTextAreaElement).value\n    });\n    await this.storeService.dispatch(ContentContextMutations.updateBlock, this.contextIdentifier, updatedBlock);\n  }\n}\n","import { autoinject, bindable, observable, ObserverLocator, Parser } from 'aurelia-framework';\nimport { I18N } from 'aurelia-i18n';\nimport { selectState, SelectStateObservableComponent } from 'common/aurelia-decorators/select-state';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { ContentEditingInsertModeSelector } from 'common/constants/content.editing.constants';\nimport { ContentElementType, NonBlockElementType } from 'common/dto/content-editor/content.editor.types';\nimport { BlockType } from 'common/dto/course/content/course.types';\nimport { IPresentationSlideDto } from 'common/dto/course/content/slide.interface';\nimport { IUpdateSlideDto } from 'common/dto/course/management/course.management.interface';\nimport { CornerAlignment, GenericAlignment, GenericFitAndFill } from 'common/enum/content.enum';\nimport { GalleryMediaType } from 'common/enum/gallery.enum';\nimport * as ContentContextMutations from 'common/store/mutations/content.context.mutations';\nimport * as PresentationContextMutations from 'common/store/mutations/presentation.context.mutations';\nimport { presentationIdSelectorCreator, selectIsSlideEmpty, selectSlide } from 'common/store/selectors/presentation.context.selectors';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { ContentEditorApiService } from '../../../services/api/ContentEditorApiService';\nimport { ContentEditorService } from '../../../services/ContentEditorService';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport {\n  DndActivityEventData,\n  DndContext,\n  DndDropEventData,\n  DndDropzoneIdentifier,\n  DndManager,\n  DndManagerService,\n  DndPayloadUniversal,\n  DndSubContext,\n  DND_EMPTY_SLIDE_ID,\n  IDndDropzoneConfig\n} from '../../../services/DndManagerService';\nimport * as ClipboardMutations from '../../../store/clipboard.mutations';\nimport * as ContentEditorMutations from '../../../store/content.editor.mutations';\nimport { InspectorMode, IState } from '../../../store/state';\nimport { BlockUtils } from '../../../utils/BlockUtils';\nimport { IContentEditorBlockEditorBlock } from '../../dialogs/content-editor-block-editor-dialog/content-editor-block-editor-dialog';\nimport { ColorSelectedEvent } from '../content-editor-color-select/content-editor-color-select';\nimport { IEnumOptionItem, SelectedEnumOptionChangedEvent } from '../content-editor-enum-options/content-editor-enum-options';\nimport { InlineEditField, InlineEditSaveEvent } from '../inline-edit-field/inline-edit-field';\nimport { RichTextEditorToolbar } from '../rich-text-editor/rich-text-editor-toolbar/rich-text-editor-toolbar';\nimport { UceComposableLayoutContainer } from '../uce-composable-layout-container/uce-composable-layout-container';\n\n@autoinject()\nexport class UceSlideEditor extends SelectStateObservableComponent {\n  @bindable() public contextIdentifier: string | null = null;\n\n  @selectState(UceSlideEditor, presentationIdSelectorCreator, ['contextIdentifier']) private presentationId?: number | null = null;\n\n  private dndActive = false;\n  private dndContext: DndContext = DndContext.ContentEditorLayout;\n\n  @observable() private slide: IPresentationSlideDto | null = null;\n  private isSlideEmpty = false;\n  @observable() private showHeader = false;\n  @observable() private showFooter = false;\n  @observable() private showBackgroundImage = false;\n  private showColorSelect = false;\n  private storedFooter: string | null = null;\n  private storedHeading: {\n    heading: string | null;\n    subheading: string | null;\n  } | null = null;\n  private storedBackgroundImage: {\n    backgroundImageId: number | null;\n    backgroundImageHorizontalAlignment: GenericAlignment | null;\n    backgroundImageVerticalAlignment: GenericAlignment | null;\n    backgroundImageSizing: GenericFitAndFill | null;\n  } | null = null;\n\n  private layoutContainerViewModel = UceComposableLayoutContainer;\n\n  @observable() private contentWrapperElement: HTMLElement | null = null;\n  private subscriptionManager: SubscriptionManager;\n  private isAttached = false;\n  private inspectorMode: InspectorMode | null = null;\n\n  private dndTemplateActive = false;\n  private dndManager: DndManager<ContentElementType> | null = null;\n  private dndEmptySlideTargeted = false;\n  private dndEmptySlideIdentifier: DndDropzoneIdentifier = {\n    superId: DND_EMPTY_SLIDE_ID,\n    subId: DND_EMPTY_SLIDE_ID,\n  };\n  private firstDropzoneConfig: IDndDropzoneConfig = {\n    identifier: this.dndEmptySlideIdentifier,\n    insertModeSelector: ContentEditingInsertModeSelector.Plain,\n    subContext: DndSubContext.Content,\n  };\n\n  private headingInlineEditField?: InlineEditField;\n  private subheadingInlineEditField?: InlineEditField;\n  private footerInlineEditField?: InlineEditField;\n  private backgroundImageSizingSelectedOption: GenericFitAndFill | null = null;\n  private backgroundImageSizingOptions: Array<IEnumOptionItem<GenericFitAndFill>> = [\n    {\n      option: GenericFitAndFill.Fill,\n      icon: 'Uce/Fill',\n      textTk: 'pages.content-editor.imageSizingFill',\n    },\n    {\n      option: GenericFitAndFill.Fit,\n      icon: 'Uce/Fit',\n      textTk: 'pages.content-editor.imageSizingFit',\n    },\n    {\n      option: GenericFitAndFill.Original,\n      icon: 'Uce/OriginalSize',\n      textTk: 'pages.content-editor.imageSizingOriginal',\n    },\n  ];\n  private backgroundImageAlignmentSelectedOption: CornerAlignment | null = null;\n  private backgroundImageAlignmentOptions: Array<IEnumOptionItem<CornerAlignment>> = [\n    {\n      option: CornerAlignment.TopLeft,\n      icon: 'Uce/CornerPinUpperLeft',\n      textTk: 'pages.content-editor.imageAlignmentCornerTopLeft',\n    },\n    {\n      option: CornerAlignment.TopRight,\n      icon: 'Uce/CornerPinUpperRight',\n      textTk: 'pages.content-editor.imageAlignmentCornerTopRight',\n    },\n    {\n      option: CornerAlignment.CenterCenter,\n      icon: 'Uce/CornerPinCenterCenter',\n      textTk: 'pages.content-editor.imageAlignmentCenterCenter',\n    },\n    {\n      option: CornerAlignment.BottomRight,\n      icon: 'Uce/CornerPinLowerRight',\n      textTk: 'pages.content-editor.imageAlignmentCornerBottomRight',\n    },\n    {\n      option: CornerAlignment.BottomLeft,\n      icon: 'Uce/CornerPinLowerLeft',\n      textTk: 'pages.content-editor.imageAlignmentCornerBottomLeft',\n    },\n  ];\n\n  constructor(\n    subscriptionManagerService: SubscriptionManagerService,\n    private storeService: StoreService<IState>,\n    private dndManagerService: DndManagerService,\n    private customDialogService: CustomDialogService,\n    private contentEditorApi: ContentEditorApiService,\n    private contentEditorService: ContentEditorService,\n    parser: Parser,\n    observerLocator: ObserverLocator,\n    private i18n: I18N,\n  ) {\n    super(parser, observerLocator);\n    this.subscriptionManager = subscriptionManagerService.createSubscriptionManager();\n  }\n\n  protected attached(): void {\n    this.isAttached = true;\n\n    this.subscribeToState();\n\n    this.dndManager = this.dndManagerService.getDndManager<ContentElementType>(this.dndContext);\n    this.dndManager.subscribeDrop(this, this.dndOnDropHandler.bind(this), [this.dndEmptySlideIdentifier]);\n    this.dndManager.subscribeToDragActivity(this, this.dndDragActivityHandler.bind(this), [this.dndEmptySlideIdentifier]);\n    this.dndManager.subscribePayloadChanged(this, this.dndPayloadChangedHandler.bind(this));\n  }\n\n  protected detached(): void {\n    this.storeService.disposeAllSubscriptions(this);\n    this.subscriptionManager.disposeSubscriptions();\n    this.dndManager?.disposeSubscriptions(this);\n  }\n\n  private contextIdentifierChanged(): void {\n    this.subscribeToState();\n  }\n\n  private subscribeToState(): void {\n    const { contextIdentifier } = this;\n    if(!this.isAttached || contextIdentifier == null) return;\n\n    this.storeService.disposeAllSubscriptions(this);\n\n    this.storeService.subscribeMemoized(\n      this,\n      selectSlide,\n      slide => {\n        const slideIdChanged = this.slide?.id !== slide?.id;\n        this.slide = slide;\n        if(slideIdChanged) this.handleSlideIdChanged(slide);\n      }, contextIdentifier,\n    );\n\n    this.storeService.subscribeMemoized(\n      this,\n      selectIsSlideEmpty,\n      isSlideEmpty => {\n        this.isSlideEmpty = isSlideEmpty;\n      }, contextIdentifier,\n    );\n\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.contentEditor.dragAndDrop.active,\n    ], (_state, active) => {\n      if(!active) {\n        this.dndEmptySlideTargeted = false;\n      }\n    });\n\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.contentEditor.inspector.mode,\n    ], (_state, inspectorMode) => {\n      this.inspectorMode = inspectorMode;\n    });\n  }\n\n  private async showHeaderChanged(): Promise<void> {\n    const { contextIdentifier, slide } = this;\n    if(!contextIdentifier || !slide) return;\n\n    if(!this.showHeader) {\n      this.storedHeading = {\n        heading: slide.heading,\n        subheading: slide.subheading,\n      };\n      await this.unsetHeading();\n    } else if(this.storedHeading != null) {\n      const heading = this.storedHeading;\n      this.storedHeading = null;\n      await this.updateSlide(contextIdentifier, slide.id, { ...heading });\n    }\n  }\n\n  private async showFooterChanged(): Promise<void> {\n    const { contextIdentifier, slide } = this;\n    if(!contextIdentifier || !slide) return;\n\n    if(!this.showFooter) {\n      this.storedFooter = slide.footer;\n      await this.unsetFooter();\n    } else if(this.storedFooter != null) {\n      const footer = this.storedFooter;\n      this.storedFooter = null;\n      await this.updateSlide(contextIdentifier, slide.id, { footer });\n    }\n  }\n\n  private async showBackgroundImageChanged(): Promise<void> {\n    const { contextIdentifier, slide } = this;\n    if(!contextIdentifier || !slide) return;\n\n    if(!this.showBackgroundImage) {\n      this.storedBackgroundImage = {\n        backgroundImageId: slide.backgroundImage?.id ?? null,\n        backgroundImageSizing: slide.backgroundImageSizing,\n        backgroundImageHorizontalAlignment: slide.backgroundImageHorizontalAlignment,\n        backgroundImageVerticalAlignment: slide.backgroundImageVerticalAlignment,\n      };\n      await this.unsetBackgroundImage();\n    } else if(this.storedBackgroundImage != null) {\n      const backgroundImage = this.storedBackgroundImage;\n      this.storedBackgroundImage = null;\n      await this.updateSlide(contextIdentifier, slide.id, { ...backgroundImage });\n    }\n  }\n\n  private async handleSlideIdChanged(newSlide: IPresentationSlideDto | null): Promise<void> {\n    this.updateSlideSettings(newSlide);\n\n    assertNotNullOrUndefined(this.contextIdentifier, 'contextIdentifier cannot be null or undefined');\n    await this.storeService\n      .pipe(ContentEditorMutations.updateContainerStructure, this.contextIdentifier)\n      .pipe(ContentEditorMutations.resetContainerHoverState)\n      .dispatch();\n  }\n\n  private updateSlideSettings(newSlide: Partial<IPresentationSlideDto> | null): void {\n    this.storedFooter = null;\n    this.storedHeading = null;\n    this.storedBackgroundImage = null;\n    this.showHeader = newSlide?.heading != null || newSlide?.subheading != null;\n    this.showFooter = newSlide?.footer != null;\n    this.showBackgroundImage = newSlide?.backgroundImage != null;\n  }\n\n  private dndPayloadChangedHandler(payload: DndPayloadUniversal<ContentElementType>): void {\n    this.dndTemplateActive = payload != null && payload.source === DndSubContext.Template;\n  }\n\n  private async dndOnDropHandler(dropInfo: DndDropEventData<ContentElementType>): Promise<void> {\n    // stop dnd before BE-API activity\n    this.stopDnd();\n    if(this.slide == null\n      || this.contextIdentifier == null\n      || dropInfo.dndPayload == null\n      || dropInfo.dropTarget.subContext !== DndSubContext.Content) return;\n\n    if(dropInfo.dndPayload.source === DndSubContext.Clipboard) {\n      if(dropInfo.dndPayload.existingPayloadId == null) return;\n      const changes = await this.contentEditorApi.moveElementFromClipboard(\n        dropInfo.dndPayload.existingPayloadId,\n        {\n          insertMode: ContentEditingInsertModeSelector.Plain,\n          slideId: this.slide.id,\n        },\n      );\n      await this.storeService\n        .pipe(ContentContextMutations.applyLayoutChanges, this.contextIdentifier, changes.targetLayoutChanges)\n        .pipe(\n          ClipboardMutations.applyRemoveChanges,\n          changes.clipboardLayoutContainerChanges,\n          changes.clipboardCardContentChanges,\n          changes.clipboardRemovedBlockId,\n        ).pipe(ContentEditorMutations.updateContainerStructure, this.contextIdentifier)\n        .dispatch();\n    } else if(dropInfo.dndPayload.source === DndSubContext.New) {\n      // insert a fresh block\n      if(!dropInfo.dndPayload?.createNewType) return;\n      let createBlockDto: IContentEditorBlockEditorBlock | null = null;\n      switch(dropInfo.dndPayload.createNewType) {\n        case NonBlockElementType.Card:\n          createBlockDto = {\n            type: BlockType.EMPTY_CARD,\n            text: this.i18n.tr('pages.content-editor.cardTextBlockPlaceholder'),\n          };\n          break;\n        case BlockType.TEXT:\n        case BlockType.SPACER:\n          createBlockDto = {\n            type: dropInfo.dndPayload.createNewType,\n            text: '',\n          };\n          break;\n        case NonBlockElementType.AiText:\n          createBlockDto = {\n            type: BlockType.TEXT,\n            text: '',\n          };\n          break;\n\n        case NonBlockElementType.AiQuiz:\n          createBlockDto = await this.customDialogService.showContentEditorBlockEditorDialog({\n            contextIdentifier: this.contextIdentifier,\n            blockId: null,\n            block: {\n              type: BlockType.QUESTION,\n              nonBlockElementType: dropInfo.dndPayload.createNewType,\n            }\n          });\n          break;\n\n        default:\n          createBlockDto = await this.customDialogService.showContentEditorBlockEditorDialog({\n            contextIdentifier: this.contextIdentifier,\n            blockId: null,\n            block: {\n              type: dropInfo.dndPayload.createNewType\n            }\n          });\n          break;\n      }\n      if(createBlockDto == null) return;\n\n      const changes = await this.contentEditorApi.addFirstElementOnSlide(this.slide.id, BlockUtils.convertToCreateUniversalBlock(createBlockDto));\n      await this.storeService\n        .pipe(ContentContextMutations.applyLayoutChanges, this.contextIdentifier, changes)\n        .pipe(ContentEditorMutations.updateContainerStructure, this.contextIdentifier)\n        .dispatch();\n\n      if(dropInfo.dndPayload.createNewType === NonBlockElementType.AiText) RichTextEditorToolbar.showAiTray();\n      await this.contentEditorService.focusNewTextBlock(changes);\n    } else if(dropInfo.dndPayload.source === DndSubContext.Template) {\n      // apply template to slide:\n      if(dropInfo.dndPayload.existingPayloadId == null || this.presentationId == null) return;\n      const changes = await this.contentEditorApi.applyTemplateExisting(\n        this.presentationId,\n        this.slide.id,\n        dropInfo.dndPayload.existingPayloadId,\n      );\n      if(changes.updatedSlide) {\n        await this.storeService.dispatch(PresentationContextMutations.updateSlide, this.contextIdentifier, changes.updatedSlide);\n        this.updateSlideSettings(changes.updatedSlide);\n      }\n      await this.storeService\n        .pipe(ContentContextMutations.applyLayoutChanges, this.contextIdentifier, changes.layoutChanges)\n        .pipe(ContentEditorMutations.updateContainerStructure, this.contextIdentifier)\n        .dispatch();\n    }\n  }\n\n  private stopDnd(): void {\n    if(this.inspectorMode !== InspectorMode.Container) this.storeService\n      .dispatch(ContentEditorMutations.updateContainerStylesState, { showExtraContainerSpace: false });\n\n    this.storeService\n      .pipe(ContentEditorMutations.updateDragAndDropState, { active: false })\n      .pipe(ContentEditorMutations.resetContainerHoverState)\n      .dispatch();\n  }\n\n  private dndDragActivityHandler(dndInfo: DndActivityEventData<IDndDropzoneConfig>): void {\n    this.dndEmptySlideTargeted = dndInfo.enterEvent;\n  }\n\n  private contentWrapperElementChanged(): void {\n    if(!this.subscriptionManager) return;\n    this.subscriptionManager.disposeSubscriptions();\n\n    if(this.contentWrapperElement) {\n      this.subscriptionManager.subscribeToClickOutside(\n        this.contentWrapperElement,\n        () => this.storeService.dispatch(ContentEditorMutations.updateEditBlockId, null),\n      );\n    }\n  }\n\n  private async handleChangeBackgroundImageClicked(): Promise<false> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n    const item = await this.customDialogService.showMediaSelect({\n      selectedMediaType: GalleryMediaType.Picture\n    });\n    if(item == null || item.type !== GalleryMediaType.Picture) return false;\n\n    const { id } = item.value;\n    await this.updateSlide(contextIdentifier, slide.id, { backgroundImageId: id });\n\n    return false;\n  }\n\n  private async onSaveSlideHeading(evt: InlineEditSaveEvent): Promise<void> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n    try {\n      await this.updateSlide(contextIdentifier, slide.id, { heading: evt.detail.value || null });\n    } catch(e) {\n      this.headingInlineEditField?.cancelEditing();\n    }\n  }\n\n  private async onSaveSlideSubheading(evt: InlineEditSaveEvent): Promise<void> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n    try {\n      await this.updateSlide(contextIdentifier, slide.id, { subheading: evt.detail.value || null });\n    } catch(e) {\n      this.subheadingInlineEditField?.cancelEditing();\n    }\n  }\n\n  private async onSaveSlideFooter(evt: InlineEditSaveEvent): Promise<void> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n    try {\n      await this.updateSlide(contextIdentifier, slide.id, { footer: evt.detail.value || null });\n    } catch(e) {\n      this.footerInlineEditField?.cancelEditing();\n    }\n  }\n\n  private async handleColorSelected(event: ColorSelectedEvent): Promise<void> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n    try {\n      await this.updateSlide(contextIdentifier, slide.id, { backgroundColor: event.detail.color });\n    } finally {\n      this.showColorSelect = false;\n    }\n  }\n\n  private unsetHeading(): Promise<void> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n    return this.updateSlide(contextIdentifier, slide.id, { heading: null, subheading: null });\n  }\n\n  private unsetFooter(): Promise<void> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n    return this.updateSlide(contextIdentifier, slide.id, { footer: null });\n  }\n\n  private unsetBackgroundImage(): Promise<void> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n    return this.updateSlide(contextIdentifier, slide.id, { backgroundImageId: null });\n  }\n\n  private ensureContextIdentifierAndSlide(): { contextIdentifier: string; slide: IPresentationSlideDto } {\n    const { contextIdentifier, slide } = this;\n    assertNotNullOrUndefined(contextIdentifier, 'contextIdentifier cannot be null or undefined');\n    assertNotNullOrUndefined(slide, 'slide cannot be null or undefined');\n\n    return { contextIdentifier, slide };\n  }\n\n  private async updateSlide(contextIdentifier: string, slideId: number, dto: IUpdateSlideDto): Promise<void> {\n    const updatedSlide = await this.contentEditorApi.updateSlide(slideId, dto);\n    await this.storeService.dispatch(PresentationContextMutations.updateSlide, contextIdentifier, updatedSlide);\n  }\n\n  private toggleColorSelect(): void {\n    this.showColorSelect = !this.showColorSelect;\n  }\n\n  private slideChanged(): void {\n    this.backgroundImageSizingSelectedOption = this.slide?.backgroundImageSizing ?? GenericFitAndFill.Original;\n\n    let alignment: CornerAlignment | null = null;\n    if(this.slide?.backgroundImageHorizontalAlignment == null || this.slide?.backgroundImageVerticalAlignment == null) {\n      alignment = CornerAlignment.CenterCenter;\n    } else {\n      switch(this.slide.backgroundImageHorizontalAlignment) {\n        case GenericAlignment.Start:\n          switch(this.slide.backgroundImageVerticalAlignment) {\n            case GenericAlignment.Start:\n              alignment = CornerAlignment.TopLeft;\n              break;\n            case GenericAlignment.End:\n              alignment = CornerAlignment.BottomLeft;\n              break;\n          }\n          break;\n        case GenericAlignment.Middle:\n          alignment = CornerAlignment.CenterCenter;\n          break;\n        case GenericAlignment.End:\n          switch(this.slide.backgroundImageVerticalAlignment) {\n            case GenericAlignment.Start:\n              alignment = CornerAlignment.TopRight;\n              break;\n            case GenericAlignment.End:\n              alignment = CornerAlignment.BottomRight;\n              break;\n          }\n          break;\n      }\n    }\n    this.backgroundImageAlignmentSelectedOption = alignment;\n  }\n\n  private async backgroundImageSizingChanged(event: SelectedEnumOptionChangedEvent<GenericFitAndFill>): Promise<void> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n    const sizing = event.detail.option ?? GenericFitAndFill.Original;\n    await this.updateSlide(contextIdentifier, slide.id, { backgroundImageSizing: sizing });\n  }\n\n  private async backgroundImageAlignmentChanged(event: SelectedEnumOptionChangedEvent<CornerAlignment>): Promise<void> {\n    const { contextIdentifier, slide } = this.ensureContextIdentifierAndSlide();\n\n    let verticalAlignment = GenericAlignment.Middle;\n    let horizontalAlignment = GenericAlignment.Middle;\n    switch(event.detail.option) {\n      case CornerAlignment.TopLeft:\n        verticalAlignment = GenericAlignment.Start;\n        horizontalAlignment = GenericAlignment.Start;\n        break;\n      case CornerAlignment.TopRight:\n        verticalAlignment = GenericAlignment.Start;\n        horizontalAlignment = GenericAlignment.End;\n        break;\n      case CornerAlignment.BottomLeft:\n        verticalAlignment = GenericAlignment.End;\n        horizontalAlignment = GenericAlignment.Start;\n        break;\n      case CornerAlignment.BottomRight:\n        verticalAlignment = GenericAlignment.End;\n        horizontalAlignment = GenericAlignment.End;\n        break;\n      case CornerAlignment.CenterCenter:\n        verticalAlignment = GenericAlignment.Middle;\n        horizontalAlignment = GenericAlignment.Middle;\n        break;\n    }\n\n    await this.updateSlide(contextIdentifier, slide.id, {\n      backgroundImageHorizontalAlignment: horizontalAlignment,\n      backgroundImageVerticalAlignment: verticalAlignment,\n    });\n  }\n}\n","import { autoinject, bindable, computedFrom } from 'aurelia-framework';\nimport { I18N } from 'aurelia-i18n';\nimport { Router } from 'aurelia-router';\nimport { STATES } from 'common/constants/states.constants';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { IFilterDto } from 'common/dto/filter/filter.interface';\nimport { ITenantUserInfoDto } from 'common/dto/tenant/other/tenant.user.info.interface';\nimport { RoleName } from 'common/enum/role.enums';\nimport { StringUtils } from 'common/utils/StringUtils';\nimport { TenantApiService } from '../../../services/api/TenantApiService';\nimport { CustomTableCell } from '../../elements/custom-table/custom-table-cell/custom-table-cell';\nimport {\n  VirtualFilterField,\n  createPrepareServersideSortingFunction,\n  prepareServersideFiltering\n} from '../../elements/custom-table/custom-table-helpers';\nimport { IColumnDefinition } from '../../elements/custom-table/custom-table-interfaces';\nimport { NavigationActionCell } from '../../elements/custom-table/navigation-action-cell/navigation-action-cell';\n\n@autoinject()\nexport class UserAdministrationTable {\n  @bindable() private powerUserMode: boolean | null = null;\n  @bindable() private tableRefreshVersion = 0;\n  // from-view binding only\n  @bindable() public filter: IFilterDto<ITenantUserInfoDto> = {\n    offset: 0,\n    limit: 10,\n    order: [{\n      field: 'enabled',\n      dir: ColumnSortDirection.DESC,\n    }],\n    filter: [],\n  };\n\n  constructor(\n    private tenantApiService: TenantApiService,\n    private router: Router,\n    private i18n: I18N,\n  ) { }\n\n  @computedFrom('powerUserMode')\n  private get tableColumns(): Array<IColumnDefinition<CustomTableCell<ITenantUserInfoDto> | NavigationActionCell<ITenantUserInfoDto>>> {\n    return [\n      {\n        id: 'firstname',\n        nameTk: 'globals.firstname',\n        cellViewModel: CustomTableCell,\n        cellOptions: {\n          transformer: user => user.firstname ?? '-',\n          callback: user => this.navigateToUserDetails(user),\n          clickable: () => true\n        },\n        prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'firstname'),\n        filter: {\n          placeholderTk: 'globals.filter',\n          prepare: user => prepareServersideFiltering(this.filter, 'firstname', user),\n        },\n      },\n      {\n        id: 'lastname',\n        nameTk: 'globals.lastname',\n        cellViewModel: CustomTableCell,\n        cellOptions: {\n          transformer: user => user.lastname ?? '-',\n          callback: user => this.navigateToUserDetails(user),\n          clickable: () => true\n        },\n        prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'lastname'),\n        filter: {\n          placeholderTk: 'globals.filter',\n          prepare: user => prepareServersideFiltering(this.filter, 'lastname', user),\n        },\n      },\n      {\n        id: 'school',\n        nameTk: 'globals.school',\n        cellViewModel: CustomTableCell,\n        cellOptions: {\n          transformer: user => user.school?.name ?? '-'\n        },\n        prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'schoolName' as VirtualFilterField),\n        filter: {\n          placeholderTk: 'globals.filter',\n          prepare: user => prepareServersideFiltering(this.filter, 'schoolName' as VirtualFilterField, user),\n        },\n      },\n      ...(!this.powerUserMode ? [\n        {\n          id: 'state',\n          nameTk: 'globals.state',\n          cellViewModel: CustomTableCell,\n          cellOptions: {\n            transformer: user => user.address?.state ?? '-'\n          },\n          prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'state' as VirtualFilterField),\n          filter: {\n            options: Object.values(STATES).map(state => ({ keyTk: state.nameTk, value: state.key })),\n            placeholderTk: 'globals.filter',\n            prepare: tR => prepareServersideFiltering(this.filter, 'state' as VirtualFilterField, tR, FilterOperationType.Eq),\n          },\n        },\n        {\n          id: 'assignedStates',\n          nameTk: 'pages.user-administration.assignedStates',\n          cellViewModel: CustomTableCell,\n          cellOptions: {\n            transformer: user => user.assignedStates ? StringUtils.truncateWithEllipsis(user.assignedStates.sort((a, b) => a.localeCompare(b)).join(', '), 32) : '-',\n          },\n          filter: {\n            options: Object.values(STATES).map(state => ({ keyTk: state.nameTk, value: state.key })),\n            placeholderTk: 'globals.filter',\n            prepare: tR => prepareServersideFiltering(this.filter, 'assignedState' as VirtualFilterField, tR, FilterOperationType.Eq),\n          },\n        },\n      ] as Array<IColumnDefinition<CustomTableCell<ITenantUserInfoDto> | NavigationActionCell<ITenantUserInfoDto>>> : []),\n      {\n        id: 'email',\n        nameTk: 'globals.email',\n        cellViewModel: CustomTableCell,\n        cellOptions: {\n          transformer: user => user.email\n        },\n        prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'email'),\n        filter: {\n          placeholderTk: 'globals.filter',\n          prepare: user => prepareServersideFiltering(this.filter, 'email', user),\n        },\n      },\n      {\n        id: 'role',\n        nameTk: 'pages.administrative-staff.role',\n        cellViewModel: CustomTableCell,\n        cellOptions: {\n          transformer: user => this.i18n.tr(`globals.${user.role.toLowerCase()}`),\n        },\n        prepareSorting: this.powerUserMode\n          ? undefined\n          : createPrepareServersideSortingFunction(() => this.filter, 'role' as VirtualFilterField),\n        filter: this.powerUserMode\n          ? undefined\n          : {\n            placeholderTk: 'globals.filter',\n            prepare: user => prepareServersideFiltering(this.filter, 'role', user, FilterOperationType.Eq),\n            options: Object.values([RoleName.PowerUser, RoleName.Lecturer]).map(role => ({\n              key: this.i18n.tr(`globals.${role.toLowerCase()}`),\n              value: role\n            }))\n          },\n      },\n      {\n        id: 'status',\n        nameTk: 'globals.status',\n        cellViewModel: CustomTableCell,\n        cellOptions: {\n          tk: user => user.enabled ? 'globals.active' : 'globals.blocked',\n          spanClasses: user => user.enabled ? 'text-success' : 'text-danger'\n        },\n        prepareSorting: createPrepareServersideSortingFunction(this.filter, 'enabled'),\n        filter: {\n          initialValue: 'true',\n          placeholderTk: 'globals.filter',\n          prepare: user => prepareServersideFiltering(this.filter, 'enabled', user, FilterOperationType.Eq),\n          options: [\n            { keyTk: 'globals.active', value: 'true', },\n            { keyTk: 'globals.blocked', value: 'false' }\n          ]\n        }\n      },\n      ...(!this.powerUserMode ? [{\n        id: 'navigation',\n        nameTk: '',\n        cellViewModel: NavigationActionCell,\n        shrink: true,\n        cellOptions: {\n          styleClasses: ['navigation-action-cell'],\n          clickCallback: user => this.navigateToUserDetails(user),\n        },\n      }] as Array<IColumnDefinition<CustomTableCell<ITenantUserInfoDto> | NavigationActionCell<ITenantUserInfoDto>>> : []),\n    ];\n  }\n\n  private navigateToUserDetails(user: ITenantUserInfoDto): void {\n    this.router.navigateToRoute(\n      'user-details/id',\n      { userId: user.id, isPowerUser: user.role === RoleName.PowerUser },\n    );\n  }\n\n  private async fetchTableData(limit: number, offset: number): Promise<{ total: number; data: ITenantUserInfoDto[] }> {\n    this.filter.limit = limit;\n    this.filter.offset = offset;\n\n    const response = await this.tenantApiService.getPersonnel( this.filter);\n\n    return {\n      data: response.rows,\n      total: response.count,\n    };\n  }\n}\n","import { autoinject, bindable, computedFrom } from 'aurelia-framework';\nimport { IUserDataWithId } from 'common/dto/auth/auth.interface';\nimport { CodeStatus } from 'common/dto/course/code/course.code.interface';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { IFilterDto } from 'common/dto/filter/filter.interface';\nimport { IAssignableStudent } from '../../../interfaces/assign.interface';\nimport { InvitationApiService } from '../../../services/api/InvitationApiService';\nimport { VirtualFilterField } from '../../elements/custom-table/custom-table-helpers';\nimport { EntrySelectedEvent, ITextTagsWidgetTableColumn, SearchStringChangedEvent, TagsEditedEvent } from '../../elements/text-tags-widget/text-tags-widget';\n\n@autoinject()\nexport class UserSelect {\n  @bindable() public users: IAssignableStudent<string>[] = [];\n  private selectedUsers: IAssignableStudent<string>[] = [];\n  private availableUsers: IUserDataWithId<string>[] = [];\n  private foundUsers: IUserDataWithId<string>[] = [];\n  private userTableColumns: ITextTagsWidgetTableColumn<IUserDataWithId<string>>[] = [\n    {\n      transformer: e => e.firstname ?? '-'\n    },\n    {\n      transformer: e => e.lastname ?? '-'\n    },\n    {\n      transformer: e => e.nickname ?? '-'\n    },\n  ];\n\n  constructor(\n    private invitationApi: InvitationApiService,\n  ) { }\n\n  protected async attached(): Promise<void> {\n    this.selectedUsers = [...this.users];\n\n    await this.updateAvailableUsers();\n  }\n\n  private usersChanged(): void {\n    this.selectedUsers = [...this.users];\n  }\n\n  private async updateAvailableUsers(): Promise<void> {\n    const filter: IFilterDto<IUserDataWithId<string>> = {\n      offset: 0,\n      limit: 500,\n      order: [{\n        field: 'lastname',\n        dir: ColumnSortDirection.ASC,\n      }],\n      filter: [\n        {\n          field: 'status' as VirtualFilterField,\n          value: CodeStatus.Active,\n          type: FilterOperationType.Eq\n        }\n      ]\n    };\n\n    const result = await this.invitationApi.getUsers(filter);\n    this.availableUsers = result.rows;\n    this.updateUserData();\n  }\n\n  private updateUserData(searchString?: string): void {\n    this.users = this.selectedUsers.map(u => ({\n      ...u,\n      required: false,\n      examDate: null\n    }));\n    this.foundUsers = this.availableUsers.filter(\n      user => searchString == null || searchString.length === 0\n        || user.firstname?.toLocaleLowerCase().includes(searchString.toLocaleLowerCase())\n        || user.lastname?.toLocaleLowerCase().includes(searchString.toLocaleLowerCase())\n        || user.nickname?.toLocaleLowerCase().includes(searchString.toLocaleLowerCase())\n        || this.getFullUserName(user).toLocaleLowerCase().includes(searchString.toLocaleLowerCase())\n    );\n  }\n\n  private handleUserSearchStringChanged(event: SearchStringChangedEvent): void {\n    this.updateUserData(event.detail.searchString.trim());\n  }\n\n  private handleUserEntrySelected(event: EntrySelectedEvent<IUserDataWithId<string>>): void {\n    const selected = event.detail.entry;\n    if(!this.isUserSelected(selected)) {\n      this.selectedUsers = [...this.selectedUsers, selected];\n      this.updateUserData();\n    }\n  }\n\n  private isUserSelected(student: IUserDataWithId<string>): boolean {\n    return !!this.selectedUsers.find(user => user.id === student.id);\n  }\n\n  private handleUserEntryRemoved(event: TagsEditedEvent): void {\n    this.selectedUsers = this.selectedUsers.filter(user => event.detail.tags.includes(this.getFullUserName(user)));\n    this.updateUserData();\n  }\n\n  private getFullUserName(user: IUserDataWithId<string>): string {\n    return (user.lastname ?? '') + (user.lastname ? ' ' : '') + (user.firstname ?? '');\n  }\n\n  @computedFrom('selectedUsers')\n  private get selectedUsersTags(): string[] {\n    return this.selectedUsers.map(user => this.getFullUserName(user));\n  }\n}","import { autoinject, bindable } from 'aurelia-framework';\nimport { VimeoApiService } from 'common/aurelia-services/VimeoApiService';\nimport { IGalleryVideoDto } from 'common/dto/content/videos/video.gallery.interface';\nimport { BunnyUtils } from 'common/utils/BunnyUtils';\n\n@autoinject()\nexport class VimeoThumbnail {\n  @bindable() public item: IGalleryVideoDto<string> | null = null;\n  private src: string | null = null;\n\n  private isAttached = false;\n\n  constructor(\n    private vimeoApiService: VimeoApiService,\n  ) { }\n\n  protected attached(): void {\n    this.isAttached = true;\n    this.tryLoadThumbnail();\n  }\n\n  protected detached(): void {\n    this.isAttached = false;\n    this.src = null;\n  }\n\n  private itemChanged(): void {\n    if(this.isAttached) {\n      this.tryLoadThumbnail();\n    }\n  }\n\n  private async tryLoadThumbnail(): Promise<void> {\n    if(this.item?.bunnyId) {\n      this.src = BunnyUtils.thumbnailFromUrl(this.item.videoSources[0]?.url);\n    } else if(this.item?.vimeoId != null) {\n      try {\n        const videoInfo = await this.vimeoApiService.getVideoInfo(this.item.vimeoId);\n        // eslint-disable-next-line camelcase\n        this.src = videoInfo?.thumbnail_url ?? null;\n      } catch(ex) {\n        this.src = null;\n      }\n    }\n  }\n}\n","import { FrameworkConfiguration, PLATFORM } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('resources/elements/x-select/x-select'));\n}\n","import { autoinject, bindable } from 'aurelia-framework';\nimport { DomUtils } from 'common/utils/DomUtils';\n\n@autoinject()\nexport class XSelect<T = unknown> {\n  @bindable() public name: string = `${Math.random()}`;\n  @bindable() public value: T | null = null;\n  @bindable() public label: string | null = null;\n  @bindable() public items: Array<ISelectItem<T>> = [];\n\n  private domElement: HTMLElement;\n\n  constructor(\n    element: Element,\n  ) {\n    this.domElement = element as HTMLElement;\n  }\n\n  private selectedChanged(): void {\n    DomUtils.fireEvent<ISelectedEventDetail<T>>(this.domElement, {\n      name: 'selection-changed',\n      detail: { value: this.value },\n    });\n  }\n}\n\nexport interface ISelectItem<T> {\n  value: T | null;\n  label: string;\n}\n\nexport interface ISelectedEventDetail<T> { value: T | null }\nexport type SelectedEvent<T> = CustomEvent<ISelectedEventDetail<T>>;\n","import arrow from '@popperjs/core/lib/modifiers/arrow';\nimport offset from '@popperjs/core/lib/modifiers/offset';\nimport preventOverflow from '@popperjs/core/lib/modifiers/preventOverflow';\nimport { createPopper, Instance as Popper } from '@popperjs/core/lib/popper-lite';\nimport { autoinject, bindable } from 'aurelia-framework';\nimport { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\n\n@autoinject()\nexport class XTooltip {\n  @bindable() public forElement: HTMLElement | null = null;\n\n  private element: HTMLElement;\n  private popper: Popper | null = null;\n  private isShown = false;\n  private subscriptionManager: SubscriptionManager;\n\n  constructor(\n    element: Element,\n    subscriptionManagerService: SubscriptionManagerService,\n  ) {\n    this.element = element as HTMLElement;\n    this.subscriptionManager = subscriptionManagerService.createSubscriptionManager();\n  }\n\n  protected attached(): void {\n    this.setup();\n  }\n\n  protected detached(): void {\n    this.teardown();\n  }\n\n  protected forElementChanged(): void {\n    this.setup();\n  }\n\n  public toggle(force?: boolean): void {\n    this.isShown = force ?? !this.isShown;\n    if(this.isShown) {\n      this.popper?.forceUpdate();\n    }\n  }\n\n  private setup(): void {\n    this.teardown();\n\n    if(!this.forElement || !this.element.isConnected) return;\n\n    this.subscriptionManager.subscribeToDomEvent(this.forElement, 'click', e => { e.preventDefault(); this.toggle(); });\n    this.subscriptionManager.subscribeToDomEvent(this.forElement, 'mouseleave', () => this.toggle(false));\n    this.subscriptionManager.subscribeToDomEvent(this.element, 'mouseleave', () => this.toggle(false));\n    this.subscriptionManager.subscribeToDomEvent(this.forElement, 'mouseenter', () => this.toggle(true));\n    this.subscriptionManager.subscribeToDomEvent(this.element, 'mouseenter', () => this.toggle(true));\n    this.subscriptionManager.subscribeToElementResize(this.forElement, () => this.popper?.forceUpdate());\n\n    this.popper = createPopper(this.forElement, this.element, {\n      placement: 'top',\n      strategy: 'fixed',\n      modifiers: [\n        arrow,\n        offset,\n        {\n          name: 'offset',\n          options: {\n            offset: [0, 5],\n          },\n        },\n        preventOverflow,\n        {\n          name: 'preventOverflow',\n          options: {\n            rootBoundary: 'viewport',\n            altAxis: true,\n          },\n        }\n      ],\n    });\n  }\n\n  private teardown(): void {\n    this.popper?.destroy();\n    this.subscriptionManager.disposeSubscriptions();\n  }\n}\n"],"names":[],"sourceRoot":""}