UNPKG

@universis/common

Version:

Universis - common directives and services

1 lines 366 kB
{"version":3,"file":"universis-common.mjs","sources":["../../src/shared/services/configuration.service.ts","../../src/shared/components/modal/dialog.component.ts","../../src/shared/services/modal.service.ts","../../src/shared/components/msgbox/msgbox.component.ts","../../src/shared/components/msgbox/msgbox.component.html","../../src/shared/components/modal/spinner.component.ts","../../src/shared/components/modal/toast.component.ts","../../src/shared/services/toast.service.ts","../../src/shared/services/grade-scale.service.ts","../../src/shared/services/loading.service.ts","../../src/shared/pipes/localized-date.pipe.ts","../../src/shared/services/numeral/locales.ts","../../src/shared/pipes/semester.pipe.ts","../../src/shared/pipes/template.pipe.ts","../../src/shared/pipes/truncate.pipe.ts","../../src/shared/directives/ngvar.directive.ts","../../src/auth/services/user.service.ts","../../src/auth/services/activated-user.service.ts","../../src/error/error.service.ts","../../src/auth/guards/auth.guard.ts","../../src/shared/directives/if-location.directive.ts","../../src/shared/services/diagnostics.service.ts","../../src/shared/directives/if-service.directive.ts","../../src/shared/pipes/localized-attributes.pipe.ts","../../src/shared/services/user-storage.ts","../../src/shared/services/user-activity/user-activity.service.ts","../../src/shared/services/session-user-activity/session-user-activity.service.ts","../../src/shared/services/persistent-user-activity/persistent-user-activity.service.ts","../../src/shared/services/request-types/request-types.service.ts","../../src/shared/services/app-sidebar.service.ts","../../src/shared/services/app-guest-sidebar.service.ts","../../src/shared/services/browser-storage.service.ts","../../src/shared/services/fallback-user-storage.service.ts","../../src/shared/services/app-event.service.ts","../../src/shared/components/modal/xmas-spinner.component.ts","../../src/shared/services/xmas-loading.service.ts","../../src/shared/services/certificate-service/certificate-service.service.ts","../../src/environments/environment.ts","../../src/shared/components/signature-info/i18n/signature-info.en.ts","../../src/shared/components/signature-info/i18n/signature-info.el.ts","../../src/shared/components/signature-info/i18n/signature-info.translations.ts","../../src/shared/components/signature-info/signature-info.component.ts","../../src/shared/components/signature-info/signature-info.component.html","../../src/shared/services/referrer-route.service.ts","../../src/shared/services/export-spreadsheet.service.ts","../../src/shared/shared.module.ts","../../src/events/services/server-event.service.ts","../../src/error/error.custom.ts","../../src/auth/services/authentication.service.ts","../../src/auth/components/login/login.component.ts","../../src/auth/components/logout/logout.component.ts","../../src/auth/auth-callback.component.ts","../../src/auth/components/refresh/refresh-token.component.ts","../../src/auth/auth.routing.ts","../../src/auth/services/pkce-authentication.service.ts","../../src/auth/auth.module.ts","../../src/events/server-event.module.ts","../../src/error/components/error-base/error-base.component.ts","../../src/error/components/error-base/error-base.component.html","../../src/error/error.handler.ts","../../src/error/components/network-changed-error/network-changed-error.component.ts","../../src/error/error.routing.ts","../../src/error/i18n/error.en.ts","../../src/error/i18n/error.el.ts","../../src/error/i18n/index.ts","../../src/error/error.module.ts","../../src/helpers/decorators.ts","../../src/helpers/session-storage-helper.service.ts","../../src/public_api.ts","../../src/universis-common.ts"],"sourcesContent":["import {EventEmitter, Injectable, InjectionToken, Injector} from '@angular/core';\nimport { TranslateService } from '@ngx-translate/core';\nimport { HttpClient } from '@angular/common/http';\nimport { isDevMode } from '@angular/core';\nimport { DATA_CONTEXT_CONFIG } from '@themost/angular';\nimport { BehaviorSubject } from 'rxjs';\n\n\nexport declare interface ApplicationSettingsConfiguration {\n name?: string;\n image?: string;\n description?: string;\n thumbnail?: string;\n}\n\nexport declare interface RemoteSettingsConfiguration {\n server: string;\n}\n\nexport declare interface LocalizationSettingsConfiguration {\n locales?: Array<string>;\n uiLocales?: Array<string>;\n defaultLocale?: string;\n append?: any;\n}\n\nexport declare interface SettingsConfiguration {\n app?: ApplicationSettingsConfiguration;\n remote: RemoteSettingsConfiguration;\n i18n?: LocalizationSettingsConfiguration;\n auth?: any;\n}\n\nexport declare interface ApplicationConfiguration {\n settings: SettingsConfiguration;\n}\n\nexport let APP_CONFIGURATION = new InjectionToken('app.configuration');\n\n\n/**\n *\n * This Service is used to get or set global configuration for the project\n * @export\n * @class ConfigurationService\n */\n@Injectable()\nexport class ConfigurationService {\n\n public config: any;\n public loaded: BehaviorSubject<ApplicationConfiguration | null> = new BehaviorSubject<ApplicationConfiguration | null>(null);\n constructor(private _translateService: TranslateService,\n private _injector: Injector,\n private _http: HttpClient) {\n //\n }\n\n /**\n *\n * Load Configs saved in Project\n * @returns {Promise<any>}\n * @memberof ConfigurationService\n */\n public async load(): Promise<boolean> {\n if (this.config) {\n return true;\n }\n // get environment\n const env = isDevMode() ? 'development' : 'production';\n // load configuration based on environment\n // e.g. assets/config/app.production.json or\n // assets/config/app.development.json\n try {\n return await this.loadFrom(`assets/config/app.${env}.json`);\n } catch (err: Error | any) {\n if (err.status === 404) { // not found\n // load default application configuration\n return await this.loadFrom(`assets/config/app.json`);\n }\n throw err;\n }\n }\n\n public async loadFrom(url: string): Promise<boolean> {\n\n // get configuration from url\n this.config = await new Promise((resolve, reject) => {\n this._http.get(url).subscribe( result => {\n return resolve(result);\n }, err => {\n return reject(err);\n });\n });\n // get DATA_CONTEXT_CONFIG\n const dataContextConfig = this._injector.get(DATA_CONTEXT_CONFIG);\n // IMPORTANT: Set DATA_CONTEXT_CONFIG base URI from configuration\n dataContextConfig.base = this.config.settings.remote.server;\n // set locale for translate service\n this._translateService.use(this.currentLocale);\n // emit event for loaded configuration\n this.loaded.next(this.config as ApplicationConfiguration);\n // return\n return true;\n }\n\n /**\n * Gets current application settings\n */\n get settings(): SettingsConfiguration {\n return this.config && this.config.settings;\n }\n\n /**\n * Gets the current user language\n */\n get currentLocale(): string {\n const currentLang = localStorage.getItem('currentLang');\n if (currentLang) {\n return currentLang;\n }\n if (this.settings &&\n this.settings.i18n &&\n this.settings.i18n.defaultLocale) {\n // return current language\n return this.settings.i18n.defaultLocale;\n }\n // use fallback language\n return 'en';\n }\n\n set currentLocale(locale: string) {\n // save current locale\n localStorage.setItem('currentLang', locale);\n // set locale for translate service\n this._translateService.use(locale);\n }\n\n}\n","import {Component, OnInit, Input, ElementRef} from '@angular/core';\nimport {TranslateService} from '@ngx-translate/core';\nimport 'jquery';\nimport 'bootstrap/js/dist/modal';\n\ndeclare var jQuery: any;\n\nexport enum DIALOG_BUTTONS {\n Ok = 1,\n Yes = 2,\n No = 4,\n Abort = 8,\n Retry = 16,\n Ignore = 32,\n Cancel = 64,\n YesNo = 6,\n AbortRetryIgnore = 54,\n OkCancel = 65,\n YesNoCancel = 70\n}\n\n@Component({\n selector: 'universis-dialog.modal',\n styles: [`\n .modal-dialog .modal-body {\n margin-top: 0;\n margin-bottom: 0;\n }\n .modal-footer {\n border-top: 0;\n }\n .btn {\n text-transform: uppercase;\n font-size: 16px;\n }\n `\n ],\n template: `\n <div class=\"modal-dialog\" [ngClass]=\"theme\" role=\"document\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h4 class=\"modal-title\">{{title | translate}}</h4>\n </div>\n <div class=\"modal-body\" [innerHTML]=\"message\"></div>\n <div class=\"modal-footer\">\n <button [lang]=\"language\" type=\"button\" *ngIf=\"bitwiseAnd(buttons, 1)\"\n (click)=\"hide('ok')\" class=\"btn btn-theme btn-ok\" [translate]=\"'OK'\"></button>\n <button [lang]=\"language\" type=\"button\" *ngIf=\"bitwiseAnd(buttons, 2)\"\n (click)=\"hide('yes')\" class=\"btn btn-theme btn-yes\" [translate]=\"'Yes'\"></button>\n <button [lang]=\"language\" type=\"button\" *ngIf=\"bitwiseAnd(buttons, 4)\"\n (click)=\"hide('no')\" class=\"btn btn-gray-100 btn-no\" [translate]=\"'No'\"></button>\n <button [lang]=\"language\" type=\"button\" *ngIf=\"bitwiseAnd(buttons, 8)\"\n (click)=\"hide('abort')\" class=\"btn btn-danger btn-abort\" [translate]=\"'Abort'\"></button>\n <button [lang]=\"language\" type=\"button\" *ngIf=\"bitwiseAnd(buttons, 16)\"\n (click)=\"hide('retry')\" class=\"btn btn-gray-100 btn-retry\" [translate]=\"'Retry'\"></button>\n <button [lang]=\"language\" type=\"button\" *ngIf=\"bitwiseAnd(buttons, 32)\"\n (click)=\"hide('ignore')\" class=\"btn btn-gray-100 btn-ignore\" [translate]=\"'Ignore'\"></button>\n <button [lang]=\"language\" type=\"button\" *ngIf=\"bitwiseAnd(buttons, 64)\"\n (click)=\"hide('cancel')\" class=\"btn btn-gray-100 btn-cancel\" [translate]=\"'Cancel'\"></button>\n </div>\n </div>\n </div>\n `\n})\n/**\n *\n * A modal dialog component with ok and cancel buttons\n * @export\n * @class DialogComponent\n */\nexport class DialogComponent implements OnInit {\n\n\n @Input() title: string = '';\n @Input() message: string = '';\n @Input() theme: string | undefined;\n public buttons: DIALOG_BUTTONS = DIALOG_BUTTONS.Ok;\n private modalRef: any;\n public language = 'en';\n\n constructor(private _element: ElementRef, private _translateService: TranslateService) {\n this.language = this._translateService.currentLang;\n }\n\n public bitwiseAnd(a, b) {\n return a & b;\n }\n\n /**\n * Shows modal dialog\n * @returns Promise<any>\n */\n show() {\n return new Promise((resolve, reject) => {\n if (this.modalRef) {\n this.modalRef.one('hide.bs.modal', () => {\n // get related target if any\n const result = this.modalRef.data('result');\n // return result\n return resolve(result);\n });\n const modalRef: { modal(action: string): void } = this.modalRef;\n modalRef.modal('show');\n } else {\n reject('Modal element may not be empty at this context');\n }\n });\n }\n\n /**\n * Hides modal dialog\n * @param value\n */\n hide(value: any) {\n this.modalRef.data('result', value);\n const modalRef: { modal(action: string): void } = this.modalRef;\n modalRef.modal('hide');\n }\n\n /**\n * Converts modal buttons classes modal-ok, modal-yes-no etc to dialog buttons\n * @param {Array<string>} classList\n */\n private classListToButtons(classList: Array<string>) {\n return classList.map( classListElement => {\n // maps each item to an array of matches (if match)\n return /^modal(-ok)?(-yes)?(-no)?(-abort)?(-retry)?(-ignore)?(-cancel)?/ig.exec(classListElement);\n }).filter(match => {\n // filter not matched elements\n return match != null;\n }).map( (match) => {\n // maps each match as an array of 2 ^ k results\n // @ts-ignore\n return match.map((item, k) => {\n if (item && k > 0) {\n return Math.pow(2, k - 1);\n }\n return 0;\n }).reduce( (a, b) => {\n // return a sum of results\n return a + b;\n });\n }).reduce( (a, b) => {\n // return a final sum of results\n return a | b;\n });\n }\n\n async ngOnInit() {\n // get element classes\n const classList = Array.from((<HTMLDivElement>this._element.nativeElement).classList);\n // get buttons from element class list\n const classListButtons = this.classListToButtons(classList);\n // if element has button classes (modal-ok, modal-yes-no etc)\n if (classListButtons) {\n // set dialog buttons\n this.buttons = classListButtons;\n }\n this.modalRef = jQuery(this._element.nativeElement);\n // initialize modal\n const modalRef: { modal(options: any): void } = this.modalRef;\n modalRef.modal({\n backdrop: 'static',\n focus: true,\n keyboard: false,\n show: false\n });\n }\n}\n","import {ApplicationRef, ComponentFactoryResolver, EmbeddedViewRef, Injectable, Injector} from '@angular/core';\nimport {DialogComponent, DIALOG_BUTTONS} from '../components/modal/dialog.component';\nimport { BsModalService } from 'ngx-bootstrap/modal';\nimport { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';\n\nexport interface DialogComponentExtras {\n theme?: string;\n}\n\n/**\n *\n * Displays a Modal window or a type of Notification (based on choice the color changes)\n * @export\n * @class ModalService\n */\n@Injectable()\nexport class ModalService {\n modalRef: any;\n config = {\n ignoreBackdropClick: true,\n keyboard: false,\n initialState: null,\n class: 'modal-content-base'\n };\n\n constructor(private componentFactoryResolver: ComponentFactoryResolver,\n private appRef: ApplicationRef,\n private injector: Injector,\n private modalService: BsModalService) { }\n\n showDialog(title: string, message: string, buttons: DIALOG_BUTTONS = DIALOG_BUTTONS.Ok, extras?: DialogComponentExtras) {\n const componentRef = this.componentFactoryResolver\n .resolveComponentFactory(DialogComponent)\n .create(this.injector);\n componentRef.instance.title = title;\n componentRef.instance.message = message;\n componentRef.instance.buttons = buttons;\n if (extras) {\n componentRef.instance.theme = extras.theme;\n }\n // attach component to the appRef so that it's inside the ng component tree\n this.appRef.attachView(componentRef.hostView);\n // get DOM element from component\n const modalElement = (componentRef.hostView as EmbeddedViewRef<any>)\n .rootNodes[0] as HTMLElement;\n // append DOM element to the body\n document.body.appendChild(modalElement);\n return componentRef.instance.ngOnInit().then(() => {\n // show dialog\n return componentRef.instance.show().then( result => {\n // detach view\n this.appRef.detachView(componentRef.hostView);\n // destroy component ref\n componentRef.destroy();\n //\n return Promise.resolve(result);\n });\n });\n }\n\n showWarningDialog(title: string, message: string, buttons: DIALOG_BUTTONS = DIALOG_BUTTONS.OkCancel) {\n return this.showDialog(\n '',\n `<div class=\"text-center\">\n <div class=\"icon-circle icon-circle-warning\">\n <i class=\"fa fa-exclamation\"></i>\n </div>\n <div class=\"font-2xl font-weight-bold mt-2\">\n ${title}\n </div>\n <p class=\"mt-2\">\n ${message}\n </p>\n </div>\n `,\n buttons, {\n theme: 'modal-dialog-warning'\n });\n }\n\n showSuccessDialog(title: string, message: string, buttons: DIALOG_BUTTONS = DIALOG_BUTTONS.Ok) {\n return this.showDialog(\n '',\n `<div class=\"text-center\">\n <div class=\"icon-circle icon-circle-success\">\n <i class=\"fa fa-check\"></i>\n </div>\n <div class=\"font-2xl font-weight-bold mt-2\">\n ${title}\n </div>\n <p class=\"mt-2\">\n ${message}\n </p>\n </div>\n `,\n buttons, {\n theme: 'modal-dialog-success'\n });\n }\n\n showErrorDialog(title: string, message: string, buttons: DIALOG_BUTTONS = DIALOG_BUTTONS.Ok) {\n return this.showDialog(\n '',\n `<div class=\"text-center\">\n <div class=\"icon-circle icon-circle-danger\">\n <i class=\"fa fa-times\"></i>\n </div>\n <div class=\"font-2xl font-weight-bold mt-2\">\n ${title}\n </div>\n <p class=\"mt-2\">\n ${message}\n </p>\n </div>\n `,\n buttons, {\n theme: 'modal-dialog-danger'\n });\n }\n\n showInfoDialog(title: string, message: string, buttons: DIALOG_BUTTONS = DIALOG_BUTTONS.Ok) {\n return this.showDialog(\n '',\n `<div class=\"text-center\">\n <div class=\"icon-circle icon-circle-info\">\n <i class=\"fa fa-info\"></i>\n </div>\n <div class=\"font-2xl font-weight-bold mt-2\">\n ${title}\n </div>\n <p class=\"mt-2\">\n ${message}\n </p>\n </div>\n `,\n buttons, {\n theme: 'modal-dialog-info'\n });\n }\n\n openModal(template: any, customClass?: string): BsModalRef {\n let config;\n if (customClass) {\n config = JSON.parse(JSON.stringify(this.config));\n config.class = customClass;\n } else {\n config = this.config;\n }\n return this.modalRef = this.modalService.show(template, config);\n }\n\n openModalComponent(template: any, options?: any): BsModalRef {\n return this.modalRef = this.modalService.show(template, options);\n }\n}\n","import { Component, EventEmitter, Input, Output } from '@angular/core';\n\n/**\n *\n * Component for Card-Box element with Inputs:\n * Usage <universis-msgbox [title]=\"HelloWorld\" ...></universis-msgbox>\n * @Input() title: Title of Box\n * @Input() icon: Icon displayed on the left of the element\n * @Input() info: The Status displayed on user\n * @Input() message: Explanation of the status\n * @Input() extraMessage: Some extra guidance\n * @Input() actionButton: Text displayed as Text in button\n * @Input() actionText: Text displayed as an action\n * @Input() disableBut: Disable button\n * @export\n */\n@Component({\n selector: 'universis-msgbox',\n templateUrl: './msgbox.component.html',\n styleUrls: ['./msgbox.component.scss']\n})\nexport class MsgboxComponent {\n\n @Input() title: string | undefined;\n @Input() icon: string | undefined;\n @Input() info: string | undefined;\n @Input() message: string | undefined;\n @Input() extraMessage: string | undefined;\n @Input() actionButton: string | undefined;\n @Input() actionText: string | undefined;\n @Input() disableBut: boolean | undefined;\n // Default class sets the color to green, otherwise pass it the correct bootstrap class\n @Input() buttonClass = 'btn-success';\n // Usage (action)=\"someFunction()\"\n @Output() action = new EventEmitter<any>();\n\n btnClicked: boolean | undefined;\n\n clicked() {\n this.action.emit();\n }\n}\n","<div class=\"card\">\n <div class=\"msgbox_main-container\">\n <div class=\"msgbox_icon\">\n <span class=\"fa-4x far {{icon}}\" style=\"color:#678898;\"></span>\n </div>\n <div class=\"msgbox_details\">\n <div *ngIf=\"title && !title.includes('title')\" class=\"msgbox_title\" id=\"title\">{{title}}</div>\n <div class=\"font pt-3 msgbox_item\" id=\"message\">{{message}}</div>\n <div class=\"pt-3 msgbox_item\" *ngIf=\"extraMessage && !extraMessage.includes('extraMessage')\">{{extraMessage}}</div>\n <div>\n <div class=\"pt-3 msgbox_item\" *ngIf=\"actionButton && !actionButton.includes('actionButton')\">\n <button type=\"button\" class=\"btn s--btn\" [ngClass]=\"buttonClass\" [disabled]=\"disableBut\" (click)=\"clicked()\">{{actionButton}}</button>\n </div>\n <div class=\"pt-3 msgbox_item\" *ngIf=\"actionText && !actionText.includes('actionText')\" id=\"text\">\n <span><span class=\"fa-1x icon-arrow-right\"></span>{{actionText}}</span>\n </div>\n </div>\n </div>\n </div>\n</div>\n","import { Component, OnInit } from '@angular/core';\n\n/**\n *\n * A native spinner component\n * @export\n * @class SpinnerComponent\n * @implements {OnInit}\n */\n@Component({\n selector: 'universis-spinner',\n styleUrls: [`./spinner.component.scss`],\n template: `\n <div class=\"s--spinner\">\n <div class=\"sk-three-bounce\">\n <div class=\"sk-child sk-bounce1\"></div>\n <div class=\"sk-child sk-bounce2\"></div>\n <div class=\"sk-child sk-bounce3\"></div>\n </div>\n </div>\n `\n})\nexport class SpinnerComponent implements OnInit {\n\n constructor() { }\n\n ngOnInit() {\n //\n }\n}\n","import {AfterViewInit, Component, ElementRef, Input, OnInit} from '@angular/core';\nimport 'jquery';\nimport * as _Toast from 'bootstrap/js/dist/toast';\nconst Toast = (_Toast as any).default || _Toast;\n/**\n *\n * A native spinner component\n * @export\n * @class SpinnerComponent\n * @implements {OnInit}\n */\n@Component({\n selector: 'universis-toast.toast',\n template: `\n <div class=\"toast-header d-flex p-0\">\n <strong class=\"mr-auto\">{{ title }}</strong>\n <button type=\"button\" class=\"ml-2 mb-1 align-self-start close\" data-dismiss=\"toast\" (click)=\"hide();\" aria-label=\"Close\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"toast-body p-0\">\n <div class=\"toast-body-content\" [innerHTML]=\"message\"></div>\n </div>\n <div class=\"toast-header p-0\">\n <small class=\"toast-date\">{{ dateCreated | date: 'shortTime'}}</small>\n </div>\n `,\n styles: [\n `:host {\n z-index: auto;\n }`\n ]\n})\n\n\nexport class ToastComponent implements OnInit, AfterViewInit {\n\n @Input() title: string | undefined;\n @Input() message: string | undefined;\n @Input() autoHide = true;\n @Input() delay = 5000;\n public dateCreated = new Date();\n\n private toast: any;\n\n constructor(private _element: ElementRef) { }\n\n ngOnInit() {\n\n }\n\n ngAfterViewInit(): void {\n this.toast = new Toast(this._element.nativeElement, {\n animation: false,\n autohide: this.autoHide,\n delay: this.delay\n });\n return this.toast.show();\n }\n\n public show() {\n if (this.toast) {\n this.toast.show();\n }\n }\n\n public hide() {\n const container = document.body.getElementsByClassName('universis-toast-container')[0];\n if (container.getElementsByClassName('show').length === 1) {\n container.classList.add('hidden');\n }\n\n if (this.toast) {\n this.toast.hide();\n }\n }\n}\n","import {Injectable, ComponentFactoryResolver, ApplicationRef, Injector, EmbeddedViewRef} from '@angular/core';\nimport {ToastComponent} from '../components/modal/toast.component';\n\n\n/**\n * @export\n * @class ToastService\n */\n@Injectable()\nexport class ToastService {\n\n constructor(private componentFactoryResolver: ComponentFactoryResolver,\n private appRef: ApplicationRef,\n private injector: Injector) { }\n\n /**\n * Shows a toast message\n * @param {string} title A string which represents message title\n * @param {string} message A string which represents message body\n * @param {boolean=} autoHide A boolean which indicates whether a message will auto hide or not\n * @param {number=} delay A number which indicates the number of milliseconds before auto hide message\n */\n show(title: string, message: string, autoHide: boolean | undefined = true, delay: number | undefined = 5000) {\n // search for toast container\n let container = document.body.getElementsByClassName('universis-toast-container')[0];\n if (container == null) {\n // create toast container\n container = document.createElement('div');\n container.classList.add('universis-toast-container', 'p-3');\n // append to boyd\n document.body.appendChild(container);\n }\n // create a component reference for toast component\n const componentRef = this.componentFactoryResolver\n .resolveComponentFactory(ToastComponent)\n .create(this.injector);\n componentRef.instance.title = title;\n componentRef.instance.message = message;\n componentRef.instance.autoHide = autoHide;\n componentRef.instance.delay = delay;\n componentRef.location.nativeElement.classList.add('show');\n\n // attach component to the appRef so that it's inside the ng component tree\n this.appRef.attachView(componentRef.hostView);\n\n // get DOM element from component\n const domElem = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;\n\n // append DOM element to the body\n container.appendChild(domElem);\n\n container.classList.remove('hidden');\n\n setTimeout(() => {\n componentRef.instance.hide();\n }, delay);\n }\n\n // noinspection JSMethodCanBeStatic\n /**\n * Clears toast messages\n */\n clear() {\n // search for toast container\n const container = document.body.getElementsByClassName('universis-toast-container')[0];\n if (container) {\n // remove children\n while (container.firstChild) {\n container.removeChild(container.firstChild);\n }\n }\n }\n\n}\n","import { Injectable, Pipe, PipeTransform} from '@angular/core';\nimport {AngularDataContext} from '@themost/angular';\nimport {DecimalPipe} from '@angular/common';\nimport {ConfigurationService} from './configuration.service';\n\nexport function round(x: any, n?: number): number {\n if (typeof x !== 'number') {\n return 0;\n }\n if (n) {\n return parseFloat(x.toFixed(n));\n }\n return Math.round(x);\n}\n\nexport class GradeScale {\n\n public id?: number;\n public name?: string;\n public scaleType?: number;\n public scaleFactor?: number;\n public scaleBase?: number;\n public formatPrecision = 2;\n public scalePrecision = 2;\n public values?: any[];\n\n private _formatter: DecimalPipe;\n private readonly _decimalCharRegExp: RegExp;\n\n constructor(private _locale: string, private scale: any = null) {\n // init decimal pipe\n this._formatter = new DecimalPipe(this._locale);\n // get decimal separator regular expression\n // @ts-ignore\n this._decimalCharRegExp = new RegExp('\\\\' + this._formatter.transform(0.1, '1.1-1').substr(1, 1), 'ig');\n if (scale) {\n this.id = scale['id'];\n this.name = scale['name'];\n this.scaleType = scale['scaleType'];\n this.scaleFactor = scale['scaleFactor'];\n this.scaleBase = scale['scaleBase'];\n this.formatPrecision = scale['formatPrecision'];\n this.scalePrecision = scale['scalePrecision'];\n this.values = scale['values'];\n }\n }\n\n /**\n * Formats the specified based on this grade scale and returns the formatted value\n * @param {number} grade\n * @returns string\n */\n format(grade: number): string {\n if (this.scaleType === 0) {\n if (typeof this.scaleFactor !== 'number') {\n throw new TypeError('Grade scale factor must be a number.');\n }\n if (this.scaleFactor <= 0) {\n throw new TypeError('Grade scale factor must greater than zero.');\n }\n // arithmetic grade scale\n if (typeof (grade as any) === 'number') {\n // get final grade by applying rounding\n const finalGrade = round((grade / this.scaleFactor), this.formatPrecision);\n // return formatted value based on the current locale\n const result = this._formatter.transform(finalGrade, `1.${this.formatPrecision}-${this.formatPrecision}`);\n if (result != null) {\n return result;\n }\n }\n return '';\n } else if (this.scaleType === 1 || this.scaleType === 3) {\n let finalValue = round(grade, this.scalePrecision);\n if (this.values != null) {\n let findValue = this.values.find(x => {\n return finalValue >= x.valueFrom && finalValue <= x.valueTo;\n });\n if (findValue) {\n return findValue.name;\n }\n }\n throw new RangeError('Out of range value for grade');\n }\n throw new Error('Not yet implemented');\n }\n\n /**\n * Converts the given grade to the equivalent grade value base on this grade scale\n * @param grade\n */\n convert(grade: any): number | undefined {\n if (this.scaleType === 0) {\n let finalGrade;\n // if grade is a number\n if (typeof grade === 'undefined' || grade === null) {\n return;\n } else if (typeof grade === 'number') {\n finalGrade = grade;\n } else if (typeof grade === 'string') {\n // try to convert the given grade\n finalGrade = parseFloat(grade.replace(this._decimalCharRegExp, '.'));\n if (isNaN(finalGrade)) {\n return;\n }\n }\n if (typeof this.scaleFactor !== 'number') {\n throw new TypeError('Grade scale factor must be a number.');\n }\n if (this.scaleFactor <= 0) {\n throw new TypeError('Grade scale factor must greater than zero.');\n }\n // validate grade\n const res = <number>round((finalGrade * this.scaleFactor), this.formatPrecision + 1 );\n // throw error if result is greater than 1\n if (res < 0 || res > 1) {\n throw new Error('Grade is out of range. It must be between 0 to 1.');\n }\n return res;\n } else if (this.scaleType === 1 || this.scaleType === 3) {\n if (this.values) {\n let findValue = this.values.find( x => {\n return x.name === grade || x.alternateName === grade;\n });\n if (findValue) {\n return findValue.exactValue;\n }\n }\n throw new RangeError('Out of range value for grade');\n }\n throw new Error('Not yet implemented');\n }\n\n}\n\n@Injectable()\nexport class GradeScaleService {\n\n private _gradeScales?: Array<GradeScale>;\n\n constructor(private _context: AngularDataContext,\n private _configurationService: ConfigurationService) {\n\n //\n }\n\n /**\n * Gets all the available grade scales\n */\n getGradeScales() {\n if (this._gradeScales) {\n return Promise.resolve(this._gradeScales);\n }\n return this._context.model('GradeScales').getItems().then(result => {\n const locale = this._configurationService.currentLocale;\n this._gradeScales = result.map( x => {\n return Object.assign(new GradeScale(locale), x);\n });\n return Promise.resolve(this._gradeScales);\n }, (err) => {\n\n console.log(err);\n return null;\n });\n }\n\n /**\n * Gets a grade scale based on the given identifier\n * @param {*} id\n */\n getGradeScale(id: any): Promise<GradeScale | undefined> {\n if (this._gradeScales) {\n return Promise.resolve(this._gradeScales.find(value => {\n return value.id === id;\n }));\n }\n return this.getGradeScales().then(res => {\n if (res == null) {\n return;\n }\n return Promise.resolve(res.find(value => {\n return value.id === id;\n }));\n });\n }\n\n}\n\n@Pipe({\n name: 'grade'\n})\nexport class GradePipe implements PipeTransform {\n\n constructor(private _gradeScaleService: GradeScaleService) {\n //\n }\n\n transform(value: any, gradeScale?: any): any {\n if (gradeScale instanceof GradeScale) {\n return Promise.resolve(gradeScale.format(value));\n }\n return this._gradeScaleService.getGradeScale(gradeScale).then( result => {\n if (typeof result === 'undefined') {\n return Promise.reject(new Error('The specified grade scale cannot be found or is inaccessible'));\n }\n return Promise.resolve(result.format(value));\n });\n }\n\n}\n","import {ApplicationRef, ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, Injectable, Injector} from '@angular/core';\nimport {SpinnerComponent} from '../components/modal/spinner.component';\n/**\n *\n * Displays Loading Spinner\n * @export\n * @class LoadingService\n */\n@Injectable()\nexport class LoadingService {\n private componentRef: ComponentRef<any> | null = null;\n\n constructor(private componentFactoryResolver: ComponentFactoryResolver,\n private appRef: ApplicationRef,\n private injector: Injector) {\n }\n\n private appendComponentToBody(component: any) {\n\n // do nothing if component ref already exists\n if (this.componentRef) {\n return;\n }\n\n // create a component reference from the component\n this.componentRef = this.componentFactoryResolver\n .resolveComponentFactory(component)\n .create(this.injector);\n\n // attach component to the appRef so that it's inside the ng component tree\n this.appRef.attachView(this.componentRef.hostView);\n\n // get DOM element from component\n const domElem = (this.componentRef.hostView as EmbeddedViewRef<any>)\n .rootNodes[0] as HTMLElement;\n\n // append DOM element to the body\n document.body.appendChild(domElem);\n }\n\n /**\n *\n * Toggles show/hide state of spinner\n */\n toggle() {\n if (this.componentRef) {\n this.hideLoading();\n } else {\n this.showLoading();\n }\n }\n\n /**\n *\n * Shows spinner\n */\n showLoading() {\n if (this.componentRef == null) {\n this.appendComponentToBody(SpinnerComponent);\n }\n }\n\n /**\n *\n * Hides spinner\n */\n hideLoading() {\n if (this.componentRef) {\n this.appRef.detachView(this.componentRef.hostView);\n this.componentRef.destroy();\n this.componentRef = null;\n }\n }\n}\n","import {Pipe, PipeTransform} from '@angular/core';\nimport {TranslateService} from '@ngx-translate/core';\nimport {DatePipe} from '@angular/common';\n\n/**\n *\n * Custom DatePipe representation\n * @export\n * @class LocalizedDatePipe\n * @implements {PipeTransform}\n */\n@Pipe({\n name: 'localizedDate',\n pure: false\n})\nexport class LocalizedDatePipe implements PipeTransform {\n\n constructor(private translateService: TranslateService) {\n }\n\n /**\n *\n * Converts Date value that been passed\n * @param {*} value The Date that needs to be converted\n * @param {string} [pattern='mediumDate'] Pattern of Date\n * @returns {*} Converted Date\n * @memberof LocalizedDatePipe\n */\n transform(value: any, pattern: string = 'mediumDate'): any {\n const datePipe: DatePipe = new DatePipe(this.translateService.currentLang);\n if (typeof value === 'string' && /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2}$/.test(value)) {\n return datePipe.transform(new Date(value + ':00'), pattern);\n }\n return datePipe.transform(value, pattern);\n }\n}\n","export async function registration (_numeral: { register(locale: string, shortLocale: string, config: any) }){\n await _numeral.register('locale', 'el', {\n delimiters: {\n thousands: '.',\n decimal: ','\n },\n abbreviations: {\n thousand: 'χιλ',\n million: 'εκ',\n billion: 'δισ',\n trillion: 'τρισ'\n },\n ordinal: function (number) {\n return 'ο';\n },\n currency: {\n symbol: '€'\n }\n });\n\n await _numeral.register('locale', 'el-gr', {\n delimiters: {\n thousands: '.',\n decimal: ','\n },\n abbreviations: {\n thousand: 'χιλ',\n million: 'εκ',\n billion: 'δισ',\n trillion: 'τρισ'\n },\n ordinal: function (number) {\n return 'ο';\n },\n currency: {\n symbol: '€'\n }\n });\n\n await _numeral.register('locale', 'cy', {\n delimiters: {\n thousands: '.',\n decimal: ','\n },\n abbreviations: {\n thousand: 'χιλ',\n million: 'εκ',\n billion: 'δισ',\n trillion: 'τρισ'\n },\n ordinal: function (number) {\n return 'ο';\n },\n currency: {\n symbol: '€'\n }\n });\n\n await _numeral.register('locale', 'cy-cy', {\n delimiters: {\n thousands: '.',\n decimal: ','\n },\n abbreviations: {\n thousand: 'χιλ',\n million: 'εκ',\n billion: 'δισ',\n trillion: 'τρισ'\n },\n ordinal: function (number) {\n return 'ο';\n },\n currency: {\n symbol: '€'\n }\n });\n}\n","import {Pipe, PipeTransform} from '@angular/core';\nimport {TranslateService} from '@ngx-translate/core';\nimport * as _numeral from 'numeral';\nconst numeral = (_numeral as any).default || _numeral;\nimport 'numeral/locales';\nimport {registration} from '../services/numeral/locales';\n\n@Pipe({\n name: 'semester',\n pure: false\n})\nexport class SemesterPipe implements PipeTransform {\n\n constructor(private _translateService: TranslateService) {\n const numeralWithLocales: { locales: { [key: string]: any }} = numeral as any;\n if(numeralWithLocales.locales['el'] == null) {\n registration(numeral).then(async ()=> {\n await Promise.resolve();\n });\n }\n }\n\n transform(value: any, pattern: string = 'long'): any {\n if (typeof value === 'object') {\n value = value.id;\n }\n if (value >= 250) {\n return this._translateService.instant(`Semester.full.${value}`);\n }\n numeral.locale(this._translateService.currentLang);\n return this._translateService.instant(`Semester.${pattern}`,\n {\n value: value,\n ordinal: numeral(value).format('o')\n });\n }\n}\n","import {Pipe, PipeTransform} from '@angular/core';\nimport {template, TemplateSettings} from 'lodash';\n\n/**\n * Template Pipe is an impure pipe that can escape\n * HTML and interpolated data properties and\n * execute JavaScript in \"evaluate\" delimiters\n */\n@Pipe({\n name: 'template',\n pure: false\n})\nexport class TemplatePipe implements PipeTransform {\n constructor() {\n }\n\n /**\n * Uses lodash template function to generate a string\n * from a given template escaping the delimiters. It\n * deletes the template source after its initialization,\n * generates the string and nullifies the compiled template\n * function variable to be garbage-collected.\n * @param {string} inputTemplate: The template used to generate the string\n * @param {any} value: The parameter passed to compiled template function\n * @param {Object | undefined} options: Custom options to pass to lodash template\n */\n transform(inputTemplate: string, value: any, options?: TemplateSettings | undefined): any {\n let _tpl;\n if (options !== undefined) {\n /*\n lodash doesn't throw error when options are not valid, so\n there is no need to add a try catch block\n */\n _tpl = template(inputTemplate, options);\n } else {\n _tpl = template(inputTemplate);\n }\n delete _tpl.source;\n const _res = _tpl(value);\n _tpl = null;\n return _res;\n }\n}\n","import { Pipe, PipeTransform } from \"@angular/core\";\n\n@Pipe({ name: 'truncate' })\nexport class TruncatePipe implements PipeTransform {\n\n /*\n The transform method is essential to a pipe. The PipeTransform interface defines that method and guides both tooling and the compiler. Technically, it's optional; Angular looks for and executes the transform method regardless.\n */\n\n transform(value: string, limit: number): string | null {\n if (value == null) {\n return null;\n }\n return value.length < limit\n ? value\n : value.slice(0, limit) + '...';\n }\n}\n","import {Directive, Input} from '@angular/core';\n\n// tslint:disable directive-selector\n@Directive({\n selector: '[ngVar]',\n exportAs: 'ngVar'\n})\nexport class NgVarDirective {\n\n constructor() {\n }\n\n [key: string]: any;\n\n private $implicit: any;\n\n @Input('ngVar') set assign(value: any) {\n this.$implicit = value;\n }\n\n public get value(): any {\n return this.$implicit;\n }\n\n public set value(value: any) {\n this.$implicit = value;\n }\n\n}\n// tslint:enable directive-selector\n","import { Injectable } from '@angular/core';\nimport { AngularDataContext } from '@themost/angular';\nimport { ConfigurationService } from '../../shared/services/configuration.service';\n\n/**\n * @private\n */\nconst currentProfileProperty = 'currentProfile';\n/**\n * @private\n */\nconst currentUserProperty = 'currentUser';\n\ndeclare var $: any;\n\n@Injectable()\nexport class UserService {\n\n\n private _user: any;\n\n\n constructor(private _context: AngularDataContext,\n private _configurationService: ConfigurationService) {\n }\n\n /**\n * Refreshes user token taken by implicit authorization flow.\n */\n checkLogin() {\n // set refresh frame\n const settings = this._configurationService.settings.auth;\n const _iframe = $('<iframe>', {\n /* tslint:disable max-line-length */\n src: `${settings.authorizeURL}?response_type=token&client_id=${settings.oauth2.clientID}&redirect_uri=${settings.oauth2.callbackURL}&prompt=none`,\n id: 'openid',\n frameborder: 1,\n scrolling: 'no',\n onload: function() {\n //\n }\n }).hide().appendTo('body');\n }\n\n\n /**\n * Gets current user\n * @returns {Promise<*>} An object which represents the current user\n *\n * Example\n * ```typescript\nimport {UserService} from '@universis/common';\n\nconstructor(private _userService: UserService) {\n //\n}\nngOnInit() {\n this._userService.getUser().then(user => {\n // place code here...\n });\n}\n```\n */\n async getUser(): Promise<any> {\n return this.getUserSync();\n }\n\n getUserSync() {\n // if user already taken\n if (this._user) {\n // return user\n return this._user;\n }\n const sessionStorageUser = sessionStorage.getItem(currentUserProperty);\n if (sessionStorageUser) {\n // get user from storage\n const currentUser = JSON.parse(sessionStorageUser);\n // get interactive user\n this._user = Object.assign({}, currentUser);\n // set context\n Object.defineProperty(this._user, 'context', {\n configurable: true,\n enumerable: false,\n value: this._context\n });\n // set user bearer authorization\n this._context.setBearerAuthorization(this._user.token.access_token);\n // set current language\n this._context.getService().setHeader('accept-language', this._configurationService.currentLocale);\n // return user\n return this._user;\n }\n return null;\n }\n\n /**\n * Sets current user profile\n * @param {*} profile\n * @returns UserService\n */\n setProfile(profile?: any): UserService {\n if (typeof profile === 'undefined' || profile == null) {\n sessionStorage.removeItem(currentProfileProperty);\n } else {\n sessionStorage.setItem(currentProfileProperty, JSON.stringify(profile));\n }\n return this;\n }\n\n}\n","import { Injectable } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\nimport { UserService } from './user.service';\n\n@Injectable()\nexport class ActivatedUser {\n\n public user: BehaviorSubject<any> = new BehaviorSubject(null);\n\n constructor(private _userService: UserService) {\n this.user.next(this.activatedUser);\n }\n\n private get activatedUser() {\n return this._userService.getUserSync();\n }\n\n}\n","import { Injectable, Component, OnInit, Input } from '@angular/core';\nimport { Router } from '@angular/router';\nimport {HttpErrorResponse} from '@angular/common/http';\nimport { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';\nimport { TranslateService } from '@ngx-translate/core';\n\nexport declare interface TranslateError {\n title?: string;\n message?: string;\n action?: string;\n actionText?: string;\n continue?: string;\n icon?: string;\n iconClass?: string;\n}\n\nexport declare interface ShowErrorModalOptions {\n continueLink?: string;\n buttonText?: string;\n iconClass?: string;\n}\n\n@Component( {\n selector: 'universis-error-modal',\n template: `\n <div class=\"modal-header text-center\">\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"hide()\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body mt-0 mb-0 text-center\">\n <div class=\"text-center\">\n <ng-container *ngIf=\"iconClass; then iconTemplate; else iconDefault\">\n </ng-container>\n <ng-template #iconTemplate>\n <div class=\"mb-2\">\n <i [ngClass]=\"iconClass\" class=\"fa-4x\"></i>\n </div>\n </ng-template>\n\n <ng-template #iconDefault>\n <div class=\"icon-circle icon-circle-danger\">\n <i class=\"fa fa-times\"></i>\n </div>\n </ng-template>\n <div class=\"font-2xl font-weight-bold mt-2\">\n {{title}}\n </div>\n <p class=\"mt-2\">\n {{message}}\n </p>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button [lang]=\"language\" type=\"button\" (click)=\"hide()\"\n class=\"btn btn-gray-100 btn-ok text-uppercase\" [translate]=\"buttonText\"></button>\n </div>\n `, styles: [`\n .btn-ok {\n font-size: 16px;\n }\n `]\n })\n\n export class ErrorModalComponent implements OnInit {\n\n public code;\n public language;\n @Input() continueLink = '/';\n @Input() message: string | undefined;\n @Input() title: string | undefined;\n @Input() iconClass?: string;\n @Input() buttonText = 'Error.Continue';\n @Input() error: any;\n\n constructor ( private _translateService: TranslateService,\n public bsModalRef: BsModalRef,\n private _router: Router ) {\n this.language = this._translateService.currentLang;\n }\n\n hide() {\n this.bsModalRef.hide();\n if (this.continueLink == null) {\n return Promise.resolve();\n }\n if (this.continueLink === '.') {\n return Promise.resolve();\n }\n return this._router.navigate([this.continueLink]);\n }\n\n ngOnInit() {\n\n // get last error\n const error = this.error;\n // check error.code property\n if (error && typeof error.code === 'string') {\n this.code = error.code;\n } else if (error && typeof (error.status || error.statusCode) === 'number') {\n this.code = `E${error.status || error.statusCode}`;\n } else {\n this.code = 'E500';\n }\n if (error && typeof error.continue === 'string') {\n this.continueLink = error.continue;\n }\n this._translateService.get(this.code).subscribe((translation: TranslateError) => {\n if (translation) {\n this.title = translation.title;\n this.message = (this.error instanceof HttpErrorResponse && this.error.error && this.error.error.message)\n ? this.error.error.message : translation.message;\n // set custom icon class\n if (this.iconClass == null) {\n if (translation.iconClass || translation.icon) {\n this.iconClass = translation.iconClass || translation.icon;\n }\n }\n // set custom button text\n if (this.buttonText === 'Error.Continue') {\n if (translation.actionText || translation.action) {\n this.buttonText = translation.actionText || translation.action;\n }\n }\n if (translation.continue) {\n // force set message\n this.message = translation.message;\n // force set continue link\n this.continueLink = translation.continue;\n }\n } else {\n this._translateService.get('E500').subscribe((translation: TranslateError) => {\n this.title = translation.title;\n this.message = (this.error instanceof HttpErrorResponse && this.error.error && this.error.error.message)\n ? this.error.error.message : translation.message;\n });\n }\n });\n }\n }\n\n\n@Injectable()\nexport class ErrorService {\n\n private _lastError: any;\n\n constructor(private _router: Router, private _modalService: BsModalService) {\n //\n }\n\n /**\n * @param {*} error\n * @returns {Promise<boolean>}\n */\n navigateToError(error) {\n this.setLastError(error);\n // if error is an instance of HttpErrorResponse\n if (error instanceof HttpErrorResponse) {\n if (error && error.error && error.error.statusCode) {\n return this._rout