{"version":3,"file":"app-a8233362.f584a1446b2fc034d810.bundle.js","mappings":"AAGA","sources":["webpack://lernplattform-admin-frontend/../common/aurelia-components/full-screen-bottom-sheet-header/full-screen-bottom-sheet-header.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/full-screen-bottom-sheet/full-screen-bottom-sheet.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/full-screen-container/full-screen-container.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/fullscreenable-container/fullscreenable-container.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/hot-spot-button/hot-spot-button.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-block/ias-block-question/ias-block-question.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-block/ias-block-video/ias-block-video.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-block/ias-block.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-slide/ias-slide.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/icon-with-tooltip/icon-with-tooltip.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/image-cropper/image-cropper.html","webpack://lernplattform-admin-frontend/../common/aurelia-components/full-screen-bottom-sheet-header/full-screen-bottom-sheet-header.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/full-screen-bottom-sheet-header/index.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/full-screen-bottom-sheet/full-screen-bottom-sheet.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/full-screen-bottom-sheet/index.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/full-screen-container/full-screen-container.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/full-screen-container/index.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/fullscreenable-container/fullscreenable-container.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/fullscreenable-container/index.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/hot-spot-button/hot-spot-button.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/hot-spot-button/index.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-block/ias-block-question/ias-block-question.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-block/ias-block-video/ias-block-video.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-block/ias-block.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-block/index.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-slide/ias-slide.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/ias-slide/index.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/icon-with-tooltip/icon-with-tooltip.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/icon-with-tooltip/index.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/image-cropper/enhanced-croppie.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/image-cropper/image-cropper.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/image-cropper/index.ts","webpack://lernplattform-admin-frontend/../common/aurelia-components/images/index.ts"],"sourcesContent":["// Module\nvar code = \"<template class=\\\"full-screen-bottom-sheet-header lp-site-content-small ${ visible ? 'full-screen-bottom-sheet-header--visible' : '' } ${ peeking && !visible ? 'full-screen-bottom-sheet-header--peeking' : '' }\\\"> <h5 class=\\\"full-screen-bottom-sheet-header__text\\\" id.to-view=\\\"id\\\">${title}</h5> <safe-icon click.delegate=\\\"handleIconClick()\\\" class=\\\"full-screen-bottom-sheet-header__icon\\\" use-as-button t=\\\"[aria-label]general.${ peeking ? 'open' : 'close }\\\" icon-name.to-view=\\\"visible ? 'System/Close' : 'System/Arrow - Down'\\\"></safe-icon> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <full-screen-container view-model.ref=\\\"fullScreenContainer\\\" content-ready-to-render.from-view=\\\"contentReadyToRender\\\" overlap-main-navigation-bar.to-view=\\\"false\\\"> <div class=\\\"full-screen-bottom-sheet ${ visible && contentReadyToRender ? 'full-screen-bottom-sheet--visible' : '' }\\\" transitionend.trigger=\\\"handleTransitionEnd($event)\\\" tabindex=\\\"0\\\" aria-labelledby=\\\"full-screen-bottom-sheet-header\\\" ref=\\\"drawerElement\\\"> <div class=\\\"full-screen-bottom-sheet__content\\\"> <full-screen-bottom-sheet-header visible.to-view=\\\"visible\\\" peeking.to-view=\\\"peeking\\\" title.to-view=\\\"title\\\" icon-clicked.trigger=\\\"toggleOpen()\\\" id=\\\"full-screen-bottom-sheet-header\\\"></full-screen-bottom-sheet-header> <slot></slot> </div> </div> </full-screen-container> <full-screen-bottom-sheet-header visible.to-view=\\\"visible\\\" peeking.to-view=\\\"peeking\\\" title.to-view=\\\"title\\\" icon-clicked.trigger=\\\"toggleOpen()\\\" id=\\\"full-screen-bottom-sheet-header\\\"></full-screen-bottom-sheet-header> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div ref=\\\"contentWrapper\\\" class=\\\"fullscreen-container__content-wrapper ${overlapMainNavigationBar ? `fullscreen-container__content-wrapper--overlap-main-navigation-bar` : ``} ${overlapNavigationSideBar ? `fullscreen-container__content-wrapper--overlap-navigation-side-bar` : ``} ${overlapHeader === true ? `fullscreen-container__content-wrapper--overlap-header` : ``} ${overlapHeader === 'mobile' ? `fullscreen-container__content-wrapper--overlap-header-mobile` : ``} ${noSideMenu ? `fullscreen-container__content-wrapper--no-side-menu` : ``}\\\" style.to-view=\\\"{display: shown ? 'block' : 'none', 'z-index': importance}\\\"> <slot></slot> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"fullscreenable-container\\\" tabindex=\\\"0\\\"> <slot></slot> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"hot-spot-button ${ preview ? 'hot-spot-button--preview' : '' } ${ selected ? 'hot-spot-button--selected' : '' } ${ visited ? 'hot-spot-button--visited' : '' }\\\"> <div class=\\\"hot-spot-button__wrapper\\\"> <div class=\\\"hot-spot-button__ring\\\"></div> <button class=\\\"hot-spot-button__button\\\" aria-haspopup=\\\"true\\\" aria-expanded.to-view=\\\"selected\\\" tabindex.to-view=\\\"order\\\" t=\\\"[aria-label]common.course-block-mp-hot-spot.hotSpot\\\" t-params.to-view=\\\"{ count: order }\\\"> <safe-icon class=\\\"hot-spot-button__icon\\\" aria-hidden=\\\"true\\\" icon-name=\\\"${ selected ? 'System/Tabler/Minus' : 'System/Tabler/Plus' }\\\"></safe-icon> </button> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"ias-block-question ${isNonInteractivePreview ? 'ias-block-question--preview': ''}\\\"> <question-preview-widget if.to-view=\\\"isNonInteractivePreview\\\" question.to-view=\\\"question\\\"></question-preview-widget> <slot name=\\\"prev-slide-btn\\\" slot=\\\"leftHeaderElement\\\"></slot> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"ias-block-video\\\"> <template if.to-view=\\\"block != null && isNonInteractivePreview === false\\\"> <video-player view-model.ref=\\\"videoPlayer\\\" class=\\\"ias-block-video_video-player\\\" auto-play.to-view=\\\"true\\\" reset-on-end.to-view=\\\"false\\\" video-sources.to-view=\\\"block.videoSources\\\" vimeo-id.to-view=\\\"block.vimeoId\\\" bunny-id.to-view=\\\"block.bunnyId\\\" used-thumbnail-src.from-view=\\\"thumbnailSrc\\\" play-state-changed.trigger=\\\"handlePlayStateChanged($event)\\\"> </video-player> <div class=\\\"ias-block-video__replay-video-overlay\\\" style.to-view=\\\"replayVideoOverlayStyle\\\"> <div class=\\\"ias-block-video__replay-video-overlay-btn-container\\\"> <button-standard click.trigger=\\\"replayVideo()\\\" t=\\\"[btn-text]common.course-block-mp-sequence.replayVideo\\\" btn-type=\\\"transparent-outlined\\\" icon-type=\\\"custom\\\" icon-name=\\\"System/Refresh\\\" btn-shape=\\\"condensed\\\"></button-standard> <button-standard click.trigger=\\\"goToNextSlide()\\\" t=\\\"[btn-text]common.course-block-mp-sequence.next\\\" btn-type=\\\"contained\\\" btn-shape=\\\"condensed\\\"></button-standard> </div> </div> </template> <img if.to-view=\\\"block != null && isNonInteractivePreview && thumbnailSrc\\\" class=\\\"ias-block-video__thumbnail\\\" src.to-view=\\\"thumbnailSrc\\\"> <img if.to-view=\\\"block != null && isNonInteractivePreview && pipThumbnailSrc\\\" class=\\\"ias-block-video__pip-thumbnail ias-block-video__pip-thumbnail--${block.pipVideoPosition}\\\" src.to-view=\\\"pipThumbnailSrc\\\"> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"ias-block ias-block--type-${block.type}\\\" markdown-jump-slide-link-click.delegate=\\\"handleJumpSlideLinkClick($event)\\\"> <course-block-mp-sequence if.to-view=\\\"block.type === BlockType.MP_SEQUENCE_START\\\" nav-preview.to-view=\\\"navPreview\\\" context-identifier.to-view=\\\"contextIdentifier\\\" block-id.to-view=\\\"blockId\\\" block.to-view=\\\"block\\\"></course-block-mp-sequence> <x-picture if.to-view=\\\"block.type === BlockType.PICTURE\\\" picture.to-view=\\\"block.picture\\\" class=\\\"course-block-picture__img\\\"></x-picture> <ias-block-video if.to-view=\\\"block.type === BlockType.VIDEO\\\" context-identifier.to-view=\\\"contextIdentifier\\\" block-id.to-view=\\\"blockId\\\" is-non-interactive-preview.to-view=\\\"isNonInteractivePreview\\\"></ias-block-video> <div if.to-view=\\\"block.type === BlockType.AUDIO\\\" class=\\\"ias-block__block-wrapper\\\"> <course-block-audio class=\\\"ias-block__audio\\\" compact.to-view=\\\"true\\\" context-identifier.to-view=\\\"contextIdentifier\\\" block-id.to-view=\\\"blockId\\\"></course-block-audio> <course-block-mp-sequence-sidebar if.to-view=\\\"!isNonInteractivePreview && !isDesktopVersion\\\" show-next-button.to-view=\\\"showTextBlockNextButton\\\" button-clicked.trigger=\\\"goToNextSlide()\\\"></course-block-mp-sequence-sidebar> </div> <div if.to-view=\\\"block.type === BlockType.TEXT\\\" class=\\\"ias-block__block-wrapper\\\"> <button-smaller if.to-view=\\\"showPreviousSlideButton\\\" class=\\\"ias-block__text-block-previous-slide-btn\\\" click.trigger=\\\"goToPreviousSlide()\\\" btn-type=\\\"icon\\\" icon-type=\\\"custom\\\" icon-name=\\\"System/Arrow - Type2 - Left\\\"></button-smaller> <div class=\\\"ias-block__content-wrapper\\\"> <div if.to-view=\\\"block.title\\\" class=\\\"ias-block__text-title-wrapper\\\"> <span class=\\\"ias-block__text-title\\\">${block.title}</span> <text-to-speech audio-url.to-view=\\\"block.audioUrl\\\" class=\\\"course-block-text__text-to-speech\\\"></text-to-speech> </div> <div class=\\\"ias-block__text ${slide.rowStyle === SlideRowStyle.GROW_FIRST ? 'ias-block__text--center' : ''}\\\"> <rich-text text.to-view=\\\"block.text\\\" mode=\\\"content\\\"></rich-text> </div> </div> <course-block-mp-sequence-sidebar if.to-view=\\\"!isNonInteractivePreview && !isDesktopVersion\\\" show-next-button.to-view=\\\"showTextBlockNextButton\\\" button-clicked.trigger=\\\"goToNextSlide()\\\"></course-block-mp-sequence-sidebar> </div> <div if.to-view=\\\"block.type === BlockType.MP_INTERACTIVE_TEXT\\\" class=\\\"ias-block__block-wrapper\\\"> <mp-interactive-text markdown.to-view=\\\"block.text\\\" class=\\\"ias-block__mp-interactive-text\\\" allow-wrong-answers.to-view=\\\"block.errorSlideId !== null\\\" is-desktop-version.to-view=\\\"isDesktopVersion\\\" mp-game-finished.trigger=\\\"handleMpGameFinished($event)\\\" show-help.to-view=\\\"!isDesktopVersion\\\" landscape-mode.to-view=\\\"true\\\"> <button-smaller if.to-view=\\\"showPreviousSlideButton\\\" class=\\\"ias-block__interactive-text-previous-slide-btn\\\" click.trigger=\\\"goToPreviousSlide()\\\" btn-type=\\\"icon\\\" icon-type=\\\"custom\\\" icon-name=\\\"System/Arrow - Type2 - Left\\\" slot=\\\"leftHeaderElement\\\"></button-smaller> </mp-interactive-text> </div> <ias-block-question if.to-view=\\\"block.type === BlockType.MP_QUESTION\\\" context-identifier.to-view=\\\"contextIdentifier\\\" block-id.to-view=\\\"blockId\\\" is-non-interactive-preview.to-view=\\\"isNonInteractivePreview\\\"></ias-block-question> <div class=\\\"ias-block__header\\\" if.to-view=\\\"block.type === BlockType.VIDEO\\\"> <button-smaller if.to-view=\\\"showPreviousSlideButton\\\" class=\\\"ias-block__previous-slide-btn\\\" click.trigger=\\\"goToPreviousSlide()\\\" btn-type=\\\"icon\\\" icon-type=\\\"custom\\\" icon-name=\\\"System/Arrow - Type2 - Left\\\"></button-smaller> <div if.to-view=\\\"block.type === BlockType.VIDEO && nextBlockQuestion\\\" class=\\\"ias-block__video-next-block-question ${showNextBlockQuestion ? `ias-block__video-next-block-question--open` : ``}\\\"> <span if.to-view=\\\"showNextBlockQuestion\\\" class=\\\"ias-block__video-next-block-question-question\\\">${getQuestionPreviewText(nextBlockQuestion)}</span> <safe-icon class=\\\"ias-block__video-next-block-question-icon\\\" click.trigger=\\\"toggleNextBlockQuestion()\\\" icon-name.to-view=\\\"showNextBlockQuestion ? `System/IAS - Close` : `System/Question`\\\" use-as-button></safe-icon> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"ias-slide\\\" style.to-view=\\\"{ '--ias-slide-scale-factor': scaleFactor }\\\"> <aspect-ratio-container class=\\\"ias-slide__aspect-ratio-container\\\" mode=\\\"width-and-height-dependent\\\"> <div class=\\\"ias-slide__content ${!slideId ? 'ias-slide__content--empty' : ''}\\\"> <compose if.to-view=\\\"rootLayoutContainerId != null && contextIdentifier != null\\\" class=\\\"ias-slide__root-layout-container\\\" view-model.to-view=\\\"layoutContainerViewModel\\\" model.bind=\\\"{ layoutContainerId: rootLayoutContainerId, contextIdentifier, isNonInteractivePreview }\\\"></compose> <slot name=\\\"additional\\\"></slot> </div> </aspect-ratio-container> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"icon-with-tooltip\\\" style.to-view=\\\"{ '--icon-with-tooltip-width': tooltipWidthInPx + 'px' }\\\"> <safe-icon if.to-view=\\\"iconName\\\" class=\\\"icon-with-tooltip__icon\\\" icon-name.to-view=\\\"iconName\\\" icon-style.to-view=\\\"iconStyle\\\"></safe-icon> <div if.to-view=\\\"tooltipTk\\\" class=\\\"icon-with-tooltip__tooltip icon-with-tooltip__tooltip--${tooltipPosition}\\\" style.to-view=\\\"tooltipStyles\\\" t.to-view=\\\"tooltipTk\\\"></div> <div if.to-view=\\\"tooltipTk\\\" class=\\\"icon-with-tooltip__tooltip-triangle icon-with-tooltip__tooltip-triangle--${tooltipPosition}\\\"></div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"image-cropper\\\" style.to-view=\\\"{'--image-cropper-icon-count': toolbarIcons.length}\\\"> <div class=\\\"image-cropper__container\\\" ref=\\\"croppieContainer\\\"></div> <div class=\\\"image-cropper__toolbar\\\"> <button-standard repeat.for=\\\"icon of toolbarIcons\\\" class=\\\"image-cropper__toolbar-button\\\" icon-name.to-view=\\\"icon.iconName\\\" btn-color=\\\"secondary\\\" btn-size=\\\"tiny\\\" click.delegate=\\\"icon.onClick()\\\"></button-standard> </div> </template> \";\n// Exports\nexport default code;","import { bindable, PLATFORM, useView } from 'aurelia-framework';\nimport { DomUtils } from 'common/utils/DomUtils';\n\n/**\n * @event {FullScreenBottomSheetHeaderIconClickedEvent} icon-clicked\n */\n@useView(PLATFORM.moduleName('./full-screen-bottom-sheet-header.html'))\nexport class FullScreenBottomSheetHeader {\n  @bindable() private id: string = '';\n  @bindable() private title: string = '';\n  @bindable() private peeking: boolean = false;\n  @bindable() private visible: boolean = false;\n  private domElement: HTMLElement;\n\n  constructor(\n    element: Element,\n  ) {\n    this.domElement = element as HTMLElement;\n  }\n\n  private handleIconClick(): void {\n    DomUtils.fireEvent(this.domElement, {\n      name: 'icon-clicked',\n      bubbles: false,\n    });\n  }\n}\n\nexport type FullScreenBottomSheetHeaderIconClickedEvent = CustomEvent<never>;","import { PLATFORM, FrameworkConfiguration } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/full-screen-bottom-sheet-header/full-screen-bottom-sheet-header'));\n}\n","import { bindable, PLATFORM, TaskQueue, useView } from 'aurelia-framework';\nimport { FocusTrap } from 'common/aurelia-classes/FocusTrap';\nimport { SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { FullScreenContainer } from '../full-screen-container/full-screen-container';\n\n@useView(PLATFORM.moduleName('./full-screen-bottom-sheet.html'))\nexport class FullScreenBottomSheet {\n  @bindable() private title: string = '';\n  @bindable() private peeking: boolean = false;\n  @bindable() private isOpen: boolean = false; // from-view\n  @bindable() private visibleAfterOpen: boolean = false; // from-view\n\n  private fullScreenContainer: FullScreenContainer | null = null;\n  private drawerElement: HTMLElement | null = null;\n  private focusTrap: FocusTrap | null = null;\n  private visible = false;\n\n  constructor(\n    private taskQueue: TaskQueue,\n    private subscriptionManagerService: SubscriptionManagerService,\n  ) { }\n\n  public open(): void {\n    this.isOpen = true;\n\n    // wait for the fullScreenContainer to render\n    this.taskQueue.queueMicroTask(() => {\n      this.getFullScreenContainer().show();\n      this.getOrCreateFocusTrap()?.setup();\n      setTimeout(() => {\n        this.visible = true;\n      }, 0);\n    });\n  }\n\n  private toggleOpen(): void {\n    if(this.isOpen) {\n      this.close();\n    } else {\n      this.open();\n    }\n  }\n\n  private close(): void {\n    this.visible = false;\n  }\n\n  private handleTransitionEnd(event: TransitionEvent): void {\n    if(event.currentTarget === event.target) {\n      if(!this.visible) {\n        this.getFullScreenContainer().hide();\n        this.getOrCreateFocusTrap()?.destroy();\n        this.isOpen = false;\n        this.visibleAfterOpen = false;\n      } else {\n        this.visibleAfterOpen = true;\n      }\n    }\n  }\n\n  private getFullScreenContainer(): FullScreenContainer {\n    assertNotNullOrUndefined(this.fullScreenContainer, 'full screen container is not available');\n    return this.fullScreenContainer;\n  }\n\n  private getOrCreateFocusTrap(): FocusTrap | null {\n    if(this.drawerElement && !this.focusTrap) {\n      this.focusTrap = new FocusTrap(this.drawerElement, this.subscriptionManagerService);\n    }\n    return this.focusTrap;\n  }\n}\n","import { PLATFORM, FrameworkConfiguration } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/full-screen-bottom-sheet/full-screen-bottom-sheet'));\n}\n","import {\n  autoinject,\n  bindable,\n  PLATFORM,\n  useView,\n} from 'aurelia-framework';\nimport { WebAnimationPlayer } from 'common/aurelia-classes/animationPlayer/WebAnimationPlayer';\nimport { CommonFullScreenContainer } from 'common/aurelia-components/common-full-screen-container/common-full-screen-container';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { ICommonState } from 'common/store/store.interface';\n\n/**\n * @slot default - put your fullscreen content in this slot\n */\n@useView(PLATFORM.moduleName('./full-screen-container.html'))\n@autoinject()\nexport class FullScreenContainer extends CommonFullScreenContainer {\n  /**\n   * is true when the content is fully attached\n   * use this to trigger animations\n   * readonly\n   */\n  @bindable() public contentReadyToRender = false;\n\n  /**\n   * Animation player\n   */\n  @bindable() public animationPlayer: WebAnimationPlayer | null = null;\n\n  /**\n   * set this to false if you don't want the full-screen-container to go over the course-main-navigation-bar\n   */\n  @bindable() public overlapMainNavigationBar = true;\n\n  /**\n  * set this to false if you don't want the full-screen-container to go over the course-main-navigation-side-bar\n  */\n  @bindable() public overlapNavigationSideBar = true;\n\n  /**\n  * set this to false if you don't want the full-screen-container to go over the course-main-header\n  */\n  @bindable() public overlapHeader: boolean | 'mobile' = true;\n\n  /**\n   * When many full-screen-containers are open at the same time, the ones with the higher importance value will show on top\n   * For same importance containers, the last one is shown on top\n   */\n  @bindable() public importance = 1;\n\n  private noSideMenu = false;\n\n  constructor(\n    element: Element,\n    private storeService: StoreService<ICommonState>,\n  ) {\n    super(element);\n  }\n\n  public async show(): Promise<void> {\n    await super.show();\n    const state = await this.storeService.getState();\n    this.noSideMenu = state.fullscreenContainer.noSideMenu;\n  }\n}\n","import { PLATFORM, FrameworkConfiguration } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/full-screen-container/full-screen-container'));\n}\n","import { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { FocusTrap } from 'common/aurelia-classes/FocusTrap';\nimport { autoinject, bindable, PLATFORM, useView } from 'aurelia-framework';\nimport { FullscreenUtils } from 'common/utils/FullscreenUtils';\n\n@autoinject()\n@useView(PLATFORM.moduleName('./fullscreenable-container.html'))\nexport class FullscreenableContainer {\n  /**\n   * FROM VIEW only\n   */\n  @bindable() public isFullscreen = false;\n\n  private domElement: HTMLElement;\n  private subscriptionManager: SubscriptionManager;\n  private subscriptionManagerService: SubscriptionManagerService;\n\n  private contentWrapperFocusTrap: FocusTrap | null = null;\n\n  constructor(element: Element, subscriptionManagerService: SubscriptionManagerService) {\n    this.domElement = element as HTMLElement;\n    this.subscriptionManagerService = subscriptionManagerService;\n    this.subscriptionManager = subscriptionManagerService.createSubscriptionManager();\n  }\n\n  public requestFullscreen(): void {\n    if(!FullscreenUtils.isEnabled) return;\n\n    if(!this.isFullscreen) {\n      FullscreenUtils.request(this.domElement);\n      const focusTrap = this.getOrCreateContentWrapperFocusTrap();\n      focusTrap.setup();\n    }\n  }\n\n  public exitFullscreen(): void {\n    if(!FullscreenUtils.isEnabled) return;\n\n    if(this.isFullscreen) {\n      FullscreenUtils.exit(this.domElement);\n    }\n  }\n\n  protected attached(): void {\n    this.subscriptionManager.subscribeToFullscreenChange(() => {\n      this.updateIsFullscreen();\n\n      if(!this.isFullscreen) {\n        const focusTrap = this.getOrCreateContentWrapperFocusTrap();\n        focusTrap.destroy();\n      }\n    });\n\n    this.updateIsFullscreen();\n  }\n\n  protected detached(): void {\n    this.subscriptionManager.disposeSubscriptions();\n    this.contentWrapperFocusTrap?.destroy();\n  }\n\n  private updateIsFullscreen(): void {\n    this.isFullscreen = FullscreenUtils.isEnabled && FullscreenUtils.isFullscreen(this.domElement);\n  }\n\n  private getOrCreateContentWrapperFocusTrap(): FocusTrap {\n    if(!this.contentWrapperFocusTrap) {\n      this.contentWrapperFocusTrap = new FocusTrap(this.domElement, this.subscriptionManagerService);\n    }\n\n    return this.contentWrapperFocusTrap;\n  }\n}\n","import { FrameworkConfiguration, PLATFORM } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/fullscreenable-container/fullscreenable-container'));\n}\n","import { PLATFORM, bindable, useView } from 'aurelia-framework';\nimport { FULL_HD } from 'common/constants/resolution.constants';\nimport { IMpHotSpotDto } from 'common/dto/content/methodpool/hot.spot.interface';\nimport { MathUtils } from 'common/utils/MathUtils';\n\nexport const MP_HOT_SPOT_BUTTON_SIZE = 32;\n\nexport const getHotSpotStyle = (hotSpot: IMpHotSpotDto, hotSpotButtonColor: string): Record<string, string> => {\n  const leftPercentage = MathUtils.roundToPrecision(hotSpot.posX / FULL_HD.width * 100, 2);\n  const topPercentage = MathUtils.roundToPrecision(hotSpot.posY / FULL_HD.height * 100, 2);\n  return {\n    '--hot-spot-button-color': hotSpotButtonColor,\n    '--hot-spot-button-size': `${MP_HOT_SPOT_BUTTON_SIZE}px`,\n    left: `Min(calc(100% - ${MP_HOT_SPOT_BUTTON_SIZE}px), ${leftPercentage}%)`,\n    top: `min(calc(100% - ${MP_HOT_SPOT_BUTTON_SIZE}px), ${topPercentage}%)`,\n  };\n};\n\n@useView(PLATFORM.moduleName('./hot-spot-button.html'))\nexport class HotSpotButton {\n  @bindable() public selected: boolean = false;\n  @bindable() public visited: boolean = false;\n  @bindable() public preview: boolean = false;\n  @bindable() public order: number = 0;\n}","import { PLATFORM, FrameworkConfiguration } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/hot-spot-button/hot-spot-button'));\n}\n","import { autoinject, bindable, useView, PLATFORM, computedFrom } from 'aurelia-framework';\nimport { selectState } from 'common/aurelia-decorators/select-state';\nimport { IQuestionDto } from 'common/dto/course/content/question.interface';\nimport { IQuizQuestionDto } from 'common/dto/course/quiz/quiz.question.interface';\nimport { questionByBlockIdSelectorCreator } from 'common/store/selectors/course.context.selectors';\nimport { QuestionUtils } from 'common/utils/QuestionUtils';\n\n@useView(PLATFORM.moduleName('./ias-block-question.html'))\n@autoinject()\nexport class IasBlockQuestion {\n  @bindable() public contextIdentifier: string | null = null;\n  @bindable() public blockId: number | null = null;\n  /**\n   * when true questions can be answered and pictures can be zoomed\n   */\n  @bindable() public isNonInteractivePreview: boolean | null = null;\n\n  @selectState(IasBlockQuestion, questionByBlockIdSelectorCreator, ['contextIdentifier', 'blockId']) private question?: IQuestionDto | null;\n\n  @computedFrom('question')\n  private get quizQuestion(): IQuizQuestionDto | null {\n    return this.question != null\n      ? QuestionUtils.quizQuestionFromQuestion(this.question, false, null)\n      : null;\n  }\n}\n","import { autoinject, bindable, observable, PLATFORM, useView } from 'aurelia-framework';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { ICourseBlockDto } from 'common/dto/course/content/block.interface';\nimport { blockByIdSelectorCreator } from 'common/store/selectors/content.context.selectors';\nimport { ICommonState } from 'common/store/store.interface';\nimport { BlockUtils } from 'common/utils/BlockUtils';\nimport { VideoBlockUtils } from 'common/utils/VideoBlockUtils';\n\n@useView(PLATFORM.moduleName('./ias-block-video.html'))\n@autoinject()\nexport class IasBlockVideo {\n  @bindable() public contextIdentifier: string | null = null;\n  @bindable() public blockId: number | null = null;\n  @bindable() public isNonInteractivePreview: boolean | null = null;\n\n  @observable() private block: ICourseBlockDto | null = null;\n  private thumbnailSrc: string | null = null;\n  private pipThumbnailSrc: string | null = null;\n\n  constructor(\n    private storeService: StoreService<ICommonState>,\n  ) { }\n\n  protected attached(): void {\n    this.subscribeToState();\n  }\n\n  protected detached(): void {\n    this.storeService.disposeAllSubscriptions(this);\n  }\n\n  private subscribeToState(): void {\n    this.storeService.disposeAllSubscriptions(this);\n    if(this.contextIdentifier == null || this.blockId == null) return;\n\n    this.storeService.subscribeMemoized(\n      this,\n      blockByIdSelectorCreator,\n      block => { this.block = block as ICourseBlockDto | null; },\n      { contextIdentifier: this.contextIdentifier, blockId: this.blockId },\n    );\n  }\n\n  private blockIdChanged(): void {\n    this.subscribeToState();\n  }\n\n  private contextIdentifierChanged(): void {\n    this.subscribeToState();\n  }\n\n  private async blockChanged(): Promise<void> {\n    this.thumbnailSrc = null;\n    this.pipThumbnailSrc = null;\n    if(this.block != null) {\n      this.thumbnailSrc = await VideoBlockUtils.fetchVideoThumbnail(this.block);\n      if(BlockUtils.isDualVideoBlock(this.block)) this.pipThumbnailSrc = await VideoBlockUtils.fetchPipVideoThumbnail(this.block);\n    }\n  }\n}\n","import { autoinject, useView, PLATFORM, bindable } from 'aurelia-framework';\nimport { selectState } from 'common/aurelia-decorators/select-state';\nimport { ICourseBlockDto } from 'common/dto/course/content/block.interface';\nimport { BlockType } from 'common/dto/course/content/course.types';\nimport { blockByIdSelectorCreator } from 'common/store/selectors/content.context.selectors';\n\n@useView(PLATFORM.moduleName('./ias-block.html'))\n@autoinject()\nexport class IasBlock {\n  @bindable() public contextIdentifier: string | null = null;\n  @bindable() public blockId: number | null = null;\n  @bindable() public navPreview = false;\n  /**\n   * when true interactive content like videos and questions can be interacted with\n   */\n  @bindable() public isNonInteractivePreview: boolean | null = null;\n\n  @selectState(IasBlock, blockByIdSelectorCreator, ['contextIdentifier', 'blockId']) private block?: ICourseBlockDto;\n  private BlockType = BlockType;\n}\n","import { FrameworkConfiguration, PLATFORM } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/ias-block/ias-block'));\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/ias-block/ias-block-video/ias-block-video'));\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/ias-block/ias-block-question/ias-block-question'));\n}\n","import { autoinject, useView, PLATFORM, bindable } from 'aurelia-framework';\nimport { selectState } from 'common/aurelia-decorators/select-state';\nimport { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { iasSlideRootLayoutContainerIdSelectorCreator } from 'common/store/selectors/course.context.selectors';\nimport { ComposableLayoutContainer } from 'common/aurelia-components/content-editor/composable-layout-container/composable-layout-container';\n\n@useView(PLATFORM.moduleName('./ias-slide.html'))\n@autoinject()\nexport class IasSlide {\n  @bindable() public slideId: number | null = null;\n  @bindable() public layoutContainerViewModel = ComposableLayoutContainer;\n  @bindable() public contextIdentifier: string | null = null;\n  @bindable() public isNonInteractivePreview = true;\n\n  @selectState(IasSlide, iasSlideRootLayoutContainerIdSelectorCreator, ['slideId', 'contextIdentifier']) private rootLayoutContainerId?: number;\n\n  private subscriptionManager: SubscriptionManager;\n  private domElement: HTMLElement;\n  private scaleFactor = 1;\n  private isAttached = false;\n\n  constructor(\n    element: Element,\n    subscriptionManagerService: SubscriptionManagerService,\n  ) {\n    this.domElement = element as HTMLElement;\n    this.subscriptionManager = subscriptionManagerService.createSubscriptionManager();\n  }\n\n  protected attached(): void {\n    this.isAttached = true;\n\n    this.subscribeToResize();\n  }\n\n  protected detached(): void {\n    this.isAttached = false;\n\n    this.subscriptionManager.disposeSubscriptions();\n  }\n\n  private subscribeToResize(): void {\n    if(!this.isAttached) return;\n    this.subscriptionManager.disposeSubscriptions();\n\n    this.subscriptionManager.subscribeToElementResize(this.domElement, this.updateScaleFactor.bind(this));\n  }\n\n  private updateScaleFactor(): void {\n    const width = parseFloat(window.getComputedStyle(this.domElement).width);\n    this.scaleFactor = width / 1920; // magic number, by design everything has normal/default size when the slide is 1920px wide\n  }\n}\n","import { FrameworkConfiguration, PLATFORM } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/ias-slide/ias-slide'));\n}\n","import { PLATFORM, bindable, useView } from 'aurelia-framework';\n\n/**\n * Define the tooltip position relative to the icon.\n * E.g. TopLeft means above and to the left of the icon.\n */\nexport enum TooltipPosition {\n  TopLeft = 'top-left',\n  TopCenter = 'top-center',\n  TopRight = 'top-right',\n  BottomLeft = 'bottom-left',\n}\n\n/**\n * Currently tooltip is styled to appear to the top-left of the icon.\n */\n@useView(PLATFORM.moduleName('./icon-with-tooltip.html'))\nexport class IconWithTooltip {\n  @bindable() public iconName: string | null = null;\n  @bindable() public tooltipTk: string | null = null;\n  @bindable() public iconColorVar: string | null = null;\n  @bindable() public tooltipPosition: TooltipPosition = TooltipPosition.TopLeft;\n  @bindable() public tooltipWidthInPx: number = 220;\n  @bindable() public textAlign = 'center';\n\n  private iconStyle: Record<string, unknown> | null = null;\n  private tooltipStyles: { textAlign: string } | null = null;\n\n  protected attached(): void {\n    if(this.iconColorVar) {\n      this.iconStyle = {\n        color: `var(${this.iconColorVar})`,\n      };\n    }\n    this.tooltipStyles = {\n      textAlign: this.textAlign,\n    };\n  }\n}\n","import { PLATFORM, FrameworkConfiguration } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/icon-with-tooltip/icon-with-tooltip'));\n}\n","import { ValidExifAngle } from 'common/dto/content/pictures/picture.block.render.options.interface';\nimport Croppie from 'croppie';\n\n/**\n * A standalone wrapper around croppie with additional features such as proper rotation and flipping transformations\n */\nexport class EnhancedCroppie extends Croppie {\n  /** The mapping to Number happens, because the types are lying, and these are actually 4 strings */\n  public static getPointsCorrectly(points: Array<number | string>): number[] {\n    return points?.map(Number);\n  }\n\n  public static fromExifOrientation(orientation: number | ExifOrientation): { angle: ValidExifAngle; flipped: boolean } {\n    switch(orientation) {\n      case ExifOrientation.Original: return { angle: 0, flipped: false };\n      case ExifOrientation.Rotated90: return { angle: 90, flipped: false };\n      case ExifOrientation.Rotated180: return { angle: 180, flipped: false };\n      case ExifOrientation.Rotated270: return { angle: 270, flipped: false };\n      case ExifOrientation.Flipped: return { angle: 0, flipped: true };\n      case ExifOrientation.FlippedRotated90: return { angle: 90, flipped: true };\n      case ExifOrientation.FlippedRotated180: return { angle: 180, flipped: true };\n      case ExifOrientation.FlippedRotated270: return { angle: 270, flipped: true };\n      default: throw new Error(`invalid orientation ${orientation}`);\n    }\n  }\n\n  public static toExifOrientation(angle: ValidExifAngle, flipped: boolean): ExifOrientation {\n    if(flipped) switch(angle) {\n      case 0: return ExifOrientation.Flipped;\n      case 90: return ExifOrientation.FlippedRotated90;\n      case 180: return ExifOrientation.FlippedRotated180;\n      case 270: return ExifOrientation.FlippedRotated270;\n      default:\n    } else switch(angle) {\n      case 0: return ExifOrientation.Original;\n      case 90: return ExifOrientation.Rotated90;\n      case 180: return ExifOrientation.Rotated180;\n      case 270: return ExifOrientation.Rotated270;\n      default:\n    }\n\n    throw new Error(`invalid orientation ${angle}`);\n  }\n\n  public rotateClockwise(): Promise<void> {\n    return this.transform(this.rotateOrientationClockwise, this.shiftPointsClockwise);\n  }\n\n  public rotateCounterClockwise(): Promise<void> {\n    return this.transform(this.rotateOrientationCounterClockwise, this.shiftPointsCounterClockwise);\n  }\n\n  public flipHorizontally(): Promise<void> {\n    return this.transform(this.flipOrientationHorizontally, this.flipPointsHorizontally);\n  }\n\n  public flipVertically(): Promise<void> {\n    return this.transform(this.flipOrientationVertically, this.flipPointsVertically);\n  }\n\n  public async bind(options: CroppieBindOptions): Promise<void> {\n    // catch a specific error that just happens when croppie is destroyed while it is loading the image\n    return super.bind(options).catch(e => {\n      if(e instanceof TypeError && e.message.includes('img') && e.message.includes('undefined')) {\n        // eslint-disable-next-line no-console\n        console.warn(`Croppie destroyed during creation, probably during resize (${e.message})`);\n      }\n      else throw e;\n    });\n  }\n\n  private transform(orientationOperation: (orientation: number) => ExifOrientation, pointsOperation: (points: number[], size: Size) => number[]): Promise<void> {\n    const cropData = this.get();\n    const imageSize = this.getOriginalImageSize();\n\n    return this.bind({\n      url: this.getUrl(),\n      orientation: cropData.orientation != null ? orientationOperation(cropData.orientation) : undefined,\n      points: cropData.points ? pointsOperation(EnhancedCroppie.getPointsCorrectly(cropData.points), imageSize) : undefined,\n      zoom: cropData.zoom,\n    });\n  }\n\n  private getOriginalImageSize(): Size {\n    // HAX access image width from privates\n    return {\n      width: (this as any)._originalImageWidth,\n      height: (this as any)._originalImageHeight,\n    };\n  }\n\n  private getUrl(): string {\n    // HAX access image url from privates\n    return (this as any).data.url;\n  }\n\n  private shiftPointsClockwise(points: number[], { height }: Size): number[] {\n    const [left, top, right, bottom] = points;\n    const lengthsDifference = ((right - left) - (bottom - top)) / 2;\n    return [height - bottom - lengthsDifference, left + lengthsDifference, height - top + lengthsDifference, right - lengthsDifference];\n  }\n\n  private shiftPointsCounterClockwise(points: number[], { width }: Size): number[] {\n    const [left, top, right, bottom] = points;\n    const lengthsDifference = ((right - left) - (bottom - top)) / 2;\n    return [top - lengthsDifference, width - right + lengthsDifference, bottom + lengthsDifference, width - left - lengthsDifference];\n  }\n\n  private flipPointsHorizontally(points: number[], { width }: Size): number[] {\n    const [left, top, right, bottom] = points;\n    return [width - right, top, width - left, bottom];\n  }\n\n  private flipPointsVertically(points: number[], { height }: Size): number[] {\n    const [left, top, right, bottom] = points;\n    return [left, height - bottom, right, height - top];\n  }\n\n  private rotateOrientationClockwise(orientation: number | ExifOrientation): ExifOrientation {\n    const split = EnhancedCroppie.fromExifOrientation(orientation);\n    split.angle += 90;\n    if(split.angle >= 360) split.angle -= 360;\n    return EnhancedCroppie.toExifOrientation(split.angle as ValidExifAngle, split.flipped);\n  }\n\n  private rotateOrientationCounterClockwise(orientation: number | ExifOrientation): ExifOrientation {\n    const split = EnhancedCroppie.fromExifOrientation(orientation);\n    split.angle -= 90;\n    if(split.angle < 0) split.angle += 360;\n    return EnhancedCroppie.toExifOrientation(split.angle as ValidExifAngle, split.flipped);\n  }\n\n  private flipOrientationHorizontally(orientation: number | ExifOrientation): ExifOrientation {\n    const split = EnhancedCroppie.fromExifOrientation(orientation);\n    split.flipped = !split.flipped;\n    if(split.angle === 90) split.angle = 270;\n    else if(split.angle === 270) split.angle = 90;\n    return EnhancedCroppie.toExifOrientation(split.angle, split.flipped);\n  }\n\n  private flipOrientationVertically(orientation: number | ExifOrientation): ExifOrientation {\n    const split = EnhancedCroppie.fromExifOrientation(orientation);\n    split.flipped = !split.flipped;\n    if(split.angle === 0) split.angle = 180;\n    else if(split.angle === 180) split.angle = 0;\n    return EnhancedCroppie.toExifOrientation(split.angle, split.flipped);\n  }\n}\n\nenum ExifOrientation {\n  Original = 1,\n  Rotated90 = 6,\n  Rotated180 = 3,\n  Rotated270 = 8,\n  Flipped = 2,\n  FlippedRotated90 = 7,\n  FlippedRotated180 = 4,\n  FlippedRotated270 = 5,\n}\n\ntype Size = {\n  width: number;\n  height: number;\n};\n\n\nexport { CroppieOptions, CropType } from 'croppie';\n\nexport type CroppieBindOptions = Parameters<Croppie['bind']>[0] & {\n  orientation?: ExifOrientation;\n};\n\nexport type CropData = Croppie.CropData & {\n  orientation?: ExifOrientation;\n};\n","import { autoinject, bindable, PLATFORM, useView } from 'aurelia-framework';\nimport { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { ValidExifAngle } from 'common/dto/content/pictures/picture.block.render.options.interface';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { DomUtils } from 'common/utils/DomUtils';\nimport { CropData, CroppieBindOptions, CroppieOptions, EnhancedCroppie } from './enhanced-croppie';\n\n/**\n * Provide an image url to crop\n * Subscribe to the event to get updates on the left, top, right, bottom relative to the image dimensions\n * That data can be passed as initialData to continue cropping something\n *\n * uses croppie (http://foliotek.github.io/Croppie/#documentation)\n *\n * @event {ImageCropperUpdateEvent} image-cropper-update\n */\n@autoinject()\n@useView(PLATFORM.moduleName('./image-cropper.html'))\nexport class ImageCropper {\n  @bindable() url: string | null = null;\n  @bindable() initialData: IImageCropperData | null = null;\n  @bindable() useWideScreenViewPort: boolean = false;\n\n  private element: HTMLElement;\n  private subscriptionManager: SubscriptionManager;\n  private croppieContainer: HTMLDivElement | null = null;\n\n  private croppie: EnhancedCroppie | null = null;\n  private currentData: IImageCropperData | null = null;\n\n  private toolbarIcons = [\n    {\n      iconName: 'System/Tabler/FlipHorizontally',\n      onClick: () => this.croppie?.flipHorizontally(),\n    },\n    {\n      iconName: 'System/Tabler/FlipVertically',\n      onClick: () => this.croppie?.flipVertically(),\n    },\n    {\n      iconName: 'System/Tabler/Rotate90Clockwise',\n      onClick: () => this.croppie?.rotateClockwise(),\n    },\n  ];\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.setupCroppie();\n  }\n\n  protected detached(): void {\n    this.subscriptionManager.disposeSubscriptions();\n    this.croppie?.destroy();\n  }\n\n  private urlChanged(): void {\n    this.setupCroppie();\n  }\n\n  private initialDataChanged(): void {\n    this.currentData = null;\n    this.setupCroppie();\n  }\n\n  private setupCroppie(): void {\n    if(!this.element.isConnected) return;\n    assertNotNullOrUndefined(this.url, 'url must be bound');\n    assertNotNullOrUndefined(this.croppieContainer, 'forgot to ref croppieContainer');\n\n    this.subscriptionManager.disposeSubscriptions();\n    this.croppie?.destroy();\n\n    const croppieOptions: CroppieOptions = {\n      enableResize: true,\n      enableOrientation: true,\n      maxZoom: 5,\n    };\n\n    const croppieBindOptions: CroppieBindOptions = {\n      url: this.url,\n      zoom: 1,\n    };\n\n    const data = this.currentData ?? this.initialData;\n    const croppieBbox = this.croppieContainer.getBoundingClientRect();\n    if(data) {\n      const { left, top, right, bottom, angle, flipped } = data;\n\n      const croppedWidth = right - left;\n      const croppedHeight = bottom - top;\n\n      // Calculate the minimum zoom factor such that the viewport fits into the bounding rectangle\n      let zoomFactor = Math.min(1, croppieBbox.width / croppedWidth / 2, croppieBbox.height / croppedHeight / 2);\n      // Croppie prevents resizing when the viewport is \"less than\" 50 in width and height. If it already starts with less than that, you can't make it bigger either :D\n      // So we have to adjust the zoomFactor to result in at least 50 in both width and height of the viewport\n      zoomFactor = Math.max(zoomFactor, 50 / croppedHeight, 50 / croppedWidth);\n\n      // the viewport is the white rectangle in the middle\n      croppieOptions.viewport = {\n        height: croppedHeight * zoomFactor,\n        width: croppedWidth * zoomFactor,\n      };\n\n      croppieBindOptions.zoom = zoomFactor;\n      // the \"points\" are an array given in this specific order, representing the crop coordinates of the image absolutely (i.e. not accounting for the zoom factor)\n      croppieBindOptions.points = [left, top, right, bottom];\n      croppieBindOptions.orientation = EnhancedCroppie.toExifOrientation(angle, flipped);\n    }\n\n    if(this.useWideScreenViewPort) {\n      croppieOptions.viewport = { width: croppieBbox.width, height: (croppieBbox.width * 9) / 16 };\n      croppieBindOptions.zoom = 0;\n    }\n\n    this.croppie = new EnhancedCroppie(this.croppieContainer, croppieOptions);\n    this.croppie.bind(croppieBindOptions);\n\n    this.subscriptionManager.subscribeToDomEvent(this.croppieContainer, 'update', ev => {\n      this.handleCroppieUpdateEvent(ev as CustomEvent<CropData>);\n    });\n\n    const initialRect = this.element.getBoundingClientRect();\n    this.subscriptionManager.subscribeToElementResize(this.element, async () => {\n      const newRect = this.element.getBoundingClientRect();\n      if(newRect.width === initialRect.width && newRect.height === initialRect.height) return;\n      this.setupCroppie();\n    });\n  }\n\n  private handleCroppieUpdateEvent(ev: CustomEvent<CropData>): void {\n    const [left, top, right, bottom] = ev.detail.points ? EnhancedCroppie.getPointsCorrectly(ev.detail?.points) : [0, 0, 0, 0];\n    this.currentData = {\n      left, right, top, bottom,\n      ...EnhancedCroppie.fromExifOrientation(ev.detail.orientation ?? 0),\n    };\n    DomUtils.fireEvent<ImageCropperUpdateEvent['detail']>(this.element, {\n      name: 'image-cropper-update',\n      bubbles: false,\n      detail: { ...this.currentData },\n    });\n  }\n}\n\nexport type ImageCropperUpdateEvent = CustomEvent<IImageCropperData>;\n\nexport interface IImageCropperData {\n  top: number;\n  bottom: number;\n  left: number;\n  right: number;\n  angle: ValidExifAngle;\n  flipped: boolean;\n}\n","import { PLATFORM, FrameworkConfiguration } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('common/aurelia-components/image-cropper/image-cropper'));\n}\n","// this file has be autogenerated by generateIndex.js\n// typechecking disabled because ts can't handle the webpack loader\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport AddressMap from './AddressMap.svg';\nimport Downloads from './Downloads.svg';\nimport DownloadsBlue from './DownloadsBlue.svg';\nimport DownloadsOrange from './DownloadsOrange.svg';\nimport Exam from './Exam.svg';\nimport FaviconFrp from './FaviconFrp.ico';\nimport FaviconLera from './FaviconLera.ico';\nimport FaviconRisi from './FaviconRisi.ico';\nimport FaviconYarrive from './FaviconYarrive.ico';\nimport FinalExam from './FinalExam.svg';\nimport GamesCropped from './GamesCropped.svg';\nimport LearningCropped from './LearningCropped.svg';\nimport LearningCroppedGreen from './LearningCroppedGreen.svg';\nimport LearningOrange from './LearningOrange.svg';\nimport LogoFrpAlt from './LogoFrpAlt.svg';\nimport LogoFrpIcon from './LogoFrpIcon.svg';\nimport LogoFrpLarge from './LogoFrpLarge.svg';\nimport LogoFrpText from './LogoFrpText.svg';\nimport OEJRKBadge from './OEJRKBadge.svg';\nimport PlyrIconSprite from './PlyrIconSprite.svg';\nimport Police from './Police.svg';\nimport TestexamCropped from './TestexamCropped.svg';\nimport TestexamFrp from './TestexamFrp.svg';\nimport TestexamGraph from './TestexamGraph.svg';\nimport TestexamGraphCropped from './TestexamGraphCropped.svg';\nimport Train from './Train.svg';\nimport TrainCropped from './TrainCropped.svg';\nimport TrainList from './TrainList.svg';\n// @ts-check\n\nexport const imageNameToString = {\n  AddressMap,\n  Downloads,\n  DownloadsBlue,\n  DownloadsOrange,\n  Exam,\n  FaviconFrp,\n  FaviconLera,\n  FaviconRisi,\n  FaviconYarrive,\n  FinalExam,\n  GamesCropped,\n  LearningCropped,\n  LearningCroppedGreen,\n  LearningOrange,\n  LogoFrpAlt,\n  LogoFrpIcon,\n  LogoFrpLarge,\n  LogoFrpText,\n  OEJRKBadge,\n  PlyrIconSprite,\n  Police,\n  TestexamCropped,\n  TestexamFrp,\n  TestexamGraph,\n  TestexamGraphCropped,\n  Train,\n  TrainCropped,\n  TrainList,\n};\n"],"names":[],"sourceRoot":""}