diff --git a/.github/workflows/build-previews.yml b/.github/workflows/build-previews.yml index d391bbc0..3b16ab5d 100644 --- a/.github/workflows/build-previews.yml +++ b/.github/workflows/build-previews.yml @@ -26,7 +26,8 @@ jobs: ${{ runner.os }}-node- - name: npm ci for a2agc run: npm ci - - run: npm run build -- --output-hashing=none --base-href=/ + - run: npm run compile-build-data + - run: npm run build a2agc -- --output-hashing=none --base-href=/ - uses: netlify/actions/cli@master with: args: deploy --dir=dist/a2agc --filter=a2agc diff --git a/.github/workflows/production-build.yml b/.github/workflows/production-build.yml index c8923162..c82e74c5 100644 --- a/.github/workflows/production-build.yml +++ b/.github/workflows/production-build.yml @@ -30,7 +30,8 @@ jobs: ${{ runner.os }}-node- - name: npm ci for a2agc run: npm ci - - run: npm run build -- --output-hashing=none --base-href=/ + - run: npm run compile-build-data + - run: npm run build a2agc -- --output-hashing=none --base-href=/ - uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/staging-build.yml b/.github/workflows/staging-build.yml index 0688da75..817e3d79 100644 --- a/.github/workflows/staging-build.yml +++ b/.github/workflows/staging-build.yml @@ -30,7 +30,8 @@ jobs: ${{ runner.os }}-node- - name: npm ci for a2agc run: npm ci - - run: npm run build -- --output-hashing=none --base-href=/ + - run: npm run compile-build-data + - run: npm run build a2agc -- --output-hashing=none --base-href=/ - uses: peaceiris/actions-gh-pages@v3 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9e58397d..5bdf8f12 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,8 +30,7 @@ jobs: ${{ runner.os }}-node- - name: npm ci for website run: npm ci - - run: npm run lint - - run: npm run test + - run: npm run lint a2agc - name: SonarCloud Scan uses: sonarsource/sonarcloud-github-action@master env: diff --git a/.gitignore b/.gitignore index 9461d8b4..0975d78c 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ speed-measure-plugin*.json /.sass-cache /connect.lock /coverage +/documentation /libpeerconnection.log npm-debug.log yarn-error.log diff --git a/project.json b/apps/a2agc/project.json similarity index 75% rename from project.json rename to apps/a2agc/project.json index 2a3a7a76..a2024821 100644 --- a/project.json +++ b/apps/a2agc/project.json @@ -1,5 +1,5 @@ { - "$schema": "node_modules/nx/schemas/project-schema.json", + "$schema": "../../node_modules/nx/schemas/project-schema.json", "name": "a2agc", "projectType": "application", "generators": { @@ -10,27 +10,27 @@ "strict": true } }, - "sourceRoot": "src", + "sourceRoot": "apps/a2agc/src", "prefix": "agc", "targets": { "build": { "executor": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist/a2agc", - "index": "src/index.html", - "main": "src/main.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "tsconfig.app.json", + "index": "apps/a2agc/src/index.html", + "main": "apps/a2agc/src/main.ts", + "polyfills": "apps/a2agc/src/polyfills.ts", + "tsConfig": "apps/a2agc/tsconfig.app.json", "allowedCommonJsDependencies": [ "css-element-queries" ], "assets": [ - "src/favicon.ico", - "src/assets" + "apps/a2agc/src/favicon.ico", + "apps/a2agc/src/assets" ], "styles": [ - "src/styles.scss", - "src/themes.scss", + "apps/a2agc/src/styles.scss", + "apps/a2agc/src/themes.scss", "node_modules/prismjs/themes/prism-okaidia.css", "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css" ], @@ -51,8 +51,8 @@ "production": { "fileReplacements": [ { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" + "replace": "apps/a2agc/src/environments/environment.ts", + "with": "apps/a2agc/src/environments/environment.prod.ts" } ], "optimization": true, @@ -104,17 +104,17 @@ "test": { "executor": "@angular-devkit/build-angular:karma", "options": { - "main": "src/test.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "tsconfig.spec.json", + "main": "apps/a2agc/src/test.ts", + "polyfills": "apps/a2agc/src/polyfills.ts", + "tsConfig": "apps/a2agc/tsconfig.spec.json", "karmaConfig": "karma.conf.js", "codeCoverage": true, "assets": [ - "src/favicon.ico", - "src/assets" + "apps/a2agc/src/favicon.ico", + "apps/a2agc/src/assets" ], "styles": [ - "src/styles.scss" + "apps/a2agc/src/styles.scss" ], "scripts": [] } @@ -123,8 +123,8 @@ "executor": "@angular-eslint/builder:lint", "options": { "lintFilePatterns": [ - "src/**/*.ts", - "src/**/*.html" + "apps/a2agc/src/**/*.ts", + "apps/a2agc/src/**/*.html" ] }, "outputs": [ @@ -145,6 +145,9 @@ } }, "defaultConfiguration": "development" + }, + "test-doc-coverage": { + "command": "npx compodoc -p apps/a2agc/tsconfig.doc.json -e json --coverageTest --coverageMinimumPerFile 100 --coverageTestShowOnlyFailed -t" } } } diff --git a/src/app/app-routing.module.ts b/apps/a2agc/src/app/app-routing.module.ts similarity index 100% rename from src/app/app-routing.module.ts rename to apps/a2agc/src/app/app-routing.module.ts diff --git a/src/app/app.component.html b/apps/a2agc/src/app/app.component.html similarity index 89% rename from src/app/app.component.html rename to apps/a2agc/src/app/app.component.html index d61fa0f4..235239d5 100644 --- a/src/app/app.component.html +++ b/apps/a2agc/src/app/app.component.html @@ -3,7 +3,7 @@ - + diff --git a/src/app/app.component.scss b/apps/a2agc/src/app/app.component.scss similarity index 100% rename from src/app/app.component.scss rename to apps/a2agc/src/app/app.component.scss diff --git a/src/app/app.component.ts b/apps/a2agc/src/app/app.component.ts similarity index 73% rename from src/app/app.component.ts rename to apps/a2agc/src/app/app.component.ts index 16adb350..31f08d60 100644 --- a/src/app/app.component.ts +++ b/apps/a2agc/src/app/app.component.ts @@ -4,11 +4,14 @@ import { MatSidenavContainer } from '@angular/material/sidenav'; import { buildInfo } from './build-info'; import { PageLink } from './core/models/pages.model'; +import { DatasetsState } from './core/state/data/datasets.state'; import { RouterState } from './core/state/router/router.state'; import { visualizations } from './core/state/visualizations/visualizations'; import { MarkdownModalComponent, MarkdownModalData } from './shared/components/markdown-modal/markdown-modal.component'; - +/** + * A2AGC app component +*/ // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: 'agc-root', @@ -16,31 +19,56 @@ import { MarkdownModalComponent, MarkdownModalData } from './shared/components/m styleUrls: ['./app.component.scss'] }) export class AppComponent implements AfterViewInit { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-root'; + /** Sidenav container component element */ @ViewChild(MatSidenavContainer) readonly sidenavContainer!: MatSidenavContainer; + /** Whether or not sidenav should include data distributions */ + showData = true; + // TODO move these values to state + /** Sidenav menu header */ readonly menuHeader = 'Marion County Opioid Addiction Report'; + /** Page options to include in the sidenav menu */ readonly pages: PageLink[] = visualizations.map(v => ({ path: v.id, title: v.title, description: v.description })); + /** Whether or not to show the subbar under the page header */ subBarVisible = true; + /** True if menu is open */ menuOpen = false; + /** Build date of app component */ buildDate = buildInfo.buildDate; + /** + * Creates an instance of app component. + * @param router Router state + * @param datasetsState Datasets state + * @param dialog Mat dialog service + * @param zone NgZone + */ constructor( router: RouterState, + datasetsState: DatasetsState, private readonly dialog: MatDialog, private readonly zone: NgZone ) { router.navigationStart$.subscribe(() => { this.menuOpen = false; }); + + datasetsState.entitiesArray$.subscribe((datasets) => { + this.showData = datasets.length > 0; + }); } + /** + * Sets sidenav after view init + */ ngAfterViewInit(): void { // NOTE: Scrollable is not available in ngOnInit even if @ViewChild has `static: true` this.sidenavContainer.scrollable.elementScrolled().subscribe(() => { @@ -56,6 +84,9 @@ export class AppComponent implements AfterViewInit { }); } + /** + * Opens contact form + */ openContactUs(): void { this.dialog.open(MarkdownModalComponent, { width: '800px', @@ -67,6 +98,9 @@ export class AppComponent implements AfterViewInit { }); } + /** + * Opens privacy policy dialog + */ openPrivacyPolicy(): void { this.dialog.open(MarkdownModalComponent, { width: '800px', diff --git a/src/app/app.module.ts b/apps/a2agc/src/app/app.module.ts similarity index 100% rename from src/app/app.module.ts rename to apps/a2agc/src/app/app.module.ts diff --git a/src/app/app.theme.scss b/apps/a2agc/src/app/app.theme.scss similarity index 100% rename from src/app/app.theme.scss rename to apps/a2agc/src/app/app.theme.scss diff --git a/src/app/build-info.ts b/apps/a2agc/src/app/build-info.ts similarity index 87% rename from src/app/build-info.ts rename to apps/a2agc/src/app/build-info.ts index c54dce49..314ed07b 100644 --- a/src/app/build-info.ts +++ b/apps/a2agc/src/app/build-info.ts @@ -1,3 +1,4 @@ +/** Build info */ export const buildInfo = { version: '1.0.0', lastCommitDate: new Date(1603996553000), diff --git a/src/app/core/components/banner/banner.component.html b/apps/a2agc/src/app/core/components/banner/banner.component.html similarity index 100% rename from src/app/core/components/banner/banner.component.html rename to apps/a2agc/src/app/core/components/banner/banner.component.html diff --git a/src/app/core/components/banner/banner.component.scss b/apps/a2agc/src/app/core/components/banner/banner.component.scss similarity index 100% rename from src/app/core/components/banner/banner.component.scss rename to apps/a2agc/src/app/core/components/banner/banner.component.scss diff --git a/src/app/core/components/banner/banner.component.ts b/apps/a2agc/src/app/core/components/banner/banner.component.ts similarity index 85% rename from src/app/core/components/banner/banner.component.ts rename to apps/a2agc/src/app/core/components/banner/banner.component.ts index a20eda23..7f0a5ab5 100644 --- a/src/app/core/components/banner/banner.component.ts +++ b/apps/a2agc/src/app/core/components/banner/banner.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; +/** + * Page banner component + */ @Component({ selector: 'agc-banner', templateUrl: './banner.component.html', @@ -8,5 +11,6 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush }) export class BannerComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-banner'; } diff --git a/src/app/core/components/banner/banner.module.ts b/apps/a2agc/src/app/core/components/banner/banner.module.ts similarity index 100% rename from src/app/core/components/banner/banner.module.ts rename to apps/a2agc/src/app/core/components/banner/banner.module.ts diff --git a/src/app/core/components/banner/banner.theme.scss b/apps/a2agc/src/app/core/components/banner/banner.theme.scss similarity index 100% rename from src/app/core/components/banner/banner.theme.scss rename to apps/a2agc/src/app/core/components/banner/banner.theme.scss diff --git a/src/app/core/components/page-footer/page-footer.component.html b/apps/a2agc/src/app/core/components/page-footer/page-footer.component.html similarity index 100% rename from src/app/core/components/page-footer/page-footer.component.html rename to apps/a2agc/src/app/core/components/page-footer/page-footer.component.html diff --git a/src/app/core/components/page-footer/page-footer.component.scss b/apps/a2agc/src/app/core/components/page-footer/page-footer.component.scss similarity index 100% rename from src/app/core/components/page-footer/page-footer.component.scss rename to apps/a2agc/src/app/core/components/page-footer/page-footer.component.scss diff --git a/src/app/core/components/page-footer/page-footer.component.ts b/apps/a2agc/src/app/core/components/page-footer/page-footer.component.ts similarity index 76% rename from src/app/core/components/page-footer/page-footer.component.ts rename to apps/a2agc/src/app/core/components/page-footer/page-footer.component.ts index a0074f06..1fcc609a 100644 --- a/src/app/core/components/page-footer/page-footer.component.ts +++ b/apps/a2agc/src/app/core/components/page-footer/page-footer.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, Output } from '@angular/core'; +/** + * Page footer component + */ @Component({ selector: 'agc-page-footer', templateUrl: './page-footer.component.html', @@ -8,10 +11,14 @@ import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, O changeDetection: ChangeDetectionStrategy.OnPush }) export class PageFooterComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-page-footer'; + /** Build date */ @Input() buildDate = new Date(); + /** Emits when contact button clicked */ @Output() readonly contactClick = new EventEmitter(); + /** Emits when privacy policy button clicked */ @Output() readonly privacyClick = new EventEmitter(); } diff --git a/src/app/core/components/page-footer/page-footer.module.ts b/apps/a2agc/src/app/core/components/page-footer/page-footer.module.ts similarity index 100% rename from src/app/core/components/page-footer/page-footer.module.ts rename to apps/a2agc/src/app/core/components/page-footer/page-footer.module.ts diff --git a/src/app/core/components/page-footer/page-footer.theme.scss b/apps/a2agc/src/app/core/components/page-footer/page-footer.theme.scss similarity index 100% rename from src/app/core/components/page-footer/page-footer.theme.scss rename to apps/a2agc/src/app/core/components/page-footer/page-footer.theme.scss diff --git a/src/app/core/components/page-header/page-header.component.html b/apps/a2agc/src/app/core/components/page-header/page-header.component.html similarity index 100% rename from src/app/core/components/page-header/page-header.component.html rename to apps/a2agc/src/app/core/components/page-header/page-header.component.html diff --git a/src/app/core/components/page-header/page-header.component.scss b/apps/a2agc/src/app/core/components/page-header/page-header.component.scss similarity index 100% rename from src/app/core/components/page-header/page-header.component.scss rename to apps/a2agc/src/app/core/components/page-header/page-header.component.scss diff --git a/src/app/core/components/page-header/page-header.component.ts b/apps/a2agc/src/app/core/components/page-header/page-header.component.ts similarity index 76% rename from src/app/core/components/page-header/page-header.component.ts rename to apps/a2agc/src/app/core/components/page-header/page-header.component.ts index a65f59a4..0c01cba3 100644 --- a/src/app/core/components/page-header/page-header.component.ts +++ b/apps/a2agc/src/app/core/components/page-header/page-header.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, Output } from '@angular/core'; +/** + * Page header component + */ @Component({ selector: 'agc-page-header', templateUrl: './page-header.component.html', @@ -8,12 +11,17 @@ import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, O changeDetection: ChangeDetectionStrategy.OnPush }) export class PageHeaderComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-page-header'; - @HostBinding('class.mat-elevation-z3') readonly elevation = true; + /** True if the menu is open */ @Input() menuOpen = false; + /** Emits menuOpen value */ @Output() readonly menuOpenChange = new EventEmitter(); + /** + * Opens/closes the menu and emits the menu state + */ toggleMenuOpen(): void { this.menuOpen = !this.menuOpen; this.menuOpenChange.emit(this.menuOpen); diff --git a/src/app/core/components/page-header/page-header.module.ts b/apps/a2agc/src/app/core/components/page-header/page-header.module.ts similarity index 100% rename from src/app/core/components/page-header/page-header.module.ts rename to apps/a2agc/src/app/core/components/page-header/page-header.module.ts diff --git a/src/app/core/components/page-header/page-header.theme.scss b/apps/a2agc/src/app/core/components/page-header/page-header.theme.scss similarity index 100% rename from src/app/core/components/page-header/page-header.theme.scss rename to apps/a2agc/src/app/core/components/page-header/page-header.theme.scss diff --git a/src/app/core/components/page-menu/page-menu.component.html b/apps/a2agc/src/app/core/components/page-menu/page-menu.component.html similarity index 92% rename from src/app/core/components/page-menu/page-menu.component.html rename to apps/a2agc/src/app/core/components/page-menu/page-menu.component.html index 3f2a5a56..7a32f24a 100644 --- a/src/app/core/components/page-menu/page-menu.component.html +++ b/apps/a2agc/src/app/core/components/page-menu/page-menu.component.html @@ -12,7 +12,7 @@
- + Data Distributions diff --git a/src/app/core/components/page-menu/page-menu.component.scss b/apps/a2agc/src/app/core/components/page-menu/page-menu.component.scss similarity index 100% rename from src/app/core/components/page-menu/page-menu.component.scss rename to apps/a2agc/src/app/core/components/page-menu/page-menu.component.scss diff --git a/src/app/core/components/page-menu/page-menu.component.ts b/apps/a2agc/src/app/core/components/page-menu/page-menu.component.ts similarity index 63% rename from src/app/core/components/page-menu/page-menu.component.ts rename to apps/a2agc/src/app/core/components/page-menu/page-menu.component.ts index 4ac97b7e..78f496cb 100644 --- a/src/app/core/components/page-menu/page-menu.component.ts +++ b/apps/a2agc/src/app/core/components/page-menu/page-menu.component.ts @@ -3,6 +3,9 @@ import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular import { PageLink } from '../../models/pages.model'; +/** + * Page menu component + */ @Component({ selector: 'agc-page-menu', templateUrl: './page-menu.component.html', @@ -10,11 +13,22 @@ import { PageLink } from '../../models/pages.model'; changeDetection: ChangeDetectionStrategy.OnPush }) export class PageMenuComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-page-menu'; + /** Header text */ @Input() header = ''; + /** Page menu link info */ @Input() pages: PageLink[] = []; + /** Whether to show the data distributions option */ + @Input() showData = true; + /** + * Gets path from PageLink item + * @param _index page index + * @param link page link item + * @returns path name + */ linkId(_index: number, link: PageLink): string { return link.path; } diff --git a/src/app/core/components/page-menu/page-menu.module.ts b/apps/a2agc/src/app/core/components/page-menu/page-menu.module.ts similarity index 100% rename from src/app/core/components/page-menu/page-menu.module.ts rename to apps/a2agc/src/app/core/components/page-menu/page-menu.module.ts diff --git a/src/app/core/components/page-menu/page-menu.theme.scss b/apps/a2agc/src/app/core/components/page-menu/page-menu.theme.scss similarity index 100% rename from src/app/core/components/page-menu/page-menu.theme.scss rename to apps/a2agc/src/app/core/components/page-menu/page-menu.theme.scss diff --git a/src/app/core/components/sub-bar/sub-bar.component.scss b/apps/a2agc/src/app/core/components/sub-bar/sub-bar.component.scss similarity index 100% rename from src/app/core/components/sub-bar/sub-bar.component.scss rename to apps/a2agc/src/app/core/components/sub-bar/sub-bar.component.scss diff --git a/src/app/core/components/sub-bar/sub-bar.component.ts b/apps/a2agc/src/app/core/components/sub-bar/sub-bar.component.ts similarity index 80% rename from src/app/core/components/sub-bar/sub-bar.component.ts rename to apps/a2agc/src/app/core/components/sub-bar/sub-bar.component.ts index d7bb87bc..17c4b6a8 100644 --- a/src/app/core/components/sub-bar/sub-bar.component.ts +++ b/apps/a2agc/src/app/core/components/sub-bar/sub-bar.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; +/** + * Header sub bar component + */ @Component({ selector: 'agc-sub-bar', template: '', @@ -8,7 +11,9 @@ import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular changeDetection: ChangeDetectionStrategy.OnPush }) export class SubBarComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-sub-bar'; + /** If sub bar is visible */ @Input() @HostBinding('class.visible') visible = true; } diff --git a/src/app/core/components/sub-bar/sub-bar.module.ts b/apps/a2agc/src/app/core/components/sub-bar/sub-bar.module.ts similarity index 100% rename from src/app/core/components/sub-bar/sub-bar.module.ts rename to apps/a2agc/src/app/core/components/sub-bar/sub-bar.module.ts diff --git a/src/app/core/components/sub-bar/sub-bar.theme.scss b/apps/a2agc/src/app/core/components/sub-bar/sub-bar.theme.scss similarity index 100% rename from src/app/core/components/sub-bar/sub-bar.theme.scss rename to apps/a2agc/src/app/core/components/sub-bar/sub-bar.theme.scss diff --git a/src/app/core/core.module.ts b/apps/a2agc/src/app/core/core.module.ts similarity index 100% rename from src/app/core/core.module.ts rename to apps/a2agc/src/app/core/core.module.ts diff --git a/src/app/core/core.theme.scss b/apps/a2agc/src/app/core/core.theme.scss similarity index 100% rename from src/app/core/core.theme.scss rename to apps/a2agc/src/app/core/core.theme.scss diff --git a/apps/a2agc/src/app/core/models/dataset.model.ts b/apps/a2agc/src/app/core/models/dataset.model.ts new file mode 100644 index 00000000..c1b0a049 --- /dev/null +++ b/apps/a2agc/src/app/core/models/dataset.model.ts @@ -0,0 +1,44 @@ +import { Distribution } from './distribution.model'; + + +/** + * Dataset interface + */ +export interface Dataset { + /** Dataset name */ + name: string; + /** Dataset description */ + description: string; + /** Dataset variable names */ + variables: string[]; +} + +/** + * Dataset variable interface + */ +export interface DatasetVariable { + /** Dataset of variable */ + dataset: string; + /** Variable name */ + name: string; + /** Variable description */ + description: string; + /** Variable type */ + type: string; + /** Number of non null values */ + nonNullCount: number; + /** Percent of missing values */ + percentMissing: number; + /** Variable distribution info */ + distribution: Distribution; +} + +/** + * Dataset meta entry interface + */ +export interface DatasetMetaEntry { + /** Entry label */ + label: string; + /** Entry value */ + value: string; +} diff --git a/src/app/core/models/distribution.model.ts b/apps/a2agc/src/app/core/models/distribution.model.ts similarity index 52% rename from src/app/core/models/distribution.model.ts rename to apps/a2agc/src/app/core/models/distribution.model.ts index ead80efd..02c408b8 100644 --- a/src/app/core/models/distribution.model.ts +++ b/apps/a2agc/src/app/core/models/distribution.model.ts @@ -1,3 +1,6 @@ +/** + * Distribution chart type + */ export enum DistributionType { pie = 'pie-chart', histogram = 'histogram', @@ -6,20 +9,39 @@ export enum DistributionType { summary = 'summary' } +/** + * Distribution info + */ export interface Distribution { + /** Type of distribution visualization */ type: DistributionType | string; + /** Summary of distribution data */ summary: DistributionSummary; + /** Distribution url */ url: string; } +/** + * Distribution summary + */ export interface DistributionSummary { + /** Number of distinct values in distribution */ distinct: number; + /** Minimum value in distribution */ min: number; + /** Maximum value in distribution */ max: number; } +/** + * Distribution data entry + * @template T + */ export interface DistributionDataEntry { + /** Time period of entry */ period: Date | undefined; + /** Entry value */ value: T; + /** Entry count */ count: number; } diff --git a/apps/a2agc/src/app/core/models/pages.model.ts b/apps/a2agc/src/app/core/models/pages.model.ts new file mode 100644 index 00000000..6cc041fa --- /dev/null +++ b/apps/a2agc/src/app/core/models/pages.model.ts @@ -0,0 +1,13 @@ +/** + * Page link info + */ +export interface PageLink { + /** Path to visualization */ + path: string; + /** Page title */ + title: string; + /** Page description */ + description?: string; + /** Page order */ + order?: number; +} diff --git a/src/app/core/services/dataset-loader/dataset-loader.service.ts b/apps/a2agc/src/app/core/services/dataset-loader/dataset-loader.service.ts similarity index 64% rename from src/app/core/services/dataset-loader/dataset-loader.service.ts rename to apps/a2agc/src/app/core/services/dataset-loader/dataset-loader.service.ts index cad87425..c926314b 100644 --- a/src/app/core/services/dataset-loader/dataset-loader.service.ts +++ b/apps/a2agc/src/app/core/services/dataset-loader/dataset-loader.service.ts @@ -10,49 +10,95 @@ import { Dataset, DatasetVariable } from '../../models/dataset.model'; type RawData = Record; +/** + * Raw dataset info + */ interface RawDataset { + /** Dataset name */ name: string; + /** Description */ remarks: string; + /** Number of rows in dataset*/ row_count: number; + /** Record containing variable names and variable info */ columns: Record; } +/** + * Raw dataset variable info + */ interface RawDatasetVariable { + /** Variable name */ name: string; + /** Variable type */ type: string; + /** Description */ remarks: string; + /** Number of non-null values */ n_non_null: number; + /** % of missing values from total*/ pct_missing: number; + /** Distribution type */ dist_type: string; + /** Distribution data */ dist_data: RawDistribution; } +/** + * Raw distribution info + */ interface RawDistribution { + /** Distinct values */ distinct: number; + /** Minimum value */ min: number; + /** Maximum value */ max: number; + /** Url */ url: string; } /* eslint-enable @typescript-eslint/naming-convention */ +/** + * Parse results interface + */ export interface ParseResults { + /** Parsed datasets */ datasets: Dataset[]; + /** Parsed variables */ variables: DatasetVariable[]; } +/** + * Service to handle dataset loading + */ @Injectable({ providedIn: 'root' }) export class DatasetLoaderService { + /** + * Creates an instance of dataset loader service. + * @param http Angular http service + */ constructor(private readonly http: HttpClient) {} + /** + * Fetches data from url + * @param url url + * @returns observable of type parsed results + */ load(url: string): Observable { const response = this.http.get(url, { responseType: 'json' }); return response.pipe(map(this.parseRawData.bind(this))); } + /** + * Processes the raw datasets and variable data received from the server. + * @param data raw data + * @returns parsed results + */ private parseRawData(data: RawData): ParseResults { const datasets: Dataset[] = []; const variables: DatasetVariable[] = []; @@ -67,6 +113,11 @@ export class DatasetLoaderService { return { datasets, variables }; } + /** + * Constructs a Dataset object from raw data + * @param data raw data + * @returns parsed dataset + */ private parseRawDataset(data: RawDataset): Dataset { return { name: data.name, @@ -75,6 +126,12 @@ export class DatasetLoaderService { }; } + /** + * Constructs a DatasetVariable object from raw dataset and raw dataset variable + * @param dataset raw dataset + * @param data raw dataset variable + * @returns parsed dataset variable + */ private parseRawDatasetVariable(dataset: RawDataset, data: RawDatasetVariable): DatasetVariable { return { dataset: dataset.name, diff --git a/src/app/core/services/distribution-data-loader/distribution-data-loader.service.ts b/apps/a2agc/src/app/core/services/distribution-data-loader/distribution-data-loader.service.ts similarity index 71% rename from src/app/core/services/distribution-data-loader/distribution-data-loader.service.ts rename to apps/a2agc/src/app/core/services/distribution-data-loader/distribution-data-loader.service.ts index 60843683..b738c7bf 100644 --- a/src/app/core/services/distribution-data-loader/distribution-data-loader.service.ts +++ b/apps/a2agc/src/app/core/services/distribution-data-loader/distribution-data-loader.service.ts @@ -12,18 +12,35 @@ export type TransformHandlerFn = (value: string) => unknown; export type TransformHandlers = Record; +/** + * Converts date strings to Date objects + * @param value date string + * @returns Date object + */ function castDate(value: string): Date | undefined { const date = new Date(value); return Number.isNaN(+date) ? undefined : date; } +/** + * Service for fetching distribution data from a specified URL + */ @Injectable({ providedIn: 'root' }) export class DistributionDataLoaderService { + /** + * Creates an instance of distribution data loader service. + * @param http Angular http client + */ constructor(private readonly http: HttpClient) { } + /** + * Retrieves data based on the provided DatasetVariable + * @param variable dataset variable + * @returns observable with data + */ load(variable: DatasetVariable): Observable { const { distribution: { url } } = variable; const handlers = this.getTransformHandlers(variable); @@ -44,12 +61,22 @@ export class DistributionDataLoaderService { ); } + /** + * Ensures that values are dynamically typed based on the presence of a value transformation. + * @param variable dataset variable + * @returns dynamic typing config + */ protected getDynamicTypingConfig(variable: DatasetVariable): ParseConfig['dynamicTyping'] { return { value: this.getValueTransform(variable) === undefined }; } + /** + * Transforms variables to desired format + * @param variable dataset variable + * @returns transformed handlers + */ protected getTransformHandlers(variable: DatasetVariable): TransformHandlers { return { period: castDate, @@ -58,6 +85,13 @@ export class DistributionDataLoaderService { }; } + /** + * Aggregates the distribution data based on the DatasetVariable type + * If variable type is 'DATE' it groups data by year, otherwise it returns the original result + * @param variable dataset variable + * @param result data + * @returns final aggregated result + */ protected aggregateResult( variable: DatasetVariable, result: DistributionDataEntry[] @@ -71,6 +105,11 @@ export class DistributionDataLoaderService { } } + /** + * Determines the transformation function for the value field based on the variable type. + * @param variable dataset variable + * @returns value transform function + */ private getValueTransform(variable: DatasetVariable): TransformHandlerFn | undefined { switch (variable.type) { case 'BOOLEAN': @@ -84,6 +123,11 @@ export class DistributionDataLoaderService { } } + /** + * Groups data by year for the DATE type. + * @param data data with Date type + * @returns aggregated data + */ private aggregateByYear( data: DistributionDataEntry[] ): DistributionDataEntry[] { diff --git a/src/app/core/services/theme/theme.module.ts b/apps/a2agc/src/app/core/services/theme/theme.module.ts similarity index 100% rename from src/app/core/services/theme/theme.module.ts rename to apps/a2agc/src/app/core/services/theme/theme.module.ts diff --git a/src/app/core/services/theme/theme.service.ts b/apps/a2agc/src/app/core/services/theme/theme.service.ts similarity index 68% rename from src/app/core/services/theme/theme.service.ts rename to apps/a2agc/src/app/core/services/theme/theme.service.ts index 6965bd71..4779c668 100644 --- a/src/app/core/services/theme/theme.service.ts +++ b/apps/a2agc/src/app/core/services/theme/theme.service.ts @@ -2,23 +2,41 @@ import { OverlayContainer } from '@angular/cdk/overlay'; import { ComponentRef, Inject, Injectable, InjectionToken, OnDestroy, Optional } from '@angular/core'; +/** + * Theme options + */ export interface ThemeOptions { + /** Theme name */ theme?: string; + /** Default theme */ default?: string; } +/** + * Theme options injection token + */ export const THEME_OPTIONS = new InjectionToken('Theme options'); +/** + * Service for managing themes + */ @Injectable({ providedIn: 'root' }) export class ThemeService implements OnDestroy { + /** Default theme */ readonly defaultTheme: string; + /** + * Gets current theme + */ get theme(): string { return this.currentTheme; } + /** + * Sets current theme + */ set theme(theme: string) { const newTheme = theme || this.defaultTheme; const oldTheme = this.currentTheme; @@ -26,9 +44,16 @@ export class ThemeService implements OnDestroy { this.switchTheme(this.elements, newTheme, oldTheme); } + /** Elements to be managed by the theme service */ private readonly elements: HTMLElement[] = []; + /** Current theme */ private currentTheme: string; + /** + * Creates an instance of theme service. + * @param options theme options + * @param overlay overlay container + */ constructor(@Inject(THEME_OPTIONS) options: ThemeOptions, @Optional() overlay: OverlayContainer | null, ) { @@ -41,6 +66,9 @@ export class ThemeService implements OnDestroy { } } + /** + * Removes all elements from service + */ ngOnDestroy(): void { // Make a copy of the array since it is modified during the loop const elements = [...this.elements]; @@ -49,6 +77,9 @@ export class ThemeService implements OnDestroy { } } + /** + * Adds an HTML element to the list of elements managed by the service + */ addBootstrapComponent(ref: ComponentRef): void { const el = ref.location.nativeElement as HTMLElement | null; if (el) { @@ -57,11 +88,19 @@ export class ThemeService implements OnDestroy { } } + /** + * Adds HTML element to the list of elements + * @param el element + */ addElement(el: HTMLElement): void { this.switchTheme([el], this.currentTheme, ''); this.elements.push(el); } + /** + * Removes HTML element to the list of elements + * @param el element + */ removeElement(el: HTMLElement): void { const index = this.elements.indexOf(el); if (index >= 0) { @@ -70,6 +109,12 @@ export class ThemeService implements OnDestroy { } } + /** + * Switches themes for certain elements + * @param elements elements to switch themes + * @param newTheme new theme + * @param oldTheme old theme + */ private switchTheme(elements: HTMLElement[], newTheme: string, oldTheme: string): void { this.addClass(elements, 'color-transitions-disabled'); this.removeClass(elements, oldTheme); @@ -77,6 +122,11 @@ export class ThemeService implements OnDestroy { setTimeout(() => this.removeClass(elements, 'color-transitions-disabled')); } + /** + * Adds CSS class to specified elements + * @param elements array of HTML elements + * @param klass class name to add + */ private addClass(elements: HTMLElement[], klass: string): void { if (klass) { for (const el of elements) { @@ -85,6 +135,11 @@ export class ThemeService implements OnDestroy { } } + /** + * Removes CSS class from specified elements + * @param elements array of HTML elements + * @param klass class name to remove + */ private removeClass(elements: HTMLElement[], klass: string): void { if (klass) { for (const el of elements) { diff --git a/src/app/core/state/data/data.state.ts b/apps/a2agc/src/app/core/state/data/data.state.ts similarity index 79% rename from src/app/core/state/data/data.state.ts rename to apps/a2agc/src/app/core/state/data/data.state.ts index bd37604f..066f3e09 100644 --- a/src/app/core/state/data/data.state.ts +++ b/apps/a2agc/src/app/core/state/data/data.state.ts @@ -12,6 +12,9 @@ import { DatasetsState } from './datasets.state'; export type DataStateModel = Record; +/** + * Data state, contains datasets state and dataset variables state + */ @StateRepository() @State({ name: 'data', @@ -22,6 +25,12 @@ export type DataStateModel = Record; }) @Injectable() export class DataState extends NgxsImmutableDataRepository { + /** + * Creates an instance of data state. + * @param datasetLoader dataset loader service + * @param datasetsState datasets state + * @param variablesState variables state + */ constructor( private readonly datasetLoader: DatasetLoaderService, private readonly datasetsState: DatasetsState, @@ -30,6 +39,9 @@ export class DataState extends NgxsImmutableDataRepository { super(); } + /** + * Loads datasets and variables on init + */ ngxsOnInit(): void { super.ngxsOnInit(); diff --git a/src/app/core/state/data/dataset-variables.state.ts b/apps/a2agc/src/app/core/state/data/dataset-variables.state.ts similarity index 71% rename from src/app/core/state/data/dataset-variables.state.ts rename to apps/a2agc/src/app/core/state/data/dataset-variables.state.ts index cf5fb449..dcf8042b 100644 --- a/src/app/core/state/data/dataset-variables.state.ts +++ b/apps/a2agc/src/app/core/state/data/dataset-variables.state.ts @@ -11,8 +11,13 @@ import { Dataset, DatasetMetaEntry, DatasetVariable } from '../../models/dataset import { DatasetsState } from './datasets.state'; +/** + * Defines the shape of the state for managing dataset variables + */ export interface DatasetVariablesStateModel extends EntityCollections { + /** Variable sublabel */ subLabel: string; + /** Sublabel flag */ subLabelFlag: string; } @@ -24,6 +29,9 @@ export enum DatasetVariableGroup { } +/** + * Dataset variables state + */ @StateRepository() @State({ name: 'datasetVariables', @@ -35,22 +43,53 @@ export enum DatasetVariableGroup { }) @Injectable() export class DatasetVariablesState extends NgxsDataEntityCollectionsRepository { + /** + * Observable returning subLabel property from state + */ @Computed() get subLabel$(): Observable { return this.state$.pipe(pluck('subLabel'), distinctUntilChanged()); } + /** + * Observable returning subLabelFlag property from state + */ @Computed() get subLabelFlag$(): Observable { return this.state$.pipe(pluck('subLabelFlag'), distinctUntilChanged()); } + /** + * Creates an instance of dataset variables state. + * @param datasetsState datasets state + */ constructor(private readonly datasetsState: DatasetsState) { super(); } + /** + * Selects id for a dataset variable + * @param variable dataset variable + * @returns id + */ selectId(variable: DatasetVariable): string; - selectId(dataset: string | Dataset, variable: string | DatasetVariable): string; + /** + * Selects id for a dataset variable + * @param dataset dataset, can be string or Dataset object + * @param variable + * @returns id + */ + selectId( + dataset: string | Dataset, + variable: string | DatasetVariable + ): string; + /** + * Selects id for a dataset variable + * If first object is type of Dataset, assumes second argument is the variable + * @param dataset dataset, can be string, Dataset or DatasetVariable + * @param variable variable + * @returns id + */ selectId( dataset: string | Dataset | DatasetVariable, variable?: string | DatasetVariable @@ -65,19 +104,40 @@ export class DatasetVariablesState extends NgxsDataEntityCollectionsRepository { return this.entities$.pipe(pluck(key), distinctUntilChanged()); } + /** + * Retrieves an observable array of DatasetVariable from dataset and group + * @param dataset dataset + * @param group dataset variable group + * @returns observable + */ getVariables(dataset?: string | Dataset, group?: DatasetVariableGroup): Observable { const selector = this.createVariableSelector(dataset, group); return this.entitiesArray$.pipe(map(variables => variables.filter(selector))); } + /** + * Returns an observable array of DatasetVariable for data belonging to the sub group + * @param dataset dataset + * @returns observable + */ getSubVariables(dataset?: string | Dataset): Observable { return this.getVariables(dataset, DatasetVariableGroup.sub); } + /** + * Returns an observable array of metadata entries based on a variable + * @param key variable id + * @returns observable + */ getMetadata(key: string): Observable { return this.getVariable(key).pipe( map(variable => { @@ -105,6 +165,12 @@ export class DatasetVariablesState extends NgxsDataEntityCollectionsRepository; +/** + * Dataset state + */ @StateRepository() @State({ name: 'datasets', @@ -17,6 +20,11 @@ export type DatasetsStateModel = EntityCollections; }) @Injectable() export class DatasetsState extends NgxsDataEntityCollectionsRepository { + /** + * Gets dataset id + * @param dataset dataset name or object + * @returns dataset id + */ selectId(dataset: string | Dataset): string { return typeof dataset === 'string' ? dataset : dataset.name; } diff --git a/src/app/core/state/page/page.state.ts b/apps/a2agc/src/app/core/state/page/page.state.ts similarity index 76% rename from src/app/core/state/page/page.state.ts rename to apps/a2agc/src/app/core/state/page/page.state.ts index 661ced3d..87d470aa 100644 --- a/src/app/core/state/page/page.state.ts +++ b/apps/a2agc/src/app/core/state/page/page.state.ts @@ -5,12 +5,20 @@ import { State } from '@ngxs/store'; import { Observable } from 'rxjs'; import { pluck } from 'rxjs/operators'; +/** + * Page state model + */ interface PageStateModel { + /** True if help modal has been shown on the page */ hasShownHelpModal: boolean; } +/** Local storage key for help popup shown */ const LOCAL_STORAGE_HELP_MODAL_KEY = 'HELP_POPUP_SHOWN'; +/** + * Page state + */ @StateRepository() @State({ name: 'page', @@ -20,17 +28,27 @@ const LOCAL_STORAGE_HELP_MODAL_KEY = 'HELP_POPUP_SHOWN'; }) @Injectable() export class PageState extends NgxsImmutableDataRepository { + /** + * Returns observable with help modal shown state + */ @Computed() get hasShownHelpModal$(): Observable { return this.state$.pipe(pluck('hasShownHelpModal')); } + /** + * Sets hasShownHelpModal from local storage + */ ngxsOnInit(): void { super.ngxsOnInit(); const hasShownHelpModal = localStorage.getItem(LOCAL_STORAGE_HELP_MODAL_KEY)?.toLowerCase() === 'true'; this.patchState({ hasShownHelpModal }); } + /** + * Sets hasShownHelpModal to value + * @param hasShownHelpModal boolean value + */ @DataAction() setHasShownHelpModal(hasShownHelpModal: boolean): void { localStorage.setItem(LOCAL_STORAGE_HELP_MODAL_KEY, hasShownHelpModal.toString()); diff --git a/src/app/core/state/router/router.state.ts b/apps/a2agc/src/app/core/state/router/router.state.ts similarity index 62% rename from src/app/core/state/router/router.state.ts rename to apps/a2agc/src/app/core/state/router/router.state.ts index 8de6b6de..17a865ad 100644 --- a/src/app/core/state/router/router.state.ts +++ b/apps/a2agc/src/app/core/state/router/router.state.ts @@ -8,26 +8,43 @@ import { Subject } from 'rxjs'; import { filter, map, takeUntil } from 'rxjs/operators'; +/** + * Manages routing state and provides observables for navigation start and end events + */ @StateRepository() @State({ name: 'routerFacade' }) @Injectable() export class RouterState extends NgxsImmutableDataRepository implements OnDestroy { + /** + * An RxJS Subject used to manage the component’s lifecycle. It emits a value when the component is destroyed. + */ readonly destroy$ = new Subject(); + /** + * An observable that listens for Angular router navigation start events. It filters out non-NavigationStart events, extracts the URL, and emits it. + */ readonly navigationStart$ = this.router.events.pipe( filter((ev): ev is NavigationStart => ev instanceof NavigationStart), map(ev => ev.url), takeUntil(this.destroy$) ); + /** + * An observable that listens for completed router navigation actions. It maps the event URL and emits it. + */ readonly navigationEnd$ = this.actions$.pipe( ofActionCompleted(RouterNavigation), map(ev => (ev.action as RouterNavigation).event.url), takeUntil(this.destroy$) ); + /** + * Initializes the RouterState class. + * @param actions$ provides access to dispatched actions in the Ngxs store + * @param router router service + */ constructor( private readonly actions$: Actions, private readonly router: Router @@ -35,6 +52,9 @@ export class RouterState extends NgxsImmutableDataRepository implements O super(); } + /** + * Cleans up resources when the component is destroyed + */ ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); diff --git a/src/app/core/state/state.module.ts b/apps/a2agc/src/app/core/state/state.module.ts similarity index 97% rename from src/app/core/state/state.module.ts rename to apps/a2agc/src/app/core/state/state.module.ts index a88a4494..955ae902 100644 --- a/src/app/core/state/state.module.ts +++ b/apps/a2agc/src/app/core/state/state.module.ts @@ -13,7 +13,7 @@ import { PageState } from './page/page.state'; import { RouterState } from './router/router.state'; import { VisualizationsState } from './visualizations/visualizations.state'; - +/** List of root states for the app */ const ROOT_STATES: Type[] = [ RouterState, PageState, diff --git a/src/app/core/state/visualizations/visualizations.state.ts b/apps/a2agc/src/app/core/state/visualizations/visualizations.state.ts similarity index 85% rename from src/app/core/state/visualizations/visualizations.state.ts rename to apps/a2agc/src/app/core/state/visualizations/visualizations.state.ts index 3aa8be2a..8f20b0cb 100644 --- a/src/app/core/state/visualizations/visualizations.state.ts +++ b/apps/a2agc/src/app/core/state/visualizations/visualizations.state.ts @@ -6,12 +6,18 @@ import { State } from '@ngxs/store'; import { Visualization, visualizations } from './visualizations'; +/** + * Visualizations state service + */ @StateRepository() @State({ name: 'visualizations' }) @Injectable() export class VisualizationsState extends NgxsDataEntityCollectionsRepository{ + /** + * Sets all visualization entities + */ ngxsOnInit(): void { this.setAll(visualizations); } diff --git a/src/app/core/state/visualizations/visualizations.ts b/apps/a2agc/src/app/core/state/visualizations/visualizations.ts similarity index 94% rename from src/app/core/state/visualizations/visualizations.ts rename to apps/a2agc/src/app/core/state/visualizations/visualizations.ts index 1f8280e7..66020440 100644 --- a/src/app/core/state/visualizations/visualizations.ts +++ b/apps/a2agc/src/app/core/state/visualizations/visualizations.ts @@ -9,18 +9,29 @@ import { import { VisualizationOneView } from './../../../shared/components/visualization-page/shared/visualization1-data-handler'; +/** + * Visualization info + */ export interface Visualization { + /** Visualization title */ title: string; + /** Visualization description */ description: string; + /** Path to Vega-lite spec */ spec: string; + /** Visualization options */ options: Options; + /** Path to readme content */ content: string; + /** Path to SQL */ sql: string; + /** Path to CSV */ csv: string; + /** Visualization id */ id: string; } - +/** Visualization metadata */ export const visualizations: Visualization[] = [ { id: 'vis1-geomap-of-opioid-deaths', diff --git a/src/app/package.json b/apps/a2agc/src/app/package.json similarity index 100% rename from src/app/package.json rename to apps/a2agc/src/app/package.json diff --git a/src/app/pages/about/about-routing.module.ts b/apps/a2agc/src/app/pages/about/about-routing.module.ts similarity index 100% rename from src/app/pages/about/about-routing.module.ts rename to apps/a2agc/src/app/pages/about/about-routing.module.ts diff --git a/src/app/pages/about/about.component.html b/apps/a2agc/src/app/pages/about/about.component.html similarity index 100% rename from src/app/pages/about/about.component.html rename to apps/a2agc/src/app/pages/about/about.component.html diff --git a/src/app/pages/about/about.component.scss b/apps/a2agc/src/app/pages/about/about.component.scss similarity index 100% rename from src/app/pages/about/about.component.scss rename to apps/a2agc/src/app/pages/about/about.component.scss diff --git a/src/app/pages/about/about.component.ts b/apps/a2agc/src/app/pages/about/about.component.ts similarity index 86% rename from src/app/pages/about/about.component.ts rename to apps/a2agc/src/app/pages/about/about.component.ts index beac42ed..b10be57c 100644 --- a/src/app/pages/about/about.component.ts +++ b/apps/a2agc/src/app/pages/about/about.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; +/** + * About info page + */ @Component({ selector: 'agc-about', templateUrl: './about.component.html', @@ -8,5 +11,6 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush }) export class AboutComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-about'; } diff --git a/src/app/pages/about/about.module.ts b/apps/a2agc/src/app/pages/about/about.module.ts similarity index 100% rename from src/app/pages/about/about.module.ts rename to apps/a2agc/src/app/pages/about/about.module.ts diff --git a/src/app/pages/about/about.theme.scss b/apps/a2agc/src/app/pages/about/about.theme.scss similarity index 100% rename from src/app/pages/about/about.theme.scss rename to apps/a2agc/src/app/pages/about/about.theme.scss diff --git a/src/app/pages/change-log/change-log-routing.module.ts b/apps/a2agc/src/app/pages/change-log/change-log-routing.module.ts similarity index 100% rename from src/app/pages/change-log/change-log-routing.module.ts rename to apps/a2agc/src/app/pages/change-log/change-log-routing.module.ts diff --git a/src/app/pages/change-log/change-log.component.html b/apps/a2agc/src/app/pages/change-log/change-log.component.html similarity index 100% rename from src/app/pages/change-log/change-log.component.html rename to apps/a2agc/src/app/pages/change-log/change-log.component.html diff --git a/src/app/pages/change-log/change-log.component.scss b/apps/a2agc/src/app/pages/change-log/change-log.component.scss similarity index 100% rename from src/app/pages/change-log/change-log.component.scss rename to apps/a2agc/src/app/pages/change-log/change-log.component.scss diff --git a/src/app/pages/change-log/change-log.component.ts b/apps/a2agc/src/app/pages/change-log/change-log.component.ts similarity index 87% rename from src/app/pages/change-log/change-log.component.ts rename to apps/a2agc/src/app/pages/change-log/change-log.component.ts index 397499a4..36edcda2 100644 --- a/src/app/pages/change-log/change-log.component.ts +++ b/apps/a2agc/src/app/pages/change-log/change-log.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; +/** + * Changelog page + */ @Component({ selector: 'agc-change-log', templateUrl: './change-log.component.html', @@ -8,5 +11,6 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush }) export class ChangeLogComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'change-log'; } diff --git a/src/app/pages/change-log/change-log.module.ts b/apps/a2agc/src/app/pages/change-log/change-log.module.ts similarity index 100% rename from src/app/pages/change-log/change-log.module.ts rename to apps/a2agc/src/app/pages/change-log/change-log.module.ts diff --git a/src/app/pages/change-log/change-log.theme.scss b/apps/a2agc/src/app/pages/change-log/change-log.theme.scss similarity index 100% rename from src/app/pages/change-log/change-log.theme.scss rename to apps/a2agc/src/app/pages/change-log/change-log.theme.scss diff --git a/src/app/pages/data-distributions/data-distributions-routing.module.ts b/apps/a2agc/src/app/pages/data-distributions/data-distributions-routing.module.ts similarity index 100% rename from src/app/pages/data-distributions/data-distributions-routing.module.ts rename to apps/a2agc/src/app/pages/data-distributions/data-distributions-routing.module.ts diff --git a/src/app/pages/data-distributions/data-distributions.component.html b/apps/a2agc/src/app/pages/data-distributions/data-distributions.component.html similarity index 100% rename from src/app/pages/data-distributions/data-distributions.component.html rename to apps/a2agc/src/app/pages/data-distributions/data-distributions.component.html diff --git a/src/app/pages/data-distributions/data-distributions.component.scss b/apps/a2agc/src/app/pages/data-distributions/data-distributions.component.scss similarity index 100% rename from src/app/pages/data-distributions/data-distributions.component.scss rename to apps/a2agc/src/app/pages/data-distributions/data-distributions.component.scss diff --git a/src/app/pages/data-distributions/data-distributions.component.ts b/apps/a2agc/src/app/pages/data-distributions/data-distributions.component.ts similarity index 81% rename from src/app/pages/data-distributions/data-distributions.component.ts rename to apps/a2agc/src/app/pages/data-distributions/data-distributions.component.ts index 20a1724b..952e4a46 100644 --- a/src/app/pages/data-distributions/data-distributions.component.ts +++ b/apps/a2agc/src/app/pages/data-distributions/data-distributions.component.ts @@ -17,6 +17,9 @@ import { ChartFactoryService } from '../../shared/vega-charts/chart-factory.serv import { SpecVisualizationEntry, VisualizationEntry, VisualizationsManagerService } from './services/visualizations-manager.service'; +/** + * Component for data distributions page + */ @Component({ selector: 'agc-data-distributions', templateUrl: './data-distributions.component.html', @@ -28,24 +31,45 @@ export class DataDistributionsComponent { /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-data-distributions'; + /** Autosize config for visualization */ readonly autosize: Autosize = { width: true, height: false }; + /** Datasets observable */ readonly datasets$: Observable; + /** Variables observable */ readonly variables$: Observable; + /** Subselector label observable */ readonly subLabel$: Observable; + /** Subvariables observable */ readonly subVariables$: Observable; + /** Currently selected dataset */ selectedDataset?: Dataset; + /** Currently selected variables */ selectedVariables: DatasetVariable[] = []; + /** Visualization spec with filtered values */ filterSpec?: VisualizationSpec; + /** Time filter source observable */ filterSource$: Observable; + /** If filter is being applied */ filterActive = false; + /** Filter source observables */ private readonly filterSourceObservables$ = new ReplaySubject>(1); + /** Variable observables */ private readonly variableObservables$ = new ReplaySubject>(1); + /** Subvariable observables */ private readonly subVariableObservables$ = new ReplaySubject>(1); + /** + * Creates an instance of data distributions component. + * @param datasetsState datasets state + * @param variablesState dataset variables state + * @param loader distribution data loader service + * @param chartFactory chart factory service + * @param cdr change detection + */ constructor( datasetsState: DatasetsState, private readonly variablesState: DatasetVariablesState, @@ -62,10 +86,17 @@ export class DataDistributionsComponent { this.filterSource$ = this.createFilterSource(); } + /** + * Determines whether visualization entry has a spec property + */ hasSpec(this: void, entry: VisualizationEntry): entry is SpecVisualizationEntry { return 'spec' in entry && entry.spec !== undefined; } + /** + * Updates selected dataset + * @param dataset selected dataset + */ setSelectedDataset(dataset: Dataset): void { if (dataset !== this.selectedDataset) { this.selectedDataset = dataset; @@ -81,6 +112,10 @@ export class DataDistributionsComponent { } } + /** + * Updates selected variables + * @param variable selected variable + */ setSelectedVariable(variable: DatasetVariable): void { const { selectedDataset, selectedVariables } = this; const alreadySelected = selectedVariables.length === 1 && selectedVariables[0] === variable; @@ -90,6 +125,9 @@ export class DataDistributionsComponent { } } + /** + * Selects all variables in the list + */ setSelectAllVariables(): void { const { selectedDataset } = this; if (selectedDataset) { @@ -102,6 +140,10 @@ export class DataDistributionsComponent { } } + /** + * Attaches view filtered by period + * @param view view + */ attachFilterView(view: View): void { const events$ = fromEventPattern<[string, { period: TimeFilter }]>( handler => view.addSignalListener('period', handler), @@ -112,6 +154,9 @@ export class DataDistributionsComponent { this.filterSourceObservables$.next(source$); } + /** + * Loads the time slider filter spec + */ private loadFilterSpec(): void { const { variablesState, loader, chartFactory, cdr } = this; const vid = variablesState.selectId(...DATA_CONFIG.timeSliderSource); @@ -128,6 +173,9 @@ export class DataDistributionsComponent { }); } + /** + * Creates a filter source observable + */ private createFilterSource(): Observable { const { filterSourceObservables$, cdr } = this; const sources$ = using(() => { diff --git a/src/app/pages/data-distributions/data-distributions.module.ts b/apps/a2agc/src/app/pages/data-distributions/data-distributions.module.ts similarity index 100% rename from src/app/pages/data-distributions/data-distributions.module.ts rename to apps/a2agc/src/app/pages/data-distributions/data-distributions.module.ts diff --git a/src/app/pages/data-distributions/data-distributions.theme.scss b/apps/a2agc/src/app/pages/data-distributions/data-distributions.theme.scss similarity index 100% rename from src/app/pages/data-distributions/data-distributions.theme.scss rename to apps/a2agc/src/app/pages/data-distributions/data-distributions.theme.scss diff --git a/src/app/pages/data-distributions/services/visualizations-manager.service.ts b/apps/a2agc/src/app/pages/data-distributions/services/visualizations-manager.service.ts similarity index 72% rename from src/app/pages/data-distributions/services/visualizations-manager.service.ts rename to apps/a2agc/src/app/pages/data-distributions/services/visualizations-manager.service.ts index 0907a097..d88b27ca 100644 --- a/src/app/pages/data-distributions/services/visualizations-manager.service.ts +++ b/apps/a2agc/src/app/pages/data-distributions/services/visualizations-manager.service.ts @@ -12,13 +12,23 @@ import { DatasetVariablesState } from '../../../core/state/data/dataset-variable import { ChartFactoryService } from '../../../shared/vega-charts/chart-factory.service'; +/** + * Base visualization entry interface + */ export interface BaseVisualizationEntry { + /** Dataset variable */ variable: DatasetVariable; + /** Metadata observable */ metadata: Observable; } +/** + * Spec visualization entry interface + */ export interface SpecVisualizationEntry extends BaseVisualizationEntry { + /** Vega lite spec */ spec: VisualizationSpec; + /** Dataset observable */ data: Observable; } @@ -26,12 +36,23 @@ export type DataFilter = [number, number] | undefined; export type VisualizationEntry = BaseVisualizationEntry | SpecVisualizationEntry; +/** + * Visualizations manager service + */ @Injectable() export class VisualizationsManagerService { + /** All visualizations */ visualizations: VisualizationEntry[] = []; + /** Filter sources */ private readonly filterSources$ = new ReplaySubject>(1); + /** + * Creates an instance of visualizations manager service. + * @param chartFactory chart factory service + * @param dataLoader distribution data loader service + * @param variableStore dataset variables store + */ constructor( private readonly chartFactory: ChartFactoryService, private readonly dataLoader: DistributionDataLoaderService, @@ -40,6 +61,10 @@ export class VisualizationsManagerService { this.filterSources$.next(of(undefined)); } + /** + * Sets the visualizations property based on the provided dataset variables + * @param variables dataset variables + */ setVariables(variables: DatasetVariable[]): void { this.visualizations = variables.map(variable => { const key = this.variableStore.selectId(variable); @@ -51,10 +76,19 @@ export class VisualizationsManagerService { }); } + /** + * Sets the filter source for data filtering. + * @param source$ observable + */ setFilterSource(source$: ObservableInput): void { this.filterSources$.next(source$); } + /** + * Creates a data source for a given variable by combining data and filter observables. + * @param variable dataset variable + * @returns observable + */ private createDataSource(variable: DatasetVariable): Observable { const filter$ = this.filterSources$.pipe(switchAll(), startWith(undefined)); const data$ = this.dataLoader.load(variable); @@ -62,6 +96,12 @@ export class VisualizationsManagerService { return latest$.pipe(map(([data, filter]) => this.filterData(data, filter))); } + /** + * Filters data based on the specified time frame. + * @param data dataset entries + * @param filter time frame to filter + * @returns data updated dataset entries + */ private filterData(data: DistributionDataEntry[], filter: DataFilter): DistributionDataEntry[] { if (filter === undefined) { return data; diff --git a/src/app/pages/data-er-diagram/data-er-diagram-routing.module.ts b/apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram-routing.module.ts similarity index 100% rename from src/app/pages/data-er-diagram/data-er-diagram-routing.module.ts rename to apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram-routing.module.ts diff --git a/src/app/pages/data-er-diagram/data-er-diagram.component.html b/apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.component.html similarity index 100% rename from src/app/pages/data-er-diagram/data-er-diagram.component.html rename to apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.component.html diff --git a/src/app/pages/data-er-diagram/data-er-diagram.component.scss b/apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.component.scss similarity index 100% rename from src/app/pages/data-er-diagram/data-er-diagram.component.scss rename to apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.component.scss diff --git a/src/app/pages/data-er-diagram/data-er-diagram.component.ts b/apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.component.ts similarity index 83% rename from src/app/pages/data-er-diagram/data-er-diagram.component.ts rename to apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.component.ts index 01ba1dff..60319e6d 100644 --- a/src/app/pages/data-er-diagram/data-er-diagram.component.ts +++ b/apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; +/** + * Data entity-relationship diagram page + */ @Component({ selector: 'agc-data-er-diagram', templateUrl: './data-er-diagram.component.html', @@ -8,5 +11,6 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush }) export class DataErDiagramComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'data-er-diagram'; } diff --git a/src/app/pages/data-er-diagram/data-er-diagram.module.ts b/apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.module.ts similarity index 100% rename from src/app/pages/data-er-diagram/data-er-diagram.module.ts rename to apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.module.ts diff --git a/src/app/pages/data-er-diagram/data-er-diagram.theme.scss b/apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.theme.scss similarity index 100% rename from src/app/pages/data-er-diagram/data-er-diagram.theme.scss rename to apps/a2agc/src/app/pages/data-er-diagram/data-er-diagram.theme.scss diff --git a/src/app/pages/data-schema-browser/data-schema-browser-routing.module.ts b/apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser-routing.module.ts similarity index 100% rename from src/app/pages/data-schema-browser/data-schema-browser-routing.module.ts rename to apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser-routing.module.ts diff --git a/src/app/pages/data-schema-browser/data-schema-browser.component.html b/apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.component.html similarity index 100% rename from src/app/pages/data-schema-browser/data-schema-browser.component.html rename to apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.component.html diff --git a/src/app/pages/data-schema-browser/data-schema-browser.component.scss b/apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.component.scss similarity index 100% rename from src/app/pages/data-schema-browser/data-schema-browser.component.scss rename to apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.component.scss diff --git a/src/app/pages/data-schema-browser/data-schema-browser.component.ts b/apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.component.ts similarity index 86% rename from src/app/pages/data-schema-browser/data-schema-browser.component.ts rename to apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.component.ts index afd19894..6bfcbf0e 100644 --- a/src/app/pages/data-schema-browser/data-schema-browser.component.ts +++ b/apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; +/** + * Schema browser component + */ @Component({ selector: 'agc-data-schema-browser', templateUrl: './data-schema-browser.component.html', @@ -8,5 +11,6 @@ import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush }) export class DataSchemaBrowserComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'data-schema-browser'; } diff --git a/src/app/pages/data-schema-browser/data-schema-browser.module.ts b/apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.module.ts similarity index 100% rename from src/app/pages/data-schema-browser/data-schema-browser.module.ts rename to apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.module.ts diff --git a/src/app/pages/data-schema-browser/data-schema-browser.theme.scss b/apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.theme.scss similarity index 100% rename from src/app/pages/data-schema-browser/data-schema-browser.theme.scss rename to apps/a2agc/src/app/pages/data-schema-browser/data-schema-browser.theme.scss diff --git a/src/app/pages/pages.theme.scss b/apps/a2agc/src/app/pages/pages.theme.scss similarity index 100% rename from src/app/pages/pages.theme.scss rename to apps/a2agc/src/app/pages/pages.theme.scss diff --git a/src/app/pages/visualization/visualization-data-resolver.ts b/apps/a2agc/src/app/pages/visualization/visualization-data-resolver.ts similarity index 65% rename from src/app/pages/visualization/visualization-data-resolver.ts rename to apps/a2agc/src/app/pages/visualization/visualization-data-resolver.ts index 0fab1e29..3d3aeaf4 100644 --- a/src/app/pages/visualization/visualization-data-resolver.ts +++ b/apps/a2agc/src/app/pages/visualization/visualization-data-resolver.ts @@ -2,15 +2,27 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot } from '@angular/router'; import { EMPTY, Observable } from 'rxjs'; import { pluck, take } from 'rxjs/operators'; -import { Visualization } from 'src/app/core/state/visualizations/visualizations'; +import { Visualization } from '../../core/state/visualizations/visualizations'; import { VisualizationsState } from '../../core/state/visualizations/visualizations.state'; +/** + * Visualization data resolver + */ @Injectable({ providedIn: 'root' }) export class VisualizationDataResolver { + /** + * Creates an instance of visualization data resolver. + * @param service visualizations state service + */ constructor(private readonly service: VisualizationsState) { } + /** + * Resolves routes and returns observable + * @param route Route information + * @returns observable + */ resolve(route: ActivatedRouteSnapshot): Observable { const id = route.paramMap.get('id'); if (id === null) { diff --git a/src/app/pages/visualization/visualization-routing.module.ts b/apps/a2agc/src/app/pages/visualization/visualization-routing.module.ts similarity index 100% rename from src/app/pages/visualization/visualization-routing.module.ts rename to apps/a2agc/src/app/pages/visualization/visualization-routing.module.ts diff --git a/src/app/pages/visualization/visualization.component.html b/apps/a2agc/src/app/pages/visualization/visualization.component.html similarity index 100% rename from src/app/pages/visualization/visualization.component.html rename to apps/a2agc/src/app/pages/visualization/visualization.component.html diff --git a/src/app/pages/visualization/visualization.component.scss b/apps/a2agc/src/app/pages/visualization/visualization.component.scss similarity index 100% rename from src/app/pages/visualization/visualization.component.scss rename to apps/a2agc/src/app/pages/visualization/visualization.component.scss diff --git a/src/app/pages/visualization/visualization.component.ts b/apps/a2agc/src/app/pages/visualization/visualization.component.ts similarity index 70% rename from src/app/pages/visualization/visualization.component.ts rename to apps/a2agc/src/app/pages/visualization/visualization.component.ts index c9241322..6f4fb125 100644 --- a/src/app/pages/visualization/visualization.component.ts +++ b/apps/a2agc/src/app/pages/visualization/visualization.component.ts @@ -1,9 +1,12 @@ import { Component, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; -import { Visualization } from 'src/app/core/state/visualizations/visualizations'; +import { Visualization } from '../../core/state/visualizations/visualizations'; +/** + * Visualization component + */ // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: 'agc-visualization', @@ -11,10 +14,15 @@ import { Visualization } from 'src/app/core/state/visualizations/visualizations' styleUrls: ['./visualization.component.scss'] }) export class VisualizationComponent implements OnDestroy { + /** Visualization metadata */ visualization?: Visualization; + /** Subscriptions */ private readonly subscriptions = new Subscription(); + /** + * Creates an instance of visualization component and subscribes to data from route + */ constructor(route: ActivatedRoute) { const sub = route.data.subscribe(data => { this.visualization = data.visualization; @@ -22,6 +30,9 @@ export class VisualizationComponent implements OnDestroy { this.subscriptions.add(sub); } + /** + * Unsubscribes to all subscriptions on destroy + */ ngOnDestroy(): void { this.subscriptions.unsubscribe(); } diff --git a/src/app/pages/visualization/visualization.module.ts b/apps/a2agc/src/app/pages/visualization/visualization.module.ts similarity index 100% rename from src/app/pages/visualization/visualization.module.ts rename to apps/a2agc/src/app/pages/visualization/visualization.module.ts diff --git a/src/app/shared/components/dataset-summary/dataset-summary.component.html b/apps/a2agc/src/app/shared/components/dataset-summary/dataset-summary.component.html similarity index 100% rename from src/app/shared/components/dataset-summary/dataset-summary.component.html rename to apps/a2agc/src/app/shared/components/dataset-summary/dataset-summary.component.html diff --git a/src/app/shared/components/dataset-summary/dataset-summary.component.scss b/apps/a2agc/src/app/shared/components/dataset-summary/dataset-summary.component.scss similarity index 100% rename from src/app/shared/components/dataset-summary/dataset-summary.component.scss rename to apps/a2agc/src/app/shared/components/dataset-summary/dataset-summary.component.scss diff --git a/src/app/shared/components/dataset-summary/dataset-summary.component.ts b/apps/a2agc/src/app/shared/components/dataset-summary/dataset-summary.component.ts similarity index 81% rename from src/app/shared/components/dataset-summary/dataset-summary.component.ts rename to apps/a2agc/src/app/shared/components/dataset-summary/dataset-summary.component.ts index 3a00c5a2..88b8b56f 100644 --- a/src/app/shared/components/dataset-summary/dataset-summary.component.ts +++ b/apps/a2agc/src/app/shared/components/dataset-summary/dataset-summary.component.ts @@ -3,6 +3,9 @@ import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular import { DatasetMetaEntry } from '../../../core/models/dataset.model'; +/** + * Summary of dataset info + */ @Component({ selector: 'agc-dataset-summary', templateUrl: './dataset-summary.component.html', @@ -10,8 +13,11 @@ import { DatasetMetaEntry } from '../../../core/models/dataset.model'; changeDetection: ChangeDetectionStrategy.OnPush }) export class DatasetSummaryComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'dataset-summary'; + /** Dataset summary entries */ @Input() summary: DatasetMetaEntry[] | null | undefined = []; + /** Name of dataset variable */ @Input() title: string | null = ''; } diff --git a/src/app/shared/components/dataset-summary/dataset-summary.module.ts b/apps/a2agc/src/app/shared/components/dataset-summary/dataset-summary.module.ts similarity index 100% rename from src/app/shared/components/dataset-summary/dataset-summary.module.ts rename to apps/a2agc/src/app/shared/components/dataset-summary/dataset-summary.module.ts diff --git a/src/app/shared/components/help-modal/help-modal.component.html b/apps/a2agc/src/app/shared/components/help-modal/help-modal.component.html similarity index 97% rename from src/app/shared/components/help-modal/help-modal.component.html rename to apps/a2agc/src/app/shared/components/help-modal/help-modal.component.html index 57722b7f..7a3329d0 100644 --- a/src/app/shared/components/help-modal/help-modal.component.html +++ b/apps/a2agc/src/app/shared/components/help-modal/help-modal.component.html @@ -1,4 +1,4 @@ -
+
clear diff --git a/src/app/shared/components/help-modal/help-modal.component.scss b/apps/a2agc/src/app/shared/components/help-modal/help-modal.component.scss similarity index 100% rename from src/app/shared/components/help-modal/help-modal.component.scss rename to apps/a2agc/src/app/shared/components/help-modal/help-modal.component.scss diff --git a/src/app/shared/components/help-modal/help-modal.component.ts b/apps/a2agc/src/app/shared/components/help-modal/help-modal.component.ts similarity index 51% rename from src/app/shared/components/help-modal/help-modal.component.ts rename to apps/a2agc/src/app/shared/components/help-modal/help-modal.component.ts index 196415f7..2c4b77f8 100644 --- a/src/app/shared/components/help-modal/help-modal.component.ts +++ b/apps/a2agc/src/app/shared/components/help-modal/help-modal.component.ts @@ -1,26 +1,26 @@ -import { AfterViewInit, Component } from '@angular/core'; +import { Component } from '@angular/core'; import { MatDialogRef } from '@angular/material/dialog'; +/** + * Component containing help dialog for visualizations +*/ // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: 'agc-help-modal', templateUrl: './help-modal.component.html', styleUrls: ['./help-modal.component.scss'] }) -export class HelpModalComponent implements AfterViewInit { - // Workaround for angular component issue #13870 - disableAnimation = true; - +export class HelpModalComponent { + /** + * Creates an instance of help modal component. + * @param dialogRef Help modal dialog reference + */ constructor(public dialogRef: MatDialogRef) { } - ngAfterViewInit(): void { - // timeout required to avoid 'ExpressionChangedAfterItHasBeenCheckedError' - setTimeout(() => { - this.disableAnimation = false; - }); - } - + /** + * Closes help dialog + */ close(): void { this.dialogRef.close(); } diff --git a/src/app/shared/components/help-modal/help-modal.module.ts b/apps/a2agc/src/app/shared/components/help-modal/help-modal.module.ts similarity index 100% rename from src/app/shared/components/help-modal/help-modal.module.ts rename to apps/a2agc/src/app/shared/components/help-modal/help-modal.module.ts diff --git a/src/app/shared/components/help-tour-modal/help-tour-modal.component.html b/apps/a2agc/src/app/shared/components/help-tour-modal/help-tour-modal.component.html similarity index 100% rename from src/app/shared/components/help-tour-modal/help-tour-modal.component.html rename to apps/a2agc/src/app/shared/components/help-tour-modal/help-tour-modal.component.html diff --git a/src/app/shared/components/help-tour-modal/help-tour-modal.component.scss b/apps/a2agc/src/app/shared/components/help-tour-modal/help-tour-modal.component.scss similarity index 100% rename from src/app/shared/components/help-tour-modal/help-tour-modal.component.scss rename to apps/a2agc/src/app/shared/components/help-tour-modal/help-tour-modal.component.scss diff --git a/src/app/shared/components/help-tour-modal/help-tour-modal.component.ts b/apps/a2agc/src/app/shared/components/help-tour-modal/help-tour-modal.component.ts similarity index 79% rename from src/app/shared/components/help-tour-modal/help-tour-modal.component.ts rename to apps/a2agc/src/app/shared/components/help-tour-modal/help-tour-modal.component.ts index 46ca6d70..f657dae9 100644 --- a/src/app/shared/components/help-tour-modal/help-tour-modal.component.ts +++ b/apps/a2agc/src/app/shared/components/help-tour-modal/help-tour-modal.component.ts @@ -2,12 +2,21 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { MatDialogRef } from '@angular/material/dialog'; +/** + * Slide info + */ interface TourSlide { + /** Slide title */ title: string; + /** Slide text */ text: string; + /** Slide image src */ img: string; } +/** + * Help tour modal component + */ @Component({ selector: 'agc-help-tour-modal', templateUrl: './help-tour-modal.component.html', @@ -15,6 +24,7 @@ interface TourSlide { changeDetection: ChangeDetectionStrategy.OnPush }) export class HelpTourModalComponent { + /** Slides to show */ slides: TourSlide[] = [ { title: 'Select year range...', @@ -38,14 +48,25 @@ export class HelpTourModalComponent { } ]; + /** Current slide shown */ currentSlide = 0; + /** + * Creates an instance of help tour modal component. + * @param dialogRef Mat dialog reference + */ constructor(public dialogRef: MatDialogRef) { } + /** + * Closes help tour dialog + */ close(): void { this.dialogRef.close(); } + /** + * Moves to next slide + */ incrementSlide(): void { if (this.currentSlide === (this.slides.length - 1)) { return; @@ -54,6 +75,9 @@ export class HelpTourModalComponent { this.currentSlide = this.currentSlide + 1; } + /** + * Moves to previous slide + */ decrementSlide(): void { if (this.currentSlide === 0) { return; @@ -62,6 +86,10 @@ export class HelpTourModalComponent { this.currentSlide = this.currentSlide - 1; } + /** + * Moves to specified slide + * @param index slide index + */ setSlide(index: number): void { if (index < 0 || index >= this.slides.length) { return; diff --git a/src/app/shared/components/help-tour-modal/help-tour-modal.module.ts b/apps/a2agc/src/app/shared/components/help-tour-modal/help-tour-modal.module.ts similarity index 100% rename from src/app/shared/components/help-tour-modal/help-tour-modal.module.ts rename to apps/a2agc/src/app/shared/components/help-tour-modal/help-tour-modal.module.ts diff --git a/src/app/shared/components/markdown-modal/markdown-modal.component.html b/apps/a2agc/src/app/shared/components/markdown-modal/markdown-modal.component.html similarity index 100% rename from src/app/shared/components/markdown-modal/markdown-modal.component.html rename to apps/a2agc/src/app/shared/components/markdown-modal/markdown-modal.component.html diff --git a/src/app/shared/components/markdown-modal/markdown-modal.component.scss b/apps/a2agc/src/app/shared/components/markdown-modal/markdown-modal.component.scss similarity index 100% rename from src/app/shared/components/markdown-modal/markdown-modal.component.scss rename to apps/a2agc/src/app/shared/components/markdown-modal/markdown-modal.component.scss diff --git a/src/app/shared/components/markdown-modal/markdown-modal.component.ts b/apps/a2agc/src/app/shared/components/markdown-modal/markdown-modal.component.ts similarity index 68% rename from src/app/shared/components/markdown-modal/markdown-modal.component.ts rename to apps/a2agc/src/app/shared/components/markdown-modal/markdown-modal.component.ts index a18e33fb..7b2ec195 100644 --- a/src/app/shared/components/markdown-modal/markdown-modal.component.ts +++ b/apps/a2agc/src/app/shared/components/markdown-modal/markdown-modal.component.ts @@ -2,11 +2,19 @@ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +/** + * Markdown modal data + */ export interface MarkdownModalData { + /** Markdown title */ title: string; + /** Path to markdown file */ src: string; } +/** + * Markdown modal component + */ @Component({ selector: 'agc-markdown-modal', templateUrl: './markdown-modal.component.html', @@ -14,6 +22,10 @@ export interface MarkdownModalData { changeDetection: ChangeDetectionStrategy.OnPush }) export class MarkdownModalComponent { + /** + * Creates an instance of markdown modal component and injects markdown data + * @param data markdown modal data + */ constructor( @Inject(MAT_DIALOG_DATA) readonly data: MarkdownModalData ) { } diff --git a/src/app/shared/components/markdown-modal/markdown-modal.module.ts b/apps/a2agc/src/app/shared/components/markdown-modal/markdown-modal.module.ts similarity index 100% rename from src/app/shared/components/markdown-modal/markdown-modal.module.ts rename to apps/a2agc/src/app/shared/components/markdown-modal/markdown-modal.module.ts diff --git a/src/app/shared/components/menu-icon/menu-icon.component.html b/apps/a2agc/src/app/shared/components/menu-icon/menu-icon.component.html similarity index 100% rename from src/app/shared/components/menu-icon/menu-icon.component.html rename to apps/a2agc/src/app/shared/components/menu-icon/menu-icon.component.html diff --git a/src/app/shared/components/menu-icon/menu-icon.component.scss b/apps/a2agc/src/app/shared/components/menu-icon/menu-icon.component.scss similarity index 100% rename from src/app/shared/components/menu-icon/menu-icon.component.scss rename to apps/a2agc/src/app/shared/components/menu-icon/menu-icon.component.scss diff --git a/src/app/shared/components/menu-icon/menu-icon.component.ts b/apps/a2agc/src/app/shared/components/menu-icon/menu-icon.component.ts similarity index 80% rename from src/app/shared/components/menu-icon/menu-icon.component.ts rename to apps/a2agc/src/app/shared/components/menu-icon/menu-icon.component.ts index 2822e39d..f1f1fec2 100644 --- a/src/app/shared/components/menu-icon/menu-icon.component.ts +++ b/apps/a2agc/src/app/shared/components/menu-icon/menu-icon.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; +/** + * Component for menu icon in header + */ @Component({ selector: 'agc-menu-icon', templateUrl: './menu-icon.component.html', @@ -8,7 +11,9 @@ import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular changeDetection: ChangeDetectionStrategy.OnPush }) export class MenuIconComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-menu-icon'; + /** Use alternate icon */ @Input() alternateIcon = false; } diff --git a/src/app/shared/components/menu-icon/menu-icon.module.ts b/apps/a2agc/src/app/shared/components/menu-icon/menu-icon.module.ts similarity index 100% rename from src/app/shared/components/menu-icon/menu-icon.module.ts rename to apps/a2agc/src/app/shared/components/menu-icon/menu-icon.module.ts diff --git a/src/app/shared/components/menu-icon/menu-icon.theme.scss b/apps/a2agc/src/app/shared/components/menu-icon/menu-icon.theme.scss similarity index 100% rename from src/app/shared/components/menu-icon/menu-icon.theme.scss rename to apps/a2agc/src/app/shared/components/menu-icon/menu-icon.theme.scss diff --git a/src/app/shared/components/sub-selector/sub-selector.component.html b/apps/a2agc/src/app/shared/components/sub-selector/sub-selector.component.html similarity index 100% rename from src/app/shared/components/sub-selector/sub-selector.component.html rename to apps/a2agc/src/app/shared/components/sub-selector/sub-selector.component.html diff --git a/src/app/shared/components/sub-selector/sub-selector.component.scss b/apps/a2agc/src/app/shared/components/sub-selector/sub-selector.component.scss similarity index 100% rename from src/app/shared/components/sub-selector/sub-selector.component.scss rename to apps/a2agc/src/app/shared/components/sub-selector/sub-selector.component.scss diff --git a/src/app/shared/components/sub-selector/sub-selector.component.ts b/apps/a2agc/src/app/shared/components/sub-selector/sub-selector.component.ts similarity index 62% rename from src/app/shared/components/sub-selector/sub-selector.component.ts rename to apps/a2agc/src/app/shared/components/sub-selector/sub-selector.component.ts index f2eb2604..ad3168b0 100644 --- a/src/app/shared/components/sub-selector/sub-selector.component.ts +++ b/apps/a2agc/src/app/shared/components/sub-selector/sub-selector.component.ts @@ -3,6 +3,9 @@ import { } from '@angular/core'; +/** + * Data distributions sub selector component + */ @Component({ selector: 'agc-sub-selector', templateUrl: './sub-selector.component.html', @@ -10,31 +13,50 @@ import { changeDetection: ChangeDetectionStrategy.OnPush }) export class SubSelectorComponent implements OnInit, OnChanges { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-sub-selector'; + /** Dataset variable form label */ @Input() label = ''; + /** Dataset variable selection */ @Input() selection = ''; + /** Dataset variable options */ @Input() options: string[] = []; + /** Variable suboptions header label */ @Input() subLabel = ''; + /** Variable suboptions list */ @Input() subOptions: string[] = []; + /** Emits variable selection change */ @Output() readonly selectionChange = new EventEmitter(); + /** Show the variable selection menu */ showMenu = false; + /** Filters for variable suboptions that start with specified letter */ subOptionFilter = 'A'; + /** Array containing letters of the alphabet */ readonly LETTERS: string[] = [...Array(26)].map((_val, i) => String.fromCharCode(i + 65)); + /** + * Sets suboptions filter on init + */ ngOnInit(): void { if (this.subOptions.length > 0) { this.subOptionFilter = this.subOptions[0].charAt(0); } } + /** + * Closes the menu if variable selection is changed + */ ngOnChanges(changes: SimpleChanges): void { if ('selection' in changes) { this.showMenu = false; } } + /** + * Gets all dataset variable options and suboptions + */ get allOptions(): string[] { if (this.subOptions.length < 1) { return this.options; @@ -43,10 +65,16 @@ export class SubSelectorComponent implements OnInit, OnChanges { return this.options.concat(this.subOptions); } + /** + * Returns true if dataset variable options exist + */ get enabled(): boolean { return this.options.length > 0; } + /** + * Shows menu if options exist, otherwise hides menu + */ toggleMenu(): void { if (!this.options) { this.showMenu = false; @@ -57,6 +85,10 @@ export class SubSelectorComponent implements OnInit, OnChanges { } } + /** + * Changes variable selection and emits value + * @param selection selected variable + */ changeSelection(selection: string): void { if (selection === this.selection) { this.selection = ''; @@ -68,6 +100,10 @@ export class SubSelectorComponent implements OnInit, OnChanges { this.selectionChange.emit(this.selection); } + /** + * Filters suboptions by first letter specified by subOptionFilter + * @returns filtered sub options + */ getFilteredSubOptions(): string[] { if (this.subOptionFilter === '') { return this.subOptions; @@ -76,13 +112,18 @@ export class SubSelectorComponent implements OnInit, OnChanges { return this.subOptions.filter(option => option.charAt(0).toLowerCase() === this.subOptionFilter.toLowerCase()); } - validSubOption(subOption: string): boolean { + /** + * Checks if suboptions list includes items that start with a letter + * @param letter letter of alphabet + * @returns true if there are suboptions that begin with the letter + */ + validSubOption(letter: string): boolean { if (!this.subOptions) { return false; } const firstLetters = this.subOptions.map(option => option.charAt(0).toLowerCase()); - if (firstLetters.indexOf(subOption.toLowerCase()) < 0) { + if (firstLetters.indexOf(letter.toLowerCase()) < 0) { return false; } diff --git a/src/app/shared/components/sub-selector/sub-selector.module.ts b/apps/a2agc/src/app/shared/components/sub-selector/sub-selector.module.ts similarity index 100% rename from src/app/shared/components/sub-selector/sub-selector.module.ts rename to apps/a2agc/src/app/shared/components/sub-selector/sub-selector.module.ts diff --git a/src/app/shared/components/table-data-selector/table-data-selector.component.html b/apps/a2agc/src/app/shared/components/table-data-selector/table-data-selector.component.html similarity index 100% rename from src/app/shared/components/table-data-selector/table-data-selector.component.html rename to apps/a2agc/src/app/shared/components/table-data-selector/table-data-selector.component.html diff --git a/src/app/shared/components/table-data-selector/table-data-selector.component.scss b/apps/a2agc/src/app/shared/components/table-data-selector/table-data-selector.component.scss similarity index 100% rename from src/app/shared/components/table-data-selector/table-data-selector.component.scss rename to apps/a2agc/src/app/shared/components/table-data-selector/table-data-selector.component.scss diff --git a/src/app/shared/components/table-data-selector/table-data-selector.component.ts b/apps/a2agc/src/app/shared/components/table-data-selector/table-data-selector.component.ts similarity index 70% rename from src/app/shared/components/table-data-selector/table-data-selector.component.ts rename to apps/a2agc/src/app/shared/components/table-data-selector/table-data-selector.component.ts index 7a4bbb18..cc159b92 100644 --- a/src/app/shared/components/table-data-selector/table-data-selector.component.ts +++ b/apps/a2agc/src/app/shared/components/table-data-selector/table-data-selector.component.ts @@ -3,6 +3,9 @@ import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, O import { Dataset, DatasetVariable } from '../../../core/models/dataset.model'; +/** + * Dataset and variable selector for data distributions page + */ @Component({ selector: 'agc-table-data-selector', templateUrl: './table-data-selector.component.html', @@ -10,28 +13,48 @@ import { Dataset, DatasetVariable } from '../../../core/models/dataset.model'; changeDetection: ChangeDetectionStrategy.OnPush }) export class TableDataSelectorComponent { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-table-data-selector'; + /** Datasets available */ @Input() datasets: Dataset[] = []; + /** Variables for a dataset */ @Input() variables: DatasetVariable[] = []; + /** Subselector label */ @Input() subLabel = ''; + /** Subselector variables */ @Input() subVariables: DatasetVariable[] = []; + /** Emits when selected dataset is changed */ @Output() readonly datasetChange = new EventEmitter(); + /** Emits when selected dataset variable is changed */ @Output() readonly dataVariableChange = new EventEmitter(); + /** Emits when select all variables is toggled */ @Output() readonly selectAll = new EventEmitter(); + /** Selected dataset */ selectedDataset: Dataset | undefined; + /** Selected dataset variable */ selectedVariable: DatasetVariable | undefined; + /** + * Gets list of variable names + */ get variableNames(): string[] { return this.variables.map(v => v.name); } + /** + * Gets list of subvariable names + */ get subVariableNames(): string[] { return this.subVariables.map(v => v.name); } + /** + * Sets selected dataset + * @param dataset Dataset selected + */ setDataset(dataset: Dataset | undefined): void { if (dataset !== undefined && dataset !== this.selectedDataset) { this.selectedDataset = dataset; @@ -40,6 +63,10 @@ export class TableDataSelectorComponent { } } + /** + * Sets selected variable from the variable name and emits corresponding DatasetVariable + * @param name variable name + */ setVariableFromName(name: string): void { if (this.selectedDataset !== undefined) { const variable = this.variables.find(v => v.name === name) ?? diff --git a/src/app/shared/components/table-data-selector/table-data-selector.module.ts b/apps/a2agc/src/app/shared/components/table-data-selector/table-data-selector.module.ts similarity index 100% rename from src/app/shared/components/table-data-selector/table-data-selector.module.ts rename to apps/a2agc/src/app/shared/components/table-data-selector/table-data-selector.module.ts diff --git a/src/app/shared/components/variable-visualization/data-manager.service.ts b/apps/a2agc/src/app/shared/components/variable-visualization/data-manager.service.ts similarity index 66% rename from src/app/shared/components/variable-visualization/data-manager.service.ts rename to apps/a2agc/src/app/shared/components/variable-visualization/data-manager.service.ts index da34c638..3dd41119 100644 --- a/src/app/shared/components/variable-visualization/data-manager.service.ts +++ b/apps/a2agc/src/app/shared/components/variable-visualization/data-manager.service.ts @@ -11,36 +11,63 @@ import { export type TimeFilter = [number, number] | undefined; +/** + * Service to manage data related to distribution and filtering. + */ @Injectable() export class DataManagerService implements OnDestroy { + /** An observable emitting distribution data entries. */ readonly data$: Observable; + /** Used to emit distribution data entries */ private dataEmitter = new ReplaySubject(1); + /** Subscription to the data stream */ private dataSub?: Subscription; + /** Array of distribution data entries */ private data: DistributionDataEntry[] = []; + /** + * Getter that applies a filter to the data if available + */ private get filteredData(): DistributionDataEntry[] { const filterPred = this.createFilterPred(); return filterPred ? this.data.filter(filterPred) : this.data; } + /** + * Getter indicating whether data entries have a period property. + */ private get hasDataPeriods(): boolean { return this.data.some(({ period }) => !!period); } + /** Observable source for time filters. */ private filterSource?: Observable; + /** Subscription to the filter source. */ private filterSub?: Subscription; + /** Time filter (range of numbers or undefined). */ private filter?: TimeFilter; + /** + * Creates an instance of data manager service. + * @param loader distribution data loader service + */ constructor(private readonly loader: DistributionDataLoaderService) { this.data$ = this.dataEmitter.asObservable(); } + /** + * Cleans up resources when the component is destroyed. + */ ngOnDestroy(): void { this.clearData(); this.clearFilter(); } + /** + * Loads data based on the provided dataset variable. + * @param variable dataset variable + */ load(variable: DatasetVariable): void { this.clearData(); this.clearFilter(); @@ -49,6 +76,10 @@ export class DataManagerService implements OnDestroy { this.dataSub = data$.subscribe(this.setData.bind(this)); } + /** + * Sets the data and updates filtering. + * @param data distribution data + */ setData(data: DistributionDataEntry[]): void { if (data === this.data) { return; @@ -59,6 +90,9 @@ export class DataManagerService implements OnDestroy { this.updateFiltering(); } + /** + * Sets the filter source and updates filtering. + */ setFilterSource(source?: Observable): void { if (source === this.filterSource) { return; @@ -69,6 +103,10 @@ export class DataManagerService implements OnDestroy { this.updateFiltering(); } + /** + * Creates a filter predicate function based on the current time filter (if any). + * @returns predicate function + */ private createFilterPred(): ((entry: DistributionDataEntry) => boolean) | undefined { if (!this.filter) { return undefined; @@ -78,6 +116,10 @@ export class DataManagerService implements OnDestroy { return ({ period }) => !period || (start <= +period && +period <= end); } + /** + * Updates the data filtering based on the filter source (if available) and the presence of data periods. + * @returns filtering + */ private updateFiltering(): void { if (this.filterSource) { if (!this.hasDataPeriods) { @@ -101,15 +143,24 @@ export class DataManagerService implements OnDestroy { this.emitData(); } + /** + * Emits the filtered data using the dataEmitter. + */ private emitData(): void { this.dataEmitter.next(this.filteredData); } + /** + * Unsubscribes from the data subscription (dataSub) and clears the data array. + */ private clearData(): void { this.dataSub?.unsubscribe?.(); this.data = []; } + /** + * Unsubscribes from the filter subscription (filterSub) and resets the filter. + */ private clearFilter(): void { this.filterSub?.unsubscribe?.(); this.filterSub = undefined; diff --git a/src/app/shared/components/variable-visualization/variable-visualization.component.html b/apps/a2agc/src/app/shared/components/variable-visualization/variable-visualization.component.html similarity index 100% rename from src/app/shared/components/variable-visualization/variable-visualization.component.html rename to apps/a2agc/src/app/shared/components/variable-visualization/variable-visualization.component.html diff --git a/src/app/shared/components/variable-visualization/variable-visualization.component.scss b/apps/a2agc/src/app/shared/components/variable-visualization/variable-visualization.component.scss similarity index 100% rename from src/app/shared/components/variable-visualization/variable-visualization.component.scss rename to apps/a2agc/src/app/shared/components/variable-visualization/variable-visualization.component.scss diff --git a/src/app/shared/components/variable-visualization/variable-visualization.component.ts b/apps/a2agc/src/app/shared/components/variable-visualization/variable-visualization.component.ts similarity index 76% rename from src/app/shared/components/variable-visualization/variable-visualization.component.ts rename to apps/a2agc/src/app/shared/components/variable-visualization/variable-visualization.component.ts index 0db0c6e7..82f61f96 100644 --- a/src/app/shared/components/variable-visualization/variable-visualization.component.ts +++ b/apps/a2agc/src/app/shared/components/variable-visualization/variable-visualization.component.ts @@ -16,9 +16,13 @@ import { DataManagerService, TimeFilter } from './data-manager.service'; export { TimeFilter }; +/** Default filter throttle time (ms) */ const DEFAULT_FILTER_THROTTLE = 100; +/** + * Data distributions variable visualization component + */ @Component({ selector: 'agc-variable-visualization', templateUrl: './variable-visualization.component.html', @@ -27,28 +31,51 @@ const DEFAULT_FILTER_THROTTLE = 100; providers: [DataManagerService] }) export class VariableVisualizationComponent implements OnChanges, OnDestroy { + /** Dataset variable */ @Input() variable!: DatasetVariable; + /** Filter throttle time */ @Input() filterThrottle?: number; + /** Filter source observable */ @Input() filterSource?: Observable; + /** Name used for data binding */ @Input() dataBindingName?: string; + /** Whether autosizing is enabled */ @Input() autosize: Autosize = false; + /** Additional options for the visualization */ @Input() options?: Options; + /** Loading state */ loading = true; + /** Visualization spec */ spec?: VisualizationSpec; + /** Metadata observable */ metadata?: Observable; + /** Data for the distribution */ data: DistributionDataEntry[] = []; + /** + * Checks if the data distribution type is summary only + */ get isSummaryOnly(): boolean { return this.variable.distribution.type === DistributionType.summary; } + /** Visualization view */ private view?: View; + /** Task ID for updating the view data */ private viewDataUpdateTaskId?: ReturnType; + /** Subscriptions for the observables */ private readonly subscriptions = new Subscription(); + /** + * Creates an instance of variable visualization component. + * @param cdr change detection + * @param chartFactory creates chart + * @param dataManager manages data + * @param variableState variable state + */ constructor( private readonly cdr: ChangeDetectorRef, private readonly chartFactory: ChartFactoryService, @@ -64,6 +91,9 @@ export class VariableVisualizationComponent implements OnChanges, OnDestroy { this.subscriptions.add(() => this.clearViewDataUpdate()); } + /** + * Handles changes in variable and filter throttle time + */ ngOnChanges(changes: SimpleChanges): void { if ('variable' in changes) { this.onVariableChange(); @@ -74,15 +104,25 @@ export class VariableVisualizationComponent implements OnChanges, OnDestroy { } } + /** + * Unsubscribes from subscriptions on component destroy + */ ngOnDestroy(): void { this.subscriptions.unsubscribe(); } + /** + * Sets view and schedules view data update + * @param view view + */ attachView(view: View): void { this.view = view; this.scheduleViewDataUpdate(); } + /** + * Resets view + */ resetView(): void { this.loading = true; this.spec = undefined; @@ -91,6 +131,9 @@ export class VariableVisualizationComponent implements OnChanges, OnDestroy { this.view = undefined; } + /** + * Resets view, loads new data and creates new chart on variable change + */ private onVariableChange(): void { this.resetView(); @@ -106,12 +149,18 @@ export class VariableVisualizationComponent implements OnChanges, OnDestroy { } } + /** + * Sets filter source on filter change + */ private onFilterChange(): void { const { filterSource, filterThrottle = DEFAULT_FILTER_THROTTLE, dataManager } = this; const source$ = filterSource?.pipe?.(throttleTime(filterThrottle)); dataManager.setFilterSource(source$); } + /** + * Schedules view data update + */ private scheduleViewDataUpdate(): void { if (this.viewDataUpdateTaskId !== undefined) { return; @@ -140,6 +189,9 @@ export class VariableVisualizationComponent implements OnChanges, OnDestroy { }); } + /** + * Clear the timeout if the view data update task ID is not undefined + */ private clearViewDataUpdate(): void { const id = this.viewDataUpdateTaskId; if (id !== undefined) { @@ -148,6 +200,10 @@ export class VariableVisualizationComponent implements OnChanges, OnDestroy { } } + /** + * Gets data binding name + * @returns data binding name + */ private getDataBindingName(): string | undefined { if (this.dataBindingName !== undefined) { return this.dataBindingName; diff --git a/src/app/shared/components/variable-visualization/variable-visualization.module.ts b/apps/a2agc/src/app/shared/components/variable-visualization/variable-visualization.module.ts similarity index 100% rename from src/app/shared/components/variable-visualization/variable-visualization.module.ts rename to apps/a2agc/src/app/shared/components/variable-visualization/variable-visualization.module.ts diff --git a/src/app/shared/components/visualization-page/shared/data-handler-view.ts b/apps/a2agc/src/app/shared/components/visualization-page/shared/data-handler-view.ts similarity index 71% rename from src/app/shared/components/visualization-page/shared/data-handler-view.ts rename to apps/a2agc/src/app/shared/components/visualization-page/shared/data-handler-view.ts index c36d7852..a6d3c66f 100644 --- a/src/app/shared/components/visualization-page/shared/data-handler-view.ts +++ b/apps/a2agc/src/app/shared/components/visualization-page/shared/data-handler-view.ts @@ -2,9 +2,18 @@ import { Runtime, View, ViewOptions } from 'vega'; import { DataHandler, DataHandlerType } from './data-handler'; +/** + * Data handler view + */ export class DataHandlerView extends View { + /** Data handler types */ static readonly HANDLER_TYPES: DataHandlerType[] = []; + /** + * Adds data handlers + * @param handlerTypes handler types + * @returns data handler view with data handlers + */ static withDataHandlers(handlerTypes: DataHandlerType[]): typeof DataHandlerView { const superHandlerTypes = this.HANDLER_TYPES; @@ -13,8 +22,14 @@ export class DataHandlerView extends View { }; } + /** Data handlers */ readonly handlers: DataHandler[]; + /** + * Creates an instance of data handler view. + * @param runtime runtime + * @param options view options + */ constructor(runtime: Runtime, options: ViewOptions) { super(runtime, options); @@ -23,6 +38,9 @@ export class DataHandlerView extends View { this.handlers = handlerTypes.map(type => new type(this)); } + /** + * Finalizes data handlers + */ finalize(): this { this.handlers.forEach(handler => handler.finalize?.()); return super.finalize(); diff --git a/src/app/shared/components/visualization-page/shared/data-handler.ts b/apps/a2agc/src/app/shared/components/visualization-page/shared/data-handler.ts similarity index 66% rename from src/app/shared/components/visualization-page/shared/data-handler.ts rename to apps/a2agc/src/app/shared/components/visualization-page/shared/data-handler.ts index 4aa102d0..f099f0fe 100644 --- a/src/app/shared/components/visualization-page/shared/data-handler.ts +++ b/apps/a2agc/src/app/shared/components/visualization-page/shared/data-handler.ts @@ -1,7 +1,11 @@ import { View } from 'vega'; +/** + * Data handler interface with finalize function + */ export interface DataHandler { + /** Finalizes */ finalize?(): void; } diff --git a/src/app/shared/components/visualization-page/shared/geomap-zoom-patch.ts b/apps/a2agc/src/app/shared/components/visualization-page/shared/geomap-zoom-patch.ts similarity index 91% rename from src/app/shared/components/visualization-page/shared/geomap-zoom-patch.ts rename to apps/a2agc/src/app/shared/components/visualization-page/shared/geomap-zoom-patch.ts index 9796d3dc..5c453d26 100644 --- a/src/app/shared/components/visualization-page/shared/geomap-zoom-patch.ts +++ b/apps/a2agc/src/app/shared/components/visualization-page/shared/geomap-zoom-patch.ts @@ -3,23 +3,28 @@ import { Spec } from 'vega'; /** * Geographical zoom configuration. - * - * center - Center longitude/latitude pair. - * zoomLevels - Min/max pair of zoom levels. - * initialZoom - Initial zoom level. */ export interface GeoZoomOptions { + /** Center longitude/latitude pair. */ center: [number, number]; + /** Min/max pair of zoom levels. */ zoomLevels: [number, number]; + /** Initial zoom level. */ initialZoom?: number; } +/** + * Zoom config for Indiana map + */ export const INDIANA_ZOOM_CONFIG: GeoZoomOptions = { center: [86.44305475, 39.76622477], zoomLevels: [3200, 250000], initialZoom: 6400, }; +/** + * Zoom config for USA map + */ export const USA_ZOOM_CONFIG: GeoZoomOptions = { center: [96, 39], zoomLevels: [10, 250000], @@ -125,6 +130,11 @@ export function addGeoZoom(spec: Spec, opts: GeoZoomOptions): void { delete projection.size; } +/** + * Function that patches a vega spec based on zoom config + * @param opts geographical zoom configuration + * @returns vega spec + */ export function createGeoZoomPatch(opts: GeoZoomOptions): (spec: Spec) => Spec { return (spec) => { addGeoZoom(spec, opts); diff --git a/src/app/shared/components/visualization-page/shared/visualization-6-data-handler.ts b/apps/a2agc/src/app/shared/components/visualization-page/shared/visualization-6-data-handler.ts similarity index 74% rename from src/app/shared/components/visualization-page/shared/visualization-6-data-handler.ts rename to apps/a2agc/src/app/shared/components/visualization-page/shared/visualization-6-data-handler.ts index b9456eae..bff68551 100644 --- a/src/app/shared/components/visualization-page/shared/visualization-6-data-handler.ts +++ b/apps/a2agc/src/app/shared/components/visualization-page/shared/visualization-6-data-handler.ts @@ -4,45 +4,79 @@ import { View, ingest } from 'vega'; import { DataHandler, DataHandlerType } from './data-handler'; +/** Signal value */ type SignalValue = Record; +/** Fields to sort by */ type SortField = 'AGE_RANK' | 'HEALTH_RANK' | 'OVERDOSE_RANK' | 'TIME_FIRST_OD' | 'TIME_FIRST_RX' | 'OD_DIFF' | 'RX_DIFF' | 'INCARCERATIONS_RANK' | 'PRESCRIPTIONS_RANK'; +/** + * Data entry info + */ interface DataEntry { + /** Case number */ CASE_NUMBER: string; + /** Entry rank */ RANK: number; + /** Age at death */ AGE: number; + /** Time incident occurred */ PERIOD: number; + /** How long before death the incident occurred */ TIME_BEFORE_DEATH: number; + /** Total number of events */ ALL_TYPES: number; + /** Number of health encounters */ HEALTH_ENCOUNTERS: number; + /** Number of opioid prescriptions */ OPIOID_PRESCRIPTIONS: number; + /** Number of incarcerations */ INCARCERATIONS: number; + /** Number of opioid overdoses */ OVERDOSES: number; + /** Total number of health encounters */ NUM_ENCOUNTERS_TOTAL: number; + /** Total number of incarcerations */ NUM_INCARCERATIONS_TOTAL: number; + /** Age rank */ AGE_RANK: number; + /** Health encounters rank */ HEALTH_RANK: number; + /** Overdoses rank */ OVERDOSE_RANK: number; + /** Incarcerations rank */ INCARCERATIONS_RANK: number; + /** Opioid prescriptions rank */ PRESCRIPTIONS_RANK: number; + /** Final rank */ FINAL_RANK: number; + /** Time of first overdose */ TIME_FIRST_OD: number; + /** Time of first prescription */ TIME_FIRST_RX: number; + /** Time between first overdose and death */ OD_DIFF: number; + /** Time between first prescription and death */ RX_DIFF: number; } +/** + * Visualization6 data handler options + */ export interface Visualization6DataHandlerOptions { + /** Debounce time */ debounceTime?: number; + /** Maximum entries shown */ maxCasesShown?: number; } -// Used when the resulting data is empty to prevent the visualization view from blowing up +/** + * Used when the resulting data is empty to prevent the visualization view from blowing up + */ const fakeEntries: DataEntry[] = [ { CASE_NUMBER: '', @@ -101,28 +135,51 @@ const fakeEntries: DataEntry[] = [ ]; +/** + * Data handler for visualization 6 (Maps of Health) + */ export class Visualization6DataHandler implements DataHandler { + /** Visualization data handler options */ static readonly OPTIONS: Visualization6DataHandlerOptions = {}; + /** + * Withs options + * @param options + * @returns options + */ static withOptions(options: Visualization6DataHandlerOptions): DataHandlerType { return class extends this { static readonly OPTIONS = options; }; } + /** Data handler options for vis6 */ readonly options = (this.constructor as typeof Visualization6DataHandler).OPTIONS; + /** Data entries */ private data?: DataEntry[]; + /** Field to sort data entries by */ private sortBy: SortField = 'HEALTH_RANK'; + /** Ranks compilation */ private sortRanks: Record> = {}; + /** Ranks */ private ranks?: number[]; + /** Ranks lookup */ private ranksLookup?: Set; + /** Selected age range */ private age?: [number, number]; + /** Selected encounters range */ private numEncounters?: [number, number]; + /** Selected incarcerations range */ private numIncarcerations?: [number, number]; + /** Scheduled update call of visualization6 data handler */ private scheduledUpdateCall?: ReturnType; + /** + * Adds listeners to view and schedules update calls + * @param view view + */ constructor(readonly view: View) { view.addDataListener('source', (_name, data: DataEntry[]) => { this.data = data; @@ -157,6 +214,9 @@ export class Visualization6DataHandler implements DataHandler { }); } + /** + * Clears scheduled update calls and resets everything + */ finalize(): void { this.clearScheduledUpdateCall(); @@ -169,6 +229,9 @@ export class Visualization6DataHandler implements DataHandler { this.numEncounters = undefined; } + /** + * Updates data and resizes view + */ private scheduleUpdateCall(): void { this.clearScheduledUpdateCall(); @@ -180,6 +243,9 @@ export class Visualization6DataHandler implements DataHandler { }, this.options.debounceTime ?? 500); } + /** + * Clears scheduled update call + */ private clearScheduledUpdateCall(): void { if (this.scheduledUpdateCall !== undefined) { clearTimeout(this.scheduledUpdateCall); @@ -187,6 +253,9 @@ export class Visualization6DataHandler implements DataHandler { } } + /** + * Filters and sorts data in view + */ private updateData(): void { let { data = [] } = this; @@ -206,6 +275,11 @@ export class Visualization6DataHandler implements DataHandler { this.view.data('processed_source', data); } + /** + * Compiles sort values for all cases + * @param data data entries + * @returns record of ranks for each field per case + */ private compileSortRanks(data: DataEntry[]): Record> { const sortRanks: Record> = {}; for (const { CASE_NUMBER, AGE_RANK, HEALTH_RANK, OVERDOSE_RANK, TIME_FIRST_OD, TIME_FIRST_RX, OD_DIFF, RX_DIFF, INCARCERATIONS_RANK, PRESCRIPTIONS_RANK } of data) { @@ -215,6 +289,11 @@ export class Visualization6DataHandler implements DataHandler { return sortRanks; } + /** + * Filters data by rank + * @param data data entries + * @returns filtered data + */ private filterByRank(data: DataEntry[]): DataEntry[] { const { ranks } = this; if (ranks === undefined) { @@ -225,6 +304,11 @@ export class Visualization6DataHandler implements DataHandler { return data.filter(({ RANK: value }) => lookup.has(value)); } + /** + * Filters data by age range + * @param data data entries + * @returns filtered data + */ private filterByAge(data: DataEntry[]): DataEntry[] { const { age } = this; if (age === undefined) { @@ -235,6 +319,11 @@ export class Visualization6DataHandler implements DataHandler { return data.filter(({ AGE: value }) => min <= value && value <= max); } + /** + * Filters data by encounters range + * @param data data entries + * @returns filtered data + */ private filterByEncounters(data: DataEntry[]): DataEntry[] { const { numEncounters } = this; if (numEncounters === undefined) { @@ -245,6 +334,11 @@ export class Visualization6DataHandler implements DataHandler { return data.filter(({ NUM_ENCOUNTERS_TOTAL: value }) => min <= value && value <= max); } + /** + * Filters data by incarcerations + * @param data data entries + * @returns filtered data + */ private filterByIncarcerations(data: DataEntry[]): DataEntry[] { const { numIncarcerations } = this; if (numIncarcerations === undefined) { @@ -255,6 +349,11 @@ export class Visualization6DataHandler implements DataHandler { return data.filter(({ NUM_INCARCERATIONS_TOTAL: value }) => min <= value && value <= max); } + /** + * Sorts data by current sort field + * @param data data entries + * @returns data sorted data + */ private sortData(data: DataEntry[]): DataEntry[] { const { sortBy, sortRanks } = this; const getRank = (entry: DataEntry) => sortRanks[entry.CASE_NUMBER][sortBy]; @@ -262,6 +361,11 @@ export class Visualization6DataHandler implements DataHandler { return data.sort((a, b) => getRank(a) - getRank(b)); } + /** + * Limit amount of data shown + * @param data list of data entries + * @returns updated list of data entries + */ private limitData(data: DataEntry[]): DataEntry[] { const { options: { maxCasesShown = 54 } } = this; const selectedCases = new Set(); @@ -279,6 +383,11 @@ export class Visualization6DataHandler implements DataHandler { return result; } + /** + * Sets final ranks in data + * @param data data entries + * @returns data with updated final_rank + */ private setRanks(data: DataEntry[]): DataEntry[] { let previousCaseNumber = ''; let rank = -1; diff --git a/src/app/shared/components/visualization-page/shared/visualization1-data-handler.ts b/apps/a2agc/src/app/shared/components/visualization-page/shared/visualization1-data-handler.ts similarity index 71% rename from src/app/shared/components/visualization-page/shared/visualization1-data-handler.ts rename to apps/a2agc/src/app/shared/components/visualization-page/shared/visualization1-data-handler.ts index e37b412f..276e885c 100644 --- a/src/app/shared/components/visualization-page/shared/visualization1-data-handler.ts +++ b/apps/a2agc/src/app/shared/components/visualization-page/shared/visualization1-data-handler.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { loader, read, Runtime, View, ViewOptions } from 'vega'; - +/** Maps labels to data variable */ const dataVariableMapping: Record = { 'Gender': 'SEX', 'Age': 'AGE', @@ -10,9 +10,18 @@ const dataVariableMapping: Record = { 'Prescription vs. Illicit Drugs': 'ILLICIT_V_PRESCRIPTION' }; +/** + * Data handler for visualization 1 (geomap) + */ export class VisualizationOneDataHandler { + /** Data handler */ subsets: Record[]> = {}; + /** + * Creates an instance of visualization 1 data handler. + * setData method is called to initialize data subsets and set up a signal listener for data variable selection + * @param view view + */ constructor(private view: View) { this.setData(); @@ -21,6 +30,10 @@ export class VisualizationOneDataHandler { ); } + /** + * Loads data from CSV file, parses it, and creates subsets of data based on different variables + * @returns promise + */ async setData(): Promise { const data = await loader() .load('assets/generated/vis-geomap-opioid-deaths.csv') @@ -44,6 +57,11 @@ export class VisualizationOneDataHandler { this.updateDataVariable('Age'); } + /** + * Updates view's data source with corresponding subset based on selected variable + * @param dataVariable selected variable + * @returns promise + */ async updateDataVariable(dataVariable?: string): Promise { if (dataVariable) { await this.view.runAsync(); @@ -51,17 +69,32 @@ export class VisualizationOneDataHandler { } } + /** + * Resets subsets + */ finalize(): void { this.subsets = {}; } } +/** + * Visualization one view + */ export class VisualizationOneView extends View { + /** Data handler */ dataHandler: VisualizationOneDataHandler; + + /** + * Creates an instance of visualization one view. + */ constructor(runtime: Runtime, opt?: ViewOptions) { super(runtime, opt); this.dataHandler = new VisualizationOneDataHandler(this); } + + /** + * Finalizes + */ finalize(): this { this.dataHandler.finalize(); return super.finalize(); diff --git a/src/app/shared/components/visualization-page/visualization-page.component.html b/apps/a2agc/src/app/shared/components/visualization-page/visualization-page.component.html similarity index 100% rename from src/app/shared/components/visualization-page/visualization-page.component.html rename to apps/a2agc/src/app/shared/components/visualization-page/visualization-page.component.html diff --git a/src/app/shared/components/visualization-page/visualization-page.component.scss b/apps/a2agc/src/app/shared/components/visualization-page/visualization-page.component.scss similarity index 100% rename from src/app/shared/components/visualization-page/visualization-page.component.scss rename to apps/a2agc/src/app/shared/components/visualization-page/visualization-page.component.scss diff --git a/src/app/shared/components/visualization-page/visualization-page.component.ts b/apps/a2agc/src/app/shared/components/visualization-page/visualization-page.component.ts similarity index 67% rename from src/app/shared/components/visualization-page/visualization-page.component.ts rename to apps/a2agc/src/app/shared/components/visualization-page/visualization-page.component.ts index 4d48b5d4..9f18c80e 100644 --- a/src/app/shared/components/visualization-page/visualization-page.component.ts +++ b/apps/a2agc/src/app/shared/components/visualization-page/visualization-page.component.ts @@ -7,6 +7,9 @@ import { HelpModalComponent } from '../help-modal/help-modal.component'; import { HelpTourModalComponent } from '../help-tour-modal/help-tour-modal.component'; +/** + * Visualization page component + */ @Component({ selector: 'agc-visualization-page', templateUrl: './visualization-page.component.html', @@ -14,34 +17,55 @@ import { HelpTourModalComponent } from '../help-tour-modal/help-tour-modal.compo changeDetection: ChangeDetectionStrategy.OnPush }) export class VisualizationPageComponent implements OnInit { + /** HTML class name */ @HostBinding('class') readonly clsName = 'agc-visualization-page'; + /** Page headline */ @Input() headline = 'Marion County Opioid Addiction Report'; + /** Visualization title */ @Input() title?: string; + /** Visualization description */ @Input() description?: string; + /** Vega-lite spec */ @Input() spec?: Spec; + /** Visualization options */ @Input() options: Options = { renderer: 'canvas', actions: true, width: 1268 }; + /** Expansion panel content markdown src */ @Input() content?: string; + /** Expansion panel SQL src */ @Input() sql?: string; + /** Expansion panel CSV src */ @Input() csv?: string; - csvSpinnerActive = true; + /** True if spinners for expansion panel content are shown */ spinners = { sql: true, csv: true, spec: true }; + /** True if the visualization is being loaded */ loadingVegaVisualization = true; + /** + * Gets visualization spec as string + */ get specString(): string | undefined { return this.spec as string; } + /** + * Creates an instance of visualization page component. + * @param dialog mat dialog service + * @param page page state + */ constructor( private readonly dialog: MatDialog, readonly page: PageState ) { } + /** + * On load, opens help tour modal if hasn't been shown yet + */ ngOnInit(): void { if (!this.page.snapshot.hasShownHelpModal) { this.dialog.open(HelpTourModalComponent, { @@ -52,6 +76,10 @@ export class VisualizationPageComponent implements OnInit { } } + /** + * Disables spinner for expansion panel content + * @param key sql, csv, or spec + */ disableSpinner(key: string): void { this.spinners = { ...this.spinners, @@ -59,6 +87,10 @@ export class VisualizationPageComponent implements OnInit { }; } + /** + * Enables spinner for expansion panel content + * @param key sql, csv, or spec + */ enableSpinner(key: string): void { this.spinners = { ...this.spinners, @@ -66,6 +98,9 @@ export class VisualizationPageComponent implements OnInit { }; } + /** + * Opens help dialog + */ launchHelpDialog(): void { this.dialog.open(HelpModalComponent, { width: '60rem', diff --git a/src/app/shared/components/visualization-page/visualization-page.module.ts b/apps/a2agc/src/app/shared/components/visualization-page/visualization-page.module.ts similarity index 100% rename from src/app/shared/components/visualization-page/visualization-page.module.ts rename to apps/a2agc/src/app/shared/components/visualization-page/visualization-page.module.ts diff --git a/src/app/shared/components/visualization-page/visualization-page.theme.scss b/apps/a2agc/src/app/shared/components/visualization-page/visualization-page.theme.scss similarity index 100% rename from src/app/shared/components/visualization-page/visualization-page.theme.scss rename to apps/a2agc/src/app/shared/components/visualization-page/visualization-page.theme.scss diff --git a/src/app/shared/pipes/order-by/order-by.pipe.ts b/apps/a2agc/src/app/shared/pipes/order-by/order-by.pipe.ts similarity index 97% rename from src/app/shared/pipes/order-by/order-by.pipe.ts rename to apps/a2agc/src/app/shared/pipes/order-by/order-by.pipe.ts index 2b6b891b..93f4cfe8 100644 --- a/src/app/shared/pipes/order-by/order-by.pipe.ts +++ b/apps/a2agc/src/app/shared/pipes/order-by/order-by.pipe.ts @@ -10,6 +10,9 @@ export type SortableKey = keyof PickByType; export type SortOrder = 'asc' | 'desc'; +/** + * Orders items by pipe + */ @Pipe({ name: 'orderBy', pure: true diff --git a/src/app/shared/shared.module.ts b/apps/a2agc/src/app/shared/shared.module.ts similarity index 100% rename from src/app/shared/shared.module.ts rename to apps/a2agc/src/app/shared/shared.module.ts diff --git a/src/app/shared/shared.theme.scss b/apps/a2agc/src/app/shared/shared.theme.scss similarity index 100% rename from src/app/shared/shared.theme.scss rename to apps/a2agc/src/app/shared/shared.theme.scss diff --git a/src/app/shared/vega-charts/bar-chart.vl.ts b/apps/a2agc/src/app/shared/vega-charts/bar-chart.vl.ts similarity index 91% rename from src/app/shared/vega-charts/bar-chart.vl.ts rename to apps/a2agc/src/app/shared/vega-charts/bar-chart.vl.ts index 55d49ac2..2d7334cc 100644 --- a/src/app/shared/vega-charts/bar-chart.vl.ts +++ b/apps/a2agc/src/app/shared/vega-charts/bar-chart.vl.ts @@ -7,14 +7,22 @@ import { DistributionDataEntry } from '../../core/models/distribution.model'; type LayerSpec = Extract; type AxisEncoding = NonNullable['x']>; +/** + * Additional bar chart label and axis settings + */ export interface BarChartExtraOptions { + /** X axis label */ xLabel?: string; + /** Y axis label */ yLabel?: string; - + /** If axes are flipped */ flipAxes?: boolean; } +/** + * Creates horizontal bar chart vega spec + */ export function createHorizBarSpec( variable: DatasetVariable, distributionData: DistributionDataEntry[] = [], @@ -23,6 +31,9 @@ export function createHorizBarSpec( return createBarSpec(variable, distributionData, { ...options, flipAxes: true }); } +/** + * Creates bar chart spec + */ export function createBarSpec( _variable: DatasetVariable, distributionData: DistributionDataEntry[] = [], @@ -104,6 +115,9 @@ export function createBarSpec( }; } +/** + * Returns x axis encoding object + */ function getXEncoding(label?: string, labelAngle?: number): AxisEncoding { return { field: 'label', @@ -128,6 +142,9 @@ function getXEncoding(label?: string, labelAngle?: number): AxisEncoding { }; } +/** + * Returns y axis encoding object + */ function getYEncoding(label?: string): AxisEncoding { return { field: 'total', diff --git a/src/app/shared/vega-charts/chart-factory.service.ts b/apps/a2agc/src/app/shared/vega-charts/chart-factory.service.ts similarity index 89% rename from src/app/shared/vega-charts/chart-factory.service.ts rename to apps/a2agc/src/app/shared/vega-charts/chart-factory.service.ts index e03946a8..8780541a 100644 --- a/src/app/shared/vega-charts/chart-factory.service.ts +++ b/apps/a2agc/src/app/shared/vega-charts/chart-factory.service.ts @@ -9,14 +9,23 @@ import { createPieSpec } from './pie-chart.vl'; import { createTimeSpec } from './time-slider.vl'; +/** + * Service for creating charts + */ @Injectable({ providedIn: 'root' }) export class ChartFactoryService { + /** Function to create bar chart */ readonly createBarChart = createBarSpec; + /** Function to create pie chart */ readonly createPieChart = createPieSpec; + /** Function to create time slider chart */ readonly createTimeSlider = createTimeSpec; + /** + * Creates vega spec from a dataset variable + */ createChart(variable: DatasetVariable): VisualizationSpec | undefined { const { type: vtype, distribution: { type, summary: { distinct } } } = variable; const knownType = Object.values(DistributionType).includes(type); diff --git a/src/app/shared/vega-charts/pie-chart.vl.ts b/apps/a2agc/src/app/shared/vega-charts/pie-chart.vl.ts similarity index 98% rename from src/app/shared/vega-charts/pie-chart.vl.ts rename to apps/a2agc/src/app/shared/vega-charts/pie-chart.vl.ts index 25600abd..baf31072 100644 --- a/src/app/shared/vega-charts/pie-chart.vl.ts +++ b/apps/a2agc/src/app/shared/vega-charts/pie-chart.vl.ts @@ -4,6 +4,9 @@ import { DatasetVariable } from '../../core/models/dataset.model'; import { DistributionDataEntry } from '../../core/models/distribution.model'; +/** + * Creates pie chart vega spec + */ export function createPieSpec( _variable: DatasetVariable, distributionData: DistributionDataEntry[] = [] diff --git a/src/app/shared/vega-charts/time-slider.vl.ts b/apps/a2agc/src/app/shared/vega-charts/time-slider.vl.ts similarity index 98% rename from src/app/shared/vega-charts/time-slider.vl.ts rename to apps/a2agc/src/app/shared/vega-charts/time-slider.vl.ts index 36c7ed86..51ed172c 100644 --- a/src/app/shared/vega-charts/time-slider.vl.ts +++ b/apps/a2agc/src/app/shared/vega-charts/time-slider.vl.ts @@ -3,6 +3,9 @@ import { VisualizationSpec } from 'vega-embed'; import { DistributionDataEntry } from '../../core/models/distribution.model'; +/** + * Creates time slider vega spec + */ export function createTimeSpec(distributionData: DistributionDataEntry[] = []): VisualizationSpec { return { $schema: 'https://vega.github.io/schema/vega-lite/v5.json', diff --git a/src/assets/.gitkeep b/apps/a2agc/src/assets/.gitkeep similarity index 100% rename from src/assets/.gitkeep rename to apps/a2agc/src/assets/.gitkeep diff --git a/src/assets/footer/contact-us.md b/apps/a2agc/src/assets/footer/contact-us.md similarity index 100% rename from src/assets/footer/contact-us.md rename to apps/a2agc/src/assets/footer/contact-us.md diff --git a/src/assets/footer/privacy-policy.md b/apps/a2agc/src/assets/footer/privacy-policy.md similarity index 100% rename from src/assets/footer/privacy-policy.md rename to apps/a2agc/src/assets/footer/privacy-policy.md diff --git a/src/assets/icons/data-distributions.svg b/apps/a2agc/src/assets/icons/data-distributions.svg similarity index 100% rename from src/assets/icons/data-distributions.svg rename to apps/a2agc/src/assets/icons/data-distributions.svg diff --git a/src/assets/icons/data-storage.svg b/apps/a2agc/src/assets/icons/data-storage.svg similarity index 100% rename from src/assets/icons/data-storage.svg rename to apps/a2agc/src/assets/icons/data-storage.svg diff --git a/src/assets/images/help-filter-controls.gif b/apps/a2agc/src/assets/images/help-filter-controls.gif similarity index 100% rename from src/assets/images/help-filter-controls.gif rename to apps/a2agc/src/assets/images/help-filter-controls.gif diff --git a/src/assets/images/help-pan-zoom.gif b/apps/a2agc/src/assets/images/help-pan-zoom.gif similarity index 100% rename from src/assets/images/help-pan-zoom.gif rename to apps/a2agc/src/assets/images/help-pan-zoom.gif diff --git a/src/assets/images/help-variables-filterby3.gif b/apps/a2agc/src/assets/images/help-variables-filterby3.gif similarity index 100% rename from src/assets/images/help-variables-filterby3.gif rename to apps/a2agc/src/assets/images/help-variables-filterby3.gif diff --git a/src/assets/images/help-year-range-selector.gif b/apps/a2agc/src/assets/images/help-year-range-selector.gif similarity index 100% rename from src/assets/images/help-year-range-selector.gif rename to apps/a2agc/src/assets/images/help-year-range-selector.gif diff --git a/src/assets/images/hero-banner.jpg b/apps/a2agc/src/assets/images/hero-banner.jpg similarity index 100% rename from src/assets/images/hero-banner.jpg rename to apps/a2agc/src/assets/images/hero-banner.jpg diff --git a/src/assets/images/iu-white.svg b/apps/a2agc/src/assets/images/iu-white.svg similarity index 100% rename from src/assets/images/iu-white.svg rename to apps/a2agc/src/assets/images/iu-white.svg diff --git a/src/assets/images/regenstrief-white.svg b/apps/a2agc/src/assets/images/regenstrief-white.svg similarity index 100% rename from src/assets/images/regenstrief-white.svg rename to apps/a2agc/src/assets/images/regenstrief-white.svg diff --git a/src/assets/pages/about/connect.md b/apps/a2agc/src/assets/pages/about/connect.md similarity index 100% rename from src/assets/pages/about/connect.md rename to apps/a2agc/src/assets/pages/about/connect.md diff --git a/src/assets/pages/about/credits.md b/apps/a2agc/src/assets/pages/about/credits.md similarity index 100% rename from src/assets/pages/about/credits.md rename to apps/a2agc/src/assets/pages/about/credits.md diff --git a/src/assets/pages/about/data.md b/apps/a2agc/src/assets/pages/about/data.md similarity index 100% rename from src/assets/pages/about/data.md rename to apps/a2agc/src/assets/pages/about/data.md diff --git a/src/assets/pages/about/helping.md b/apps/a2agc/src/assets/pages/about/helping.md similarity index 100% rename from src/assets/pages/about/helping.md rename to apps/a2agc/src/assets/pages/about/helping.md diff --git a/src/assets/pages/about/index.md b/apps/a2agc/src/assets/pages/about/index.md similarity index 100% rename from src/assets/pages/about/index.md rename to apps/a2agc/src/assets/pages/about/index.md diff --git a/src/assets/pages/vis1-geomap-of-opioid-deaths/README.md b/apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/README.md similarity index 100% rename from src/assets/pages/vis1-geomap-of-opioid-deaths/README.md rename to apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/README.md diff --git a/src/assets/pages/vis1-geomap-of-opioid-deaths/data.sql b/apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/data.sql similarity index 100% rename from src/assets/pages/vis1-geomap-of-opioid-deaths/data.sql rename to apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/data.sql diff --git a/src/assets/pages/vis1-geomap-of-opioid-deaths/indiana.topojson b/apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/indiana.topojson similarity index 100% rename from src/assets/pages/vis1-geomap-of-opioid-deaths/indiana.topojson rename to apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/indiana.topojson diff --git a/src/assets/pages/vis1-geomap-of-opioid-deaths/vis-data-variables.csv b/apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/vis-data-variables.csv similarity index 100% rename from src/assets/pages/vis1-geomap-of-opioid-deaths/vis-data-variables.csv rename to apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/vis-data-variables.csv diff --git a/src/assets/pages/vis1-geomap-of-opioid-deaths/vis.vl.json b/apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/vis.vl.json similarity index 100% rename from src/assets/pages/vis1-geomap-of-opioid-deaths/vis.vl.json rename to apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/vis.vl.json diff --git a/src/assets/pages/vis2-age-and-gender/README.md b/apps/a2agc/src/assets/pages/vis2-age-and-gender/README.md similarity index 100% rename from src/assets/pages/vis2-age-and-gender/README.md rename to apps/a2agc/src/assets/pages/vis2-age-and-gender/README.md diff --git a/src/assets/pages/vis2-age-and-gender/data.sql b/apps/a2agc/src/assets/pages/vis2-age-and-gender/data.sql similarity index 67% rename from src/assets/pages/vis2-age-and-gender/data.sql rename to apps/a2agc/src/assets/pages/vis2-age-and-gender/data.sql index e28904fe..55cf10ef 100644 --- a/src/assets/pages/vis2-age-and-gender/data.sql +++ b/apps/a2agc/src/assets/pages/vis2-age-and-gender/data.sql @@ -5,10 +5,11 @@ -- etc. SELECT - printf('%d-%s-%d', YEAR, SEX, (7 - min(CAST((AGE / 10) AS INT), 7))) as key, + printf('%d-%s-%d', YEAR, SEX, min(CAST((AGE / 10) AS INT), 7)) as key, YEAR as year, SEX as gender, - (7 - min(CAST((AGE / 10) AS INT), 7)) AS age_group, + AGE, + min(CAST((AGE / 10) AS INT), 7) AS age_group, count(*) AS count FROM deaths GROUP BY year, gender, age_group diff --git a/src/assets/pages/vis2-age-and-gender/vis.vl.json b/apps/a2agc/src/assets/pages/vis2-age-and-gender/vis.vl.json similarity index 96% rename from src/assets/pages/vis2-age-and-gender/vis.vl.json rename to apps/a2agc/src/assets/pages/vis2-age-and-gender/vis.vl.json index d1fd897f..522cbfff 100644 --- a/src/assets/pages/vis2-age-and-gender/vis.vl.json +++ b/apps/a2agc/src/assets/pages/vis2-age-and-gender/vis.vl.json @@ -84,14 +84,12 @@ { "height": 250, "width": 50, - "data": { - "sequence": {"start": 0, "stop": 75, "step": 10, "as": "low"} - }, + "transform": [ { - "calculate": "if(datum.low < 70, datum.low + '-' + (datum.low + 9), '70>')", - "as": "label" - } + "calculate": "if(toString(10 * datum.age_group) < 70, toString(10 * datum.age_group) + \"-\" + toString(10 * datum.age_group + 9), '70>')", + "as": "label" + } ], "mark": {"type": "text", "color": "#757575"}, "encoding": { @@ -99,7 +97,7 @@ "y": { "title": "Age", "type": "ordinal", - "field": "low", + "field": "age_group", "axis": { "domain": false, "ticks": false, @@ -127,8 +125,8 @@ }, "bind": "legend", "value": [ - {"year": 2018}, - {"year": 2017}, + {"year": 2018}, + {"year": 2017}, {"year": 2016} ] }, @@ -371,12 +369,9 @@ "hconcat": [ { "height": 150, - "data": { - "sequence": {"start": 0, "stop": 75, "step": 10, "as": "low"} - }, "transform": [ { - "calculate": "if(datum.low < 70, datum.low + '-' + (datum.low + 9), '70>')", + "calculate": "if(toString(10 * datum.age_group) < 70, toString(10 * datum.age_group) + \"-\" + toString(10 * datum.age_group + 9), '70>')", "as": "label" } ], @@ -387,7 +382,7 @@ "y": { "title": "Age", "type": "ordinal", - "field": "low", + "field": "age_group", "axis": { "domain": false, "ticks": false, diff --git a/src/assets/pages/vis3-heatmap-of-accidental-overdoses/README.md b/apps/a2agc/src/assets/pages/vis3-heatmap-of-accidental-overdoses/README.md similarity index 100% rename from src/assets/pages/vis3-heatmap-of-accidental-overdoses/README.md rename to apps/a2agc/src/assets/pages/vis3-heatmap-of-accidental-overdoses/README.md diff --git a/src/assets/pages/vis3-heatmap-of-accidental-overdoses/vis.vl.json b/apps/a2agc/src/assets/pages/vis3-heatmap-of-accidental-overdoses/vis.vl.json similarity index 100% rename from src/assets/pages/vis3-heatmap-of-accidental-overdoses/vis.vl.json rename to apps/a2agc/src/assets/pages/vis3-heatmap-of-accidental-overdoses/vis.vl.json diff --git a/src/assets/pages/vis4-combined-visualization/README.md b/apps/a2agc/src/assets/pages/vis4-combined-visualization/README.md similarity index 100% rename from src/assets/pages/vis4-combined-visualization/README.md rename to apps/a2agc/src/assets/pages/vis4-combined-visualization/README.md diff --git a/src/assets/pages/vis4-combined-visualization/data.sql b/apps/a2agc/src/assets/pages/vis4-combined-visualization/data.sql similarity index 60% rename from src/assets/pages/vis4-combined-visualization/data.sql rename to apps/a2agc/src/assets/pages/vis4-combined-visualization/data.sql index 8c9cb114..2f4dcd85 100644 --- a/src/assets/pages/vis4-combined-visualization/data.sql +++ b/apps/a2agc/src/assets/pages/vis4-combined-visualization/data.sql @@ -1,8 +1,14 @@ - + DROP TABLE IF EXISTS OVERDOSE_AGG; CREATE TEMP TABLE OVERDOSE_AGG AS SELECT - * + *, + strftime('%Y', DOD) || CASE + WHEN cast(strftime('%m', DOD) as integer) BETWEEN 1 AND 3 THEN '-01-01' + WHEN cast(strftime('%m', DOD) as integer) BETWEEN 4 and 6 THEN '-04-01' + WHEN cast(strftime('%m', DOD) as integer) BETWEEN 7 and 9 THEN '-07-01' + ELSE '-10-01' + END AS "PERIOD" FROM deaths as d LEFT JOIN ems_incidents as e ON d.CASE_NUMBER = e.CASE_NUMBER; @@ -11,8 +17,8 @@ SELECT CASE_NUMBER, SEX, 'All Substances' AS SUBSTANCE_NAME, - CAST((AGE / 10) AS INT) AS AGE_GROUP, - DOD as DATE_OF_DEATH, + min(CAST((AGE / 10) AS INT), 7) AS AGE_GROUP, + PERIOD as DATE_OF_DEATH, YEAR, OVERDOSE_DUMMY, OVERDOSE_CC_MOI, @@ -26,8 +32,8 @@ SELECT CASE_NUMBER, SEX, 'Heroin' AS SUBSTANCE_NAME, - CAST((AGE / 10) AS INT) AS AGE_GROUP, - DOD as DATE_OF_DEATH, + min(CAST((AGE / 10) AS INT), 7) AS AGE_GROUP, + PERIOD as DATE_OF_DEATH, YEAR, OVERDOSE_DUMMY, OVERDOSE_CC_MOI, @@ -42,8 +48,8 @@ SELECT CASE_NUMBER, SEX, 'Cocaine' AS SUBSTANCE_NAME, - CAST((AGE / 10) AS INT) AS AGE_GROUP, - DOD as DATE_OF_DEATH, + min(CAST((AGE / 10) AS INT), 7) AS AGE_GROUP, + PERIOD as DATE_OF_DEATH, YEAR, OVERDOSE_DUMMY, OVERDOSE_CC_MOI, @@ -58,8 +64,8 @@ SELECT CASE_NUMBER, SEX, 'Fentanyl' AS SUBSTANCE_NAME, - CAST((AGE / 10) AS INT) AS AGE_GROUP, - DOD as DATE_OF_DEATH, + min(CAST((AGE / 10) AS INT), 7) AS AGE_GROUP, + PERIOD as DATE_OF_DEATH, YEAR, OVERDOSE_DUMMY, OVERDOSE_CC_MOI, @@ -74,8 +80,8 @@ SELECT CASE_NUMBER, SEX, 'Prescription Opioid' AS SUBSTANCE_NAME, - CAST((AGE / 10) AS INT) AS AGE_GROUP, - DOD as DATE_OF_DEATH, + min(CAST((AGE / 10) AS INT), 7) AS AGE_GROUP, + PERIOD as DATE_OF_DEATH, YEAR, OVERDOSE_DUMMY, OVERDOSE_CC_MOI, @@ -90,8 +96,8 @@ SELECT CASE_NUMBER, SEX, 'Any Opioid' AS SUBSTANCE_NAME, - CAST((AGE / 10) AS INT) AS AGE_GROUP, - DOD as DATE_OF_DEATH, + min(CAST((AGE / 10) AS INT), 7) AS AGE_GROUP, + PERIOD as DATE_OF_DEATH, YEAR, OVERDOSE_DUMMY, OVERDOSE_CC_MOI, @@ -106,8 +112,8 @@ SELECT CASE_NUMBER, SEX, 'Benzodiazepine' AS SUBSTANCE_NAME, - CAST((AGE / 10) AS INT) AS AGE_GROUP, - DOD as DATE_OF_DEATH, + min(CAST((AGE / 10) AS INT), 7) AS AGE_GROUP, + PERIOD as DATE_OF_DEATH, YEAR, OVERDOSE_DUMMY, OVERDOSE_CC_MOI, @@ -122,12 +128,12 @@ SELECT CASE_NUMBER, SEX, 'Methamphetamine' AS SUBSTANCE_NAME, - CAST((AGE / 10) AS INT) AS AGE_GROUP, - DOD as DATE_OF_DEATH, + min(CAST((AGE / 10) AS INT), 7) AS AGE_GROUP, + PERIOD as DATE_OF_DEATH, YEAR, OVERDOSE_DUMMY, OVERDOSE_CC_MOI, MIN_INCIDENT_DATE, MAX_INCIDENT_DATE FROM OVERDOSE_AGG -WHERE ANY_METHAMPHETAMINE == 1 \ No newline at end of file +WHERE ANY_METHAMPHETAMINE == 1 diff --git a/src/assets/pages/vis4-combined-visualization/vis.vl.json b/apps/a2agc/src/assets/pages/vis4-combined-visualization/vis.vl.json similarity index 95% rename from src/assets/pages/vis4-combined-visualization/vis.vl.json rename to apps/a2agc/src/assets/pages/vis4-combined-visualization/vis.vl.json index c635222b..442ec905 100644 --- a/src/assets/pages/vis4-combined-visualization/vis.vl.json +++ b/apps/a2agc/src/assets/pages/vis4-combined-visualization/vis.vl.json @@ -1,7 +1,7 @@ { "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "padding": {"top": 50}, - + "config": { "mark": { "tooltip": null @@ -63,8 +63,8 @@ "as": "deaths_binned" }, { - "as": "group", - "calculate": "if(toString(10 * datum.AGE_GROUP) < 70, toString(10 * datum.AGE_GROUP) + \"-\" + toString(10 * datum.AGE_GROUP + 9), '70>')" + "calculate": "if(toString(10 * datum.AGE_GROUP) < 70, toString(10 * datum.AGE_GROUP) + \"-\" + toString(10 * datum.AGE_GROUP + 9), '70>')", + "as": "group" } ], "title": { @@ -447,7 +447,7 @@ ] } } - ], + ], "encoding": { "tooltip": { "value": "Shift-Click to add more than one item" diff --git a/src/assets/pages/vis5-opioid-trajectories/README.md b/apps/a2agc/src/assets/pages/vis5-opioid-trajectories/README.md similarity index 100% rename from src/assets/pages/vis5-opioid-trajectories/README.md rename to apps/a2agc/src/assets/pages/vis5-opioid-trajectories/README.md diff --git a/src/assets/pages/vis5-opioid-trajectories/data.sql b/apps/a2agc/src/assets/pages/vis5-opioid-trajectories/data.sql similarity index 100% rename from src/assets/pages/vis5-opioid-trajectories/data.sql rename to apps/a2agc/src/assets/pages/vis5-opioid-trajectories/data.sql diff --git a/src/assets/pages/vis5-opioid-trajectories/vis.vl.json b/apps/a2agc/src/assets/pages/vis5-opioid-trajectories/vis.vl.json similarity index 100% rename from src/assets/pages/vis5-opioid-trajectories/vis.vl.json rename to apps/a2agc/src/assets/pages/vis5-opioid-trajectories/vis.vl.json diff --git a/src/assets/pages/vis6-maps-of-health/README.md b/apps/a2agc/src/assets/pages/vis6-maps-of-health/README.md similarity index 100% rename from src/assets/pages/vis6-maps-of-health/README.md rename to apps/a2agc/src/assets/pages/vis6-maps-of-health/README.md diff --git a/src/assets/pages/vis6-maps-of-health/data-aggregate.sql b/apps/a2agc/src/assets/pages/vis6-maps-of-health/data-aggregate.sql similarity index 73% rename from src/assets/pages/vis6-maps-of-health/data-aggregate.sql rename to apps/a2agc/src/assets/pages/vis6-maps-of-health/data-aggregate.sql index 13d0d28a..cf7bf79e 100644 --- a/src/assets/pages/vis6-maps-of-health/data-aggregate.sql +++ b/apps/a2agc/src/assets/pages/vis6-maps-of-health/data-aggregate.sql @@ -22,11 +22,17 @@ WITH GROUP BY CASE_NUMBER ), CN_POJH AS ( - SELECT D.CASE_NUMBER, DOD, AGE, SEX, RACE, + SELECT D.CASE_NUMBER, AGE, SEX, RACE, coalesce(OPIOID_PRESCRIPTIONS, 0) AS OPIOID_PRESCRIPTIONS, coalesce(OVERDOSES, 0) AS OVERDOSES, coalesce(INCARCERATIONS, 0) AS INCARCERATIONS, - coalesce(HEALTH_ENCOUNTERS, 0) AS HEALTH_ENCOUNTERS + coalesce(HEALTH_ENCOUNTERS, 0) AS HEALTH_ENCOUNTERS, + strftime('%Y', DOD) || CASE + WHEN cast(strftime('%m', DOD) as integer) BETWEEN 1 AND 3 THEN '-01-01' + WHEN cast(strftime('%m', DOD) as integer) BETWEEN 4 and 6 THEN '-04-01' + WHEN cast(strftime('%m', DOD) as integer) BETWEEN 7 and 9 THEN '-07-01' + ELSE '-10-01' + END AS "DOD_PERIOD" FROM deaths AS D LEFT OUTER JOIN P ON D.CASE_NUMBER = P.CASE_NUMBER LEFT OUTER JOIN O ON D.CASE_NUMBER = O.CASE_NUMBER diff --git a/src/assets/pages/vis6-maps-of-health/data.sql b/apps/a2agc/src/assets/pages/vis6-maps-of-health/data.sql similarity index 88% rename from src/assets/pages/vis6-maps-of-health/data.sql rename to apps/a2agc/src/assets/pages/vis6-maps-of-health/data.sql index 96aa6e51..602adf4a 100644 --- a/src/assets/pages/vis6-maps-of-health/data.sql +++ b/apps/a2agc/src/assets/pages/vis6-maps-of-health/data.sql @@ -123,7 +123,13 @@ WITH TOTAL_PRESCRIPTIONS, -(TOTAL_PRESCRIPTIONS * 1000000 + D.CASE_NUMBER) AS PRESCRIPTIONS_RANK, -(AGE * 1000000 + D.CASE_NUMBER) AS AGE_RANK, - DOD, AGE, SEX, RACE, OPIOID_PRESCRIPTIONS, OVERDOSES, INCARCERATIONS, HEALTH_ENCOUNTERS, ALL_TYPES, + strftime('%Y', DOD) || CASE + WHEN cast(strftime('%m', DOD) as integer) BETWEEN 1 AND 3 THEN '-01-01' + WHEN cast(strftime('%m', DOD) as integer) BETWEEN 4 and 6 THEN '-04-01' + WHEN cast(strftime('%m', DOD) as integer) BETWEEN 7 and 9 THEN '-07-01' + ELSE '-10-01' + END AS "DOD_PERIOD", + AGE, SEX, RACE, OPIOID_PRESCRIPTIONS, OVERDOSES, INCARCERATIONS, HEALTH_ENCOUNTERS, ALL_TYPES, CASE OVERDOSES WHEN 0 THEN '9999-01-01' ELSE PERIOD @@ -144,11 +150,11 @@ WITH MIN(TIME_OF_OD) OVER(PARTITION BY CASE_NUMBER) AS TIME_FIRST_OD, MIN(TIME_OF_RX) OVER(PARTITION BY CASE_NUMBER) AS TIME_FIRST_RX, CASE - WHEN julianday(DOD) - julianday(MIN(TIME_OF_OD) OVER(PARTITION BY CASE_NUMBER)) >= 0 THEN -(julianday(DOD) - julianday(MIN(TIME_OF_OD) OVER(PARTITION BY CASE_NUMBER))) + WHEN julianday(DOD_PERIOD) - julianday(MIN(TIME_OF_OD) OVER(PARTITION BY CASE_NUMBER)) >= 0 THEN -(julianday(DOD_PERIOD) - julianday(MIN(TIME_OF_OD) OVER(PARTITION BY CASE_NUMBER))) ELSE 0 END AS OD_DIFF, CASE - WHEN julianday(DOD) - julianday(MIN(TIME_OF_RX) OVER(PARTITION BY CASE_NUMBER)) >= 0 THEN -(julianday(DOD) - julianday(MIN(TIME_OF_RX) OVER(PARTITION BY CASE_NUMBER))) + WHEN julianday(DOD_PERIOD) - julianday(MIN(TIME_OF_RX) OVER(PARTITION BY CASE_NUMBER)) >= 0 THEN -(julianday(DOD_PERIOD) - julianday(MIN(TIME_OF_RX) OVER(PARTITION BY CASE_NUMBER))) ELSE 0 END AS RX_DIFF FROM CN_POJH diff --git a/src/assets/pages/vis6-maps-of-health/vis.vl.json b/apps/a2agc/src/assets/pages/vis6-maps-of-health/vis.vl.json similarity index 99% rename from src/assets/pages/vis6-maps-of-health/vis.vl.json rename to apps/a2agc/src/assets/pages/vis6-maps-of-health/vis.vl.json index a49704d6..6e511553 100644 --- a/src/assets/pages/vis6-maps-of-health/vis.vl.json +++ b/apps/a2agc/src/assets/pages/vis6-maps-of-health/vis.vl.json @@ -7,7 +7,7 @@ "type": "csv", "parse": { "PERIOD": "date", - "DOD": "date", + "DOD_PERIOD": "date", "RANK": "number", "AGE": "number", "AGE_RANK": "number", @@ -69,7 +69,7 @@ }, "transform": [ { - "calculate": "round(12 * (datum.DOD - datum.PERIOD) / 3.154e+10)", + "calculate": "round(12 * (datum.DOD_PERIOD - datum.PERIOD) / 3.154e+10)", "as": "TIME_BEFORE_DEATH" }, { diff --git a/src/assets/pages/vis6-maps-of-health/vis2.vl.json b/apps/a2agc/src/assets/pages/vis6-maps-of-health/vis2.vl.json similarity index 99% rename from src/assets/pages/vis6-maps-of-health/vis2.vl.json rename to apps/a2agc/src/assets/pages/vis6-maps-of-health/vis2.vl.json index 71bc6b70..2a244bf7 100644 --- a/src/assets/pages/vis6-maps-of-health/vis2.vl.json +++ b/apps/a2agc/src/assets/pages/vis6-maps-of-health/vis2.vl.json @@ -7,7 +7,7 @@ "type": "csv", "parse": { "PERIOD": "date", - "DOD": "date", + "DOD_PERIOD": "date", "RANK": "number", "AGE": "number", "AGE_RANK": "number", @@ -69,7 +69,7 @@ }, "transform": [ { - "calculate": "round(12 * (datum.DOD - datum.PERIOD) / 3.154e+10)", + "calculate": "round(12 * (datum.DOD_PERIOD - datum.PERIOD) / 3.154e+10)", "as": "TIME_BEFORE_DEATH" }, { diff --git a/src/assets/pages/vis6-maps-of-health/vis3.vl.json b/apps/a2agc/src/assets/pages/vis6-maps-of-health/vis3.vl.json similarity index 99% rename from src/assets/pages/vis6-maps-of-health/vis3.vl.json rename to apps/a2agc/src/assets/pages/vis6-maps-of-health/vis3.vl.json index e33a993a..5dc6cf14 100644 --- a/src/assets/pages/vis6-maps-of-health/vis3.vl.json +++ b/apps/a2agc/src/assets/pages/vis6-maps-of-health/vis3.vl.json @@ -7,7 +7,7 @@ "type": "csv", "parse": { "PERIOD": "date", - "DOD": "date", + "DOD_PERIOD": "date", "RANK": "number", "AGE": "number", "AGE_RANK": "number", @@ -69,7 +69,7 @@ }, "transform": [ { - "calculate": "round(12 * (datum.DOD - datum.PERIOD) / 3.154e+10)", + "calculate": "round(12 * (datum.DOD_PERIOD - datum.PERIOD) / 3.154e+10)", "as": "TIME_BEFORE_DEATH" }, { diff --git a/src/configs/config.ts b/apps/a2agc/src/configs/config.ts similarity index 76% rename from src/configs/config.ts rename to apps/a2agc/src/configs/config.ts index 4c6aa4cb..dc9360ae 100644 --- a/src/configs/config.ts +++ b/apps/a2agc/src/configs/config.ts @@ -1,3 +1,6 @@ +/** + * Dataset config settings + */ export const DATA_CONFIG = { datasetsPath: 'assets/generated/aggregate-table-data.json', subLabel: 'Drug', @@ -6,16 +9,26 @@ export const DATA_CONFIG = { }; +/** + * Chart type names + */ export enum ChartType { pie = 'pie-chart', verticalBar = 'vertical-bar-chart', horizontalBar = 'horizontal-bar-chart' } +/** + * Chart configuration interface + */ export interface ChartConfig { + /** Maximum distinct values allowed in chart */ maxDistinctValues: number; } +/** + * Chart config settings + */ export const CHART_CONFIG: Record = { [ChartType.pie]: { maxDistinctValues: 4 diff --git a/src/environments/environment.prod.ts b/apps/a2agc/src/environments/environment.prod.ts similarity index 100% rename from src/environments/environment.prod.ts rename to apps/a2agc/src/environments/environment.prod.ts diff --git a/src/environments/environment.ts b/apps/a2agc/src/environments/environment.ts similarity index 100% rename from src/environments/environment.ts rename to apps/a2agc/src/environments/environment.ts diff --git a/src/favicon.ico b/apps/a2agc/src/favicon.ico similarity index 100% rename from src/favicon.ico rename to apps/a2agc/src/favicon.ico diff --git a/src/index.html b/apps/a2agc/src/index.html similarity index 100% rename from src/index.html rename to apps/a2agc/src/index.html diff --git a/src/main.ts b/apps/a2agc/src/main.ts similarity index 100% rename from src/main.ts rename to apps/a2agc/src/main.ts diff --git a/src/polyfills.ts b/apps/a2agc/src/polyfills.ts similarity index 100% rename from src/polyfills.ts rename to apps/a2agc/src/polyfills.ts diff --git a/src/styles.scss b/apps/a2agc/src/styles.scss similarity index 100% rename from src/styles.scss rename to apps/a2agc/src/styles.scss diff --git a/src/test.ts b/apps/a2agc/src/test.ts similarity index 100% rename from src/test.ts rename to apps/a2agc/src/test.ts diff --git a/src/themes.scss b/apps/a2agc/src/themes.scss similarity index 100% rename from src/themes.scss rename to apps/a2agc/src/themes.scss diff --git a/src/themes/default/_colors.scss b/apps/a2agc/src/themes/default/_colors.scss similarity index 100% rename from src/themes/default/_colors.scss rename to apps/a2agc/src/themes/default/_colors.scss diff --git a/src/themes/default/_index.scss b/apps/a2agc/src/themes/default/_index.scss similarity index 100% rename from src/themes/default/_index.scss rename to apps/a2agc/src/themes/default/_index.scss diff --git a/src/themes/default/_typography.scss b/apps/a2agc/src/themes/default/_typography.scss similarity index 100% rename from src/themes/default/_typography.scss rename to apps/a2agc/src/themes/default/_typography.scss diff --git a/tsconfig.app.json b/apps/a2agc/tsconfig.app.json similarity index 75% rename from tsconfig.app.json rename to apps/a2agc/tsconfig.app.json index 82d91dc4..108fd663 100644 --- a/tsconfig.app.json +++ b/apps/a2agc/tsconfig.app.json @@ -1,6 +1,6 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { - "extends": "./tsconfig.json", + "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", "types": [] @@ -10,6 +10,7 @@ "src/polyfills.ts" ], "include": [ - "src/**/*.d.ts" + "src/**/*.d.ts", + "src/**/*.ts" ] } diff --git a/apps/a2agc/tsconfig.doc.json b/apps/a2agc/tsconfig.doc.json new file mode 100644 index 00000000..db3176a1 --- /dev/null +++ b/apps/a2agc/tsconfig.doc.json @@ -0,0 +1,4 @@ +{ + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/environments/*.ts", "src/**/app-routing.module.ts"] +} diff --git a/tsconfig.spec.json b/apps/a2agc/tsconfig.spec.json similarity index 89% rename from tsconfig.spec.json rename to apps/a2agc/tsconfig.spec.json index 092345b0..082ccc7b 100644 --- a/tsconfig.spec.json +++ b/apps/a2agc/tsconfig.spec.json @@ -1,6 +1,6 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { - "extends": "./tsconfig.json", + "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", "types": [ diff --git a/compile-build-data.js b/compile-build-data.js index 0a358e73..732d24a0 100644 --- a/compile-build-data.js +++ b/compile-build-data.js @@ -22,5 +22,5 @@ process.exec('git log -1 --pretty="%ct" --date=local', {cwd: __dirname}, functio buildDate: new Date(${buildInfo.buildDate}) }; `; - fs.writeFileSync('src/app/build-info.ts', output); + fs.writeFileSync('apps/a2agc/src/app/build-info.ts', output); }); diff --git a/data-processor/constants.sh b/data-processor/constants.sh index 260290df..1738ef83 100644 --- a/data-processor/constants.sh +++ b/data-processor/constants.sh @@ -22,7 +22,7 @@ EDB="$OUT/a2agc.db.e" DATA_SOURCES="$ORIG/box-health/[Box Health] Final Datasets" -SCHEMA_DIR="$THIS/../src/assets/schema" +SCHEMA_DIR="$THIS/../apps/a2agc/src/assets/schema" SCHEMA_NAME=A2AGC SCHEMA="$SCHEMA_DIR/$SCHEMA_NAME.public.xml" diff --git a/data-processor/run.sh b/data-processor/run.sh index bba2388b..19d5c18b 100755 --- a/data-processor/run.sh +++ b/data-processor/run.sh @@ -4,6 +4,7 @@ source constants.sh echo Run started on $(date)... echo + for f in scripts/??-*.sh do echo Running $f... diff --git a/data-processor/schemaspy.properties b/data-processor/schemaspy.properties index c29c71d3..e53d1370 100644 --- a/data-processor/schemaspy.properties +++ b/data-processor/schemaspy.properties @@ -15,7 +15,7 @@ schemaspy.I=.*_raw schemaspy.meta=schemaspy.meta.xml # output dir to save generated files -schemaspy.o=../src/assets/schema +schemaspy.o=../apps/a2agc/src/assets/schema # Use vizjs instead of graphviz schemaspy.vizjs=true diff --git a/data-processor/scripts/01-check-source-data-exists.sh b/data-processor/scripts/01-check-source-data-exists.sh new file mode 100755 index 00000000..562a5341 --- /dev/null +++ b/data-processor/scripts/01-check-source-data-exists.sh @@ -0,0 +1,9 @@ +#!/bin/bash +source constants.sh +set -ev + +if [ ! -d "${DATA_SOURCES}" ] +then +echo "Data not found in ${DATA_SOURCES}" + exit -1 +fi diff --git a/data-processor/scripts/01-sync-remote-data.sh b/data-processor/scripts/01-sync-remote-data.sh deleted file mode 100755 index 894c41f1..00000000 --- a/data-processor/scripts/01-sync-remote-data.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -source constants.sh -set -ev - -if [ "${DOSYNC}" == "false" ] -then - exit -fi - -if [ -z "$LFTP_PASSWORD" ] -then - read -s -p "Password: " LFTP_PASSWORD -fi - -mkdir -p ${ORIG}/box-health - -lftp << EOF -set ftps:initial-prot ""; -set ftp:ssl-force true; -set ftp:ssl-protect-data true; -open --user ${BOX_USER} --password "${LFTP_PASSWORD}" ftps://ftp.box.com:990; -mirror --delete --no-perms --verbose "${REMOTE_DIR}" "${ORIG}/box-health"; -EOF diff --git a/data-processor/scripts/13-aggregate-table-data.sh b/data-processor/scripts/13-aggregate-table-data.sh index c2e28ac9..759e7be9 100755 --- a/data-processor/scripts/13-aggregate-table-data.sh +++ b/data-processor/scripts/13-aggregate-table-data.sh @@ -2,4 +2,4 @@ source constants.sh set -ev -python3 ./src/tables/data.py $DB $SCHEMA $COLUMN_DISTRIBUTION_OVERRIDES -o $AGGREGATE_DATA -d $OUT/site-data -s assets/generated +python3 ./src/tables/data.py $DB $SCHEMA $COLUMN_DISTRIBUTION_OVERRIDES -o $AGGREGATE_DATA -d $OUT/site-data -s ../apps/a2agc/src/assets/generated \ No newline at end of file diff --git a/data-processor/scripts/13-vis-geomap-opioid-deaths.sh b/data-processor/scripts/13-vis-geomap-opioid-deaths.sh index 052050e6..c7a99be8 100755 --- a/data-processor/scripts/13-vis-geomap-opioid-deaths.sh +++ b/data-processor/scripts/13-vis-geomap-opioid-deaths.sh @@ -4,4 +4,4 @@ set -ev OUT_DIR=${OUT}/site-data -sqlite3 -header -csv ${DB} < ../src/assets/pages/vis1-geomap-of-opioid-deaths/data.sql > ${OUT_DIR}/vis-geomap-opioid-deaths.csv \ No newline at end of file +sqlite3 -header -csv ${DB} < ../apps/a2agc/src/assets/pages/vis1-geomap-of-opioid-deaths/data.sql > ${OUT_DIR}/vis-geomap-opioid-deaths.csv diff --git a/data-processor/scripts/13-vis2-data.sh b/data-processor/scripts/13-vis2-data.sh index cc042684..9f459bd0 100755 --- a/data-processor/scripts/13-vis2-data.sh +++ b/data-processor/scripts/13-vis2-data.sh @@ -7,6 +7,6 @@ OUT_DIR=${OUT}/site-data/vis2-data mkdir -p ${OUT_DIR} -sqlite3 -header -csv ${DB} < ../src/assets/pages/vis2-age-and-gender/data.sql > ${OUT_DIR}/death-counts.csv +sqlite3 -header -csv ${DB} < ../apps/a2agc/src/assets/pages/vis2-age-and-gender/data.sql > ${OUT_DIR}/death-counts.csv python3 ${SRC_DIR}/census_data.py ${SRC_DIR}/census-data -o ${OUT_DIR}/census-counts.csv diff --git a/data-processor/scripts/13-vis4-data.sh b/data-processor/scripts/13-vis4-data.sh index a402d0b4..52a11ae8 100755 --- a/data-processor/scripts/13-vis4-data.sh +++ b/data-processor/scripts/13-vis4-data.sh @@ -5,4 +5,4 @@ OUT_DIR=${OUT}/site-data/visualization4 mkdir -p ${OUT_DIR} -sqlite3 -header -csv ${DB} < ../src/assets/pages/vis4-combined-visualization/data.sql > ${OUT_DIR}/data.csv +sqlite3 -header -csv ${DB} < ../apps/a2agc/src/assets/pages/vis4-combined-visualization/data.sql > ${OUT_DIR}/data.csv diff --git a/data-processor/scripts/13-vis5-data.sh b/data-processor/scripts/13-vis5-data.sh index 99c4ad89..db26a25e 100755 --- a/data-processor/scripts/13-vis5-data.sh +++ b/data-processor/scripts/13-vis5-data.sh @@ -6,4 +6,4 @@ OUT_DIR=${OUT}/site-data/visualization5 mkdir -p ${OUT_DIR} -sqlite3 -header -csv ${DB} < ../src/assets/pages/vis5-opioid-trajectories/data.sql > ${OUT_DIR}/data.csv +sqlite3 -header -csv ${DB} < ../apps/a2agc/src/assets/pages/vis5-opioid-trajectories/data.sql > ${OUT_DIR}/data.csv diff --git a/data-processor/scripts/13-vis6-data.sh b/data-processor/scripts/13-vis6-data.sh index 1e9e258f..998d0bca 100755 --- a/data-processor/scripts/13-vis6-data.sh +++ b/data-processor/scripts/13-vis6-data.sh @@ -6,6 +6,6 @@ OUT_DIR=${OUT}/site-data/visualization6 mkdir -p ${OUT_DIR} -sqlite3 -header -csv ${DB} < ../src/assets/pages/vis6-maps-of-health/data-aggregate.sql > ${OUT_DIR}/data-aggregate.csv +sqlite3 -header -csv ${DB} < ../apps/a2agc/src/assets/pages/vis6-maps-of-health/data-aggregate.sql > ${OUT_DIR}/data-aggregate.csv -sqlite3 -header -csv ${DB} < ../src/assets/pages/vis6-maps-of-health/data.sql > ${OUT_DIR}/data.csv +sqlite3 -header -csv ${DB} < ../apps/a2agc/src/assets/pages/vis6-maps-of-health/data.sql > ${OUT_DIR}/data.csv diff --git a/data-processor/scripts/20-ng-generate-site.sh b/data-processor/scripts/20-ng-generate-site.sh index 0b200d75..b61944a3 100755 --- a/data-processor/scripts/20-ng-generate-site.sh +++ b/data-processor/scripts/20-ng-generate-site.sh @@ -5,9 +5,9 @@ set -ev cd ../ # Clear old data and copy latest -rm -rf src/assets/generated -mkdir -p src/assets/generated -cp -r $OUT/site-data/* src/assets/generated -cp ../CHANGELOG.md src/assets/generated +rm -rf apps/a2agc/src/assets/generated +mkdir -p apps/a2agc/src/assets/generated +cp -r $OUT/site-data/* apps/a2agc/src/assets/generated +cp CHANGELOG.md apps/a2agc/src/assets/generated ng build --prod diff --git a/data-processor/scripts/20x-ng-serve-site.sh b/data-processor/scripts/20x-ng-serve-site.sh index 82e24c3c..cfa2725b 100755 --- a/data-processor/scripts/20x-ng-serve-site.sh +++ b/data-processor/scripts/20x-ng-serve-site.sh @@ -5,9 +5,9 @@ set -ev cd ../ # Clear old data and copy latest -rm -rf src/assets/generated -mkdir -p src/assets/generated -cp -r $OUT/site-data/* src/assets/generated -cp ../CHANGELOG.md src/assets/generated +rm -rf apps/a2agc/src/assets/generated +mkdir -p apps/a2agc/src/assets/generated +cp -r $OUT/site-data/* apps/a2agc/src/assets/generated +cp CHANGELOG.md apps/a2agc/src/assets/generated ng serve --port $DEV_PORT --poll 5000 diff --git a/data-processor/scripts/20x-serve-generated-data.sh b/data-processor/scripts/20x-serve-generated-data.sh index 217623d6..94122c1c 100755 --- a/data-processor/scripts/20x-serve-generated-data.sh +++ b/data-processor/scripts/20x-serve-generated-data.sh @@ -2,12 +2,12 @@ source constants.sh set -ev -cd .. +cd ../ # Clear old data and copy latest -rm -rf src/assets/generated -mkdir -p src/assets/generated -cp -r $OUT/site-data/* src/assets/generated -cp ../CHANGELOG.md src/assets/generated +rm -rf apps/a2agc/src/assets/generated +mkdir -p apps/a2agc/src/assets/generated +cp -r $OUT/site-data/* apps/a2agc/src/assets/generated +cp CHANGELOG.md apps/a2agc/src/assets/generated -http-server -c-1 --cors=* -p $DEV_PORT src +http-server -c-1 --cors=* -p $DEV_PORT apps/a2agc/src diff --git a/data-processor/scripts/20x-serve-site.sh b/data-processor/scripts/20x-serve-site.sh index 129509c5..dbe7c47e 100755 --- a/data-processor/scripts/20x-serve-site.sh +++ b/data-processor/scripts/20x-serve-site.sh @@ -2,5 +2,7 @@ source constants.sh set -ev -http-server -c-1 --cors=* -p $DEV_PORT ../dist +cd ../ + +http-server -c-1 --cors=* -p $DEV_PORT dist diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json index 426058ef..b90b869b 100644 --- a/e2e/tsconfig.json +++ b/e2e/tsconfig.json @@ -1,6 +1,6 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { - "extends": "../tsconfig.json", + "extends": "../apps/a2agc/tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/e2e", "module": "commonjs", diff --git a/package-lock.json b/package-lock.json index f8a9bcc0..09d10afa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "@angular-eslint/template-parser": "^17.2.1", "@angular/cli": "^17.1.2", "@angular/compiler-cli": "^17.1.2", + "@compodoc/compodoc": "^1.1.23", "@nx/angular": "18.0.4", "@nx/workspace": "18.0.4", "@schematics/angular": "^17.1.2", @@ -86,6 +87,12 @@ "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", "dev": true }, + "node_modules/@aduh95/viz.js": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@aduh95/viz.js/-/viz.js-3.4.0.tgz", + "integrity": "sha512-KI2nVf9JdwWCXqK6RVf+9/096G7VWN4Z84mnynlyZKao2xQENW8WNEjLmvdlxS5X8PNWXFC1zqwm7tveOXw/4A==", + "dev": true + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -2078,6 +2085,23 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -3425,6 +3449,337 @@ "node": ">=0.1.90" } }, + "node_modules/@compodoc/compodoc": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.1.23.tgz", + "integrity": "sha512-5Zfx+CHKTxLD+TxCGt1U8krnEBCWPVxCLt3jCJEN55AzhTluo8xlMenaXlJsuVqL4Lmo/OTTzEXrm9zoQKh/3w==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@angular-devkit/schematics": "14.2.12", + "@babel/core": "^7.23.3", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/preset-env": "^7.23.3", + "@compodoc/live-server": "^1.2.3", + "@compodoc/ngd-transformer": "^2.1.3", + "bootstrap.native": "^5.0.10", + "chalk": "4.1.2", + "cheerio": "^1.0.0-rc.12", + "chokidar": "^3.5.3", + "colors": "1.4.0", + "commander": "^11.1.0", + "cosmiconfig": "^8.3.6", + "decache": "^4.6.2", + "es6-shim": "^0.35.8", + "fancy-log": "^2.0.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.1.1", + "glob": "^10.3.10", + "handlebars": "^4.7.8", + "html-entities": "^2.4.0", + "i18next": "^23.7.6", + "json5": "^2.2.3", + "lodash": "^4.17.21", + "loglevel": "^1.8.1", + "loglevel-plugin-prefix": "^0.8.4", + "lunr": "^2.3.9", + "marked": "7.0.3", + "minimist": "^1.2.8", + "opencollective-postinstall": "^2.0.3", + "os-name": "4.0.1", + "pdfjs-dist": "2.12.313", + "pdfmake": "^0.2.8", + "prismjs": "^1.29.0", + "semver": "^7.5.4", + "svg-pan-zoom": "^3.6.1", + "tablesort": "^5.3.0", + "traverse": "^0.6.7", + "ts-morph": "^20.0.0", + "uuid": "^9.0.1", + "vis": "^4.21.0-EOL", + "zepto": "^1.2.0" + }, + "bin": { + "compodoc": "bin/index-cli.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@compodoc/compodoc/node_modules/@angular-devkit/core": { + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.12.tgz", + "integrity": "sha512-tg1+deEZdm3fgk2BQ6y7tujciL6qhtN5Ums266lX//kAZeZ4nNNXTBT+oY5xgfjvmLbW+xKg0XZrAS0oIRKY5g==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@compodoc/compodoc/node_modules/@angular-devkit/schematics": { + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.12.tgz", + "integrity": "sha512-MN5yGR+SSSPPBBVMf4cifDJn9u0IYvxiHst+HWokH2AkBYy+vB1x8jYES2l1wkiISD7nvjTixfqX+Y95oMBoLg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.12", + "jsonc-parser": "3.1.0", + "magic-string": "0.26.2", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@compodoc/compodoc/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@compodoc/compodoc/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@compodoc/compodoc/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@compodoc/compodoc/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@compodoc/compodoc/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@compodoc/compodoc/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@compodoc/compodoc/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@compodoc/compodoc/node_modules/jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "node_modules/@compodoc/compodoc/node_modules/magic-string": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", + "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@compodoc/compodoc/node_modules/marked": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.3.tgz", + "integrity": "sha512-ev2uM40p0zQ/GbvqotfKcSWEa59fJwluGZj5dcaUOwDRrB1F3dncdXy8NWUApk4fi8atU3kTBOwjyjZ0ud0dxw==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/@compodoc/compodoc/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@compodoc/compodoc/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@compodoc/compodoc/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@compodoc/live-server": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@compodoc/live-server/-/live-server-1.2.3.tgz", + "integrity": "sha512-hDmntVCyjjaxuJzPzBx68orNZ7TW4BtHWMnXlIVn5dqhK7vuFF/11hspO1cMmc+2QTYgqde1TBcb3127S7Zrow==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "colors": "1.4.0", + "connect": "^3.7.0", + "cors": "latest", + "event-stream": "4.0.1", + "faye-websocket": "0.11.x", + "http-auth": "4.1.9", + "http-auth-connect": "^1.0.5", + "morgan": "^1.10.0", + "object-assign": "latest", + "open": "8.4.0", + "proxy-middleware": "latest", + "send": "latest", + "serve-index": "^1.9.1" + }, + "bin": { + "live-server": "live-server.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@compodoc/live-server/node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@compodoc/ngd-core": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@compodoc/ngd-core/-/ngd-core-2.1.1.tgz", + "integrity": "sha512-Z+wE6wWZYVnudRYg6qunDlyh3Orw39Ib66Gvrz5kX5u7So+iu3tr6sQJdqH6yGS3hAjig5avlfhWLlgsb6/x1Q==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "fancy-log": "^2.0.0", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@compodoc/ngd-transformer": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@compodoc/ngd-transformer/-/ngd-transformer-2.1.3.tgz", + "integrity": "sha512-oWxJza7CpWR8/FeWYfE6j+jgncnGBsTWnZLt5rD2GUpsGSQTuGrsFPnmbbaVLgRS5QIVWBJYke7QFBr/7qVMWg==", + "dev": true, + "dependencies": { + "@aduh95/viz.js": "3.4.0", + "@compodoc/ngd-core": "~2.1.1", + "dot": "^2.0.0-beta.1", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -3960,6 +4315,59 @@ "node": ">=14" } }, + "node_modules/@foliojs-fork/fontkit": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz", + "integrity": "sha512-U589voc2/ROnvx1CyH9aNzOQWJp127JGU1QAylXGQ7LoEAF6hMmahZLQ4eqAcgHUw+uyW4PjtCItq9qudPkK3A==", + "dev": true, + "dependencies": { + "@foliojs-fork/restructure": "^2.0.2", + "brfs": "^2.0.0", + "brotli": "^1.2.0", + "browserify-optional": "^1.0.1", + "clone": "^1.0.4", + "deep-equal": "^1.0.0", + "dfa": "^1.2.0", + "tiny-inflate": "^1.0.2", + "unicode-properties": "^1.2.2", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/@foliojs-fork/linebreak": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@foliojs-fork/linebreak/-/linebreak-1.1.1.tgz", + "integrity": "sha512-pgY/+53GqGQI+mvDiyprvPWgkTlVBS8cxqee03ejm6gKAQNsR1tCYCIvN9FHy7otZajzMqCgPOgC4cHdt4JPig==", + "dev": true, + "dependencies": { + "base64-js": "1.3.1", + "brfs": "^2.0.2", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/@foliojs-fork/linebreak/node_modules/base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "node_modules/@foliojs-fork/pdfkit": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@foliojs-fork/pdfkit/-/pdfkit-0.14.0.tgz", + "integrity": "sha512-nMOiQAv6id89MT3tVTCgc7HxD5ZMANwio2o5yvs5sexQkC0KI3BLaLakpsrHmFfeGFAhqPmZATZGbJGXTUebpg==", + "dev": true, + "dependencies": { + "@foliojs-fork/fontkit": "^1.9.1", + "@foliojs-fork/linebreak": "^1.1.1", + "crypto-js": "^4.2.0", + "png-js": "^1.0.0" + } + }, + "node_modules/@foliojs-fork/restructure": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@foliojs-fork/restructure/-/restructure-2.0.2.tgz", + "integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==", + "dev": true + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -7977,6 +8385,26 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, + "node_modules/@thednp/event-listener": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@thednp/event-listener/-/event-listener-2.0.4.tgz", + "integrity": "sha512-sc4B7AzYAIvnGnivirq0XyR7LfzEDhGiiB70Q0qdNn8wSJ2pL1buVAsEZxrlc47qRJiBV4YIP+BFkyMm2r3NLg==", + "dev": true, + "engines": { + "node": ">=16", + "pnpm": ">=8.6.0" + } + }, + "node_modules/@thednp/shorty": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@thednp/shorty/-/shorty-2.0.0.tgz", + "integrity": "sha512-kwtLivCxYIoFfGIVU4NlZtfdA/zxZ6X8UcWaJrb7XqU3WQ4Q1p5IaZlLBfOVAO06WH5oWE87QUdK/dS56Wnfjg==", + "dev": true, + "engines": { + "node": ">=16", + "pnpm": ">=8.6.0" + } + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -7986,8 +8414,50 @@ "node": ">=10.13.0" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", + "node_modules/@ts-morph/common": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.21.0.tgz", + "integrity": "sha512-ES110Mmne5Vi4ypUKrtVQfXFDtCsDXiUiGxF6ILVlE90dDD4fdpC1LSjydl/ml7xJWKSDZwUYD2zkOePMSrPBA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.12", + "minimatch": "^7.4.3", + "mkdirp": "^2.1.6", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@ts-morph/common/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true @@ -9113,6 +9583,38 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-node/node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/acorn-walk": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", @@ -9237,6 +9739,16 @@ "ajv": "^8.8.2" } }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.4.2" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -9318,6 +9830,27 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/apache-crypt": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.6.tgz", + "integrity": "sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==", + "dev": true, + "dependencies": { + "unix-crypt-td-js": "^1.1.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/apache-md5": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.8.tgz", + "integrity": "sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -9345,6 +9878,12 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, + "node_modules/array-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", + "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==", + "dev": true + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -9390,6 +9929,39 @@ "node": ">=0.8" } }, + "node_modules/ast-transform": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/ast-transform/-/ast-transform-0.0.0.tgz", + "integrity": "sha512-e/JfLiSoakfmL4wmTGPjv0HpTICVmxwXgYOB8x+mzozHL8v+dSfCbrJ8J8hJ0YBP0XcYu1aLZ6b/3TnxNK3P2A==", + "dev": true, + "dependencies": { + "escodegen": "~1.2.0", + "esprima": "~1.0.4", + "through": "~2.3.4" + } + }, + "node_modules/ast-transform/node_modules/esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ast-types": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.7.8.tgz", + "integrity": "sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", @@ -9842,6 +10414,12 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "dev": true + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -9941,6 +10519,20 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/bootstrap.native": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-5.0.11.tgz", + "integrity": "sha512-bk2i4sQcQk2KuCTs1yygTa+JGjZOpKzIZ/It6TZZOO/Q+PmVGuKuIbrznXF64BUFxXaPNy7gO9LnE7vjGdauSQ==", + "dev": true, + "dependencies": { + "@thednp/event-listener": "^2.0.4", + "@thednp/shorty": "^2.0.0" + }, + "engines": { + "node": ">=16", + "pnpm": ">=8.6.0" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -9962,6 +10554,56 @@ "node": ">=8" } }, + "node_modules/brfs": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brfs/-/brfs-2.0.2.tgz", + "integrity": "sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ==", + "dev": true, + "dependencies": { + "quote-stream": "^1.0.1", + "resolve": "^1.1.5", + "static-module": "^3.0.2", + "through2": "^2.0.0" + }, + "bin": { + "brfs": "bin/cmd.js" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dev": true, + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "dependencies": { + "resolve": "1.1.7" + } + }, + "node_modules/browser-resolve/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true + }, + "node_modules/browserify-optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-optional/-/browserify-optional-1.0.1.tgz", + "integrity": "sha512-VrhjbZ+Ba5mDiSYEuPelekQMfTbhcA2DhLk2VQWqdcCROWeFqlTcXZ7yfRkXCIl8E+g4gINJYJiRB7WEtfomAQ==", + "dev": true, + "dependencies": { + "ast-transform": "0.0.0", + "ast-types": "^0.7.0", + "browser-resolve": "^1.8.1" + } + }, "node_modules/browserslist": { "version": "4.22.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz", @@ -10070,6 +10712,15 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -10178,6 +10829,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -10273,6 +10933,44 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -10484,6 +11182,12 @@ "node": ">= 0.12.0" } }, + "node_modules/code-block-writer": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", + "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", + "dev": true + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -10505,6 +11209,15 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -10638,6 +11351,51 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -10979,6 +11737,12 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "dev": true + }, "node_modules/css-declaration-sorter": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.1.1.tgz", @@ -11297,6 +12061,16 @@ "cytoscape": "^3.2.0" } }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "node_modules/d3": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", @@ -11778,6 +12552,12 @@ "lodash-es": "^4.17.21" } }, + "node_modules/dash-ast": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz", + "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==", + "dev": true + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -11822,6 +12602,15 @@ } } }, + "node_modules/decache": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/decache/-/decache-4.6.2.tgz", + "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==", + "dev": true, + "dependencies": { + "callsite": "^1.0.0" + } + }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -11858,6 +12647,26 @@ } } }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dev": true, + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -11898,18 +12707,20 @@ } }, "node_modules/define-data-property": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", - "integrity": "sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { + "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.2", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-lazy-prop": { @@ -11921,6 +12732,23 @@ "node": ">=8" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/del": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", @@ -12069,6 +12897,12 @@ "detect-port": "bin/detect-port.js" } }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "dev": true + }, "node_modules/di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -12202,6 +13036,12 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dot": { + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/dot/-/dot-2.0.0-beta.1.tgz", + "integrity": "sha512-kxM7fSnNQTXOmaeGuBSXM8O3fEsBb7XSDBllkGbRwa0lJSJTxxDE/4eSNGLKZUmlFw0f1vJ5qSV2BljrgQtgIA==", + "dev": true + }, "node_modules/dotenv": { "version": "16.3.2", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz", @@ -12229,12 +13069,51 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -12278,6 +13157,15 @@ "integrity": "sha512-JWKDyqAdltuUcyxaECtYG6H4sqysXSLeoXuGUBfRNESMTkj+w+qdb0jya8Z/WI0jVd03WQtCGhS6FOFtlhD5FQ==", "optional": true }, + "node_modules/emitter-component": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", + "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", @@ -12459,6 +13347,18 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", @@ -12474,6 +13374,47 @@ "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true }, + "node_modules/es5-ext": { + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.63.tgz", + "integrity": "sha512-hUCZd2Byj/mNKjfP9jXrdVZ62B8KuA/VoK7X8nUh5qT+AxDmcbvZz041oDVZdbIN1qW6XY9VDNwzkvKnZvK2TQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, "node_modules/es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -12489,6 +13430,45 @@ "es6-promise": "^4.0.3" } }, + "node_modules/es6-set": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.6.tgz", + "integrity": "sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "es6-iterator": "~2.0.3", + "es6-symbol": "^3.1.3", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-set/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, + "node_modules/es6-shim": { + "version": "0.35.8", + "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.8.tgz", + "integrity": "sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==", + "dev": true + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, "node_modules/esbuild": { "version": "0.19.11", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", @@ -12562,6 +13542,71 @@ "node": ">=0.8.0" } }, + "node_modules/escodegen": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.2.0.tgz", + "integrity": "sha512-yLy3Cc+zAC0WSmoT2fig3J87TpQ8UaZGx8ahCAs9FL8qNbyV7CVyPKS74DG4bsHiL5ew9sxdYx131OkBQMFnvA==", + "dev": true, + "dependencies": { + "esprima": "~1.0.4", + "estraverse": "~1.5.0", + "esutils": "~1.0.0" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.4.0" + }, + "optionalDependencies": { + "source-map": "~0.1.30" + } + }, + "node_modules/escodegen/node_modules/esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", + "integrity": "sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/escodegen/node_modules/esutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", + "integrity": "sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==", + "dev": true, + "optional": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/eslint": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", @@ -12887,6 +13932,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -12950,6 +14016,12 @@ "node": ">=4.0" } }, + "node_modules/estree-is-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-is-function/-/estree-is-function-1.0.0.tgz", + "integrity": "sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA==", + "dev": true + }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -12974,6 +14046,31 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/event-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", + "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -13175,6 +14272,21 @@ "node": ">= 0.8" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -13216,6 +14328,18 @@ "node >=0.6.0" ] }, + "node_modules/fancy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", + "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", + "dev": true, + "dependencies": { + "color-support": "^1.1.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -13768,6 +14892,12 @@ "node": ">= 0.6" } }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -13835,6 +14965,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -13844,6 +14983,12 @@ "node": ">=6.9.0" } }, + "node_modules/get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -14023,12 +15168,51 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -14080,6 +15264,15 @@ "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", "dev": true }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -14111,12 +15304,12 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14146,6 +15339,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -14306,6 +15514,39 @@ "entities": "^4.4.0" } }, + "node_modules/http-auth": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-4.1.9.tgz", + "integrity": "sha512-kvPYxNGc9EKGTXvOMnTBQw2RZfuiSihK/mLw/a4pbtRueTE45S55Lw/3k5CktIf7Ak0veMKEIteDj4YkNmCzmQ==", + "dev": true, + "dependencies": { + "apache-crypt": "^1.1.2", + "apache-md5": "^1.0.6", + "bcryptjs": "^2.4.3", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/http-auth-connect": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/http-auth-connect/-/http-auth-connect-1.0.6.tgz", + "integrity": "sha512-yaO0QSCPqGCjPrl3qEEHjJP+lwZ6gMpXLuCBE06eWwcXomkI5TARtu0kxf9teFuBj6iaV3Ybr15jaWUvbzNzHw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/http-auth/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -14546,6 +15787,29 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "23.10.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.0.tgz", + "integrity": "sha512-/TgHOqsa7/9abUKJjdPeydoyDc0oTi/7u9F8lMSj6ufg4cbC1Oj3f/Jja7zj7WRIhEQKB7Q4eN6y68I9RDxxGQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -14786,6 +16050,22 @@ "node": ">= 10" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -14831,6 +16111,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -14980,6 +16275,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -17347,6 +18658,12 @@ "node": ">= 12" } }, + "node_modules/keycharm": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", + "integrity": "sha512-i/XBRTiLqRConPKioy2oq45vbv04e8x59b0mnsIRQM+7Ec/8BC7UcL5pnC4FMeGb8KwG7q4wOMw7CtNZf5tiIg==", + "dev": true + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -17757,6 +19074,25 @@ "node": ">=8.0" } }, + "node_modules/loglevel": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loglevel-plugin-prefix": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", + "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", + "dev": true + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -17766,6 +19102,24 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/macos-release": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", + "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/magic-string": { "version": "0.30.5", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", @@ -17830,6 +19184,12 @@ "tmpl": "1.0.5" } }, + "node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", + "dev": true + }, "node_modules/marked": { "version": "9.1.6", "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", @@ -17912,6 +19272,24 @@ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, + "node_modules/merge-source-map": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", + "integrity": "sha512-PGSmS0kfnTnMJCzJ16BLLCEe6oeYCamKFFdQKshi4BmM6FUwipjVOcBFGxqtQtirtAG4iZvHlqST9CpZKqlRjA==", + "dev": true, + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/merge-source-map/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -18720,6 +20098,58 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dev": true, + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -18835,6 +20265,12 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, "node_modules/ng-packagr": { "version": "17.1.2", "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-17.1.2.tgz", @@ -19515,6 +20951,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -19583,6 +21044,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true, + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -19714,6 +21184,22 @@ "node": ">=8" } }, + "node_modules/os-name": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "dev": true, + "dependencies": { + "macos-release": "^2.5.0", + "windows-release": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -19916,6 +21402,19 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parse5-sax-parser": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", @@ -19937,6 +21436,12 @@ "node": ">= 0.8" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -20016,6 +21521,56 @@ "node": ">=8" } }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pdfjs-dist": { + "version": "2.12.313", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.12.313.tgz", + "integrity": "sha512-1x6iXO4Qnv6Eb+YFdN5JdUzt4pAkxSp3aLAYPX93eQCyg/m7QFzXVWJHJVtoW48CI8HCXju4dSkhQZwoheL5mA==", + "dev": true, + "peerDependencies": { + "worker-loader": "^3.0.8" + }, + "peerDependenciesMeta": { + "worker-loader": { + "optional": true + } + } + }, + "node_modules/pdfmake": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.9.tgz", + "integrity": "sha512-LAtYwlR8cCQqbxESK2d50DYaVAzAC9Id9NjilRte6Tb9pyHUB+Z50nhD0imuBL0eDyXQKvEYSNjo3P5AOc2ZCg==", + "dev": true, + "dependencies": { + "@foliojs-fork/linebreak": "^1.1.1", + "@foliojs-fork/pdfkit": "^0.14.0", + "iconv-lite": "^0.6.3", + "xmldoc": "^1.1.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/pdfmake/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -20189,6 +21744,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==", + "dev": true + }, "node_modules/portfinder": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", @@ -20824,7 +22385,7 @@ "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "optional": true, + "devOptional": true, "engines": { "node": ">=6" } @@ -20863,6 +22424,15 @@ "node": ">=10" } }, + "node_modules/propagating-hammerjs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.5.0.tgz", + "integrity": "sha512-3PUXWmomwutoZfydC+lJwK1bKCh6sK6jZGB31RUX6+4EXzsbkDZrK4/sVR7gBrvJaEIwpTVyxQUAd29FKkmVdw==", + "dev": true, + "dependencies": { + "hammerjs": "^2.0.8" + } + }, "node_modules/protractor": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", @@ -21068,6 +22638,15 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, + "node_modules/proxy-middleware": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz", + "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -21081,6 +22660,16 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -21160,6 +22749,20 @@ } ] }, + "node_modules/quote-stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", + "integrity": "sha512-kKr2uQ2AokadPjvTyKJQad9xELbZwYzWlNfI3Uz2j/ib5u6H9lDP7fUUR//rMycd0gv4Z5P1qXMfXR8YpIxrjQ==", + "dev": true, + "dependencies": { + "buffer-equal": "0.0.1", + "minimist": "^1.1.3", + "through2": "^2.0.0" + }, + "bin": { + "quote-stream": "bin/cmd.js" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -21341,6 +22944,24 @@ "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", "dev": true }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regexpu-core": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", @@ -21849,6 +23470,21 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/scope-analyzer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/scope-analyzer/-/scope-analyzer-2.1.2.tgz", + "integrity": "sha512-5cfCmsTYV/wPaRIItNxatw02ua/MThdIUNnUOCYp+3LSEJvnG804ANw2VLaavNILIfWXF1D1G2KNANkBBvInwQ==", + "dev": true, + "dependencies": { + "array-from": "^2.1.1", + "dash-ast": "^2.0.1", + "es6-map": "^0.1.5", + "es6-set": "^0.1.5", + "es6-symbol": "^3.1.1", + "estree-is-function": "^1.0.0", + "get-assigned-identifiers": "^1.1.0" + } + }, "node_modules/secure-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", @@ -22131,6 +23767,21 @@ "node": ">= 0.4" } }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -22155,6 +23806,12 @@ "node": ">=8" } }, + "node_modules/shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==", + "dev": true + }, "node_modules/shallow-render": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/shallow-render/-/shallow-render-17.0.1.tgz", @@ -22415,6 +24072,13 @@ "node": ">=0.10.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -22447,98 +24111,303 @@ "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", "dev": true }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", + "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/static-eval": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.1.tgz", + "integrity": "sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==", + "dev": true, + "dependencies": { + "escodegen": "^2.1.0" + } + }, + "node_modules/static-eval/node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/static-eval/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-module": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/static-module/-/static-module-3.0.4.tgz", + "integrity": "sha512-gb0v0rrgpBkifXCa3yZXxqVmXDVE+ETXj6YlC/jt5VzOnGXR2C15+++eXuMDUYsePnbhf+lwW0pE1UXyOLtGCw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.3.0", + "concat-stream": "~1.6.0", + "convert-source-map": "^1.5.1", + "duplexer2": "~0.1.4", + "escodegen": "^1.11.1", + "has": "^1.0.1", + "magic-string": "0.25.1", + "merge-source-map": "1.0.4", + "object-inspect": "^1.6.0", + "readable-stream": "~2.3.3", + "scope-analyzer": "^2.0.1", + "shallow-copy": "~0.0.1", + "static-eval": "^2.0.5", + "through2": "~2.0.3" + } + }, + "node_modules/static-module/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/static-module/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/static-module/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-module/node_modules/magic-string": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.1.tgz", + "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.1" + } + }, + "node_modules/static-module/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" }, "engines": { - "node": ">=6.0.0" + "node": ">= 0.8.0" } }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "node_modules/static-module/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-module/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "node_modules/static-module/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "node_modules/static-module/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, + "optional": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/ssri": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", - "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", + "node_modules/static-module/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "safe-buffer": "~5.1.0" } }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "node_modules/static-module/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "dependencies": { - "escape-string-regexp": "^2.0.0" + "prelude-ls": "~1.1.2" }, "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, "node_modules/statuses": { @@ -22550,6 +24419,16 @@ "node": ">= 0.6" } }, + "node_modules/stream-combiner": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, "node_modules/streamroller": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", @@ -22828,6 +24707,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pan-zoom": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/svg-pan-zoom/-/svg-pan-zoom-3.6.1.tgz", + "integrity": "sha512-JaKkGHHfGvRrcMPdJWkssLBeWqM+Isg/a09H7kgNNajT1cX5AztDTNs+C8UzpCxjCTRrG34WbquwaovZbmSk9g==", + "dev": true + }, "node_modules/svgo": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", @@ -22871,6 +24756,12 @@ "node": ">=0.10" } }, + "node_modules/tablesort": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.3.0.tgz", + "integrity": "sha512-WkfcZBHsp47gVH9CBHG0ZXopriG01IA87arGrchvIe868d4RiXVvoYPS1zMq9IdW05kBs5iGsqxTABqLyWonbg==", + "dev": true + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -23119,6 +25010,46 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -23131,6 +25062,12 @@ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", "optional": true }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "dev": true + }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -23215,6 +25152,18 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/traverse": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -23335,6 +25284,16 @@ "node": ">=8" } }, + "node_modules/ts-morph": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-20.0.0.tgz", + "integrity": "sha512-JVmEJy2Wow5n/84I3igthL9sudQ8qzjh/6i4tmYCm6IqYyKFlNbJZi7oBdjyqcWSWYRu3CtL0xbT6fS03ESZIg==", + "dev": true, + "dependencies": { + "@ts-morph/common": "~0.21.0", + "code-block-writer": "^12.0.0" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -23513,6 +25472,12 @@ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -23565,6 +25530,12 @@ "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", "dev": true }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -23601,6 +25572,19 @@ "node": "*" } }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undici": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/undici/-/undici-6.2.1.tgz", @@ -23650,6 +25634,16 @@ "node": ">=4" } }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dev": true, + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", @@ -23659,6 +25653,22 @@ "node": ">=4" } }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dev": true, + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "dev": true + }, "node_modules/union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", @@ -23717,6 +25727,12 @@ "node": ">= 10.0.0" } }, + "node_modules/unix-crypt-td-js": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz", + "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==", + "dev": true + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -23790,11 +25806,11 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "devOptional": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "optional": true, "bin": { "uuid": "dist/bin/uuid" } @@ -24315,6 +26331,20 @@ "extsprintf": "^1.2.0" } }, + "node_modules/vis": { + "version": "4.21.0-EOL", + "resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0-EOL.tgz", + "integrity": "sha512-JVS1mywKg5S88XbkDJPfCb3n+vlg5fMA8Ae2hzs3KHAwD4ryM5qwlbFZ6ReDfY8te7I4NLCpuCoywJQEehvJlQ==", + "deprecated": "Please consider using https://github.com/visjs", + "dev": true, + "dependencies": { + "emitter-component": "^1.1.1", + "hammerjs": "^2.0.8", + "keycharm": "^0.2.0", + "moment": "^2.18.1", + "propagating-hammerjs": "^1.4.6" + } + }, "node_modules/vite": { "version": "5.0.12", "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", @@ -24941,6 +26971,83 @@ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, + "node_modules/windows-release": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "dev": true, + "dependencies": { + "execa": "^4.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/windows-release/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/windows-release/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/windows-release/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -25101,6 +27208,24 @@ "node": ">=4.0" } }, + "node_modules/xmldoc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.3.0.tgz", + "integrity": "sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==", + "dev": true, + "dependencies": { + "sax": "^1.2.4" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -25169,6 +27294,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zepto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz", + "integrity": "sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA==", + "dev": true + }, "node_modules/zone.js": { "version": "0.14.3", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.3.tgz", diff --git a/package.json b/package.json index 0e0da180..79f6f103 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "test": "nx test", "test:wsl": "nx test --browsers=ChromeHeadlessWSL", "lint": "nx lint", - "e2e": "nx e2e" + "e2e": "nx e2e", + "compodoc": "npx compodoc -p apps/a2agc/tsconfig.doc.json" }, "private": true, "dependencies": { @@ -51,6 +52,7 @@ "@angular-eslint/template-parser": "^17.2.1", "@angular/cli": "^17.1.2", "@angular/compiler-cli": "^17.1.2", + "@compodoc/compodoc": "^1.1.23", "@nx/angular": "18.0.4", "@nx/workspace": "18.0.4", "@schematics/angular": "^17.1.2", @@ -76,4 +78,3 @@ "typescript": "^5.3.3" } } - diff --git a/sonar-project.properties b/sonar-project.properties index c54fa3c0..55f0cdfb 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -16,7 +16,7 @@ sonar.links.issue=https://github.com/cns-iu/a2agc-dataset/issues sonar.host.url=https://sonarcloud.io sonar.organization=cns-iu -sonar.sources=src/app +sonar.sources=apps/a2agc/src/app sonar.exclusions=node_modules/**/* sonar.typescript.lcov.reportPaths=coverage/a2agc/lcov.info sonar.coverage.exclusions=**/*.spec.ts diff --git a/src/app/core/models/dataset.model.ts b/src/app/core/models/dataset.model.ts deleted file mode 100644 index fbf660d7..00000000 --- a/src/app/core/models/dataset.model.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Distribution } from './distribution.model'; - - -export interface Dataset { - name: string; - description: string; - variables: string[]; -} - -export interface DatasetVariable { - dataset: string; - name: string; - description: string; - - type: string; - nonNullCount: number; - percentMissing: number; - distribution: Distribution; -} - -export interface DatasetMetaEntry { - label: string; - value: string; -} diff --git a/src/app/core/models/pages.model.ts b/src/app/core/models/pages.model.ts deleted file mode 100644 index 830c4579..00000000 --- a/src/app/core/models/pages.model.ts +++ /dev/null @@ -1,7 +0,0 @@ - -export interface PageLink { - path: string; - title: string; - description?: string; - order?: number; -} diff --git a/src/app/shared/pipes/order-by/order-by.pipe.spec.ts b/src/app/shared/pipes/order-by/order-by.pipe.spec.ts deleted file mode 100644 index d892b313..00000000 --- a/src/app/shared/pipes/order-by/order-by.pipe.spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { OrderByPipe } from './order-by.pipe'; - -describe('OrderByPipe', () => { - it('create an instance', () => { - const pipe = new OrderByPipe(''); - expect(pipe).toBeTruthy(); - }); -}); diff --git a/tsconfig.json b/tsconfig.json index 47c5c316..376926c8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compileOnSave": false, "compilerOptions": { "baseUrl": "./", - "outDir": "./dist/out-tsc", + "outDir": "dist/out-tsc", "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "forceConsistentCasingInFileNames": true,