{"version":3,"file":"app-3fa49184.f584a1446b2fc034d810.bundle.js","mappings":"AAGA","sources":["webpack://lernplattform-admin-frontend/./src/resources/elements/drag-and-drop/draggable.html","webpack://lernplattform-admin-frontend/./src/resources/elements/drag-and-drop/drop-zone.html","webpack://lernplattform-admin-frontend/./src/resources/elements/drag-and-drop/draggable.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/drag-and-drop/drop-zone.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/drag-and-drop/index.ts"],"sourcesContent":["// Module\nvar code = \"<template style=\\\"display:block\\\" draggable.to-view=\\\"dragAllowed\\\" dragstart.delegate=\\\"dragStart($event)\\\" dragend.trigger=\\\"dragEnd()\\\"> <slot></slot> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"drop-zone\\\" dragover.delegate=\\\"dragOver($event)\\\" drop.trigger=\\\"drop($event)\\\" dragenter.delegate=\\\"dragEnter()\\\" dragleave.delegate=\\\"dragLeave()\\\"> <slot></slot> </template>\";\n// Exports\nexport default code;","import { autoinject, bindable } from 'aurelia-framework';\nimport { DndContext, DndManager, DndManagerService, DndPayloadUniversal } from '../../../services/DndManagerService';\n\n@autoinject()\nexport class Draggable {\n  @bindable() public context = DndContext.Default;\n  @bindable() public dragAllowed = true;\n  @bindable() public payload: DndPayloadUniversal<unknown> | null = null;\n  /**\n   * dndActive usage is optional. if desired the entity using the drag and drop elements and service\n   * must manage the dndActive state and provide it. this allows preventing new drag and drop\n   * events before possible async reaction (e.g. updating DB) onDrop/onDragEnd is finished.\n   *\n   * some advices for managing dndActive:\n   * - probably set it active when payload becomes != null\n   * - reset it at each end (success, error, pre-check-return) of the onDrop reaction\n   * - reset it when payload becomes null - except onDrop is still working, then leave resetting to onDrop\n   * this strategy does not cover the situation, when dragEnd occurs before onDrop.\n   * until now such a behavior has not been observerd\n   */\n  @bindable() public dndActive = false;\n  /**\n   * An element that can be displayed in place of the browser-default drag-element\n   */\n  @bindable() public dragDomElement: HTMLElement | null = null;\n\n  private dndManager: DndManager<unknown> | null = null;\n\n  constructor(\n    private dndManagerService: DndManagerService,\n  ) { }\n\n  protected attached(): void {\n    this.dndManager = this.dndManagerService.getDndManager<unknown>(this.context);\n  }\n\n  public dragStart(event: DragEvent): boolean {\n    if(this.dndActive || !this.dragAllowed) return false;\n    if(this.dragDomElement) {\n      // opacity to enable border-radius\n      this.dragDomElement.style.opacity = '0.999';\n      event.dataTransfer?.setDragImage(this.dragDomElement, 15, 15);\n    }\n    setTimeout(() => {\n      this.dndManager?.dragStart(this.payload);\n    }, 0);\n    return true;\n  }\n\n  public dragEnd(): boolean {\n    this.dndManager?.dragEnd();\n    return true;\n  }\n}\n","import { autoinject, bindable } from 'aurelia-framework';\nimport { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { ContentEditingInsertModeCardinal, ContentEditingInsertModeSelector } from 'common/constants/content.editing.constants';\nimport {\n  DndContext,\n  IDndDropzoneConfig,\n  DndManager,\n  DndManagerService,\n  DndInsertModeSequential,\n} from '../../../services/DndManagerService';\n\ntype Position2D = {\n  x: number;\n  y: number;\n};\n\ntype RectangleCornerPositions = {\n  topLeft: Position2D;\n  topRight: Position2D;\n  bottomLeft: Position2D;\n  bottomRight: Position2D;\n};\n\ntype QuadrantAngles = {\n  topLeft: number;\n  topRight: number;\n  bottomLeft: number;\n  bottomRight: number;\n};\n\nexport enum SubDropzoneCalcMode {\n  Off = 'off',\n  Cardinal = 'cardinal',\n  Horizontal = 'horizontal',\n}\n\n@autoinject\nexport class DropZone {\n  @bindable() public config: IDndDropzoneConfig | null = null;\n  @bindable() public context = DndContext.Default;\n  /**\n   * subDropzoneCalcMode !== SubDropzoneCalcMode.Off:\n   * causes the drop-zone to calculate the active sub-drop-zone\n   * in case of:\n   * * SubDropzoneCalcMode.Cardinal: in the shape of a triangles.\n   * The 4 possible triangles are defined by the diagonals of the drop-zone.\n   * * SubDropzoneCalcMode.Horizontal: in the shape of two rectangles.\n   * The 2 possible zones are left and right of the center of the drop-zone.\n   *\n   * to prevent endless calculations (if this mode is configured) the\n   * dndActive state has to be provided. a falling edge on this flag\n   * will stop the calculations.\n   */\n  @bindable() protected subDropzoneCalcMode = SubDropzoneCalcMode.Off;\n  @bindable() protected provideDropzoneIdentifiers = true;\n  @bindable() protected dndActive = false;\n\n  private dndManager: DndManager<unknown> | null = null;\n  private dragCounter = 0;\n\n  private specialDropzoneInfo: IDndDropzoneConfig = {\n    identifier: {\n      superId: 0,\n      subId: 0,\n    },\n    insertModeSelector: ContentEditingInsertModeSelector.Cardinal,\n  };\n  private subscriptionManager: SubscriptionManager;\n  private subDropzoneCalcActive = false;\n  private domElement: HTMLElement;\n  private domElementRect: DOMRect = new DOMRect();\n  private mousePos: Position2D = { x: 0, y: 0 };\n  private domElementCenter: Position2D = { x: 0, y: 0 };\n  private domElementCorners: RectangleCornerPositions = { topLeft: { x: 0, y: 0 }, topRight: { x: 0, y: 0 }, bottomLeft: { x: 0, y: 0 }, bottomRight: { x: 0, y: 0 } };\n  private relevantCorner: Position2D = { x: 0, y: 0 };\n  private relevantCornerHelper: Position2D = { x: 0, y: 0 };\n  private quadrantAnglesCornerToCenter: QuadrantAngles = { topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0 };\n  private calculatedInsertModeCardinal: ContentEditingInsertModeCardinal = ContentEditingInsertModeCardinal.Top;\n  private lastCalculatedInsertModeCardinal: ContentEditingInsertModeCardinal = ContentEditingInsertModeCardinal.Left;\n  private calculatedInsertModeHorizontal: DndInsertModeSequential | null = null;\n  private lastCalculatedInsertModeHorizontal: DndInsertModeSequential | null = null;\n\n  constructor(\n    private dndManagerService: DndManagerService,\n    subscriptionManagerService: SubscriptionManagerService,\n    element: Element,\n  ) {\n    this.subscriptionManager = subscriptionManagerService.createSubscriptionManager();\n    this.domElement = element as HTMLElement;\n  }\n\n  protected attached(): void {\n    this.dndManager = this.dndManagerService.getDndManager<unknown>(this.context);\n  }\n\n  protected detached(): void {\n    /**\n     * drop-zone might be detached based on dndActive = false\n     * this could prevent stopping a possibly running sub-dropzone calculation\n     * ensure stopping:\n     */\n    this.stopCalc(true);\n  }\n\n  public dndActiveChanged(): void {\n    this.stopCalc(true);\n  }\n\n  public dragOver(event: any): boolean {\n    event.preventDefault();\n    if(this.subDropzoneCalcMode !== SubDropzoneCalcMode.Off) {\n      this.mousePos.x = event.clientX;\n      this.mousePos.y = event.clientY;\n    }\n    return true;\n  }\n\n  public dragEnter(): boolean {\n    this.dragCounter++;\n    if(this.dragCounter === 1) {\n      if(this.subDropzoneCalcMode === SubDropzoneCalcMode.Off) {\n        this.emitDragActivity(true);\n      } else {\n        this.startCalc();\n      }\n    }\n    return true;\n  }\n\n  public dragLeave(): boolean {\n    this.dragCounter--;\n    if(this.dragCounter < 1) {\n      if(this.subDropzoneCalcMode === SubDropzoneCalcMode.Off) {\n        this.emitDragActivity(false);\n      } else {\n        this.stopCalc(true);\n      }\n    }\n    return true;\n  }\n\n  private emitDragActivity(enterEvent: boolean): void {\n    this.dndManager?.dragActivity(\n      this.config,\n      enterEvent,\n      this.provideDropzoneIdentifiers ? this.config?.identifier : undefined,\n      this.config?.subContext ?? undefined,\n    );\n  }\n\n  public drop(event: DragEvent): boolean {\n    this.dragCounter = 0;\n    if(this.subDropzoneCalcMode === SubDropzoneCalcMode.Off) {\n      if(!this.provideDropzoneIdentifiers) {\n        this.dndManager?.drop(this.config, event, undefined, this.config?.subContext ?? undefined);\n      } else {\n        this.dndManager?.drop(this.config, event, this.config?.identifier, this.config?.subContext ?? undefined);\n      }\n    } else {\n      this.dndManager?.drop(this.specialDropzoneInfo, event, this.specialDropzoneInfo.identifier, this.config?.subContext ?? undefined);\n      this.stopCalc(false);\n    }\n    return true;\n  }\n\n  private startCalc(): void {\n    // TODO - react to domRect changes during DnD (e.g. animations), add flag for domUpdate binding? add state?\n    if(this.subDropzoneCalcMode !== SubDropzoneCalcMode.Off) {\n      this.domElementRect = this.domElement.getBoundingClientRect();\n      this.domElementCenter.y = (this.domElementRect.top + this.domElementRect.bottom) / 2;\n      this.domElementCenter.x = (this.domElementRect.left + this.domElementRect.right) / 2;\n      this.domElementCorners = {\n        topLeft: { x: this.domElementRect.left, y: this.domElementRect.top },\n        topRight: { x: this.domElementRect.right, y: this.domElementRect.top },\n        bottomLeft: { x: this.domElementRect.left, y: this.domElementRect.bottom },\n        bottomRight: { x: this.domElementRect.right, y: this.domElementRect.bottom }\n      };\n      if(this.subDropzoneCalcMode === SubDropzoneCalcMode.Cardinal) {\n        this.quadrantAnglesCornerToCenter = {\n          topLeft: this.calculateAngle(this.domElementCorners.topLeft, this.domElementCorners.bottomLeft, this.domElementCenter),\n          topRight: this.calculateAngle(this.domElementCorners.topRight, this.domElementCorners.topLeft, this.domElementCenter),\n          bottomLeft: this.calculateAngle(this.domElementCorners.bottomLeft, this.domElementCorners.bottomRight, this.domElementCenter),\n          bottomRight: this.calculateAngle(this.domElementCorners.bottomRight, this.domElementCorners.topRight, this.domElementCenter),\n        };\n      }\n    }\n    if(this.config == null) return;\n    this.specialDropzoneInfo.identifier = this.config.identifier;\n    this.specialDropzoneInfo.insertModeSelector = this.config.insertModeSelector;\n    this.subDropzoneCalcActive = true;\n    switch(this.subDropzoneCalcMode) {\n      case SubDropzoneCalcMode.Cardinal:\n        this.subscriptionManager.subscribeToInterval(this.calculateSubDropzoneActivityCardinal.bind(this), 100);\n        break;\n      case SubDropzoneCalcMode.Horizontal:\n        this.specialDropzoneInfo.sequentialOrder = this.config.sequentialOrder;\n        this.specialDropzoneInfo.cardinalInto = this.config.cardinalInto; // todo(improve dnd) currently required workaround for dnd manager issue for sequential dnd\n        this.subscriptionManager.subscribeToInterval(this.calculateSubDropzoneActivityHorizontal.bind(this), 100);\n        break;\n    }\n  }\n\n  private stopCalc(sendLast?: boolean): void {\n    if(this.subDropzoneCalcActive) {\n      this.subscriptionManager.disposeSubscriptions();\n      this.subDropzoneCalcActive = false;\n      this.specialDropzoneInfo.innerEvent = false;\n\n      if(sendLast === true) {\n        this.dndManager?.dragActivity(\n          this.specialDropzoneInfo,\n          false,\n          this.specialDropzoneInfo.identifier,\n          this.config?.subContext ?? undefined,\n        );\n      }\n\n      switch(this.subDropzoneCalcMode) {\n        case SubDropzoneCalcMode.Cardinal:\n          if(sendLast === true) {\n            const copyOfInfoForNotifyingAboutOverallLeaving = this.specialDropzoneInfo;\n            copyOfInfoForNotifyingAboutOverallLeaving.insertModeCardinal = ContentEditingInsertModeCardinal.None;\n            // todo(improve dnd) why this duplicate false event?! -> should this be \"copyOfInfoForNotifyingAboutOverallLeaving\" instead of \"this.specialDropzoneInfo\" here?\n            // but why is it not a problem? is it needed? or not?\n            this.dndManager?.dragActivity(\n              this.specialDropzoneInfo,\n              false,\n              this.specialDropzoneInfo.identifier,\n              this.config?.subContext ?? undefined,\n            );\n          }\n          this.calculatedInsertModeCardinal = ContentEditingInsertModeCardinal.None;\n          this.lastCalculatedInsertModeCardinal = ContentEditingInsertModeCardinal.None;\n          break;\n\n        case SubDropzoneCalcMode.Horizontal:\n          this.calculatedInsertModeHorizontal = null;\n          this.lastCalculatedInsertModeHorizontal = null;\n          break;\n      }\n    }\n  }\n\n  private calculateSubDropzoneActivityHorizontal(): void {\n    // check in which half the current mouse-position is\n    if(this.mousePos.x <= this.domElementCenter.x) {\n      this.calculatedInsertModeHorizontal = DndInsertModeSequential.Before;\n    } else {\n      this.calculatedInsertModeHorizontal = DndInsertModeSequential.After;\n    }\n\n    // very first calculation -> drag enter!\n    if(this.lastCalculatedInsertModeHorizontal == null) {\n      this.specialDropzoneInfo.insertModeSequential = this.calculatedInsertModeHorizontal;\n      this.specialDropzoneInfo.innerEvent = false;\n      this.dndManager?.dragActivity(\n        this.specialDropzoneInfo,\n        true,\n        this.specialDropzoneInfo.identifier,\n        this.config?.subContext ?? undefined,\n      );\n\n      this.lastCalculatedInsertModeHorizontal = this.calculatedInsertModeHorizontal;\n    }\n\n    if(this.calculatedInsertModeHorizontal !== this.lastCalculatedInsertModeHorizontal) {\n      this.specialDropzoneInfo.innerEvent = true;\n      // do a drag-leave for the lastInsertMode - virtualDropZone\n      this.specialDropzoneInfo.insertModeSequential = this.lastCalculatedInsertModeHorizontal;\n      this.dndManager?.dragActivity(\n        this.specialDropzoneInfo,\n        false,\n        this.specialDropzoneInfo.identifier,\n        this.config?.subContext ?? undefined,\n      );\n      // do a drag-enter for the currentInsertMode - virtualDropZone\n      this.specialDropzoneInfo.insertModeSequential = this.calculatedInsertModeHorizontal;\n      this.dndManager?.dragActivity(\n        this.specialDropzoneInfo,\n        true,\n        this.specialDropzoneInfo.identifier,\n        this.config?.subContext ?? undefined,\n      );\n\n      this.lastCalculatedInsertModeHorizontal = this.calculatedInsertModeHorizontal;\n    }\n  }\n\n  private calculateSubDropzoneActivityCardinal(): void {\n    // check in which quadrant the current mouse-position is\n    if(this.mousePos.y <= this.domElementCenter.y) {\n      if(this.mousePos.x <= this.domElementCenter.x) {\n        this.relevantCorner = this.domElementCorners.topLeft;\n        this.relevantCornerHelper = this.domElementCorners.bottomLeft;\n      } else {\n        this.relevantCorner = this.domElementCorners.topRight;\n        this.relevantCornerHelper = this.domElementCorners.topLeft;\n      }\n    } else {\n      if(this.mousePos.x <= this.domElementCenter.x) {\n        this.relevantCorner = this.domElementCorners.bottomLeft;\n        this.relevantCornerHelper = this.domElementCorners.bottomRight;\n      } else {\n        this.relevantCorner = this.domElementCorners.bottomRight;\n        this.relevantCornerHelper = this.domElementCorners.topRight;\n      }\n    }\n    // calculate and compare angles to decide in which triangle of the quadrant the mouse-position is\n    const angle = this.calculateAngle(this.relevantCorner, this.relevantCornerHelper, this.mousePos);\n    if(this.mousePos.y <= this.domElementCenter.y) {\n      if(this.mousePos.x <= this.domElementCenter.x) {\n        if(angle > this.quadrantAnglesCornerToCenter.topLeft) {\n          this.calculatedInsertModeCardinal = ContentEditingInsertModeCardinal.Top;\n        } else {\n          this.calculatedInsertModeCardinal = ContentEditingInsertModeCardinal.Left;\n        }\n      } else {\n        if(angle > this.quadrantAnglesCornerToCenter.topRight) {\n          this.calculatedInsertModeCardinal = ContentEditingInsertModeCardinal.Right;\n        } else {\n          this.calculatedInsertModeCardinal = ContentEditingInsertModeCardinal.Top;\n        }\n      }\n    } else {\n      if(this.mousePos.x <= this.domElementCenter.x) {\n        if(angle > this.quadrantAnglesCornerToCenter.bottomLeft) {\n          this.calculatedInsertModeCardinal = ContentEditingInsertModeCardinal.Left;\n        } else {\n          this.calculatedInsertModeCardinal = ContentEditingInsertModeCardinal.Bottom;\n        }\n      } else {\n        if(angle > this.quadrantAnglesCornerToCenter.bottomRight) {\n          this.calculatedInsertModeCardinal = ContentEditingInsertModeCardinal.Bottom;\n        } else {\n          this.calculatedInsertModeCardinal = ContentEditingInsertModeCardinal.Right;\n        }\n      }\n    }\n\n    if(this.calculatedInsertModeCardinal !== this.lastCalculatedInsertModeCardinal) {\n      // do a drag-leave for the lastInsertMode - virtualDropZone\n      this.specialDropzoneInfo.insertModeCardinal = this.lastCalculatedInsertModeCardinal;\n      this.dndManager?.dragActivity(\n        this.specialDropzoneInfo,\n        false,\n        this.specialDropzoneInfo.identifier,\n        this.config?.subContext ?? undefined,\n      );\n      // do a drag-enter for the currentInsertMode - virtualDropZone\n      this.specialDropzoneInfo.insertModeCardinal = this.calculatedInsertModeCardinal;\n      this.dndManager?.dragActivity(\n        this.specialDropzoneInfo,\n        true,\n        this.specialDropzoneInfo.identifier,\n        this.config?.subContext ?? undefined,\n      );\n\n      this.lastCalculatedInsertModeCardinal = this.calculatedInsertModeCardinal;\n    }\n  }\n\n  /**\n   * calculates the angle between two lines which share the point called \"corner\"\n   * @param corner both lines share this point\n   * @param fixed 2nd point of reference line, in this class the border of the domElementRectangle\n   * @param moving 2nd point of the line either to the center of the rectangle or the current mouse position\n   * @returns the angle between the two lines (unit: radiants)\n   */\n  private calculateAngle(corner: Position2D, fixed: Position2D, moving: Position2D): number {\n    const dAx = fixed.x - corner.x;\n    const dAy = fixed.y - corner.y;\n    const dBx = moving.x - corner.x;\n    const dBy = moving.y - corner.y;\n    return Math.abs(Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy));\n  }\n}\n","import { PLATFORM } from 'aurelia-pal';\nimport { FrameworkConfiguration } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('resources/elements/drag-and-drop/drop-zone'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/drag-and-drop/draggable'));\n}\n"],"names":[],"sourceRoot":""}