{"version":3,"file":"app-c62e50be.f584a1446b2fc034d810.bundle.js","mappings":"AAGA","sources":["webpack://lernplattform-admin-frontend/./src/resources/elements/course-lecture-quiz-header/course-lecture-quiz-header.html","webpack://lernplattform-admin-frontend/./src/resources/elements/course-lecture-slide-preview/course-lecture-slide-preview.html","webpack://lernplattform-admin-frontend/./src/resources/elements/course-media-shares/course-media-shares.html","webpack://lernplattform-admin-frontend/./src/resources/elements/course-questions-overview/course-questions-overview.html","webpack://lernplattform-admin-frontend/./src/resources/elements/course-rating-preview/course-rating-preview.html","webpack://lernplattform-admin-frontend/./src/resources/elements/course-ratings-overview/course-ratings-overview.html","webpack://lernplattform-admin-frontend/./src/resources/elements/create-question-form/create-question-form.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/actions-cell/actions-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/avatar-cell/avatar-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/button-cell/button-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/course-code-cell/course-code-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/custom-table-cell/custom-table-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/custom-table.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/icon-button-cell/icon-button-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/icon-cell/icon-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/input-date-cell/input-date-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/input-number-cell/input-number-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/json-cell/json-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/navigation-action-cell/navigation-action-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/progress-cell/progress-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/rating-cell/rating-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/select-cell/select-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/shared-to-cell/shared-to-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/skill-icon-cell/skill-icon-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/sort-cell/sort-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/tagged-text-cell/tagged-text-cell.html","webpack://lernplattform-admin-frontend/./src/resources/elements/course-lecture-quiz-header/course-lecture-quiz-header.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/course-lecture-slide-preview/course-lecture-slide-preview.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/course-media-shares/course-media-shares.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/course-questions-overview/course-questions-overview.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/course-rating-preview/course-rating-preview.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/course-ratings-overview/course-ratings-overview.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/create-question-form/create-question-form.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/actions-cell/actions-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/avatar-cell/avatar-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/button-cell/button-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/course-code-cell/course-code-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/custom-table-cell/custom-table-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/custom-table-helpers.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/custom-table-interfaces.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/custom-table.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/icon-button-cell/icon-button-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/icon-cell/icon-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/index.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/input-date-cell/input-date-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/input-number-cell/input-number-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/json-cell/json-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/navigation-action-cell/navigation-action-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/progress-cell/progress-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/rating-cell/rating-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/select-cell/select-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/shared-to-cell/shared-to-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/skill-icon-cell/skill-icon-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/sort-cell/sort-cell.ts","webpack://lernplattform-admin-frontend/./src/resources/elements/custom-table/tagged-text-cell/tagged-text-cell.ts"],"sourcesContent":["// Module\nvar code = \"<template class=\\\"course-lecture-quiz-header course-lecture-quiz-header--contrast-${contrastMode}\\\"> <div class=\\\"course-lecture-quiz-header__text\\\" t-params.to-view=\\\"textTkParams\\\" t.to-view=\\\"textTk\\\"></div> <button if.to-view=\\\"buttonTk && showButton\\\" class=\\\"btn btn-primary course-lecture-quiz-header__button\\\" click.delegate=\\\"handleButtonClicked()\\\"> <span t.to-view=\\\"buttonTk\\\"></span> </button> <progress-bar if.to-view=\\\"progress != null && showProgress\\\" class=\\\"course-lecture-quiz-header__progress-bar\\\" progress.to-view=\\\"progress\\\" transition.to-view=\\\"false\\\" type.to-view=\\\"`secondary3`\\\"></progress-bar> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <svg viewBox=\\\"0 0 16 9\\\" class=\\\"course-lecture-slide-preview__ratio-svg\\\"></svg> <content-editor-presentation-slide class=\\\"course-lecture-slide-preview__slide\\\" context-identifier.to-view=\\\"contextIdentifier\\\" slide-id.to-view=\\\"slideId\\\" empty-placeholder-tk=\\\"common.presentation-slide.noPresentationSelected\\\"></content-editor-presentation-slide> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"d-flex mb-4\\\"> <share-media-button table-refresh-version.two-way=\\\"tableRefreshVersion\\\" course-id.to-view=\\\"courseId\\\"></share-media-button> </div> <div class=\\\"row row-cards row-deck\\\"> <div class=\\\"col-12\\\"> <media-share-table table-refresh-version.two-way=\\\"tableRefreshVersion\\\" course-id.to-view=\\\"courseId\\\"></media-share-table> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"d-flex mb-4\\\"> <button-standard click.delegate=\\\"addQuestion()\\\" class=\\\"mr-2\\\" t=\\\"[btnText]globals.add\\\" icon-name=\\\"System/Tabler/Plus\\\" btn-color=\\\"primary\\\"></button-standard> <button-standard click.delegate=\\\"showImportDialog()\\\" class=\\\"mr-2\\\" t=\\\"[btnText]elements.courseQuestionsOverview.import\\\" icon-name=\\\"System/Tabler/Upload\\\" btn-color=\\\"secondary\\\"></button-standard> <button-standard click.delegate=\\\"copyQuestionFromCourse()\\\" t=\\\"[btnText]elements.courseQuestionsOverview.copyFromCourse\\\" icon-name=\\\"System/Tabler/Upload\\\" btn-color=\\\"secondary\\\"></button-standard> </div> <question-list-widget course-id.to-view=\\\"id\\\" table-refresh-version.two-way=\\\"tableRefreshVersion\\\"></question-list-widget> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"course-rating-preview\\\"> <div class=\\\"card\\\"> <div class=\\\"card-header\\\"> <h2 class=\\\"card-title\\\" t=\\\"elements.ratingTable.recentlyRatedCourses\\\"></h2> </div> <div class=\\\"row\\\"> <div class=\\\"col-12\\\"> <custom-table data.to-view=\\\"ratings\\\" unbounded=\\\"true\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\" hide-footer.to-view=\\\"true\\\" columns.to-view=\\\"tableColumns\\\"></custom-table> </div> </div> <div class=\\\"card-footer\\\"> <div class=\\\"text-right\\\"> <button class=\\\"btn btn-secondary mt-2\\\" click.delegate=\\\"downloadRatings()\\\"> <i class=\\\"fe fe-printer mr-2\\\"></i> <span t=\\\"elements.ratingTable.exportRatings\\\"></span> </button> </div> </div> </div>  </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"card\\\"> <div class=\\\"row\\\"> <div class=\\\"col-12\\\"> <custom-table unbounded.to-view=\\\"true\\\" columns.to-view=\\\"tableColumns\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\" get-shown-entries-data.call=\\\"fetchTableData(limit, offset)\\\"></custom-table> </div> </div> <div class=\\\"card-footer\\\"> <div class=\\\"text-right\\\"> <button class=\\\"btn btn-secondary mt-2\\\" click.delegate=\\\"downloadRatings()\\\"> <i class=\\\"fe fe-printer mr-2\\\"></i> <span t=\\\"elements.ratingTable.exportRatings\\\"></span> </button> </div> </div> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"row\\\"> <div class=\\\"col-12\\\"> <div class=\\\"card\\\"> <div class=\\\"card-header\\\"> <h1 class=\\\"card-title\\\" t=\\\"pages.questions.titleQuestion\\\"></h1> <div class=\\\"form-group mb-0 ml-auto\\\"> <label class=\\\"custom-control custom-checkbox mb-0\\\"> <input type=\\\"checkbox\\\" class=\\\"custom-control-input\\\" checked.two-way=\\\"question.learningOnly\\\"> <span class=\\\"custom-control-label text-nowrap\\\" t=\\\"pages.questions.labelLearningOnly\\\"></span> </label> </div> </div> <div class=\\\"card-body\\\"> <div if.to-view=\\\"questionMediaType === MediaType.Picture && question.type !== QuestionType.LT\\\" class=\\\"row\\\"> <div class=\\\"col-12\\\"> <label class=\\\"form-label\\\" t=\\\"pages.questions.imageLabel\\\"></label> </div> <div class=\\\"col-10\\\"> <div class=\\\"form-group\\\"> <exam-image if.to-view=\\\"picture.url\\\" alt-text.to-view=\\\"question.text\\\" img-src.to-view=\\\"picture.url\\\" blink-img-src.to-view=\\\"pictureBlink.url\\\" enable-zoom.to-view=\\\"false\\\"></exam-image> </div> </div> <div class=\\\"col-2\\\"> <div class=\\\"form-group\\\"> <button class=\\\"btn btn-secondary btn-sm w-100 mb-2\\\" click.delegate=\\\"handleSelectQuestionImage()\\\" t=\\\"pages.questions.${picture.url ? 'editImage' : 'selectImage'}\\\"></button> <button if.to-view=\\\"picture.url\\\" class=\\\"btn btn-danger btn-sm w-100\\\" click.delegate=\\\"handleDeleteQuestionImage()\\\" t=\\\"globals.delete\\\"></button> </div> <div class=\\\"form-group\\\"> <button if.to-view=\\\"picture.url\\\" class=\\\"btn btn-secondary btn-sm w-100 mb-2\\\" click.delegate=\\\"handleSelectQuestionBlinkImage()\\\" t=\\\"pages.questions.${pictureBlink.url ? 'editBlinkImage' : 'selectBlinkImage'}\\\"></button> <button if.to-view=\\\"pictureBlink.url\\\" class=\\\"btn btn-danger btn-sm w-100\\\" click.delegate=\\\"handleDeleteQuestionBlinkImage()\\\" t=\\\"pages.questions.deleteBlinkImage\\\"></button> </div> </div> </div> <audio-select-form if.to-view=\\\"questionMediaType === MediaType.Audio && question.type !== QuestionType.LT\\\" t=\\\"[title]pages.questions.audioLabel\\\" course-id.to-view=\\\"courseId\\\" audio-id.two-way=\\\"question.audioId\\\" audio-url.to-view=\\\"audioUrl\\\"></audio-select-form> <template if.to-view=\\\"question.type !== QuestionType.LT\\\"> <div class=\\\"row\\\"> <div class=\\\"col-md-12\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"pages.questions.textLabel\\\"></label> <textarea class=\\\"form-control\\\" value.two-way=\\\"question.text\\\"></textarea> </div> </div> </div> <div class=\\\"row\\\"> <div class=\\\"col-md-12\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"pages.questions.helpTextLabel\\\"></label> <textarea class=\\\"form-control\\\" value.two-way=\\\"question.helpText\\\"></textarea> </div> </div> </div> </template> <template if.to-view=\\\"question.type === QuestionType.LT\\\"> <template repeat.for=\\\"count of 3\\\"> <div class=\\\"row\\\"> <div class=\\\"col-md-12\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"pages.questions.textLabel\\\"></label> <textarea class=\\\"form-control\\\" value.two-way=\\\"ltQuestionParts[$index]\\\"></textarea> </div> </div> </div> <div class=\\\"row\\\"> <div class=\\\"col-md-12\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"pages.questions.helpTextLabel\\\"></label> <textarea class=\\\"form-control\\\" value.two-way=\\\"ltHelpTextParts[$index]\\\"></textarea> </div> </div> </div> </template> </template> </div> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <dropdown-button t=\\\"[label]globals.actions\\\" placement=\\\"bottom-end\\\"> <span repeat.for=\\\"entry of model.options.entries\\\"> <div if.to-view=\\\"isDivider(entry)\\\" class=\\\"dropdown-divider\\\"></div> <button if.to-view=\\\"!isDivider(entry)\\\" class=\\\"dropdown-item pointer ${entry.additionalClasses}\\\" click.delegate=\\\"entry.clickCallback(model.data)\\\" show.to-view=\\\"isVisible(entry, model.data)\\\" t.to-view=\\\"'[aria-label]' + (entry.labelTk && getLabelTk(entry.labelTk, model.data))\\\"> <div if.to-view=\\\"entry.icon\\\" class=\\\"float-right\\\"><i class=\\\"fe ${entry.icon}\\\"></i></div> <div t.to-view=\\\"entry.labelTk && getLabelTk(entry.labelTk, model.data)\\\"></div> </button> </span> </dropdown-button> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <avatar-icon avatar-svg-download-link.to-view=\\\"model.options.avatarSvgDownloadLink(model.data)\\\" level-mode=\\\"primary-color-indicator\\\" online.to-view=\\\"model.options.online(model.data)\\\" points.to-view=\\\"model.options.points(model.data)\\\"></avatar-icon> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <button-standard disabled.to-view=\\\"model.options.disabled(model.data)\\\" click.delegate=\\\"model.options.clickCallback(model.data)\\\" btn-size=\\\"btn-sm\\\" btn-color.to-view=\\\"model.options.buttonColor(model.data) || 'secondary'\\\" icon-name.to-view=\\\"model.options.iconName(model.data)\\\" t=\\\"[btnText]${model.options.buttonText(model.data).tk}\\\" t-params.to-view=\\\"model.options.buttonText(model.data).tkParams\\\"></button-standard> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <span repeat.for=\\\"code of model.data.accessCodes\\\"> <a if.bind=\\\"model.options.showLinks()\\\" route-href=\\\"route: invitation/id; params.bind: { id: code.id }\\\">${ code.course.title }</a><span if.bind=\\\"!model.options.showLinks()\\\">${ code.course.title }</span>${ isLast($index, code.length) ? ',' : '' } </span> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <span class=\\\"${model.options.callback && clickable ? 'btn btn-link p-0' : ''}\\\" click.trigger=\\\"click()\\\">${!spanClasses && !href ? value : ''}</span> <span if.to-view=\\\"spanClasses || tk && !href\\\" class=\\\"${spanClasses}\\\" click.trigger=\\\"click()\\\"><span if.to-view=\\\"tk\\\" t.to-view=\\\"tk\\\"></span>${value}</span> <a if.to-view=\\\"href && value\\\" class=\\\"btn-link ${spanClasses}\\\" href.to-view=\\\"href(data)\\\"><span if.to-view=\\\"tk\\\" t.to-view=\\\"tk\\\"></span>${value}</a> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"custom-table ${ alignContentCenter ? 'custom-table--align-content-center' : '' }\\\"> <div class=\\\"custom-table__controls ${ hideControls ? 'd-none' : '' } ${ tableId && unbounded ? 'custom-table__controls--inside-container' : '' }\\\"> <div class=\\\"custom-table__controls-additional\\\"> <slot></slot> </div> <div class=\\\"custom-table__controls-buttons\\\"> <button-standard if.to-view=\\\"tableId && filterActive\\\" readonly.to-view=\\\"readonly\\\" icon-name=\\\"System/Filter - Reset\\\" btn-color=\\\"danger\\\" btn-type=\\\"outline\\\" btn-size=\\\"tiny\\\" click.delegate=\\\"clearFilters()\\\"></button-standard> <button-standard if.to-view=\\\"tableId && filterStatus.sortColumnId\\\" readonly.to-view=\\\"readonly\\\" icon-name=\\\"System/Sort - Reset\\\" btn-color=\\\"danger\\\" btn-type=\\\"outline\\\" btn-size=\\\"tiny\\\" click.delegate=\\\"clearSort()\\\"></button-standard> <button-standard if.to-view=\\\"extendable\\\" readonly.to-view=\\\"readonly\\\" icon-name=\\\"System/Tabler/${ extended ? 'Minimize' : 'Maximize' }\\\" btn-color=\\\"secondary\\\" btn-size=\\\"tiny\\\" click.delegate=\\\"toggleSize()\\\"></button-standard> </div> </div> <div class=\\\"custom-table__expandable-card ${ extendable && extended ? 'custom-table__expandable-card--extended' : '' } ${ !unbounded ? 'card' : '' }\\\"> <div class=\\\"table-responsive\\\"> <table class=\\\"table table-hover table-outline align-top text-nowrap card-table\\\"> <thead> <tr> <th if.to-view=\\\"selectable\\\"></th> <th if.to-view=\\\"hasSubTable\\\"></th> <th repeat.for=\\\"column of columns\\\"> <div click.delegate=\\\"handleSortingChanged(column)\\\" css=\\\"cursor: ${ readonly ? 'not-allowed' : allowSorting && (column.prepareSorting || column.sortFunction) ? 'pointer' : '' }\\\"> <span if.to-view=\\\"!column.nameHtml\\\" t.to-view=\\\"column.nameTk\\\"></span> <span if.to-view=\\\"column.nameHtml\\\" innerhtml.bind=\\\"column.nameHtml\\\"></span> <i if.to-view=\\\"allowSorting && column.id === currentSortColumn.id\\\" class=\\\"fe ${ isSortedAsc(currentSortDirection) ? 'fe-chevron-down' : 'fe-chevron-up' }\\\"></i> <i if.to-view=\\\"allowSorting && !currentSortColumn && (column.prepareSorting || column.sortFunction)\\\" class=\\\"fe fe-code small text-muted custom-table__unsorted-icon\\\"></i> </div> </th> </tr> <tr if.to-view=\\\"hasFilters(columns) && showFilters\\\"> <th if.to-view=\\\"selectable\\\"> <label class=\\\"custom-control custom-checkbox d-flex\\\"> <input class=\\\"custom-control-input\\\" type=\\\"checkbox\\\" readonly.to-view=\\\"readonly\\\" checked.to-view=\\\"allSelected\\\" click.delegate=\\\"handleSelectAllClicked()\\\"> <span class=\\\"custom-control-label\\\"></span> </label> </th> <th if.to-view=\\\"hasSubTable\\\"></th> <th repeat.for=\\\"column of columns\\\" class=\\\"${ column.visible && !column.visible() ? 'hide-content' : '' }\\\"> <api-text-filter-input if.to-view=\\\"column.filter && !column.filter.options && !column.filter.daterange\\\" class=\\\"custom-table__filter-cell custom-table__filter-cell--size-${column.filter.size}\\\" type.to-view=\\\"column.filter.inputType\\\" theme=\\\"table-head\\\" readonly.to-view=\\\"readonly\\\" label-tk.to-view=\\\"column.nameTk\\\" placeholder-tk.to-view=\\\"column.filter.placeholderTk\\\" initial-value.to-view=\\\"column.filter.initialValue.toString()\\\" set-value.to-view=\\\"getFilterSetValue(filterStatus, column)\\\" filter-changed.trigger=\\\"handleFilterChanged(column, $event)\\\"></api-text-filter-input> <api-select-filter-input if.to-view=\\\"column.filter && column.filter.options && !column.filter.daterange\\\" class=\\\"custom-table__filter-cell custom-table__filter-cell--size-${column.filter.size}\\\" options.to-view=\\\"column.filter.options\\\" theme=\\\"table-head\\\" readonly.to-view=\\\"readonly\\\" label-tk.to-view=\\\"column.nameTk\\\" placeholder-tk.to-view=\\\"column.filter.placeholderTk\\\" initial-value.to-view=\\\"column.filter.initialValue.toString()\\\" set-value.to-view=\\\"getFilterSetValue(filterStatus, column)\\\" filter-changed.trigger=\\\"handleFilterChanged(column, $event)\\\"></api-select-filter-input> <api-date-filter-input if.to-view=\\\"column.filter.daterange\\\" class=\\\"custom-table__filter-cell custom-table__filter-cell--size-${column.filter.size}\\\" start-date.one-time=\\\"column.filter.initialValue\\\" theme=\\\"table-head\\\" readonly.to-view=\\\"readonly\\\" initial-value.to-view=\\\"column.filter.initialValue.toString()\\\" set-value.to-view=\\\"getFilterSetValue(filterStatus, column)\\\" filter-changed.trigger=\\\"handleFilterChanged(column, $event)\\\"></api-date-filter-input> </th> </tr> </thead> <tbody> <template repeat.for=\\\"entry of shownEntries\\\"> <tr> <td if.to-view=\\\"selectable\\\" class=\\\"cell-shrink\\\"> <label class=\\\"custom-control custom-checkbox d-flex mb-0 form-check-label\\\"> <input class=\\\"custom-control-input form-check-input\\\" attach-focus.to-view=\\\"pullFocus && $index === 0\\\" type=\\\"checkbox\\\" readonly.to-view=\\\"readonly\\\" model.to-view=\\\"entry\\\" checked.two-way=\\\"selected\\\"> <span class=\\\"custom-control-label\\\"></span> </label> </td> <td if.to-view=\\\"hasSubTable\\\"> <safe-icon class=\\\"custom-table__sub-table-collapse-icon\\\" icon-name.to-view=\\\"subTableOpenIdx === $index ? `System/Tabler/Minus` : `System/Tabler/Plus`\\\" click.trigger=\\\"toggleSubTable($index)\\\"></safe-icon> </td> <td class=\\\"custom-table__cell ${ column.wrap ? 'custom-table__cell--wrap' : '' } ${ column.centered ? 'custom-table__cell--centered' : '' } ${ column.shrink ? 'cell-shrink' : '' } ${ column.visible && !column.visible(entry) ? 'hide-content' : '' } ${ column.cellOptions.styleClasses ? column.cellOptions.styleClasses.join(' ') : '' }\\\" repeat.for=\\\"column of columns\\\" as-element=\\\"compose\\\" view-model.to-view=\\\"column.cellViewModel\\\" model.to-view=\\\"{ data: entry, options: column.cellOptions }\\\"></td> </tr> <tr class=\\\"custom-table__sub-table-row\\\"> <td colspan.to-view=\\\"columnCount\\\" style.to-view=\\\"{\\n                    'display': subTableOpenIdx === $index ? 'table-cell' : 'none',\\n                    'padding': '0',\\n                  }\\\"> <custom-table if.to-view=\\\"subTableOpenIdx === $index\\\" class=\\\"custom-table--sub-table\\\" delegate.to-view=\\\"subTableDelegate\\\" readonly.to-view=\\\"readonly\\\" selectable.to-view=\\\"subTableSelectable\\\" selected.two-way=\\\"subTableSelected\\\" hide-footer.to-view=\\\"true\\\" hide-controls.to-view=\\\"true\\\" unbounded.to-view=\\\"true\\\"></custom-table> </td> </tr> </template> </tbody> </table> </div> <div if.to-view=\\\"!hideFooter\\\" class=\\\"d-flex custom-table__footer\\\"> <pagination-widget class=\\\"flex-grow-1\\\" limit.to-view=\\\"filter.limit\\\" offset.to-view=\\\"filter.offset\\\" total.to-view=\\\"total\\\" readonly.to-view=\\\"readonly\\\" new-offset-callback.to-view=\\\"newOffsetCallback\\\" settings-callback.to-view=\\\"settingsCallback\\\" num-page-quick-links.to-view=\\\"6\\\"></pagination-widget> </div> <div if.to-view=\\\"showPageConfig\\\" class=\\\"custom-table__footer\\\"> <div class=\\\"row\\\"> <label class=\\\"form-group col-6\\\"> <div class=\\\"form-label\\\" t=\\\"elements.customTable.rowsPerPageSelectLabel\\\"></div> <select class=\\\"custom-select\\\" value.two-way=\\\"newRowsPerPage\\\" readonly.to-view=\\\"readonly\\\" change.delegate=\\\"handleRowsPerPageChanged()\\\"> <option repeat.for=\\\"option of rowsPerPageOptions\\\" model.to-view=\\\"option\\\">${option}</option> </select> </label> </div> </div> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <button-standard class=\\\"ml-auto\\\" btn-size=\\\"tiny\\\" disabled.to-view=\\\"!clickable\\\" click.delegate=\\\"click()\\\" btn-color=\\\"none\\\" icon-name.to-view=\\\"iconName\\\"></button-standard> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div ref=\\\"iconElement\\\" class=\\\"icon-cell ${classes}\\\" css=\\\"cursor: ${ clickable ? 'pointer' : '' }\\\" aria-label.to-view=\\\"(model.options.ariaLabel ? model.options.ariaLabel(model.data) : null) & t & attr\\\" role.to-view=\\\"clickable ? 'button' : 'img' & attr\\\" click.delegate=\\\"click()\\\"> <safe-icon if.to-view=\\\"icon\\\" class=\\\"icon-cell__icon\\\" icon-name.to-view=\\\"icon\\\"></safe-icon> <span if.to-view=\\\"model.options.trailingTextTransformer\\\" class=\\\"text-dark\\\">&nbsp${trailingText}</span> <x-tooltip if.to-view=\\\"tooltipTk\\\" for-element.to-view=\\\"iconElement\\\">${tooltipTk | t}</x-tooltip> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"form-group mb-0 ml-auto input-date-cell\\\"> <div class=\\\"input-group\\\"> <date-input class=\\\"input-date-cell__input\\\" positioning-strategy=\\\"fixed\\\" mode=\\\"date\\\" click.trigger=\\\"resetError()\\\" is-invalid.to-view=\\\"!!error\\\" dense.to-view=\\\"true\\\" value.two-way=\\\"value\\\"></date-input> <span class=\\\"input-group-append\\\"> <button-standard click.delegate=\\\"handleChange()\\\" icon-name=\\\"System/Tabler/Save\\\" btn-color=\\\"primary\\\"></button-standard> </span> </div> <div if.to-view=\\\"error\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${error}\\\"></div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"form-group mb-0 ml-auto input-number-cell\\\"> <div class=\\\"input-group\\\"> <input class=\\\"form-control input-number-cell__input\\\" type=\\\"number\\\" min=\\\"0\\\" value.two-way=\\\"value | number\\\"> <span class=\\\"input-group-append\\\"> <button-standard click.delegate=\\\"handleChange()\\\" icon-name=\\\"System/Tabler/Save\\\" btn-color=\\\"primary\\\"></button-standard> </span> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"json-cell\\\"> <span class=\\\"json-cell__data-teaser\\\">${model.options.json(model.data)}</span> <div class=\\\"json-cell__tooltip\\\"> <span class=\\\"json-cell__tooltip-code\\\">${formattedJSON}</span> </div> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <button class=\\\"navigation-action-cell__btn btn\\\" click.delegate=\\\"model.options.clickCallback(model.data)\\\" alt=\\\"start lecture\\\"> <i class=\\\"navigation-action-cell__icon fe fe-chevron-right\\\"></i> </button> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"progress-cell\\\"> <progress-circle class=\\\"progress-cell__circle\\\" progress.to-view=\\\"model.options.percent(model.data)\\\"></progress-circle> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <star-rating if.to-view=\\\"model.options.rating(model.data)\\\" rating.to-view=\\\"model.options.rating(model.data)\\\" size=\\\"small\\\"></star-rating> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"form-group mb-0 ml-auto select-cell\\\"> <select class=\\\"custom-select select-cell__select\\\" change.delegate=\\\"handleChange()\\\" value.two-way=\\\"selectedOption\\\"> <option repeat.for=\\\"option of options\\\" model.bind=\\\"option.value\\\">${option.label}</option> </select> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <template if.to-view=\\\"!isFiltered()\\\"> <div t=\\\"elements.customTable.sharedToCell.numCourseShares\\\" t-params.to-view=\\\"{ count: model.data.numCourseShares }\\\"></div> <div if.to-view=\\\"showCourseGroupCount()\\\" t=\\\"elements.customTable.sharedToCell.numCourseGroupShares\\\" t-params.to-view=\\\"{ count: model.data.numCourseGroupShares }\\\"></div> </template> <div else class=\\\"text-success\\\" t=\\\"elements.customTable.sharedToCell.numEntrySearchResults\\\" t-params.to-view=\\\"{ count: showCourseGroupCount() ? model.data.numCourseGroupShares : model.data.numCourseShares }\\\"></div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div if.to-view=\\\"skill\\\" class=\\\"skill-icon-cell\\\"> <skill-icon icon-key.to-view=\\\"skill.iconKey\\\"></skill-icon> <a class=\\\"skill-icon-cell__link\\\" click.trigger=\\\"handleClick()\\\">${skill.title}</a> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"sort-cell flex-column align-items-center\\\"> <safe-icon click.trigger=\\\"handleChangeOrder(EOrderDirection.UP)\\\" class=\\\"sort-cell__btn\\\" icon-name=\\\"System/Tabler/Triangle-Up\\\" use-as-button></safe-icon> <safe-icon click.trigger=\\\"handleChangeOrder(EOrderDirection.DOWN)\\\" class=\\\"sort-cell__btn\\\" icon-name=\\\"System/Tabler/Triangle-Down\\\" use-as-button></safe-icon> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <span if.to-view=\\\"tagTk\\\" class=\\\"tag\\\" t.to-view=\\\"tagTk\\\"></span> <span class=\\\"${ model.options.callback && clickable ? 'btn btn-link p-0 text-left' : '' }\\\" click.trigger=\\\"click()\\\">${ !spanClasses && !href ? value : '' }</span> <span if.to-view=\\\"spanClasses || tk && !href\\\" class=\\\"${ spanClasses }\\\" click.trigger=\\\"click()\\\"> <span if.to-view=\\\"tk\\\" t.to-view=\\\"tk\\\"></span> ${value} </span> <a if.to-view=\\\"href && value\\\" class=\\\"${ spanClasses }\\\" href.to-view=\\\"href(data)\\\"> <span if.to-view=\\\"tk\\\" t.to-view=\\\"tk\\\"></span> ${value} </a> </template>\";\n// Exports\nexport default code;","import { autoinject, bindable } from 'aurelia-framework';\nimport { ContrastMode } from 'common/enum/quiz.attempt.status.enum';\nimport { DomUtils } from 'common/utils/DomUtils';\n\n/**\n * @event {ButtonClickedEvent} button-clicked\n */\n@autoinject()\nexport class CourseLectureQuizHeader {\n  @bindable() public textTk: string | null = null;\n  @bindable() public textTkParams: Record<string, unknown> = {};\n  @bindable() public buttonTk: string | null = null;\n  @bindable() public progress: number | null = null;\n  @bindable() public showButton = true;\n  @bindable() public showProgress = true;\n  @bindable() public contrastMode: ContrastMode = ContrastMode.Default;\n\n  constructor(\n    private element: Element\n  ) { }\n\n  private handleButtonClicked(): void {\n    DomUtils.fireEvent(this.element as HTMLElement, {\n      name: 'button-clicked',\n      bubbles: false,\n    });\n  }\n}\n\nexport type ButtonClickedEvent = CustomEvent<never>;\n","import { useView, PLATFORM, bindable } from 'aurelia-framework';\n\n@useView(PLATFORM.moduleName('./course-lecture-slide-preview.html'))\nexport class CourseLectureSlidePreview {\n  @bindable() public contextIdentifier: string | null = null;\n  @bindable() public slideId: number | null = null;\n}\n","import { bindable } from 'aurelia-framework';\n\nexport class CourseMediaShares {\n  @bindable() public courseId: number | null = null;\n  private tableRefreshVersion: number = 0;\n}","import { autoinject, bindable } from 'aurelia-framework';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { ExportImportService } from '../../../services/ExportImportService';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\n\nconst OperationContext = Symbol();\n\n@autoinject()\nexport class CourseQuestionsOverview {\n  @bindable() public id: number | null = null;\n  private tableRefreshVersion = 0;\n\n  constructor(\n    private dialogService: CustomDialogService,\n    private exportImportService: ExportImportService,\n  ) { }\n\n  protected detached(): void {\n    this.exportImportService.untrackOperations(OperationContext);\n  }\n\n  private async addQuestion(): Promise<void> {\n    this.tableRefreshVersion++;\n\n    assertNotNullOrUndefined(this.id, 'courseId cannot be null or undefined');\n    const result = await this.dialogService.showCreateQuestionDialog(this.id);\n    if(!result) return;\n    const question = await this.dialogService.showEditQuestionDialog({\n      courseId: this.id,\n      questionId: result.id,\n      dialogService: this.dialogService\n    });\n    if(question) {\n      this.tableRefreshVersion++;\n    }\n  }\n\n  private async showImportDialog(): Promise<void> {\n    assertNotNullOrUndefined(this.id, 'courseId cannot be null or undefined');\n    const dialogResult = await this.dialogService.showQuestionsImportDialog({ courseId: this.id });\n\n    if(dialogResult != null) {\n      await this.exportImportService.trackImportOperation(OperationContext, dialogResult.operationId, dialogResult.fileName);\n      await this.dialogService.showOperationStatusDialog({\n        operationContext: OperationContext,\n        title: { textTk: 'elements.courseQuestionsOverview.questionsImport' },\n      });\n      this.tableRefreshVersion++;\n    }\n  }\n\n  private async copyQuestionFromCourse(): Promise<void> {\n    assertNotNullOrUndefined(this.id, 'courseId cannot be null or undefined');\n    const questions = await this.dialogService.showCopyQuestionDialog({ targetCourseId: this.id, canSelectMultiple: true });\n    if(questions?.length) {\n      this.tableRefreshVersion++;\n    }\n  }\n}","import { autoinject, bindable } from 'aurelia-framework';\nimport { Router } from 'aurelia-router';\nimport { ICourseRatingDto } from 'common/dto/course/content/course.interface';\nimport { DomUtils } from 'common/utils/DomUtils';\nimport { StringUtils } from 'common/utils/StringUtils';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { DateService } from '../../../services/DateService';\nimport { SubPages } from '../../pages/course-page/course-page';\nimport { ButtonCell } from '../custom-table/button-cell/button-cell';\nimport { CustomTableCell } from '../custom-table/custom-table-cell/custom-table-cell';\nimport { IColumnDefinition } from '../custom-table/custom-table-interfaces';\nimport { RatingCell } from '../custom-table/rating-cell/rating-cell';\n\n/**\n * @event {DownloadRatingsEvent} download-ratings\n */\n@autoinject()\nexport class CourseRatingPreview {\n  @bindable public ratings: ICourseRatingDto<string>[] | null = null;\n\n  private element: HTMLElement;\n\n  private tableRefreshVersion = 0;\n\n  constructor(\n    element: Element,\n    private dateService: DateService,\n    private customDialogService: CustomDialogService,\n    private router: Router,\n  ) {\n    this.element = element as HTMLElement;\n  }\n\n  protected tableColumns: Array<IColumnDefinition<CustomTableCell<ICourseRatingDto<string>> | RatingCell<ICourseRatingDto<string>> | ButtonCell<ICourseRatingDto<string>>>> = [\n    {\n      id: 'course',\n      nameTk: 'globals.course',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        property: 'courseTitle'\n      },\n    },\n    {\n      id: 'rating',\n      nameTk: 'elements.ratingTable.rating',\n      cellViewModel: RatingCell,\n      cellOptions: {\n        rating: r => r.rating,\n      },\n    },\n    {\n      id: 'comment',\n      nameTk: 'elements.ratingTable.comment',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: r => StringUtils.truncateWithEllipsis(r.comment, 50),\n        clickable: () => true,\n        callback: async rating => {\n          const result = await this.customDialogService.showSingleRatingDialog({ rating, showNavigateButton: true });\n          if(result) this.navigateToCourse(rating.courseId);\n        }\n      },\n    },\n    {\n      id: 'user',\n      nameTk: 'globals.user',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        property: 'userName',\n      }\n    },\n    {\n      id: 'ratedAt',\n      nameTk: 'elements.ratingTable.ratedAt',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: r => this.dateService.formatDate(r.updatedAt)\n      },\n    },\n    {\n      id: 'button',\n      nameTk: '',\n      cellViewModel: ButtonCell,\n      shrink: true,\n      cellOptions: {\n        buttonText: () => ({\n          tk: 'globals.showAll'\n        }),\n        clickCallback: rating => this.navigateToCourse(rating.courseId)\n      }\n    },\n  ];\n\n  private ratingsChanged(): void {\n    if(this.ratings) {\n      this.tableRefreshVersion++;\n    }\n  }\n\n  public downloadRatings(): void {\n    DomUtils.fireEvent(this.element, {\n      name: 'download-ratings'\n    });\n  }\n\n  private navigateToCourse(courseId: number): void {\n    this.router.navigateToRoute('courses/id', { id: courseId, subPage: SubPages.Ratings });\n  }\n}\n\nexport type DownloadRatingsEvent = CustomEvent<never>;\n","import { autoinject, bindable } from 'aurelia-framework';\nimport { ICourseRatingDto } from 'common/dto/course/content/course.interface';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { IFilterDto } from 'common/dto/filter/filter.interface';\nimport { CourseRatingApiService } from 'services/api/CourseRatingsApiService';\nimport { CustomDialogService } from 'services/CustomDialogService';\nimport { DateService } from 'services/DateService';\nimport { FilterUtils } from 'utils/FilterUtils';\nimport { ButtonCell } from '../custom-table/button-cell/button-cell';\nimport { CustomTableCell } from '../custom-table/custom-table-cell/custom-table-cell';\nimport { createPrepareServersideSortingFunction } from '../custom-table/custom-table-helpers';\nimport { IColumnDefinition } from '../custom-table/custom-table-interfaces';\nimport { RatingCell } from '../custom-table/rating-cell/rating-cell';\n\n@autoinject()\nexport class CourseRatingsOverview {\n  @bindable() public id: number | null = null;\n\n  private tableRefreshVersion = 0;\n  private filter: IFilterDto<ICourseRatingDto<string>> = {\n    offset: 0,\n    limit: 10,\n    order: [{\n      field: 'updatedAt',\n      dir: ColumnSortDirection.DESC,\n    }],\n    filter: []\n  };\n\n  constructor(\n    private dateService: DateService,\n    private courseRatingApiService: CourseRatingApiService,\n    private customDialogService: CustomDialogService,\n  ) { }\n\n  protected tableColumns: Array<IColumnDefinition<CustomTableCell<ICourseRatingDto<string>> | RatingCell<ICourseRatingDto<string>> | ButtonCell<ICourseRatingDto<string>>>> = [\n    {\n      id: 'rating',\n      nameTk: 'elements.ratingTable.rating',\n      cellViewModel: RatingCell,\n      cellOptions: {\n        rating: r => r.rating,\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'rating'),\n    },\n    {\n      id: 'comment',\n      nameTk: 'elements.ratingTable.comment',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        spanClasses: 'course-rating-preview__comment',\n        property: 'comment',\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'comment'),\n    },\n    {\n      id: 'user',\n      nameTk: 'globals.user',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        property: 'userName',\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'userName'),\n    },\n    {\n      id: 'ratedAt',\n      nameTk: 'elements.ratingTable.ratedAt',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: r => this.dateService.formatDate(r.updatedAt)\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'updatedAt'),\n    },\n    {\n      id: 'button',\n      nameTk: '',\n      cellViewModel: ButtonCell,\n      shrink: true,\n      cellOptions: {\n        buttonText: () => ({\n          tk: 'elements.ratingTable.showRating'\n        }),\n        clickCallback: async rating => {\n          await this.customDialogService.showSingleRatingDialog({ rating, showNavigateButton: false });\n        }\n      }\n    },\n  ];\n\n  private async fetchTableData(limit: number, offset: number): Promise<{ total: number; data: Array<ICourseRatingDto<string>> }> {\n    this.filter.limit = limit;\n    this.filter.offset = offset;\n    this.filter = FilterUtils.removeDuplicateFilterFields(\n      this.filter,\n      this.getCourseIdFilter()\n    );\n\n    return this.courseRatingApiService.getCourseRatings(this.filter);\n  }\n\n  private async downloadRatings(): Promise<void> {\n    const csvType = await this.customDialogService.showChooseCsvTypeDialog();\n    if(!csvType) return;\n    this.courseRatingApiService.exportRatingsCsv(csvType, { filter: [this.getCourseIdFilter()] });\n  }\n\n  private getCourseIdFilter(): { field: keyof ICourseRatingDto<string>; type: FilterOperationType; value: any } {\n    return {\n      field: 'courseId',\n      type: FilterOperationType.Eq,\n      value: this.id,\n    };\n  }\n}\n","import { bindable, autoinject } from 'aurelia-framework';\nimport { IPictureDto } from 'common/dto/course/content/course.interface';\nimport { QuestionType } from 'common/dto/course/content/course.types';\nimport { IQuestionEditDto } from 'common/dto/course/content/question.interface';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { GalleryMediaType } from 'common/enum/gallery.enum';\nimport { MediaType } from '../../dialogs/edit-question-dialog/edit-question-dialog';\n\n@autoinject()\nexport class CreateQuestionForm {\n  @bindable() public courseId: number | null = null;\n  @bindable() public question: IQuestionEditDto | null = null;\n  @bindable() public questionMediaType: MediaType = MediaType.Picture;\n  @bindable() public ltQuestionParts: [string, string, string] = ['', '', ''];\n  @bindable() public ltHelpTextParts: [string, string, string] = ['', '', ''];\n  private picture: IPictureDto | null = null;\n  private pictureBlink: IPictureDto | null = null;\n  private audioUrl: string | null = null;\n  private MediaType = MediaType;\n  private QuestionType = QuestionType;\n\n  constructor(private dialogService: CustomDialogService) { }\n\n  private handleDeleteQuestionImage(): void {\n    assertNotNullOrUndefined(this.question, 'question cannot be null or undefined');\n\n    this.question.pictureId = null;\n    this.picture = null;\n  }\n\n  private handleDeleteQuestionBlinkImage(): void {\n    assertNotNullOrUndefined(this.question, 'question cannot be null or undefined');\n\n    this.question.pictureBlinkId = null;\n    this.pictureBlink = null;\n  }\n\n  private async handleSelectQuestionImage(): Promise<void> {\n    assertNotNullOrUndefined(this.question, 'question cannot be null or undefined');\n    assertNotNullOrUndefined(this.courseId, 'courseId cannot be null or undefined');\n\n    const result = await this.dialogService.showProviderMediaSelect({ courseId: this.courseId, selectedMediaType: GalleryMediaType.Picture });\n    if(result?.type === GalleryMediaType.Picture) {\n      this.question.pictureId = result.value.id;\n      this.picture = {\n        id: result.value.id,\n        url: result.value.url,\n        blurHash: result.value.blurHash,\n        width: result.value.width,\n        height: result.value.height\n      };\n    }\n  }\n\n  private async handleSelectQuestionBlinkImage(): Promise<void> {\n    assertNotNullOrUndefined(this.question, 'question cannot be null or undefined');\n    assertNotNullOrUndefined(this.courseId, 'courseId cannot be null or undefined');\n\n    const result = await this.dialogService.showProviderMediaSelect({ courseId: this.courseId, selectedMediaType: GalleryMediaType.Picture });\n    if(result?.type === GalleryMediaType.Picture) {\n      this.question.pictureBlinkId = result.value.id;\n      this.pictureBlink = {\n        id: result.value.id,\n        url: result.value.url,\n        blurHash: result.value.blurHash,\n        width: result.value.width,\n        height: result.value.height\n      };\n    }\n  }\n}\n","import { ICellViewModel, ICellModel } from '../custom-table-interfaces';\n\nexport class ActionsCell<T> implements ICellViewModel<ICellModel<T, IActionsCellOptions<T>>> {\n  public model!: ICellModel<T, IActionsCellOptions<T>>;\n\n  public activate(model: ICellModel<T, IActionsCellOptions<T>>): void {\n    this.model = model;\n  }\n\n  protected getLabelTk(labelTk: string | ((entry: T) => string), data: T): string {\n    return (typeof labelTk === 'string') ? labelTk : labelTk(data);\n  }\n\n  protected isVisible(entry: IActionCellEntry<T>, data: T): boolean {\n    return entry.showCallback ? entry.showCallback(data) : true;\n  }\n\n  protected isDivider(entry: IActionCellEntry<T> | 'divider'): boolean {\n    return entry === 'divider';\n  }\n}\n\nexport interface IActionsCellOptions<T> {\n  entries: Array<IActionCellEntry<T> | 'divider'>;\n}\n\nexport interface IActionCellEntry<T> {\n  labelTk: string | ((entry: T) => string);\n  clickCallback: (entry: T) => void;\n  showCallback?: (entry: T) => boolean;\n  icon?: string;\n  additionalClasses?: string;\n}\n","import { ICellViewModel, ICellModel } from '../custom-table-interfaces';\n\nexport class AvatarCell<T> implements ICellViewModel<ICellModel<T, IAvatarCellOptions<T>>>  {\n  public model!: ICellModel<T, IAvatarCellOptions<T>>;\n\n  public activate(model: ICellModel<T, IAvatarCellOptions<T>>): void {\n    this.model = model;\n  }\n}\n\nexport interface IAvatarCellOptions<T> {\n  avatarSvgDownloadLink: (data: T) => string | null;\n  points: (data: T) => number | null;\n  online: (data: T) => boolean;\n}\n","import { IText } from 'common/interfaces/translations.interface';\nimport { ButtonColor } from '../../../elements/button-standard/button-standard';\nimport { ICellModel, ICellViewModel } from '../custom-table-interfaces';\n\nexport class ButtonCell<T> implements ICellViewModel<ICellModel<T, IButtonCellOptions<T>>> {\n  public model!: ICellModel<T, IButtonCellOptions<T>>;\n\n  public activate(model: ICellModel<T, IButtonCellOptions<T>>): void {\n    this.model = model;\n  }\n}\n\nexport interface IButtonCellOptions<T> {\n  clickCallback: (entry: T) => void;\n  buttonText: (entry: T) => IText;\n  disabled?: (entry: T) => boolean;\n  buttonColor?: (entry: T) => ButtonColor;\n  iconName?: (entry: T) => string;\n}\n\n","import { IStudentDto } from 'common/dto/user/students/students.interface';\nimport { ICellModel, ICellViewModel } from '../custom-table-interfaces';\n\nexport class CourseCodeCell<T extends IStudentDto> implements ICellViewModel<ICellModel<T, ICourseCodeCellOptions>> {\n  public model!: ICellModel<T, ICourseCodeCellOptions>;\n\n  public activate(model: ICellModel<T, ICourseCodeCellOptions>): void {\n    this.model = model;\n  }\n\n  protected isLast(index: number): boolean {\n    return index < this.model.data.accessCodes.length - 1;\n  }\n}\n\nexport interface ICourseCodeCellOptions {\n  showLinks: () => boolean;\n}\n","import { ICellViewModel, ICellModel } from '../custom-table-interfaces';\nimport { computedFrom } from 'aurelia-framework';\n\nexport class CustomTableCell<T> implements ICellViewModel<ICellModel<T, IDefaultCellOptions<T>>> {\n  public data!: T;\n  public model!: ICellModel<T, IDefaultCellOptions<T>>;\n  public options!: IDefaultCellOptions<T>;\n  public href?: (data: T) => string;\n\n  protected value?: string;\n  protected tk?: string;\n  protected spanClasses: string = '';\n\n  public activate(model: ICellModel<T, IDefaultCellOptions<T>>): void {\n    this.model = model;\n    this.data = model.data;\n    this.options = model.options;\n    this.href = model.options.href;\n    if(model.options.spanClasses) {\n      this.spanClasses = (typeof model.options.spanClasses === 'string') ? model.options.spanClasses : model.options.spanClasses(model.data);\n    }\n    if(model.options.tk) {\n      this.tk = model.options.tk(model.data);\n    } else if(model.options.transformer) {\n      this.value = model.options.transformer(model.data);\n    } else if(model.options?.property) {\n      this.value = (model as any).data[model.options.property];\n    } else {\n      this.value = model.data as unknown as string;\n    }\n  }\n\n  protected click(): void {\n    if(this.model.options.callback) {\n      this.model.options.callback(this.model.data);\n    }\n  }\n\n  @computedFrom('model.options.clickable')\n  protected get clickable(): boolean {\n    return this.model.options.clickable ? this.model.options.clickable(this.model.data) : false;\n  }\n}\n\nexport interface IDefaultCellOptions<T> {\n  transformer?: (data: T) => string;\n  tk?: (data: T) => string;\n  callback?: (data: T) => void;\n  clickable?: (data: T) => boolean;\n  property?: keyof T;\n  spanClasses?: string | ((data: T) => string);\n  href?: (data: T) => string;\n}\n","import { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { FilterField, IFilterDto } from 'common/dto/filter/filter.interface';\nimport dayjs from 'dayjs';\nimport { get, orderBy } from 'lodash';\n\nexport type FilterDef<TEntity> = IGroupableFilterDto<TEntity> | (() => IGroupableFilterDto<TEntity>);\n\nexport const propertySorter = <T>(propertyName: keyof T) =>\n  (data: Array<T>, dir: ColumnSortDirection): Array<T> =>\n    orderBy(data, [propertyName], [dir === ColumnSortDirection.ASC ? 'asc' : 'desc']);\n\nexport const deepPropertySorter = <T>(propertyPath: string) =>\n  (data: Array<T>, dir: ColumnSortDirection): Array<T> =>\n    orderBy(data, [propertyPath], [dir === ColumnSortDirection.ASC ? 'asc' : 'desc']);\n\nexport const deepPropertyString = <T>(propertyPath: string) =>\n  (data: T): string =>\n    get(data, propertyPath, '').toString();\n\nexport const deepPropertyFilter = <T>(propertyPath: string) =>\n  (entry: T, filterValue: string): boolean =>\n    get(entry, propertyPath, '').toString().toLocaleLowerCase().includes(filterValue.toLocaleLowerCase());\n\nexport const deepPropertiesFilter = <T>(...propertyPaths: string[]) =>\n  (entry: T, filterValue: string): boolean =>\n    propertyPaths.some((propertyPath: string) => deepPropertyFilter(propertyPath)(entry, filterValue));\n\nexport const multiTermDeepPropertiesFilter = <T>(...propertyPaths: string[]) =>\n  (entry: T, filterValue: string): boolean =>\n    filterValue.split(' ').every(term => deepPropertiesFilter(...propertyPaths)(entry, term));\n\nexport const dateRangeFilter = <T>(propertyName: keyof T) => (entry: T, value: string): boolean => {\n  if(value === '|') return true;\n  const [start, end] = value.split('|');\n  const startDate = dayjs(start);\n  const endDate = end ? (dayjs(end).add(1, 'day')).toISOString() : '';\n  const entryDate = dayjs(entry[propertyName] as any);\n  return entryDate.isAfter(startDate) && entryDate.isBefore(endDate);\n};\n\nexport const createPrepareServersideSortingFunction = <T>(\n  filterDef: IFilterDto<T> | (() => IFilterDto<T>),\n  property: keyof T,\n): ((dir: ColumnSortDirection) => Promise<void>) => {\n  return async (dir: ColumnSortDirection) => {        \n    const filter: IFilterDto<T> = (filterDef instanceof Function) ? filterDef() : filterDef;\n    \n    if(!filter.order) return;\n    filter.order[0].dir = dir;\n    filter.order[0].field = property;    \n  };\n};\n\nexport interface IGroupableFilterDto<T> extends IFilterDto<T> {\n  filter?: Array<{ field: keyof T; type: FilterOperationType; value: any; group?: string }>;\n}\n\nexport const prepareServersideFiltering = <T>(\n  filter: IGroupableFilterDto<T>, \n  property: keyof T, \n  value: string | null,\n  operation: FilterOperationType = FilterOperationType.Like,\n  group?: string,\n): void => {\n  if(!filter.filter) {\n    filter.filter = [];\n  }\n  filter.filter = filter.filter.filter(f => !(f.field === property && f.type === operation && f.group === group));\n  if(!value) {\n    filter.offset = 0;\n    return;\n  }\n  const field = createFilterField(property, value, operation, group);\n  filter.filter.push(field);\n  filter.offset = 0;\n};\n\n/**\n * tries to add a filter field with FilterOperationType.In to filter for within value array\n */\nexport const prepareServersideFilteringArray = <T>(\n  filter: IGroupableFilterDto<T>, \n  property: keyof T, \n  value: string[] | null,\n  group?: string,\n): void => {\n  if(!filter.filter) {\n    filter.filter = [];\n  }\n  filter.filter = filter.filter.filter(f => !(f.field === property && f.group === group));\n  if(!value) {\n    filter.offset = 0;\n    return;\n  }\n  const field = createFilterFieldArray(property, value, group);\n  filter.filter.push(field);\n  filter.offset = 0;\n};\n\nexport const prepareServersideRangeFiltering = <T>(filter: IFilterDto<T>, property: keyof T, value: string): void => {\n  const [start, end] = value.split('|');\n  const startDate = start ? dayjs(start).startOf('day').toISOString() : '';\n  const filterEndDate = end ? dayjs(end).endOf('day').toISOString() : '';\n  prepareServersideFiltering(filter, property, startDate, FilterOperationType.Gte);\n  prepareServersideFiltering(filter, property, filterEndDate, FilterOperationType.Lte);\n};\n\nexport type VirtualFilterField = any;\n\nexport function createFilterFuntion<T>(property: keyof T, value: string | null, operation: FilterOperationType = FilterOperationType.Like, group?: string): ((filter: IGroupableFilterDto<T>) => void) {\n  return filter => prepareServersideFiltering(filter, property, value, operation, group);\n}\n\nexport function createRangeFilterFunction<T>(property: keyof T, value: string): (filter: IGroupableFilterDto<T>) => void {\n  return filter => prepareServersideRangeFiltering(filter, property, value);\n}\n\nexport function createSortFunction<T>(property: keyof T): ((dir: ColumnSortDirection) => (filterDef: FilterDef<T>) => void) {\n  return (dir: ColumnSortDirection) => {\n    return filterDef => {\n      const filter: IFilterDto<T> = (filterDef instanceof Function) ? filterDef() : filterDef;\n      if(!filter.order) return;\n      filter.order[0] = {\n        dir,\n        field: property,\n      };\n    };\n  };\n}\n\nexport function createFilterField<T>(\n  fieldName: keyof T, \n  value: string | null, \n  operation: FilterOperationType = FilterOperationType.Like,\n  group?: string,\n): FilterField<T> {\n  return {\n    field: fieldName,\n    type: operation,\n    value: operation === FilterOperationType.Like\n      ? `%${value}%`\n      : value,\n    group,\n  };\n}\n\nexport function createFilterFieldArray<T>(\n  fieldName: keyof T, \n  value: string[] | null, \n  group?: string,\n): FilterField<T> {\n  return {\n    field: fieldName,\n    type: FilterOperationType.In,\n    value,\n    group,\n  };\n}\n\nexport function createRangeFilterFields<T>(fieldName: keyof T, value: string): Array<FilterField<T>> {\n  const [start, end] = value.split('|');\n  const startDate = start ? dayjs(start).startOf('day').toISOString() : '';\n  const endDate = end ? dayjs(end).endOf('day').toISOString() : '';\n\n  return [\n    createFilterField(fieldName, startDate, FilterOperationType.Gte),\n    createFilterField(fieldName, endDate, FilterOperationType.Lte),\n  ];\n}\n","import { ColumnSortDirection } from 'common/dto/filter/filter.enum';\nimport { FilterOptions } from 'common/dto/filter/filter.interface';\nimport { TInputType } from '../api-text-filter-input/api-text-filter-input';\nimport { FilterDef, IGroupableFilterDto } from './custom-table-helpers';\n\nexport interface ICellModel<M = unknown, O = unknown> {\n  data: M;\n  options: O;\n}\n\nexport interface ICellViewModel<CM extends ICellModel = ICellModel<unknown, unknown>> {\n  model: CM;\n  activate(model: CM): void;\n}\n\nexport interface IColumnDefinition<VM extends ICellViewModel> {\n  id: string;\n  nameTk: string;\n  nameHtml?: string;\n  cellViewModel: new () => VM;\n  cellOptions: VM['model']['options'];\n  prepareSorting?: (order: ColumnSortDirection) => ((filterDef: FilterDef<VM['model']['data']>) => void | Promise<void>) | void | Promise<void>;\n  sortFunction?: (data: Array<VM['model']['data']>, order: ColumnSortDirection) => Array<VM['model']['data']>;\n  filter?: IColumnFilterDefinition<VM>;\n  shrink?: boolean;\n  wrap?: boolean;\n  centered?: boolean;\n  sortNulls?: SortNulls;\n  visible?: (entry?: VM['model']['data'], data?: Array<VM['model']['data']>) => boolean;\n}\n\nexport interface IColumnFilterDefinition<VM extends ICellViewModel> {\n  placeholderTk: string;\n  prepare?: (value: string) => ((filter: IGroupableFilterDto<VM['model']['data']>) => void) | void;\n  filterCallback?: (entry: VM['model']['data'], value: string) => boolean;\n  options?: FilterOptions;\n  daterange?: boolean;\n  initialValue?: string | Date;\n  inputType?: TInputType;\n  size?: FilterSize;\n}\n\nexport type ColumnWithFilterAndInitialValue = IColumnDefinition<ICellViewModel<ICellModel<unknown>>> & { filter: IColumnFilterDefinition<ICellViewModel<ICellModel<unknown>>> & { initialValue: string } };\n\nexport enum SortNulls {\n  /** nulls will always be sorted first, regardless of asc/desc */\n  First = 'first',\n  /** nulls will always be sorted last, regardless of asc/desc */\n  Last = 'last',\n  /** nulls will get treated as the lowest value, so for asc they come first, for desc last */\n  Smallest = 'smallest',\n  /** nulls will get treated as the highest value, so for asc they come last, for desc first */\n  Largest = 'largest',\n}\n\nexport enum FilterSize {\n  LARGE = 'large',\n}\n","import { autoinject, bindable, computedFrom } from 'aurelia-framework';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { ColumnSortDirection } from 'common/dto/filter/filter.enum';\nimport { FilterChangedEvent, FilterField, IFilterDto, OrderField } from 'common/dto/filter/filter.interface';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { FunctionUtils } from 'common/utils/FunctionUtils';\nimport {\n  clearTableFilter,\n  clearTableSort,\n  setExtendTableSizeAction,\n  setRowsPerTablePageAction,\n  setTableFilterStatusOffset,\n  setTableFilterStatusSort,\n  setTableFilterStatusValue\n} from '../../../store/actions';\nimport { IState, ITableFilterStatus } from '../../../store/state';\nimport { IGroupableFilterDto } from './custom-table-helpers';\nimport { ColumnWithFilterAndInitialValue, ICellModel, ICellViewModel, IColumnDefinition, IColumnFilterDefinition, SortNulls } from './custom-table-interfaces';\n\nexport interface ICustomTablePaginationData<T> {\n  data: Array<T>;\n  total: number;\n}\n\nexport type GetShownEntriesDelegate = ((params: { limit: number; offset: number }) => Promise<ICustomTablePaginationData<unknown>>);\nexport type CustomTableDefinition<TEntity, TCellViewModel extends ICellViewModel<ICellModel<TEntity, unknown>>> = Array<IColumnDefinition<TCellViewModel>>;\n\nexport interface ICustomTableDelegate<\n  TEntity,\n  TEntityCellViewModel extends ICellViewModel<ICellModel<TEntity, unknown>>,\n  TSubEntity = any,\n  TSubEntityCellViewModel extends ICellViewModel<ICellModel<TSubEntity, unknown>> = any,\n> {\n  /**\n   * Fetch data for the table.\n   * @param filter The filter with current filter values.\n   */\n  fetchData(filter: IFilterDto<TEntity>): Promise<ICustomTablePaginationData<TEntity>>;\n  /**\n   * Fetch data for the table.\n   * @param filter The filter with current filter values.\n   * @param parent The collapsed row entity of the parent table.\n   */\n  fetchSubData?(filter: IFilterDto<TSubEntity>, parent: TEntity): Promise<ICustomTablePaginationData<TSubEntity>>;\n  /**\n   * Defines columns, filtering and sorting behaviour the table.\n   */\n  getTableDefinition(): CustomTableDefinition<TEntity, TEntityCellViewModel>;\n  /**\n   * Defines columns, filtering and sorting behaviour for sub tables. The parent table\n   * will automatically be collapsible if implemented (and not returning an empty array).\n   */\n  getSubTableDefinition?(): CustomTableDefinition<TSubEntity, TSubEntityCellViewModel>;\n  /**\n   * Implement if global filtering is needed for the entity, or filter logic outside of `CustomTable` is used.\n   * Call `CustomTable.refresh()` or update bound `tableRefreshVersion` for changes to take effect.\n   */\n  getTableBaseFilters?(): Array<FilterField<TEntity>>;\n  /**\n   * Implement if global ordering is needed for the entity, or order logic outside of `CustomTable` is used.\n   * Call `CustomTable.refresh()` or update bound `tableRefreshVersion` for changes to take effect.\n   */\n  getTableBaseOrdering?(): Array<OrderField<TEntity>>;\n  /**\n   * Implement if global filtering is needed for the entity, or filter logic outside of `CustomTable` is used.\n   * Call `CustomTable.refresh()` or update bound `tableRefreshVersion` for changes to take effect.\n   */\n  getSubTableBaseFilters?(parent: TEntity): Array<FilterField<TSubEntity>>;\n  /**\n   * Implement if global ordering is needed for the entity, or order logic outside of `CustomTable` is used.\n   * Call `CustomTable.refresh()` or update bound `tableRefreshVersion` for changes to take effect.\n   */\n  getSubTableBaseOrdering?(parent: TEntity): Array<OrderField<TSubEntity>>;\n}\n\n@autoinject()\nexport class CustomTable {\n  @bindable() public delegate: ICustomTableDelegate<unknown, ICellViewModel<ICellModel<unknown, unknown>>, unknown, ICellViewModel<ICellModel<unknown, unknown>>> | null = null;\n  @bindable() public data: Array<unknown> = [];\n  @bindable() public columns: Array<IColumnDefinition<ICellViewModel<ICellModel<unknown>>>> = [];\n\n  @bindable() public currentSortColumn: IColumnDefinition<ICellViewModel<ICellModel<unknown>>> | null = null;\n  @bindable() public currentSortDirection = ColumnSortDirection.ASC;\n\n  @bindable() public getShownEntriesData: GetShownEntriesDelegate | null = this.getDefaultShownEntries;\n\n  @bindable() public numPageQuickLinks = 7;\n\n  @bindable() public refreshVersion = 0;\n  @bindable() public preventFetching = false;\n  @bindable() public readonly: boolean = false;\n\n  @bindable() public pullFocus = false;\n  @bindable() public selectable = false;\n  @bindable() public subTableSelectable = false;\n  @bindable() public extendable = false;\n  @bindable() public unbounded = false;\n  @bindable() public selected: Array<unknown> = [];\n  @bindable() public subTableSelected: Array<unknown> = [];\n  @bindable() public hideFooter: boolean = false;\n  @bindable() public hideControls: boolean = false;\n  @bindable() public showFilters: boolean = true;\n  @bindable() public allowSorting: boolean = true;\n  @bindable() public alignContentCenter: boolean = false;\n\n  @bindable() public tableId: string | null = null;\n\n  private showPageConfig = false;\n  private newRowsPerPage = 0;\n  private rowsPerPageOptions = [10, 20, 30, 50, 100];\n  private rowsPerPage?: number;\n  private pages = 1;\n  private currentPage = 1;\n\n  private filterValues: Map<IColumnDefinition<ICellViewModel<ICellModel<unknown>>>, string> = new Map();\n  private filterStatus: ITableFilterStatus | null = null;\n  private globalFilters: Partial<Record<string, string>> = {};\n\n  private extended = true;\n\n  private shownEntries: Array<unknown> = [];\n  private total = 0;\n\n  private filterRange = {\n    from: 0,\n    to: 0,\n    of: 0,\n  };\n\n  private subTableColumns: CustomTableDefinition<unknown, ICellViewModel<ICellModel<unknown, unknown>>> = [];\n\n  private subTableOpenIdx: number | null = null;\n  private subTableDelegate: ICustomTableDelegate<unknown, ICellViewModel<ICellModel<unknown, unknown>>> | null = null;\n\n  private filter: IGroupableFilterDto<unknown> & { limit: number; offset: number } = { limit: 10, offset: 0, order: [] };\n\n  constructor(private storeService: StoreService<IState>) { }\n\n  protected attached(): void {\n    if(this.delegate != null) {\n      this.updateFromDelegate();\n\n      this.subTableDelegate = {\n        getTableDefinition: () => this.delegate?.getSubTableDefinition?.() ?? [],\n        fetchData: async filter => {\n          const idx = this.subTableOpenIdx;\n          if(idx == null) return { data: [], total: 0 };\n          const parent = this.shownEntries[idx];\n          /**\n           * sub table limit can't be controlled separately (yet)\n           * set to max to show all entries\n           */\n          filter.limit = 500;\n          return this.delegate?.fetchSubData?.(filter, parent) ?? { data: [], total: 0 };\n        },\n        getTableBaseFilters: () => {\n          const idx = this.subTableOpenIdx;\n          if(idx == null) return [];\n          const parent = this.shownEntries[idx];\n          return this.delegate?.getSubTableBaseFilters?.(parent) ?? [];\n        },\n        getTableBaseOrdering: () => {\n          const idx = this.subTableOpenIdx;\n          if(idx == null) return [];\n          const parent = this.shownEntries[idx];\n          return this.delegate?.getSubTableBaseOrdering?.(parent) ?? [];\n        }\n      };\n\n      // manually prepare filters to avoid timing issues with initial value\n      for(const column of this.columns.filter((col): col is ColumnWithFilterAndInitialValue => col.filter != null && col.filter.initialValue != null)) {\n        this.prepareFilter(column.filter, column.filter.initialValue);\n      }\n    }\n\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.tables.rowsPerPage,\n      state => (this.tableId && state.tables.filterStatus[this.tableId]) || null,\n    ], (_, rowsPerPage, filterStatus) => {\n      if((!this.filterStatus && filterStatus) || (this.filterStatus && !filterStatus)) {\n        // Special treatment when filter status is set for the first time, or unset\n        this.filterStatus = filterStatus;\n        this.newRowsPerPage = rowsPerPage;\n        this.filter.limit = rowsPerPage;\n        this.filter.offset = filterStatus?.offset ?? 0;\n        this.currentSortColumn = (filterStatus?.sortColumnId && this.columns.find(c => c.id === filterStatus.sortColumnId)) || null;\n        this.currentSortDirection = filterStatus?.sortDirection ?? this.getSortDirectionAsc(this.currentSortColumn);\n        this.currentSortColumn?.prepareSorting?.(this.currentSortDirection);\n        this.updateFilterSort(this.currentSortColumn);\n        this.calculateShownEntries();\n      } else {\n        // otherwise just handle the rowsPerPage\n        this.filterStatus = filterStatus;\n        this.currentSortColumn = (filterStatus?.sortColumnId && this.columns.find(c => c.id === filterStatus.sortColumnId)) || null;\n        this.currentSortDirection = filterStatus?.sortDirection ?? this.getSortDirectionAsc(this.currentSortColumn);\n        this.currentSortColumn?.prepareSorting?.(this.currentSortDirection);\n        this.rowsPerPageChanged(rowsPerPage, this.rowsPerPage);\n      }\n    });\n\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.tables.extended,\n      state => (\n        this.tableId != null\n          ? Object.values(state.globalFilters).find(globalFilter => globalFilter?.tableId === this.tableId)?.filters ?? {}\n          : {}\n      ),\n    ], (_, extended, globalFilters) => {\n      this.extended = extended;\n      this.globalFilters = globalFilters;\n    });\n\n    this.calculateShownEntries();\n  }\n\n  protected detached(): void {\n    this.storeService.disposeAllSubscriptions(this);\n  }\n\n  public refresh(): void {\n    this.updateFromDelegate();\n    this.calculateShownEntries();\n  }\n\n  public updateTableEntry<TEntity>(filterFunc: (data: TEntity) => boolean, newData: TEntity): void {\n    for(let i = 0; i < this.shownEntries.length; i++) {\n      const row = this.shownEntries[i];\n      if(filterFunc(row as TEntity)) {\n        this.shownEntries.splice(i, 1, newData);\n      }\n    }\n  }\n\n  public getCurrentFilterValue<TEntity>(): IGroupableFilterDto<TEntity> {\n    return structuredClone(this.filter);\n  }\n\n  public handleSelectAllClicked(): boolean {\n    if(this.selected.length === this.shownEntries.length) {\n      this.selected = [];\n    } else {\n      this.selected = this.shownEntries.slice();\n    }\n    return true;\n  }\n\n  @computedFrom('shownEntries.length', 'selected.length')\n  public get allSelected(): boolean {\n    return this.shownEntries.length > 0 && this.selected.length === this.shownEntries.length;\n  }\n\n  public handleSortingChanged(column: IColumnDefinition<ICellViewModel<ICellModel<unknown>>>): void {\n    if(this.readonly || !this.allowSorting) return;\n    if(!column.prepareSorting && !column.sortFunction) return;\n\n    this.currentSortDirection = this.currentSortColumn === column && this.isSortedAsc(this.currentSortDirection)\n      ? this.getSortDirectionDesc(column)\n      : this.getSortDirectionAsc(column);\n\n    this.currentSortColumn = column;\n    this.updateFilterSort(column);\n    this.filter.offset = 0;\n    this.calculateShownEntries();\n    if(this.tableId) {\n      void this.storeService.dispatch(setTableFilterStatusSort, this.tableId, column.id, this.currentSortDirection);\n    }\n  }\n\n  private updateFromDelegate(): void {\n    if(this.delegate == null) return;\n    this.columns = this.delegate.getTableDefinition();\n    this.subTableColumns = this.delegate.getSubTableDefinition?.() ?? [];\n    this.filter.filter = this.delegate.getTableBaseFilters?.() ?? [];\n    this.filter.order = this.filter.order ?? this.delegate.getTableBaseOrdering?.() ?? [];\n  }\n\n  private updateFilterSort(column: IColumnDefinition<ICellViewModel<ICellModel<unknown>>> | null): void {\n    if(column?.prepareSorting) {\n      const prepareFunction = column.prepareSorting(this.currentSortDirection);\n      if(typeof prepareFunction === 'function') {\n        prepareFunction(this.filter);\n      }\n    }\n  }\n\n  private getSortDirectionAsc(column: IColumnDefinition<ICellViewModel<ICellModel<unknown, unknown>>> | null): ColumnSortDirection {\n    switch(column?.sortNulls) {\n      case SortNulls.Last:\n      case SortNulls.Largest:\n        return ColumnSortDirection.ASC_NULLS_LAST;\n      case SortNulls.First:\n      case SortNulls.Smallest:\n        return ColumnSortDirection.ASC_NULLS_FIRST;\n      default:\n        return ColumnSortDirection.ASC;\n    }\n  }\n\n  private getSortDirectionDesc(column: IColumnDefinition<ICellViewModel<ICellModel<unknown, unknown>>> | null): ColumnSortDirection {\n    switch(column?.sortNulls) {\n      case SortNulls.Last:\n      case SortNulls.Smallest:\n        return ColumnSortDirection.DESC_NULLS_LAST;\n      case SortNulls.First:\n      case SortNulls.Largest:\n        return ColumnSortDirection.DESC_NULLS_FIRST;\n      default:\n        return ColumnSortDirection.DESC;\n    }\n  }\n\n  public hasFilters(defs: Array<IColumnDefinition<ICellViewModel<ICellModel<unknown>>>>): boolean {\n    return defs.some(def => def.filter);\n  }\n\n  public isSortedAsc(sortDirection: ColumnSortDirection): boolean {\n    return [ColumnSortDirection.ASC, ColumnSortDirection.ASC_NULLS_FIRST, ColumnSortDirection.ASC_NULLS_LAST].includes(sortDirection);\n  }\n\n  public resultRangeChanged(): void {\n    this.filterRange = {\n      from: this.total === 0 ? 0 : Math.min(this.filter.offset + 1, this.total),\n      to: Math.min(this.filter.offset + this.filter.limit, this.total),\n      of: this.total\n    };\n  }\n\n  @computedFrom('subTableColumns')\n  private get hasSubTable(): boolean {\n    return this.subTableColumns.length > 0;\n  }\n\n  private newOffsetCallback = (offset: number): void => {\n    this.filter.offset = offset;\n    if(this.tableId) void this.storeService.dispatch(setTableFilterStatusOffset, this.tableId, offset);\n    this.calculateShownEntries();\n  };\n\n  @computedFrom('filterStatus', 'columns', 'globalFilters')\n  private get filterActive(): boolean {\n    return this.columns.some(column => this.filterStatus?.filters[column.id]?.length) || Object.values(this.globalFilters).length > 0;\n  }\n\n  private settingsCallback = (): void => {\n    this.showPageConfig = !this.showPageConfig;\n  };\n\n  private dataChanged(): void {\n    this.calculateShownEntries();\n  }\n\n  private calculateShownEntries = FunctionUtils.debounce(async (): Promise<void> => {\n    const result = await this.fetchData();\n    if(result == null) return;\n\n    this.calculateSafePage(result.total);\n\n    this.total = result.total;\n    this.shownEntries = result.data;\n    this.selected = [];\n    this.resultRangeChanged();\n    this.rowsPerPage = this.newRowsPerPage;\n  }, 60); // prevent re-fetches caused by multiple changes at the same time (e.g. when initializing a table, or resetting all filters)\n\n  private calculateSafePage(total: number): void {\n    const maxPages = Math.ceil(total / this.filter.limit);\n    const currentPage = Math.ceil(this.filter.offset / this.filter.limit) + 1;\n\n    if(currentPage > maxPages && total > 0) {\n      const newOffset = Math.max(0, (maxPages - 1) * this.filter.limit);\n      this.filter.offset = newOffset;\n      this.calculateShownEntries();\n      return;\n    }\n\n    this.pages = maxPages;\n    this.currentPage = Math.min(currentPage, maxPages);\n  }\n\n  private async fetchData(): Promise<ICustomTablePaginationData<unknown> | null> {\n    if(this.preventFetching || (this.delegate == null && this.getShownEntriesData == null)) return null;\n\n    this.subTableOpenIdx = null;\n    if(this.delegate != null) return this.delegate.fetchData(this.filter);\n    return this.getShownEntriesData!({ limit: this.filter.limit, offset: this.filter.offset });\n  }\n\n  private refreshVersionChanged(_newValue: number, oldValue?: number): void {\n    if(oldValue == null) return;\n    this.refresh();\n  }\n\n  private preventFetchingChanged(): void {\n    this.calculateShownEntries();\n  }\n\n  private async getDefaultShownEntries(params: { limit: number; offset: number }): Promise<{ data: Array<unknown>; total: number }> {\n    if(!this.data) {\n      this.shownEntries = [];\n      return { data: [], total: 0 };\n    }\n    let nextShownEntries = this.data.slice();\n    for(const column of this.columns) {\n      if(column.filter?.filterCallback) {\n        const value = this.filterValues.get(column);\n        if(value) {\n          nextShownEntries = nextShownEntries.filter(e => {\n            if(column?.filter?.filterCallback) {\n              return column.filter.filterCallback(e, value);\n            }\n            return true;\n          });\n        }\n      }\n    }\n    const total = nextShownEntries.length;\n    if(this.currentSortColumn && this.currentSortColumn.sortFunction) {\n      nextShownEntries = this.currentSortColumn.sortFunction(nextShownEntries, this.currentSortDirection);\n    }\n    nextShownEntries = nextShownEntries.slice(params.offset, params.offset + params.limit);\n    return { total, data: nextShownEntries };\n  }\n\n  private handleFilterChanged(column: IColumnDefinition<ICellViewModel<ICellModel<unknown>>>, event: FilterChangedEvent): void {\n    const value = event.detail.filter;\n    const initialValue = column.filter?.initialValue instanceof Date\n      // normalize timezone-differences, otherwise comparison would fail\n      ? new Date(Date.parse(column.filter?.initialValue as unknown as string)).toISOString()\n      : column.filter?.initialValue;\n\n    if(column.filter) {\n      this.filter.offset = 0;\n      this.filterValues.set(column, value);\n      this.prepareFilter(column.filter, value);\n      this.calculateShownEntries();\n      if(initialValue && value.includes(initialValue)) return;\n      if(this.tableId && column.id) void this.storeService.dispatch(setTableFilterStatusValue, this.tableId, column.id, value);\n    }\n  }\n\n  private prepareFilter(filterDefinition: IColumnFilterDefinition<ICellViewModel<ICellModel<unknown>>>, value: string): void {\n    if(filterDefinition.prepare) {\n      const prepareFunction = filterDefinition.prepare(value);\n      if(typeof prepareFunction === 'function') {\n        prepareFunction(this.filter);\n      }\n    }\n  }\n\n  private handleRowsPerPageChanged(): void {\n    this.showPageConfig = false;\n    void this.storeService.dispatch(setRowsPerTablePageAction, this.newRowsPerPage);\n  }\n\n  private rowsPerPageChanged(newValue?: number, oldValue?: number): void {\n    if(newValue && newValue !== oldValue) {\n      this.newRowsPerPage = newValue;\n      this.filter.limit = newValue;\n      this.filter.offset = 0;\n      this.calculateShownEntries();\n    }\n  }\n\n  private getFilterSetValue(filterStatus: ITableFilterStatus | null, column: IColumnDefinition<ICellViewModel<ICellModel<unknown>>>): string | null {\n    return (column.id && filterStatus?.filters[column.id]) ?? null;\n  }\n\n  private toggleSize(): void {\n    void this.storeService.dispatch(setExtendTableSizeAction, !this.extended);\n  }\n\n  private clearFilters(): void {\n    assertNotNullOrUndefined(this.tableId, 'tableId cannot be null or undefined');\n    void this.storeService.dispatch(clearTableFilter, this.tableId);\n  }\n\n  private clearSort(): void {\n    assertNotNullOrUndefined(this.tableId, 'tableId cannot be null or undefined');\n    void this.storeService.dispatch(clearTableSort, this.tableId);\n  }\n\n  @computedFrom('columns', 'selectable', 'hasSubTable')\n  private get columnCount(): number {\n    let count = this.columns.length;\n    if(this.selectable) count++;\n    if(this.hasSubTable) count++;\n    return count;\n  }\n\n  private async toggleSubTable(idx: number): Promise<void> {\n    this.subTableSelected = [];\n    if(this.subTableOpenIdx === idx) {\n      this.subTableOpenIdx = null;\n      return;\n    }\n    this.subTableOpenIdx = idx;\n  }\n}\n","import { computedFrom } from 'aurelia-framework';\nimport { ICellViewModel, ICellModel } from '../custom-table-interfaces';\n\nexport class IconButtonCell<T> implements ICellViewModel<ICellModel<T, IIconButtonCellOptions<T>>>  {\n  public model!: ICellModel<T, IIconButtonCellOptions<T>>;\n  protected iconName: string | null = null;\n\n  public activate(model: ICellModel<T, IIconButtonCellOptions<T>>): void {\n    this.model = model;\n    if(model.options.iconName) {\n      this.iconName = model.options.iconName;\n    } else if(model.options.iconNameTransformer) {\n      this.iconName = model.options.iconNameTransformer(model.data);\n    }\n  }\n\n  protected click(): void {\n    if(this.model.options.callback) {\n      this.model.options.callback(this.model.data);\n    }\n  }\n\n  @computedFrom('model.options.callback')\n  protected get clickable(): boolean {\n    return !!this.model.options.callback;\n  }\n}\n\nexport interface IIconButtonCellOptions<T> {\n  iconName?: string;\n  iconNameTransformer?: (data: T) => string;\n  callback?: (data: T) => void;\n}\n","import { computedFrom } from 'aurelia-framework';\nimport { ICellViewModel, ICellModel } from '../custom-table-interfaces';\n\nexport class IconCell<T> implements ICellViewModel<ICellModel<T, IIconCellOptions<T>>>  {\n  public model!: ICellModel<T, IIconCellOptions<T>>;\n  private icon?: string;\n  private trailingText?: string;\n  private classes = 'text-right';\n  private tooltipTk?: string;\n\n  public activate(model: ICellModel<T, IIconCellOptions<T>>): void {\n    this.model = model;\n    if(model.options.icon) {\n      this.icon = model.options.icon;\n    } else if(model.options.iconTransformer) {\n      this.icon = model.options.iconTransformer(model.data);\n    }\n\n    if(model.options.trailingTextTransformer) {\n      this.trailingText = model.options.trailingTextTransformer(model.data);\n    }\n\n    if(model.options.classes) {\n      this.classes = typeof model.options.classes === 'function'\n        ? model.options.classes(model.data)\n        : model.options.classes;\n    }\n\n    this.tooltipTk = model.options?.tooltipTk?.(model.data);\n  }\n\n  private click(): void {\n    if(this.model.options.callback) {\n      this.model.options.callback(this.model.data);\n    }\n  }\n\n  @computedFrom('model.options.callback')\n  private get clickable(): boolean {\n    return !!this.model.options.callback;\n  }\n}\n\nexport interface IIconCellOptions<T> {\n  icon?: string;\n  iconTransformer?: (data: T) => string;\n  trailingTextTransformer?: (data: T) => string;\n  callback?: (data: T) => void;\n  ariaLabel?: (data: T) => string;\n  tooltipTk?: (data: T) => string;\n  classes?: string | ((data: T) => string);\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/custom-table/actions-cell/actions-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/avatar-cell/avatar-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/button-cell/button-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/course-code-cell/course-code-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/custom-table-cell/custom-table-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/custom-table'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/icon-button-cell/icon-button-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/icon-cell/icon-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/json-cell/json-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/navigation-action-cell/navigation-action-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/progress-cell/progress-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/rating-cell/rating-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/select-cell/select-cell'));\n  config.globalResources(PLATFORM.moduleName('resources/elements/custom-table/skill-icon-cell/skill-icon-cell'));\n}\n","import { ICellModel, ICellViewModel } from '../custom-table-interfaces';\n\nexport class InputDateCell<T> implements ICellViewModel<ICellModel<T, IInputDateCellOptions<T>>> {\n  public model!: ICellModel<T, IInputDateCellOptions<T>>;\n  private value: string = '';\n  private error: string | null = null;\n\n  public activate(model: ICellModel<T, IInputDateCellOptions<T>>): void {\n    this.model = model;\n    this.value = model.options.value(model.data);\n  }\n\n  private handleChange(): void {\n    this.error = this.model.options?.validationCallback?.(this.model.data, this.value) ?? null;\n    if(this.error) return;\n    this.model.options.changeCallback(this.model.data, this.value);\n  }\n\n  private resetError(): void {\n    this.error = null;\n  }\n}\n\nexport interface IInputDateCellOptions<T> {\n  value: (data: T) => string;\n  changeCallback: (data: T, value: string) => void;\n  validationCallback?: (data: T, value: string) => string | null;\n}\n","import { ICellModel, ICellViewModel } from '../custom-table-interfaces';\n\nexport class InputNumberCell<T> implements ICellViewModel<ICellModel<T, IInputNumberCellOptions<T>>>  {\n  public model!: ICellModel<T, IInputNumberCellOptions<T>>;\n  private value: number = 0;\n\n  public activate(model: ICellModel<T, IInputNumberCellOptions<T>>): void {\n    this.model = model;\n    this.value = model.options.value(model.data);\n  }\n\n  private handleChange(): void {\n    this.model.options.changeCallback(this.model.data, this.value);\n  }\n}\n\nexport interface IInputNumberCellOptions<T> {\n  value: (data: T) => number;\n  changeCallback: (data: T, value: number) => void;\n}\n","import { ICellViewModel, ICellModel } from '../custom-table-interfaces';\n\n/**\n * This is a custom cell for a JSON string.\n * Showing a shortened version in the table-field.\n * And showing the formatted JSON as a tooltip onhover.\n */\nexport class JsonCell<T> implements ICellViewModel<ICellModel<T, IJsonCellOptions<T>>>  {\n  public model!: ICellModel<T, IJsonCellOptions<T>>;\n  private formattedJSON: string = '';\n  private showTooltip = false;\n\n  public activate(model: ICellModel<T, IJsonCellOptions<T>>): void {\n    this.model = model;\n    const parsedJSON = model.options.json(model.data) !== '' ? JSON.parse(model.options.json(model.data)) : '';\n    this.formattedJSON = JSON.stringify(parsedJSON, null, 2);\n  }\n}\n\nexport interface IJsonCellOptions<T> {\n  json: (data: T) => string;\n}\n","import { ICellViewModel, ICellModel } from '../custom-table-interfaces';\n\nexport class NavigationActionCell<T> implements ICellViewModel<ICellModel<T, INavigationActionCellOptions<T>>> {\n  public model!: ICellModel<T, INavigationActionCellOptions<T>>;\n\n  public activate(model: ICellModel<T, INavigationActionCellOptions<T>>): void {\n    this.model = model;\n  }\n}\n\nexport interface INavigationActionCellOptions<T> {\n  clickCallback: (entry: T) => void;\n  styleClasses: string[];\n}\n\n","import { ICellViewModel, ICellModel } from '../custom-table-interfaces';\n\nexport class ProgressCell<T> implements ICellViewModel<ICellModel<T, IProgressCellOptions<T>>>  {\n  public model!: ICellModel<T, IProgressCellOptions<T>>;\n\n  public activate(model: ICellModel<T, IProgressCellOptions<T>>): void {\n    this.model = model;\n  }\n}\n\nexport interface IProgressCellOptions<T> {\n  percent?: (data: T) => number;\n}\n","import { ICellViewModel, ICellModel } from '../custom-table-interfaces';\n\nexport class RatingCell<T> implements ICellViewModel<ICellModel<T, IRatingCellOptions<T>>>  {\n  public model!: ICellModel<T, IRatingCellOptions<T>>;\n\n  public activate(model: ICellModel<T, IRatingCellOptions<T>>): void {\n    this.model = model;\n  }\n}\n\nexport interface IRatingCellOptions<T> {\n  rating: (data: T) => number | null;\n}\n","import { ICellModel, ICellViewModel } from '../custom-table-interfaces';\n\nexport class SelectCell<T, U> implements ICellViewModel<ICellModel<T, ISelectCellOptions<T, U>>>  {\n  public model!: ICellModel<T, ISelectCellOptions<T, U>>;\n  private options: { value: U; label: string }[] = [];\n  private selectedOption: U | null = null;\n\n  public activate(model: ICellModel<T, ISelectCellOptions<T, U>>): void {\n    this.model = model;\n    this.options = model.options.options;\n    this.selectedOption = model.options.selectedOption(model.data);\n  }\n\n  private handleChange(): void {\n    if(this.selectedOption == null) return;\n    this.model.options.changeCallback(this.model.data, this.selectedOption);\n  }\n}\n\nexport interface ISelectCellOptions<T, U> {\n  options: { value: U; label: string }[];\n  selectedOption: (data: T) => U;\n  changeCallback: (data: T, value: U) => void;\n}\n","import { IMediaShareEntry } from 'common/dto/content/share/media.share.interface';\nimport { ICellModel, ICellViewModel } from '../custom-table-interfaces';\n\nexport class SharedToCell<T extends IMediaShareEntry> implements ICellViewModel<ICellModel<T, ISharedToCellOptions>> {\n  public model!: ICellModel<T, ISharedToCellOptions>;\n\n  public activate(model: ICellModel<T, ISharedToCellOptions>): void {\n    this.model = model;\n  }\n\n  private isFiltered(): boolean {\n    return this.model.options.isFiltered();\n  }\n\n  private showCourseGroupCount(): boolean {\n    return this.model.options.showCourseGroupCount();\n  }\n}\n\ninterface ISharedToCellOptions {\n  isFiltered: () => boolean;\n  showCourseGroupCount: () => boolean;\n}\n","import { ISkillDto } from 'common/dto/skill/management/skill.management.interface';\nimport { ICellModel, ICellViewModel } from '../custom-table-interfaces';\n\nexport class SkillIconCell<T> implements ICellViewModel<ICellModel<T, ISkillIconCellOptions<T>>>  {\n  public model!: ICellModel<T, ISkillIconCellOptions<T>>;\n  private skill: ISkillDto | null = null;\n\n  public activate(model: ICellModel<T, ISkillIconCellOptions<T>>): void {\n    this.model = model;\n    this.skill = model.options.skillTransformer(this.model.data);\n  }\n\n  private handleClick(): void {\n    this.model.options.callback(this.model.data);\n  }\n}\n\nexport interface ISkillIconCellOptions<T> {\n  skillTransformer: (data: T) => ISkillDto;\n  callback: (data: T) => void;\n}\n\n","import { ICellModel, ICellViewModel } from '../custom-table-interfaces';\n\nexport enum EOrderDirection {\n  UP = 'UP',\n  DOWN = 'DOWN',\n}\n\nexport class SortCell<T> implements ICellViewModel<ICellModel<T, ISortCellOptions<T>>>  {\n  public model!: ICellModel<T, ISortCellOptions<T>>;\n  private EOrderDirection = EOrderDirection;\n\n  public activate(model: ICellModel<T, ISortCellOptions<T>>): void {\n    this.model = model;\n  }\n\n  private handleChangeOrder(dir: EOrderDirection): void {\n    this.model.options.changeOrderCallback(this.model.data, dir);\n  }\n}\n\nexport interface ISortCellOptions<T> {\n  changeOrderCallback: (data: T, dir: EOrderDirection) => void;\n}\n","import { ICellViewModel, ICellModel } from '../custom-table-interfaces';\nimport { computedFrom } from 'aurelia-framework';\n\nexport class TaggedTextTableCell<T> implements ICellViewModel<ICellModel<T, ITaggedTextCellOptions<T>>> {\n  public data!: T;\n  public model!: ICellModel<T, ITaggedTextCellOptions<T>>;\n  public options!: ITaggedTextCellOptions<T>;\n  public href?: (data: T) => string;\n\n  protected value?: string;\n  protected tk?: string;\n  protected tagTk?: string;\n  protected spanClasses: string = '';\n\n  public activate(model: ICellModel<T, ITaggedTextCellOptions<T>>): void {\n    this.model = model;\n    this.data = model.data;\n    this.options = model.options;\n    this.href = model.options.href;\n    if(model.options.spanClasses) {\n      this.spanClasses = (typeof model.options.spanClasses === 'string') ? model.options.spanClasses : model.options.spanClasses(model.data);\n    }\n    if(model.options.tk) {\n      this.tk = model.options.tk(model.data);\n    } else if(model.options.transformer) {\n      this.value = model.options.transformer(model.data);\n    } else if(model.options?.property) {\n      this.value = (model as any).data[model.options.property];\n    } else {\n      this.value = model.data as unknown as string;\n    }\n\n    this.tagTk = model.options.tagTk?.(model.data);\n  }\n\n  protected click(): void {\n    if(this.model.options.callback) {\n      this.model.options.callback(this.model.data);\n    }\n  }\n\n  @computedFrom('model.options.clickable')\n  protected get clickable(): boolean {\n    return this.model.options.clickable ? this.model.options.clickable(this.model.data) : false;\n  }\n}\n\nexport interface ITaggedTextCellOptions<T> {\n  transformer?: (data: T) => string;\n  tk?: (data: T) => string;\n  tagTk?: (data: T) => string | undefined;\n  callback?: (data: T) => void;\n  clickable?: (data: T) => boolean;\n  property?: keyof T;\n  spanClasses?: string | ((data: T) => string);\n  href?: (data: T) => string;\n}\n"],"names":[],"sourceRoot":""}