diff --git a/src/atlasViewer/atlasViewer.animation.ts b/src/atlasViewer/atlasViewer.animation.ts index 27985ba1adbc4a74c041f7cf0dd28cbadee7a955..4bbc770e04a3a0e4453516e64e2fef075d9f8dde 100644 --- a/src/atlasViewer/atlasViewer.animation.ts +++ b/src/atlasViewer/atlasViewer.animation.ts @@ -1,12 +1,12 @@ -import { trigger, transition, animate, style } from "@angular/animations"; +import { animate, style, transition, trigger } from "@angular/animations"; -export const colorAnimation = trigger('newEvent',[ +export const colorAnimation = trigger('newEvent', [ transition('* => *', [ animate('180ms ease-in', style({ - 'opacity' : '1.0' + opacity : '1.0', })), animate('380ms ease-out', style({ - 'opacity' : '0.0' - })) - ]) -]) \ No newline at end of file + opacity : '0.0', + })), + ]), +]) diff --git a/src/atlasViewer/atlasViewer.apiService.service.ts b/src/atlasViewer/atlasViewer.apiService.service.ts index f3f7771b88be779c23619eda2847746549cce6a7..6f39895df751b5bd4dcea1fc42e9c3987ef54149 100644 --- a/src/atlasViewer/atlasViewer.apiService.service.ts +++ b/src/atlasViewer/atlasViewer.apiService.service.ts @@ -1,42 +1,44 @@ import { Injectable } from "@angular/core"; -import { Store, select } from "@ngrx/store"; -import { safeFilter, getLabelIndexMap, getMultiNgIdsRegionsLabelIndexMap, IavRootStoreInterface } from "src/services/stateStore.service"; +import { select, Store } from "@ngrx/store"; import { Observable } from "rxjs"; -import { map, distinctUntilChanged } from "rxjs/operators"; +import { distinctUntilChanged, map } from "rxjs/operators"; +import { DialogService } from "src/services/dialogService.service"; +import { LoggingService } from "src/services/logging.service"; +import { getLabelIndexMap, getMultiNgIdsRegionsLabelIndexMap, IavRootStoreInterface, safeFilter } from "src/services/stateStore.service"; import { ModalHandler } from "../util/pluginHandlerClasses/modalHandler"; import { ToastHandler } from "../util/pluginHandlerClasses/toastHandler"; -import { PluginManifest } from "./atlasViewer.pluginService.service"; -import { DialogService } from "src/services/dialogService.service"; +import { IPluginManifest } from "./atlasViewer.pluginService.service"; declare var window @Injectable({ - providedIn : 'root' + providedIn : 'root', }) -export class AtlasViewerAPIServices{ +export class AtlasViewerAPIServices { - private loadedTemplates$ : Observable<any> - private selectParcellation$ : Observable<any> - public interactiveViewer : InteractiveViewerInterface + private loadedTemplates$: Observable<any> + private selectParcellation$: Observable<any> + public interactiveViewer: IInteractiveViewerInterface - public loadedLibraries : Map<string,{counter:number,src:HTMLElement|null}> = new Map() + public loadedLibraries: Map<string, {counter: number, src: HTMLElement|null}> = new Map() constructor( private store: Store<IavRootStoreInterface>, private dialogService: DialogService, - ){ + private log: LoggingService, + ) { this.loadedTemplates$ = this.store.pipe( select('viewerState'), safeFilter('fetchedTemplates'), - map(state=>state.fetchedTemplates) + map(state => state.fetchedTemplates), ) this.selectParcellation$ = this.store.pipe( select('viewerState'), safeFilter('parcellationSelected'), - map(state => state.parcellationSelected) + map(state => state.parcellationSelected), ) this.interactiveViewer = { @@ -44,18 +46,20 @@ export class AtlasViewerAPIServices{ selectedTemplateBSubject : this.store.pipe( select('viewerState'), safeFilter('templateSelected'), - map(state=>state.templateSelected)), + map(state => state.templateSelected)), selectedParcellationBSubject : this.store.pipe( select('viewerState'), safeFilter('parcellationSelected'), - map(state=>state.parcellationSelected)), + map(state => state.parcellationSelected)), selectedRegionsBSubject : this.store.pipe( select('viewerState'), safeFilter('regionsSelected'), - map(state=>state.regionsSelected), - distinctUntilChanged((arr1, arr2) => arr1.length === arr2.length && (arr1 as any[]).every((item, index) => item.name === arr2[index].name)) + map(state => state.regionsSelected), + distinctUntilChanged((arr1, arr2) => + arr1.length === arr2.length && + (arr1 as any[]).every((item, index) => item.name === arr2[index].name)), ), loadedTemplates : [], @@ -63,13 +67,13 @@ export class AtlasViewerAPIServices{ // TODO deprecate regionsLabelIndexMap : new Map(), - layersRegionLabelIndexMap: new Map(), + layersRegionLabelIndexMap: new Map(), datasetsBSubject : this.store.pipe( select('dataStore'), safeFilter('fetchedDataEntries'), - map(state=>state.fetchedDataEntries) - ) + map(state => state.fetchedDataEntries), + ), }, uiHandle : { getModalHandler : () => { @@ -94,14 +98,14 @@ export class AtlasViewerAPIServices{ // }) } handler.hide = () => { - if(modalRef){ + if (modalRef) { modalRef.hide() modalRef = null } } return handler }, - + /* to be overwritten by atlasViewer.component.ts */ getToastHandler : () => { throw new Error('getToast Handler not overwritten by atlasViewer.component.ts') @@ -115,17 +119,17 @@ export class AtlasViewerAPIServices{ }, getUserInput: config => this.dialogService.getUserInput(config), - getUserConfirmation: config => this.dialogService.getUserConfirm(config) + getUserConfirmation: config => this.dialogService.getUserConfirm(config), }, pluginControl : { - loadExternalLibraries : ()=>Promise.reject('load External Library method not over written') + loadExternalLibraries : () => Promise.reject('load External Library method not over written') , - unloadExternalLibraries : ()=>{ - console.warn('unloadExternalLibrary method not overwritten by atlasviewer') - } - } + unloadExternalLibraries : () => { + this.log.warn('unloadExternalLibrary method not overwritten by atlasviewer') + }, + }, } - window['interactiveViewer'] = this.interactiveViewer + window.interactiveViewer = this.interactiveViewer this.init() /** @@ -134,8 +138,8 @@ export class AtlasViewerAPIServices{ window.uiHandle = this.interactiveViewer.uiHandle } - private init(){ - this.loadedTemplates$.subscribe(templates=>this.interactiveViewer.metadata.loadedTemplates = templates) + private init() { + this.loadedTemplates$.subscribe(templates => this.interactiveViewer.metadata.loadedTemplates = templates) this.selectParcellation$.subscribe(parcellation => { this.interactiveViewer.metadata.regionsLabelIndexMap = getLabelIndexMap(parcellation.regions) this.interactiveViewer.metadata.layersRegionLabelIndexMap = getMultiNgIdsRegionsLabelIndexMap(parcellation) @@ -143,87 +147,83 @@ export class AtlasViewerAPIServices{ } } -export interface InteractiveViewerInterface{ +export interface IInteractiveViewerInterface { - metadata : { - selectedTemplateBSubject : Observable<any|null> - selectedParcellationBSubject : Observable<any|null> - selectedRegionsBSubject : Observable<any[]|null> - loadedTemplates : any[] - regionsLabelIndexMap : Map<number,any> | null + metadata: { + selectedTemplateBSubject: Observable<any|null> + selectedParcellationBSubject: Observable<any|null> + selectedRegionsBSubject: Observable<any[]|null> + loadedTemplates: any[] + regionsLabelIndexMap: Map<number, any> | null layersRegionLabelIndexMap: Map<string, Map<number, any>> - datasetsBSubject : Observable<any[]> + datasetsBSubject: Observable<any[]>, }, - viewerHandle? : { - setNavigationLoc : (coordinates:[number,number,number],realSpace?:boolean)=>void - moveToNavigationLoc : (coordinates:[number,number,number],realSpace?:boolean)=>void - setNavigationOri : (quat:[number,number,number,number])=>void - moveToNavigationOri : (quat:[number,number,number,number])=>void - showSegment : (labelIndex : number)=>void - hideSegment : (labelIndex : number)=>void - showAllSegments : ()=>void - hideAllSegments : ()=>void + viewerHandle?: { + setNavigationLoc: (coordinates: [number, number, number], realSpace?: boolean) => void + moveToNavigationLoc: (coordinates: [number, number, number], realSpace?: boolean) => void + setNavigationOri: (quat: [number, number, number, number]) => void + moveToNavigationOri: (quat: [number, number, number, number]) => void + showSegment: (labelIndex: number) => void + hideSegment: (labelIndex: number) => void + showAllSegments: () => void + hideAllSegments: () => void // TODO deprecate - segmentColourMap : Map<number,{red:number,green:number,blue:number}> + segmentColourMap: Map<number, {red: number, green: number, blue: number}> - getLayersSegmentColourMap: () => Map<string, Map<number, {red:number, green:number, blue: number}>> + getLayersSegmentColourMap: () => Map<string, Map<number, {red: number, green: number, blue: number}>> // TODO deprecate - applyColourMap : (newColourMap : Map<number,{red:number,green:number,blue:number}>)=>void + applyColourMap: (newColourMap: Map<number, {red: number, green: number, blue: number}>) => void - applyLayersColourMap: (newLayerColourMap: Map<string, Map<number, {red:number, green: number, blue: number}>>) => void + applyLayersColourMap: (newLayerColourMap: Map<string, Map<number, {red: number, green: number, blue: number}>>) => void - loadLayer : (layerobj:NGLayerObj)=>NGLayerObj - removeLayer : (condition:{name : string | RegExp})=>string[] - setLayerVisibility : (condition:{name : string|RegExp},visible:boolean)=>void + loadLayer: (layerobj: any) => any + removeLayer: (condition: {name: string | RegExp}) => string[] + setLayerVisibility: (condition: {name: string|RegExp}, visible: boolean) => void - add3DLandmarks : (landmarks: UserLandmark[]) => void - remove3DLandmarks : (ids:string[]) => void + add3DLandmarks: (landmarks: IUserLandmark[]) => void + remove3DLandmarks: (ids: string[]) => void - mouseEvent : Observable<{eventName:string,event:MouseEvent}> - mouseOverNehuba : Observable<{labelIndex : number, foundRegion : any | null}> + mouseEvent: Observable<{eventName: string, event: MouseEvent}> + mouseOverNehuba: Observable<{labelIndex: number, foundRegion: any | null}> /** * TODO add to documentation */ - mouseOverNehubaLayers: Observable<{layer:{name:string}, segment: any | number }[]> + mouseOverNehubaLayers: Observable<Array<{layer: {name: string}, segment: any | number }>> - getNgHash : () => string + getNgHash: () => string, } - uiHandle : { + uiHandle: { getModalHandler: () => ModalHandler getToastHandler: () => ToastHandler - launchNewWidget: (manifest:PluginManifest) => Promise<any> - getUserInput: (config:GetUserInputConfig) => Promise<string> - getUserConfirmation: (config: GetUserConfirmation) => Promise<any> + launchNewWidget: (manifest: IPluginManifest) => Promise<any> + getUserInput: (config: IGetUserInputConfig) => Promise<string> + getUserConfirmation: (config: IGetUserConfirmation) => Promise<any>, } - pluginControl : { - loadExternalLibraries : (libraries:string[])=>Promise<void> - unloadExternalLibraries : (libraries:string[])=>void - [key:string] : any + pluginControl: { + loadExternalLibraries: (libraries: string[]) => Promise<void> + unloadExternalLibraries: (libraries: string[]) => void + [key: string]: any, } } -interface GetUserConfirmation{ +interface IGetUserConfirmation { title?: string message?: string } -interface GetUserInputConfig extends GetUserConfirmation{ +interface IGetUserInputConfig extends IGetUserConfirmation { placeholder?: string defaultValue?: string } -export interface UserLandmark{ - name : string - position : [number, number, number] - id : string /* probably use the it to track and remove user landmarks */ - highlight : boolean -} - -export interface NGLayerObj{ - +export interface IUserLandmark { + name: string + position: [number, number, number] + id: string /* probably use the it to track and remove user landmarks */ + highlight: boolean } diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index 8932257fbcf4ee364c4cc101fd1e1828c58119de..6672c0c41d8530a659741d0d7df623ecd1ce7ad1 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -1,46 +1,46 @@ import { + AfterViewInit, Component, HostBinding, - ViewChild, OnDestroy, OnInit, - TemplateRef, - AfterViewInit, Renderer2, + TemplateRef, + ViewChild, } from "@angular/core"; -import { Store, select, ActionsSubject } from "@ngrx/store"; -import { - isDefined, - safeFilter, - IavRootStoreInterface -} from "../services/stateStore.service"; -import {Observable, Subscription, combineLatest, interval, merge, of} from "rxjs"; +import { ActionsSubject, select, Store } from "@ngrx/store"; +import {combineLatest, interval, merge, Observable, of, Subscription} from "rxjs"; import { - map, - filter, - distinctUntilChanged, - delay, concatMap, + delay, + distinctUntilChanged, + filter, + map, withLatestFrom, } from "rxjs/operators"; -import { WidgetServices } from "./widgetUnit/widgetService.service"; import { LayoutMainSide } from "../layouts/mainside/mainside.component"; -import { AtlasViewerConstantsServices, UNSUPPORTED_PREVIEW, UNSUPPORTED_INTERVAL } from "./atlasViewer.constantService.service"; +import { + IavRootStoreInterface, + isDefined, + safeFilter, +} from "../services/stateStore.service"; import { AtlasViewerAPIServices } from "./atlasViewer.apiService.service"; +import { AtlasViewerConstantsServices, UNSUPPORTED_INTERVAL, UNSUPPORTED_PREVIEW } from "./atlasViewer.constantService.service"; +import { WidgetServices } from "./widgetUnit/widgetService.service"; -import { NehubaContainer } from "../ui/nehubaContainer/nehubaContainer.component"; -import { colorAnimation } from "./atlasViewer.animation" -import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive"; -import { AGREE_COOKIE, AGREE_KG_TOS, SHOW_KG_TOS, SHOW_BOTTOM_SHEET } from "src/services/state/uiState.store"; +import { MatBottomSheet, MatBottomSheetRef, MatDialog, MatDialogRef, MatSnackBar, MatSnackBarRef } from "@angular/material"; import { TabsetComponent } from "ngx-bootstrap/tabs"; import { LocalFileService } from "src/services/localFile.service"; -import { MatDialog, MatDialogRef, MatSnackBar, MatSnackBarRef, MatBottomSheet, MatBottomSheetRef } from "@angular/material"; +import { LoggingService } from "src/services/logging.service"; +import { AGREE_COOKIE, AGREE_KG_TOS, SHOW_BOTTOM_SHEET, SHOW_KG_TOS } from "src/services/state/uiState.store"; import { CLOSE_SIDE_PANEL, - OPEN_SIDE_PANEL + OPEN_SIDE_PANEL, } from "src/services/state/uiState.store"; -import { isSame } from "src/util/fn"; - +import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive"; +import { getViewer, isSame } from "src/util/fn"; +import { NehubaContainer } from "../ui/nehubaContainer/nehubaContainer.component"; +import { colorAnimation } from "./atlasViewer.animation" /** * TODO @@ -53,43 +53,43 @@ const compareFn = (it, item) => it.name === item.name selector: 'atlas-viewer', templateUrl: './atlasViewer.template.html', styleUrls: [ - `./atlasViewer.style.css` + `./atlasViewer.style.css`, ], animations : [ - colorAnimation - ] + colorAnimation, + ], }) export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { public compareFn = compareFn - @ViewChild('cookieAgreementComponent', {read: TemplateRef}) cookieAgreementComponent : TemplateRef<any> - @ViewChild('kgToS', {read: TemplateRef}) kgTosComponent: TemplateRef<any> - @ViewChild(LayoutMainSide) layoutMainSide: LayoutMainSide + @ViewChild('cookieAgreementComponent', {read: TemplateRef}) public cookieAgreementComponent: TemplateRef<any> + @ViewChild('kgToS', {read: TemplateRef}) public kgTosComponent: TemplateRef<any> + @ViewChild(LayoutMainSide) public layoutMainSide: LayoutMainSide - @ViewChild(NehubaContainer) nehubaContainer: NehubaContainer + @ViewChild(NehubaContainer) public nehubaContainer: NehubaContainer - @ViewChild(FixedMouseContextualContainerDirective) rClContextualMenu: FixedMouseContextualContainerDirective + @ViewChild(FixedMouseContextualContainerDirective) public rClContextualMenu: FixedMouseContextualContainerDirective - @ViewChild('mobileMenuTabs') mobileMenuTabs: TabsetComponent + @ViewChild('mobileMenuTabs') public mobileMenuTabs: TabsetComponent /** * required for styling of all child components */ @HostBinding('attr.darktheme') - darktheme: boolean = false + public darktheme: boolean = false @HostBinding('attr.ismobile') public ismobile: boolean = false - meetsRequirement: boolean = true + public meetsRequirement: boolean = true public sidePanelView$: Observable<string|null> private newViewer$: Observable<any> public selectedRegions$: Observable<any[]> - public selectedPOI$ : Observable<any[]> - + public selectedPOI$: Observable<any[]> + private snackbarRef: MatSnackBarRef<any> public snackbarMessage$: Observable<string> private bottomSheetRef: MatBottomSheetRef @@ -98,7 +98,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { public dedicatedView$: Observable<string | null> public onhoverSegments$: Observable<string[]> - public onhoverLandmark$ : Observable<{landmarkName: string, datasets: any} | null> + public onhoverLandmark$: Observable<{landmarkName: string, datasets: any} | null> private subscriptions: Subscription[] = [] /* handlers for nglayer */ @@ -106,18 +106,17 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { * TODO make untangle nglayernames and its dependency on ng * TODO deprecated */ - public ngLayerNames$ : Observable<any> - public ngLayers : NgLayerInterface[] - private disposeHandler : any + public ngLayerNames$: Observable<any> + public ngLayers: INgLayerInterface[] + private disposeHandler: any public unsupportedPreviewIdx: number = 0 public unsupportedPreviews: any[] = UNSUPPORTED_PREVIEW public sidePanelIsOpen$: Observable<boolean> - - onhoverSegmentsForFixed$: Observable<string[]> - regionToolsMenuVisible = false + public onhoverSegmentsForFixed$: Observable<string[]> + public regionToolsMenuVisible = false constructor( private store: Store<IavRootStoreInterface>, @@ -129,18 +128,19 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { private rd: Renderer2, public localFileService: LocalFileService, private snackbar: MatSnackBar, - private bottomSheet: MatBottomSheet + private bottomSheet: MatBottomSheet, + private log: LoggingService, ) { this.snackbarMessage$ = this.store.pipe( select('uiState'), - select("snackbarMessage") + select("snackbarMessage"), ) this.bottomSheet$ = this.store.pipe( select('uiState'), select('bottomSheetTemplate'), - distinctUntilChanged() + distinctUntilChanged(), ) /** @@ -149,28 +149,28 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { this.ngLayerNames$ = this.store.pipe( select('viewerState'), filter(state => isDefined(state) && isDefined(state.templateSelected)), - distinctUntilChanged((o,n) => o.templateSelected.name === n.templateSelected.name), + distinctUntilChanged((o, n) => o.templateSelected.name === n.templateSelected.name), map(state => Object.keys(state.templateSelected.nehubaConfig.dataset.initialNgState.layers)), - delay(0) + delay(0), ) this.sidePanelView$ = this.store.pipe( - select('uiState'), + select('uiState'), filter(state => isDefined(state)), - map(state => state.focusedSidePanel) + map(state => state.focusedSidePanel), ) this.sidePanelIsOpen$ = this.store.pipe( - select('uiState'), + select('uiState'), filter(state => isDefined(state)), - map(state => state.sidePanelIsOpen) + map(state => state.sidePanelIsOpen), ) this.selectedRegions$ = this.store.pipe( select('viewerState'), - filter(state=>isDefined(state)&&isDefined(state.regionsSelected)), - map(state=>state.regionsSelected), - distinctUntilChanged() + filter(state => isDefined(state) && isDefined(state.regionsSelected)), + map(state => state.regionsSelected), + distinctUntilChanged(), ) this.selectedPOI$ = combineLatest( @@ -179,51 +179,52 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { select('viewerState'), filter(state => isDefined(state) && isDefined(state.landmarksSelected)), map(state => state.landmarksSelected), - distinctUntilChanged() - ) + distinctUntilChanged(), + ), ).pipe( - map(results => [...results[0], ...results[1]]) + map(results => [...results[0], ...results[1]]), ) this.newViewer$ = this.store.pipe( select('viewerState'), select('templateSelected'), - distinctUntilChanged(isSame) + distinctUntilChanged(isSame), ) this.dedicatedView$ = this.store.pipe( select('viewerState'), filter(state => isDefined(state) && typeof state.dedicatedView !== 'undefined'), map(state => state.dedicatedView), - distinctUntilChanged() + distinctUntilChanged(), ) this.onhoverLandmark$ = combineLatest( this.store.pipe( select('uiState'), - map(state => state.mouseOverLandmark) + map(state => state.mouseOverLandmark), ), this.store.pipe( select('dataStore'), safeFilter('fetchedSpatialData'), - map(state=>state.fetchedSpatialData) - ) + map(state => state.fetchedSpatialData), + ), ).pipe( map(([landmark, spatialDatas]) => { - if(landmark === null) + if (landmark === null) { return landmark - const idx = Number(landmark.replace('label=','')) - if(isNaN(idx)) { - console.warn(`Landmark index could not be parsed as a number: ${landmark}`) + } + const idx = Number(landmark.replace('label=', '')) + if (isNaN(idx)) { + this.log.warn(`Landmark index could not be parsed as a number: ${landmark}`) return { - landmarkName: idx + landmarkName: idx, } } else { return { - landmarkName: spatialDatas[idx].name + landmarkName: spatialDatas[idx].name, } } - }) + }), ) // TODO temporary hack. even though the front octant is hidden, it seems if a mesh is present, hover will select the said mesh @@ -232,67 +233,68 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { select('uiState'), select('mouseOverSegments'), filter(v => !!v), - distinctUntilChanged((o, n) => o.length === n.length && n.every(segment => o.find(oSegment => oSegment.layer.name === segment.layer.name && oSegment.segment === segment.segment) ) ) + distinctUntilChanged((o, n) => o.length === n.length && n.every(segment => o.find(oSegment => oSegment.layer.name === segment.layer.name && oSegment.segment === segment.segment) ) ), /* cannot filter by state, as the template expects a default value, or it will throw ExpressionChangedAfterItHasBeenCheckedError */ ), - this.onhoverLandmark$ + this.onhoverLandmark$, ).pipe( map(([segments, onhoverLandmark]) => onhoverLandmark ? null : segments ), map(segments => { - if (!segments) return null + if (!segments) { return null } const filteredSeg = segments.filter(filterFn) return filteredSeg.length > 0 - ? segments.map(s => s.segment) + ? segments.map(s => s.segment) : null - }) + }), ) this.selectedParcellation$ = this.store.pipe( select('viewerState'), safeFilter('parcellationSelected'), - map(state=>state.parcellationSelected), + map(state => state.parcellationSelected), distinctUntilChanged(), ) this.subscriptions.push( this.selectedParcellation$.subscribe(parcellation => { this.selectedParcellation = parcellation - }) + }), ) this.subscriptions.push( this.bottomSheet$.subscribe(templateRef => { if (!templateRef) { - this.bottomSheetRef && this.bottomSheetRef.dismiss() + if (!!this.bottomSheetRef) { + this.bottomSheetRef.dismiss() + } } else { this.bottomSheetRef = this.bottomSheet.open(templateRef) this.bottomSheetRef.afterDismissed().subscribe(() => { this.store.dispatch({ type: SHOW_BOTTOM_SHEET, - bottomSheetTemplate: null + bottomSheetTemplate: null, }) this.bottomSheetRef = null }) } - }) + }), ) } - private selectedParcellation$: Observable<any> private selectedParcellation: any private cookieDialogRef: MatDialogRef<any> private kgTosDialogRef: MatDialogRef<any> - ngOnInit() { + public ngOnInit() { this.meetsRequirement = this.meetsRequirements() if (!this.meetsRequirement) { merge( of(-1), - interval(UNSUPPORTED_INTERVAL) + interval(UNSUPPORTED_INTERVAL), ).pipe( map(v => { let idx = v @@ -300,14 +302,14 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { idx = v + this.unsupportedPreviews.length } return idx % this.unsupportedPreviews.length - }) + }), ).subscribe(val => { this.unsupportedPreviewIdx = val }) } this.subscriptions.push( - this.constantsService.useMobileUI$.subscribe(bool => this.ismobile = bool) + this.constantsService.useMobileUI$.subscribe(bool => this.ismobile = bool), ) this.subscriptions.push( @@ -317,16 +319,16 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { // and https://github.com/angular/components/issues/11357 delay(0), ).subscribe(messageSymbol => { - this.snackbarRef && this.snackbarRef.dismiss() + if (!!this.snackbarRef) { this.snackbarRef.dismiss() } - if (!messageSymbol) return + if (!messageSymbol) { return } // https://stackoverflow.com/a/48191056/6059235 const message = messageSymbol.toString().slice(7, -1) this.snackbarRef = this.snackbar.open(message, 'Dismiss', { - duration: 5000 + duration: 5000, }) - }) + }), ) /** @@ -334,36 +336,37 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { */ this.subscriptions.push( this.ngLayerNames$.pipe( - concatMap(data => this.constantsService.loadExportNehubaPromise.then(data)) + concatMap(data => this.constantsService.loadExportNehubaPromise.then(data)), ).subscribe(() => { this.ngLayersChangeHandler() - this.disposeHandler = window['viewer'].layerManager.layersChanged.add(() => this.ngLayersChangeHandler()) - window['viewer'].registerDisposer(this.disposeHandler) - }) + const viewer = getViewer() + this.disposeHandler = viewer.layerManager.layersChanged.add(() => this.ngLayersChangeHandler()) + viewer.registerDisposer(this.disposeHandler) + }), ) this.subscriptions.push( this.newViewer$.subscribe(() => { this.widgetServices.clearAllWidgets() - }) + }), ) this.subscriptions.push( this.sidePanelView$.pipe( - filter(() => typeof this.layoutMainSide !== 'undefined') - ).subscribe(v => this.layoutMainSide.showSide = isDefined(v)) + filter(() => typeof this.layoutMainSide !== 'undefined'), + ).subscribe(v => this.layoutMainSide.showSide = isDefined(v)), ) this.subscriptions.push( this.constantsService.darktheme$.subscribe(flag => { - this.rd.setAttribute(document.body,'darktheme', flag.toString()) - }) + this.rd.setAttribute(document.body, 'darktheme', flag.toString()) + }), ) } - ngAfterViewInit() { + public ngAfterViewInit() { /** - * preload the main bundle after atlas viewer has been loaded. + * preload the main bundle after atlas viewer has been loaded. * This should speed up where user first navigate to the home page, * and the main.bundle should be downloading after atlasviewer has been rendered */ @@ -385,7 +388,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { select('uiState'), select('agreedCookies'), filter(agreed => !agreed), - delay(0) + delay(0), ).subscribe(() => { this.cookieDialogRef = this.matDialog.open(this.cookieAgreementComponent) }) @@ -394,53 +397,53 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { filter(({type}) => type === SHOW_KG_TOS), withLatestFrom(this.store.pipe( select('uiState'), - select('agreedKgTos') + select('agreedKgTos'), )), map(([_, agreed]) => agreed), filter(flag => !flag), - delay(0) + delay(0), ).subscribe(val => { this.kgTosDialogRef = this.matDialog.open(this.kgTosComponent) }) this.onhoverSegmentsForFixed$ = this.rClContextualMenu.onShow.pipe( withLatestFrom(this.onhoverSegments$), - map(([_flag, onhoverSegments]) => onhoverSegments || []) + map(([_flag, onhoverSegments]) => onhoverSegments || []), ) } - mouseDownNehuba(event) { + public mouseDownNehuba(event) { this.regionToolsMenuVisible = false this.rClContextualMenu.hide() } - mouseUpNehuba(event) { + public mouseUpNehuba(event) { // if (this.mouseUpLeftPosition === event.pageX && this.mouseUpTopPosition === event.pageY) {} this.regionToolsMenuVisible = true - if (!this.rClContextualMenu) return + if (!this.rClContextualMenu) { return } this.rClContextualMenu.mousePos = [ event.clientX, - event.clientY + event.clientY, ] this.rClContextualMenu.show() } - toggleSideNavMenu(opened) { - this.store.dispatch({type: opened? CLOSE_SIDE_PANEL : OPEN_SIDE_PANEL}) + public toggleSideNavMenu(opened) { + this.store.dispatch({type: opened ? CLOSE_SIDE_PANEL : OPEN_SIDE_PANEL}) } /** - * For completeness sake. Root element should never be destroyed. + * For completeness sake. Root element should never be destroyed. */ - ngOnDestroy() { + public ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()) } /** * perhaps move this to constructor? */ - meetsRequirements():boolean { + public meetsRequirements(): boolean { const canvas = document.createElement('canvas') const gl = canvas.getContext('webgl2') as WebGLRenderingContext @@ -450,7 +453,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { } const colorBufferFloat = gl.getExtension('EXT_color_buffer_float') - + if (!colorBufferFloat) { return false } @@ -461,40 +464,41 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { /** * TODO deprecated */ - ngLayersChangeHandler(){ - this.ngLayers = (window['viewer'].layerManager.managedLayers as any[]) + public ngLayersChangeHandler() { + const viewer = getViewer() + this.ngLayers = (viewer.layerManager.managedLayers as any[]) // .filter(obj => obj.sourceUrl && /precomputed|nifti/.test(obj.sourceUrl)) .map(obj => ({ name : obj.name, type : obj.initialSpecification.type, source : obj.sourceUrl, - visible : obj.visible - }) as NgLayerInterface) + visible : obj.visible, + }) as INgLayerInterface) } - kgTosClickedOk(){ - this.kgTosDialogRef && this.kgTosDialogRef.close() + public kgTosClickedOk() { + if (this.kgTosDialogRef) { this.kgTosDialogRef.close() } this.store.dispatch({ - type: AGREE_KG_TOS + type: AGREE_KG_TOS, }) } - cookieClickedOk(){ - this.cookieDialogRef && this.cookieDialogRef.close() + public cookieClickedOk() { + if (this.cookieDialogRef) { this.cookieDialogRef.close() } this.store.dispatch({ - type: AGREE_COOKIE + type: AGREE_COOKIE, }) } @HostBinding('attr.version') - public _version : string = VERSION + public _version: string = VERSION } -export interface NgLayerInterface{ - name : string - visible : boolean - source : string - type : string // image | segmentation | etc ... - transform? : [[number, number, number, number],[number, number, number, number],[number, number, number, number],[number, number, number, number]] | null +export interface INgLayerInterface { + name: string + visible: boolean + source: string + type: string // image | segmentation | etc ... + transform?: [[number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number]] | null // colormap : string } diff --git a/src/atlasViewer/atlasViewer.constantService.service.spec.ts b/src/atlasViewer/atlasViewer.constantService.service.spec.ts index 72f7f4c3f146f2fef352a2a3e0c6daa3c91ea224..e9fe3faeeb0dae335c04d2e0b28d840966b87d5a 100644 --- a/src/atlasViewer/atlasViewer.constantService.service.spec.ts +++ b/src/atlasViewer/atlasViewer.constantService.service.spec.ts @@ -1,14 +1,12 @@ -import { encodeNumber, decodeToNumber } from './atlasViewer.constantService.service' import {} from 'jasmine' +import { decodeToNumber, encodeNumber } from './atlasViewer.constantService.service' const FLOAT_PRECISION = 6 describe('encodeNumber/decodeToNumber', () => { - - const getCompareOriginal = (original: number[]) => (element:string, index: number) => + const getCompareOriginal = (original: number[]) => (element: string, index: number) => original[index].toString().length >= element.length - const lengthShortened = (original: number[], encodedString: string[]) => encodedString.every(getCompareOriginal(original)) @@ -19,13 +17,13 @@ describe('encodeNumber/decodeToNumber', () => { 0, 1, 99999999999, - 12347 + 12347, ] const encodedString = positiveInt.map(n => encodeNumber(n)) const decodedString = encodedString.map(s => decodeToNumber(s)) expect(decodedString).toEqual(positiveInt) - + expect(lengthShortened(positiveInt, encodedString)).toBe(true) }) @@ -41,7 +39,6 @@ describe('encodeNumber/decodeToNumber', () => { expect(lengthShortened(posInt, encodedString)).toBe(true) }) - it('should encode/decode signed integer as expected', () => { const signedInt = [ @@ -50,9 +47,9 @@ describe('encodeNumber/decodeToNumber', () => { -1, 1, 128, - -54 + -54, ] - + const encodedString = signedInt.map(n => encodeNumber(n)) const decodedNumber = encodedString.map(s => decodeToNumber(s)) @@ -81,12 +78,11 @@ describe('encodeNumber/decodeToNumber', () => { expect(lengthShortened(signedInt, encodedString)).toBe(true) }) - it('should encode/decode float as expected', () => { const floatNum = [ 0.111, 12.23, - 1723.0 + 1723.0, ] const encodedString = floatNum.map(f => encodeNumber(f, { float: true })) @@ -98,7 +94,7 @@ describe('encodeNumber/decodeToNumber', () => { const floatNums = Array(1000).fill(null).map(() => { const numDig = Math.ceil(Math.random() * 7) return (Math.random() > 0.5 ? 1 : -1) * Math.floor( - Math.random() * Math.pow(10, numDig) + Math.random() * Math.pow(10, numDig), ) }) @@ -110,8 +106,8 @@ describe('encodeNumber/decodeToNumber', () => { it('poisoned hash should throw', () => { const illegialCharacters = './\\?#!@#^%&*()+={}[]\'"\n\t;:' - for (let char of illegialCharacters.split('')) { - expect(function (){ + for (const char of illegialCharacters.split('')) { + expect(() => { decodeToNumber(char) }).toThrow() } @@ -129,4 +125,4 @@ describe('encodeNumber/decodeToNumber', () => { }).filter(v => !!v) expect(decodedNum.length).toEqual(2) }) -}) \ No newline at end of file +}) diff --git a/src/atlasViewer/atlasViewer.constantService.service.ts b/src/atlasViewer/atlasViewer.constantService.service.ts index 803f2935cec27b6a077a85c22cd998bad4a4b071..ace6a01a22e8617ba374838cd401e6ac55c2fccf 100644 --- a/src/atlasViewer/atlasViewer.constantService.service.ts +++ b/src/atlasViewer/atlasViewer.constantService.service.ts @@ -1,17 +1,18 @@ +import { HttpClient } from "@angular/common/http"; import { Injectable, OnDestroy } from "@angular/core"; -import { Store, select } from "@ngrx/store"; -import { IavRootStoreInterface } from "../services/stateStore.service"; -import { Observable, Subscription, throwError, of, merge } from "rxjs"; -import { map, shareReplay, filter, switchMap, catchError, tap } from "rxjs/operators"; +import { select, Store } from "@ngrx/store"; +import { merge, Observable, of, Subscription, throwError } from "rxjs"; +import { catchError, filter, map, shareReplay, switchMap, tap } from "rxjs/operators"; +import { LoggingService } from "src/services/logging.service"; import { SNACKBAR_MESSAGE } from "src/services/state/uiState.store"; -import { HttpClient } from "@angular/common/http"; +import { IavRootStoreInterface } from "../services/stateStore.service"; export const CM_THRESHOLD = `0.05` export const CM_MATLAB_JET = `float r;if( x < 0.7 ){r = 4.0 * x - 1.5;} else {r = -4.0 * x + 4.5;}float g;if (x < 0.5) {g = 4.0 * x - 0.5;} else {g = -4.0 * x + 3.5;}float b;if (x < 0.3) {b = 4.0 * x + 0.5;} else {b = -4.0 * x + 2.5;}float a = 1.0;` export const GLSL_COLORMAP_JET = `void main(){float x = toNormalized(getDataValue());${CM_MATLAB_JET}if(x>${CM_THRESHOLD}){emitRGB(vec3(r,g,b));}else{emitTransparent();}}` @Injectable({ - providedIn : 'root' + providedIn : 'root', }) export class AtlasViewerConstantsServices implements OnDestroy { @@ -20,7 +21,7 @@ export class AtlasViewerConstantsServices implements OnDestroy { public darktheme$: Observable<boolean> public useMobileUI$: Observable<boolean> - public loadExportNehubaPromise : Promise<boolean> + public loadExportNehubaPromise: Promise<boolean> public ngLandmarkLayerName = 'spatial landmark layer' public ngUserLandmarkLayerName = 'user landmark layer' @@ -40,11 +41,10 @@ export class AtlasViewerConstantsServices implements OnDestroy { */ private TIMEOUT = 16000 - /* TODO to be replaced by @id: Landmark/UNIQUE_ID in KG in the future */ - public testLandmarksChanged : (prevLandmarks : any[], newLandmarks : any[]) => boolean = (prevLandmarks:any[], newLandmarks:any[]) => { - return prevLandmarks.every(lm => typeof lm.name !== 'undefined') && - newLandmarks.every(lm => typeof lm.name !== 'undefined') && + public testLandmarksChanged: (prevLandmarks: any[], newLandmarks: any[]) => boolean = (prevLandmarks: any[], newLandmarks: any[]) => { + return prevLandmarks.every(lm => typeof lm.name !== 'undefined') && + newLandmarks.every(lm => typeof lm.name !== 'undefined') && prevLandmarks.length === newLandmarks.length } @@ -52,35 +52,36 @@ export class AtlasViewerConstantsServices implements OnDestroy { public backendUrl = BACKEND_URL || `${window.location.origin}${window.location.pathname}` private fetchTemplate = (templateUrl) => this.http.get(`${this.backendUrl}${templateUrl}`, { responseType: 'json' }).pipe( - switchMap((template:any) => { - if (template.nehubaConfig) return of(template) - if (template.nehubaConfigURL) return this.http.get(`${this.backendUrl}${template.nehubaConfigURL}`, { responseType: 'json' }).pipe( + switchMap((template: any) => { + if (template.nehubaConfig) { return of(template) } + if (template.nehubaConfigURL) { return this.http.get(`${this.backendUrl}${template.nehubaConfigURL}`, { responseType: 'json' }).pipe( map(nehubaConfig => { return { ...template, - nehubaConfig + nehubaConfig, } - }) + }), ) + } throwError('neither nehubaConfig nor nehubaConfigURL defined') - }) + }), ) public totalTemplates = null public initFetchTemplate$ = this.http.get(`${this.backendUrl}templates`, { responseType: 'json' }).pipe( - tap((arr:any[]) => this.totalTemplates = arr.length), + tap((arr: any[]) => this.totalTemplates = arr.length), switchMap((templates: string[]) => merge( - ...templates.map(this.fetchTemplate) + ...templates.map(this.fetchTemplate), )), catchError((err) => { - console.warn(`fetching templates error`, err) + this.log.warn(`fetching templates error`, err) return of(null) - }) + }), ) /* to be provided by KG in future */ - public templateUrlsPr : Promise<string[]> = new Promise((resolve, reject) => { + public templateUrlsPr: Promise<string[]> = new Promise((resolve, reject) => { fetch(`${this.backendUrl}templates`, this.getFetchOption()) .then(res => res.json()) .then(arr => { @@ -94,54 +95,54 @@ export class AtlasViewerConstantsServices implements OnDestroy { public templateUrls = Array(100) /* to be provided by KG in future */ - private _mapArray : [string,string[]][] = [ - [ 'JuBrain Cytoarchitectonic Atlas' , + private _mapArray: Array<[string, string[]]> = [ + [ 'JuBrain Cytoarchitectonic Atlas' , [ 'res/json/pmapsAggregatedData.json', - 'res/json/receptorAggregatedData.json' - ] + 'res/json/receptorAggregatedData.json', + ], ], [ 'Fibre Bundle Atlas - Short Bundle', [ - 'res/json/swmAggregatedData.json' - ] + 'res/json/swmAggregatedData.json', + ], ], [ 'Allen Mouse Common Coordinate Framework v3 2015', [ - 'res/json/allenAggregated.json' - ] + 'res/json/allenAggregated.json', + ], ], [ 'Fibre Bundle Atlas - Long Bundle', [ - 'res/json/dwmAggregatedData.json' - ] + 'res/json/dwmAggregatedData.json', + ], ], [ 'Whole Brain (v2.0)', [ - 'res/json/waxholmAggregated.json' - ] - ] + 'res/json/waxholmAggregated.json', + ], + ], ] - public mapParcellationNameToFetchUrl : Map<string,string[]> = new Map(this._mapArray) + public mapParcellationNameToFetchUrl: Map<string, string[]> = new Map(this._mapArray) public spatialSearchUrl = 'https://kg-int.humanbrainproject.org/solr/' public spatialResultsPerPage = 10 public spatialWidth = 600 - public landmarkFlatProjection : boolean = false + public landmarkFlatProjection: boolean = false public chartBaseStyle = { - fill : 'origin' + fill : 'origin', } public chartSdStyle = { fill : false, backgroundColor : 'rgba(0,0,0,0)', - borderDash : [10,3], + borderDash : [10, 3], pointRadius : 0, pointHitRadius : 0, } @@ -154,11 +155,11 @@ export class AtlasViewerConstantsServices implements OnDestroy { public minReqMD = ` # Hmm... it seems like we hit a snag -It seems your browser has trouble loading interactive atlas viewer. -Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float\` extension enabled. +It seems your browser has trouble loading interactive atlas viewer. +Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float\` extension enabled. - We recommend using _Chrome >= 56_ or _Firefox >= 51_. You can check your browsers' support of webgl2.0 by visiting <https://caniuse.com/#feat=webgl2> - If you are on _Chrome < 56_ or _Firefox < 51_, you may be able to enable **webgl2.0** by turning on experimental flag <https://get.webgl.org/webgl2/enable.html>. -- If you are on an Android device we recommend _Chrome for Android_ or _Firefox for Android_. +- If you are on an Android device we recommend _Chrome for Android_ or _Firefox for Android_. - Unfortunately, Safari and iOS devices currently do not support **webgl2.0**: <https://webkit.org/status/#specification-webgl-2> ` public minReqModalHeader = `Hmm... it seems your browser and is having trouble loading interactive atlas viewer` @@ -173,21 +174,21 @@ Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float * in nginx, it can result in 400 header to large * as result, trim referer to only template and parcellation selected */ - private getScopedReferer(): string{ + private getScopedReferer(): string { const url = new URL(window.location.href) url.searchParams.delete('regionsSelected') return url.toString() } - public getFetchOption() : RequestInit{ + public getFetchOption(): RequestInit { return { - referrer: this.getScopedReferer() + referrer: this.getScopedReferer(), } } - get floatingWidgetStartingPos() : [number,number]{ - return [400,100] - } + get floatingWidgetStartingPos(): [number, number] { + return [400, 100] + } /** * message when user on hover a segment or landmark @@ -197,37 +198,37 @@ Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float /** * Observable for showing config modal */ - public showConfigTitle: String = 'Settings' + public showConfigTitle: string = 'Settings' private showHelpGeneralMobile = [ ['hold 🌠+ ↕', 'change oblique slice mode'], - ['hold 🌠+ ↔', 'oblique slice'] + ['hold 🌠+ ↔', 'oblique slice'], ] private showHelpGeneralDesktop = [ ['num keys [0-9]', 'toggle layer visibility [0-9]'], ['h', 'show help'], ['?', 'show help'], - ['o', 'toggle perspective/orthographic'] + ['o', 'toggle perspective/orthographic'], ] public showHelpGeneralMap = this.showHelpGeneralDesktop private showHelpSliceViewMobile = [ - ['drag', 'pan'] + ['drag', 'pan'], ] private showHelpSliceViewDesktop = [ ['drag', 'pan'], - ['shift + drag', 'oblique slice'] + ['shift + drag', 'oblique slice'], ] public showHelpSliceViewMap = this.showHelpSliceViewDesktop private showHelpPerspectiveMobile = [ - ['drag', 'change perspective view'] + ['drag', 'change perspective view'], ] - + private showHelpPerspectiveDesktop = [ - ['drag', 'change perspective view'] + ['drag', 'change perspective view'], ] public showHelpPerspectiveViewMap = this.showHelpPerspectiveDesktop @@ -237,15 +238,14 @@ Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float public supportEmailAddress = `inm1-bda@fz-juelich.de` - public showHelpSupportText:string = `Did you encounter an issue? + public showHelpSupportText: string = `Did you encounter an issue? Send us an email: <a target = "_blank" href = "mailto:${this.supportEmailAddress}">${this.supportEmailAddress}</a>` - - incorrectParcellationNameSearchParam(title) { + public incorrectParcellationNameSearchParam(title) { return `The selected parcellation - ${title} - is not available. The the first parcellation of the template is selected instead.` } - incorrectTemplateNameSearchParam(title) { + public incorrectTemplateNameSearchParam(title) { return `The selected template - ${title} - is not available.` } @@ -254,26 +254,27 @@ Send us an email: <a target = "_blank" href = "mailto:${this.supportEmailAddress constructor( private store$: Store<IavRootStoreInterface>, private http: HttpClient, - ){ + private log: LoggingService, + ) { this.darktheme$ = this.store$.pipe( select('viewerState'), select('templateSelected'), map(template => { - if (!template) return false + if (!template) { return false } return template.useTheme === 'dark' }), - shareReplay(1) + shareReplay(1), ) this.useMobileUI$ = this.store$.pipe( select('viewerConfigState'), select('useMobileUI'), - shareReplay(1) + shareReplay(1), ) this.subscriptions.push( - this.darktheme$.subscribe(flag => this.darktheme = flag) + this.darktheme$.subscribe(flag => this.darktheme = flag), ) this.subscriptions.push( @@ -289,75 +290,75 @@ Send us an email: <a target = "_blank" href = "mailto:${this.supportEmailAddress this.showHelpPerspectiveViewMap = this.showHelpPerspectiveDesktop this.dissmissUserLayerSnackbarMessage = this.dissmissUserLayerSnackbarMessageDesktop } - }) + }), ) } private subscriptions: Subscription[] = [] - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } - catchError(e: Error | string){ + public catchError(e: Error | string) { this.store$.dispatch({ type: SNACKBAR_MESSAGE, - snackbarMessage: e.toString() + snackbarMessage: e.toString(), }) } public cyclePanelMessage: string = `[spacebar] to cycle through views` - private dissmissUserLayerSnackbarMessageDesktop = `You can dismiss extra layers with [ESC]` + private dissmissUserLayerSnackbarMessageDesktop = `You can dismiss extra layers with [ESC]` private dissmissUserLayerSnackbarMessageMobile = `You can dismiss extra layers in the 🌠menu` public dissmissUserLayerSnackbarMessage: string = this.dissmissUserLayerSnackbarMessageDesktop } -const parseURLToElement = (url:string):HTMLElement=>{ +const parseURLToElement = (url: string): HTMLElement => { const el = document.createElement('script') - el.setAttribute('crossorigin','true') + el.setAttribute('crossorigin', 'true') el.src = url return el } export const UNSUPPORTED_PREVIEW = [{ text: 'Preview of Colin 27 and JuBrain Cytoarchitectonic', - previewSrc: './res/image/1.png' -},{ + previewSrc: './res/image/1.png', +}, { text: 'Preview of Big Brain 2015 Release', - previewSrc: './res/image/2.png' -},{ + previewSrc: './res/image/2.png', +}, { text: 'Preview of Waxholm Rat V2.0', - previewSrc: './res/image/3.png' + previewSrc: './res/image/3.png', }] export const UNSUPPORTED_INTERVAL = 7000 -export const SUPPORT_LIBRARY_MAP : Map<string,HTMLElement> = new Map([ - ['jquery@3',parseURLToElement('https://code.jquery.com/jquery-3.3.1.min.js')], - ['jquery@2',parseURLToElement('https://code.jquery.com/jquery-2.2.4.min.js')], - ['webcomponentsLite@1.1.0',parseURLToElement('https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.1.0/webcomponents-lite.js')], - ['react@16',parseURLToElement('https://unpkg.com/react@16/umd/react.development.js')], - ['reactdom@16',parseURLToElement('https://unpkg.com/react-dom@16/umd/react-dom.development.js')], - ['vue@2.5.16',parseURLToElement('https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js')], - ['preact@8.4.2',parseURLToElement('https://cdn.jsdelivr.net/npm/preact@8.4.2/dist/preact.min.js')], - ['d3@5.7.0',parseURLToElement('https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js')] +export const SUPPORT_LIBRARY_MAP: Map<string, HTMLElement> = new Map([ + ['jquery@3', parseURLToElement('https://code.jquery.com/jquery-3.3.1.min.js')], + ['jquery@2', parseURLToElement('https://code.jquery.com/jquery-2.2.4.min.js')], + ['webcomponentsLite@1.1.0', parseURLToElement('https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.1.0/webcomponents-lite.js')], + ['react@16', parseURLToElement('https://unpkg.com/react@16/umd/react.development.js')], + ['reactdom@16', parseURLToElement('https://unpkg.com/react-dom@16/umd/react-dom.development.js')], + ['vue@2.5.16', parseURLToElement('https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js')], + ['preact@8.4.2', parseURLToElement('https://cdn.jsdelivr.net/npm/preact@8.4.2/dist/preact.min.js')], + ['d3@5.7.0', parseURLToElement('https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js')], ]) /** * First attempt at encoding int (e.g. selected region, navigation location) from number (loc info density) to b64 (higher info density) * The constraint is that the cipher needs to be commpatible with URI encoding - * and a URI compatible separator is required. - * - * The implementation below came from + * and a URI compatible separator is required. + * + * The implementation below came from * https://stackoverflow.com/a/6573119/6059235 - * + * * While a faster solution exist in the same post, this operation is expected to be done: * - once per 1 sec frequency * - on < 1000 numbers - * + * * So performance is not really that important (Also, need to learn bitwise operation) */ @@ -366,10 +367,10 @@ export const separator = "." const negString = '~' const encodeInt = (number: number) => { - if (number % 1 !== 0) throw 'cannot encodeInt on a float. Ensure float flag is set' - if (isNaN(Number(number)) || number === null || number === Number.POSITIVE_INFINITY) throw 'The input is not valid' + if (number % 1 !== 0) { throw new Error('cannot encodeInt on a float. Ensure float flag is set') } + if (isNaN(Number(number)) || number === null || number === Number.POSITIVE_INFINITY) { throw new Error('The input is not valid') } - let rixit // like 'digit', only in some non-decimal radix + let rixit // like 'digit', only in some non-decimal radix let residual let result = '' @@ -382,31 +383,33 @@ const encodeInt = (number: number) => { while (true) { rixit = residual % 64 - // console.log("rixit : " + rixit) - // console.log("result before : " + result) + // this.log.log("rixit : " + rixit) + // this.log.log("result before : " + result) result = cipher.charAt(rixit) + result - // console.log("result after : " + result) - // console.log("residual before : " + residual) + // this.log.log("result after : " + result) + // this.log.log("residual before : " + residual) residual = Math.floor(residual / 64) - // console.log("residual after : " + residual) + // this.log.log("residual after : " + residual) - if (residual == 0) + if (residual === 0) { break; } + } return result } -interface B64EncodingOption { +interface IB64EncodingOption { float: boolean } const defaultB64EncodingOption = { - float: false + float: false, } -export const encodeNumber: (number:number, option?: B64EncodingOption) => string = (number: number, { float = false }: B64EncodingOption = defaultB64EncodingOption) => { - if (!float) return encodeInt(number) - else { +export const encodeNumber: + (number: number, option?: IB64EncodingOption) => string = + (number: number, { float = false }: IB64EncodingOption = defaultB64EncodingOption) => { + if (!float) { return encodeInt(number) } else { const floatArray = new Float32Array(1) floatArray[0] = number const intArray = new Uint32Array(floatArray.buffer) @@ -416,23 +419,25 @@ export const encodeNumber: (number:number, option?: B64EncodingOption) => string } const decodetoInt = (encodedString: string) => { - let _encodedString, negFlag = false + let _encodedString + let negFlag = false if (encodedString.slice(-1) === negString) { negFlag = true _encodedString = encodedString.slice(0, -1) } else { _encodedString = encodedString } - return (negFlag ? -1 : 1) * [..._encodedString].reduce((acc,curr) => { + return (negFlag ? -1 : 1) * [..._encodedString].reduce((acc, curr) => { const index = cipher.indexOf(curr) - if (index < 0) throw new Error(`Poisoned b64 encoding ${encodedString}`) + if (index < 0) { throw new Error(`Poisoned b64 encoding ${encodedString}`) } return acc * 64 + index }, 0) } -export const decodeToNumber: (encodedString:string, option?: B64EncodingOption) => number = (encodedString: string, {float = false} = defaultB64EncodingOption) => { - if (!float) return decodetoInt(encodedString) - else { +export const decodeToNumber: + (encodedString: string, option?: IB64EncodingOption) => number = + (encodedString: string, {float = false} = defaultB64EncodingOption) => { + if (!float) { return decodetoInt(encodedString) } else { const _int = decodetoInt(encodedString) const intArray = new Uint32Array(1) intArray[0] = _int diff --git a/src/atlasViewer/atlasViewer.history.service.ts b/src/atlasViewer/atlasViewer.history.service.ts index aa676870381f32cb57feac78cdf54be0c8cf19ce..2710552a22e0581a775bbf49c756049cf2210cf7 100644 --- a/src/atlasViewer/atlasViewer.history.service.ts +++ b/src/atlasViewer/atlasViewer.history.service.ts @@ -1,40 +1,39 @@ import { Injectable, OnDestroy } from "@angular/core"; +import { Actions, Effect, ofType } from '@ngrx/effects' import { Store } from "@ngrx/store"; -import { Effect, Actions, ofType } from '@ngrx/effects' -import { IavRootStoreInterface, GENERAL_ACTION_TYPES, defaultRootState } from "src/services/stateStore.service"; -import { debounceTime, distinctUntilChanged, map, startWith, filter, withLatestFrom, switchMap, take, switchMapTo } from "rxjs/operators"; -import { Subscription, fromEvent, merge, of } from "rxjs"; -import { cvtStateToSearchParam, cvtSearchParamToState } from "./atlasViewer.urlUtil"; +import { fromEvent, merge, of, Subscription } from "rxjs"; +import { catchError, debounceTime, distinctUntilChanged, filter, map, startWith, switchMap, switchMapTo, take, withLatestFrom } from "rxjs/operators"; +import { defaultRootState, GENERAL_ACTION_TYPES, IavRootStoreInterface } from "src/services/stateStore.service"; import { AtlasViewerConstantsServices } from "src/ui/databrowserModule/singleDataset/singleDataset.base"; +import { cvtSearchParamToState, cvtStateToSearchParam } from "./atlasViewer.urlUtil"; const getSearchParamStringFromState = state => { try { return cvtStateToSearchParam(state).toString() } catch (e) { - console.warn(`cvt state to search param error`, e) - return null + throw new Error(`cvt state to search param error ${e.toString()}`) } } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class AtlasViewerHistoryUseEffect implements OnDestroy{ +export class AtlasViewerHistoryUseEffect implements OnDestroy { // ensure that fetchedTemplates are all populated @Effect() - parsingSearchUrlToState$ = this.store$.pipe( + public parsingSearchUrlToState$ = this.store$.pipe( filter(state => state.viewerState.fetchedTemplates.length === this.constantService.totalTemplates), take(1), switchMapTo(merge( // parsing state can occur via 2 ways: // either pop state event or on launch fromEvent(window, 'popstate').pipe( - map(({ state } : PopStateEvent) => state) + map(({ state }: PopStateEvent) => state), ), - of(new URLSearchParams(window.location.search).toString()) - )) + of(new URLSearchParams(window.location.search).toString()), + )), ).pipe( withLatestFrom(this.store$), map(([searchUrl, storeState]: [string, IavRootStoreInterface] ) => { @@ -48,56 +47,57 @@ export class AtlasViewerHistoryUseEffect implements OnDestroy{ ...defaultRootState, viewerState: { ...defaultRootState.viewerState, - fetchedTemplates: storeState.viewerState.fetchedTemplates - } - } + fetchedTemplates: storeState.viewerState.fetchedTemplates, + }, + }, } } else { // if non empty search param const newState = cvtSearchParamToState(search, storeState) return { type: GENERAL_ACTION_TYPES.APPLY_STATE, - state: newState + state: newState, } } } catch (e) { // usually parsing error - // TODO should log + // TODO should log return { type: GENERAL_ACTION_TYPES.APPLY_STATE, state: { ...defaultRootState, viewerState: { ...defaultRootState.viewerState, - fetchedTemplates: storeState.viewerState.fetchedTemplates - } - } + fetchedTemplates: storeState.viewerState.fetchedTemplates, + }, + }, } } - }) + }), ) private subscriptions: Subscription[] = [] private currentStateSearchParam$ = this.store$.pipe( map(getSearchParamStringFromState), - filter(v => v !== null) + catchError((err, obs) => of(null)), + filter(v => v !== null), ) constructor( private store$: Store<IavRootStoreInterface>, private actions$: Actions, - private constantService: AtlasViewerConstantsServices - ){ + private constantService: AtlasViewerConstantsServices, + ) { this.subscriptions.push( - + // GENERAL_ACTION_TYPES.APPLY_STATE is triggered by pop state or initial // conventiently, the action has a state property this.actions$.pipe( ofType(GENERAL_ACTION_TYPES.APPLY_STATE), // subscribe to inner obs on init startWith({}), - switchMap(({ state } :any) => + switchMap(({ state }: any) => this.currentStateSearchParam$.pipe( distinctUntilChanged(), debounceTime(100), @@ -105,22 +105,27 @@ export class AtlasViewerHistoryUseEffect implements OnDestroy{ // compares the searchParam triggerd by change of state with the searchParam generated by GENERAL_ACTION_TYPES.APPLY_STATE // if the same, the change is induced by GENERAL_ACTION_TYPES.APPLY_STATE, and should NOT be pushed to history filter((newSearchParam, index) => { - const oldSearchParam = (state && getSearchParamStringFromState(state)) || '' + try { - // in the unlikely event that user returns to the exact same state without use forward/back button - return index > 0 || newSearchParam !== oldSearchParam - }) - ) - ) + const oldSearchParam = (state && getSearchParamStringFromState(state)) || '' + + // in the unlikely event that user returns to the exact same state without use forward/back button + return index > 0 || newSearchParam !== oldSearchParam + } catch (e) { + return index > 0 || newSearchParam !== '' + } + }), + ), + ), ).subscribe(newSearchString => { const url = new URL(window.location.toString()) url.search = newSearchString window.history.pushState(newSearchString, '', url.toString()) - }) + }), ) } - ngOnDestroy(){ - while(this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe() + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } -} \ No newline at end of file +} diff --git a/src/atlasViewer/atlasViewer.pluginService.service.spec.ts b/src/atlasViewer/atlasViewer.pluginService.service.spec.ts index 12e9b33ffa22a735d71178c332e3fd56de231c1a..e9dd0b1fbce6e6c0c173ed429a34923675dfe694 100644 --- a/src/atlasViewer/atlasViewer.pluginService.service.spec.ts +++ b/src/atlasViewer/atlasViewer.pluginService.service.spec.ts @@ -46,17 +46,17 @@ // it( // 'fetches templateURL and scriptURL properly', // inject([HttpTestingController], (httpMock: HttpTestingController) => { - + // pluginService.launchPlugin(MOCK_PLUGIN_MANIFEST) - + // const mockTemplate = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.templateURL) // const mockScript = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.scriptURL) - + // expect(mockTemplate).toBeTruthy() // expect(mockScript).toBeTruthy() // }) // ) - + // it( // 'template overrides templateURL', // inject([HttpTestingController], (httpMock: HttpTestingController) => { @@ -64,26 +64,26 @@ // ...MOCK_PLUGIN_MANIFEST, // template: '' // }) - + // httpMock.expectNone(MOCK_PLUGIN_MANIFEST.templateURL) // const mockScript = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.scriptURL) - + // expect(mockScript).toBeTruthy() // }) // ) - + // it( // 'script overrides scriptURL', - + // inject([HttpTestingController], (httpMock: HttpTestingController) => { // pluginService.launchPlugin({ // ...MOCK_PLUGIN_MANIFEST, // script: '' // }) - + // const mockTemplate = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.templateURL) // httpMock.expectNone(MOCK_PLUGIN_MANIFEST.scriptURL) - + // expect(mockTemplate).toBeTruthy() // }) // ) @@ -108,4 +108,4 @@ // }) // TODO currently crashes test somehow -// TODO figure out why \ No newline at end of file +// TODO figure out why diff --git a/src/atlasViewer/atlasViewer.pluginService.service.ts b/src/atlasViewer/atlasViewer.pluginService.service.ts index 8ec00d8febfc76936ff4dd2caf381d78af96f979..bfbc24f73aacbaabcb4859e0fe90f92f1e7e2858 100644 --- a/src/atlasViewer/atlasViewer.pluginService.service.ts +++ b/src/atlasViewer/atlasViewer.pluginService.service.ts @@ -1,53 +1,55 @@ -import { Injectable, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, NgZone } from "@angular/core"; -import { ACTION_TYPES as PLUGIN_STATE_ACTION_TYPES } from "src/services/state/pluginState.store"; import { HttpClient } from '@angular/common/http' -import { isDefined, IavRootStoreInterface } from 'src/services/stateStore.service' +import { ComponentFactory, ComponentFactoryResolver, Injectable, NgZone, ViewContainerRef } from "@angular/core"; +import { ACTION_TYPES as PLUGIN_STATE_ACTION_TYPES } from "src/services/state/pluginState.store"; +import { IavRootStoreInterface, isDefined } from 'src/services/stateStore.service' import { AtlasViewerAPIServices } from "./atlasViewer.apiService.service"; import { PluginUnit } from "./pluginUnit/pluginUnit.component"; import { WidgetServices } from "./widgetUnit/widgetService.service"; +import { Effect } from "@ngrx/effects"; +import { select, Store } from "@ngrx/store"; +import { BehaviorSubject, merge, Observable, of } from "rxjs"; +import { filter, map, shareReplay, startWith } from "rxjs/operators"; +import { LoggingService } from 'src/services/logging.service'; +import { PluginHandler } from 'src/util/pluginHandler'; import '../res/css/plugin_styles.css' -import { BehaviorSubject, Observable, merge, of } from "rxjs"; -import { map, shareReplay, filter, startWith } from "rxjs/operators"; -import { Store, select } from "@ngrx/store"; -import { WidgetUnit } from "./widgetUnit/widgetUnit.component"; import { AtlasViewerConstantsServices } from "./atlasViewer.constantService.service"; -import { ACTION_TYPES as PLUGINSTORE_ACTION_TYPES, CONSTANTS as PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.store' -import { Effect } from "@ngrx/effects"; +import { WidgetUnit } from "./widgetUnit/widgetUnit.component"; @Injectable({ - providedIn : 'root' + providedIn : 'root', }) -export class PluginServices{ +export class PluginServices { - public fetchedPluginManifests : PluginManifest[] = [] - public pluginViewContainerRef : ViewContainerRef - public appendSrc : (script:HTMLElement)=>void - public removeSrc: (script:HTMLElement) => void - private pluginUnitFactory : ComponentFactory<PluginUnit> - public minimisedPlugins$ : Observable<Set<string>> + public fetchedPluginManifests: IPluginManifest[] = [] + public pluginViewContainerRef: ViewContainerRef + public appendSrc: (script: HTMLElement) => void + public removeSrc: (script: HTMLElement) => void + private pluginUnitFactory: ComponentFactory<PluginUnit> + public minimisedPlugins$: Observable<Set<string>> /** * TODO remove polyfil and convert all calls to this.fetch to http client */ - public fetch: (url:string, httpOption?: any) => Promise<any> = (url, httpOption = {}) => this.http.get(url, httpOption).toPromise() + public fetch: (url: string, httpOption?: any) => Promise<any> = (url, httpOption = {}) => this.http.get(url, httpOption).toPromise() constructor( - private apiService : AtlasViewerAPIServices, - private constantService : AtlasViewerConstantsServices, - private widgetService : WidgetServices, - private cfr : ComponentFactoryResolver, + private apiService: AtlasViewerAPIServices, + private constantService: AtlasViewerConstantsServices, + private widgetService: WidgetServices, + private cfr: ComponentFactoryResolver, private store: Store<IavRootStoreInterface>, private http: HttpClient, - zone: NgZone - ){ + zone: NgZone, + private log: LoggingService, + ) { - // TODO implement + // TODO implement this.store.pipe( select('pluginState'), select('initManifests'), - filter(v => !!v) + filter(v => !!v), ) this.pluginUnitFactory = this.cfr.resolveComponentFactory( PluginUnit ) @@ -57,77 +59,77 @@ export class PluginServices{ .then(arg2 => { // trigger change detection in Angular // otherwise, model won't be updated until user input - zone.run(() => { - }) + // tslint:disable-next-line + zone.run(() => { }) return arg2 }) - } - + } + /** * TODO convert to rxjs streams, instead of Promise.all */ - const promiseFetchedPluginManifests : Promise<PluginManifest[]> = new Promise((resolve, reject) => { + const promiseFetchedPluginManifests: Promise<IPluginManifest[]> = new Promise((resolve, reject) => { Promise.all([ // TODO convert to use this.fetch PLUGINDEV ? fetch(PLUGINDEV, this.constantService.getFetchOption()).then(res => res.json()) : Promise.resolve([]), - new Promise(resolve => { + new Promise(rs => { fetch(`${this.constantService.backendUrl}plugins`, this.constantService.getFetchOption()) .then(res => res.json()) .then(arr => Promise.all( - arr.map(url => new Promise(rs => + arr.map(url => new Promise(rs2 => /** * instead of failing all promises when fetching manifests, only fail those that fails to fetch */ - fetch(url, this.constantService.getFetchOption()).then(res => res.json()).then(rs).catch(e => (console.log('fetching manifest error', e), rs(null)))) - ) + fetch(url, this.constantService.getFetchOption()).then(res => res.json()).then(rs2).catch(e => (this.log.log('fetching manifest error', e), rs2(null)))), + ), )) - .then(manifests => resolve( - manifests.filter(m => !!m) + .then(manifests => rs( + manifests.filter(m => !!m), )) .catch(e => { this.constantService.catchError(e) - resolve([]) + rs([]) }) }), Promise.all( BUNDLEDPLUGINS .filter(v => typeof v === 'string') - .map(v => fetch(`res/plugin_examples/${v}/manifest.json`, this.constantService.getFetchOption()).then(res => res.json())) + .map(v => fetch(`res/plugin_examples/${v}/manifest.json`, this.constantService.getFetchOption()).then(res => res.json())), ) - .then(arr => arr.reduce((acc,curr) => acc.concat(curr) ,[])) + .then(arr => arr.reduce((acc, curr) => acc.concat(curr) , [])), ]) .then(arr => resolve( [].concat(arr[0]).concat(arr[1]) )) .catch(reject) }) - + promiseFetchedPluginManifests - .then(arr=> + .then(arr => this.fetchedPluginManifests = arr) - .catch(console.error) + .catch(this.log.error) this.minimisedPlugins$ = merge( of(new Set()), - this.widgetService.minimisedWindow$ + this.widgetService.minimisedWindow$, ).pipe( map(set => { const returnSet = new Set<string>() - for (let [pluginName, wu] of this.mapPluginNameToWidgetUnit) { + for (const [pluginName, wu] of this.mapPluginNameToWidgetUnit) { if (set.has(wu)) { returnSet.add(pluginName) } } return returnSet }), - shareReplay(1) + shareReplay(1), ) this.launchedPlugins$ = new BehaviorSubject(new Set()) } - launchNewWidget = (manifest) => this.launchPlugin(manifest) + public launchNewWidget = (manifest) => this.launchPlugin(manifest) .then(handler => { this.orphanPlugins.add(manifest) handler.onShutdown(() => { @@ -135,51 +137,50 @@ export class PluginServices{ }) }) - readyPlugin(plugin:PluginManifest):Promise<any>{ + public readyPlugin(plugin: IPluginManifest): Promise<any> { return Promise.all([ isDefined(plugin.template) ? Promise.resolve() : isDefined(plugin.templateURL) - ? this.fetch(plugin.templateURL, {responseType: 'text'}).then(template=>plugin.template = template) + ? this.fetch(plugin.templateURL, {responseType: 'text'}).then(template => plugin.template = template) : Promise.reject('both template and templateURL are not defined') , - isDefined(plugin.scriptURL) ? Promise.resolve() : Promise.reject(`inline script has been deprecated. use scriptURL instead`) + isDefined(plugin.scriptURL) ? Promise.resolve() : Promise.reject(`inline script has been deprecated. use scriptURL instead`), ]) } private launchedPlugins: Set<string> = new Set() public launchedPlugins$: BehaviorSubject<Set<string>> - pluginHasLaunched(pluginName:string) { + public pluginHasLaunched(pluginName: string) { return this.launchedPlugins.has(pluginName) } - addPluginToLaunchedSet(pluginName:string){ + public addPluginToLaunchedSet(pluginName: string) { this.launchedPlugins.add(pluginName) this.launchedPlugins$.next(this.launchedPlugins) } - removePluginFromLaunchedSet(pluginName:string){ + public removePluginFromLaunchedSet(pluginName: string) { this.launchedPlugins.delete(pluginName) this.launchedPlugins$.next(this.launchedPlugins) } - - pluginIsLaunching(pluginName:string){ + public pluginIsLaunching(pluginName: string) { return this.launchingPlugins.has(pluginName) } - addPluginToIsLaunchingSet(pluginName:string) { + public addPluginToIsLaunchingSet(pluginName: string) { this.launchingPlugins.add(pluginName) } - removePluginFromIsLaunchingSet(pluginName:string){ + public removePluginFromIsLaunchingSet(pluginName: string) { this.launchingPlugins.delete(pluginName) } private mapPluginNameToWidgetUnit: Map<string, WidgetUnit> = new Map() - pluginIsMinimised(pluginName:string) { + public pluginIsMinimised(pluginName: string) { return this.widgetService.isMinimised( this.mapPluginNameToWidgetUnit.get(pluginName) ) } private launchingPlugins: Set<string> = new Set() - public orphanPlugins: Set<PluginManifest> = new Set() - launchPlugin(plugin:PluginManifest){ + public orphanPlugins: Set<IPluginManifest> = new Set() + public launchPlugin(plugin: IPluginManifest) { if (this.pluginIsLaunching(plugin.name)) { // plugin launching please be patient // TODO add visual feedback @@ -190,7 +191,7 @@ export class PluginServices{ // TODO add visual feedback // if widget window is minimized, maximize it - + const wu = this.mapPluginNameToWidgetUnit.get(plugin.name) if (this.widgetService.isMinimised(wu)) { this.widgetService.unminimise(wu) @@ -201,12 +202,12 @@ export class PluginServices{ } this.addPluginToIsLaunchingSet(plugin.name) - + return this.readyPlugin(plugin) - .then(()=>{ + .then(() => { const pluginUnit = this.pluginViewContainerRef.createComponent( this.pluginUnitFactory ) /* TODO in v0.2, I used: - + const template = document.createElement('div') template.insertAdjacentHTML('afterbegin',template) @@ -234,19 +235,19 @@ export class PluginServices{ type : PLUGIN_STATE_ACTION_TYPES.SET_INIT_PLUGIN, manifest : { name : plugin.name, - initManifestUrl : url - } + initManifestUrl : url, + }, }) const shutdownCB = [ () => { this.removePluginFromLaunchedSet(plugin.name) - } + }, ] handler.onShutdown = (cb) => { - if(typeof cb !== 'function'){ - console.warn('onShutdown requires the argument to be a function') + if (typeof cb !== 'function') { + this.log.warn('onShutdown requires the argument to be a function') return } shutdownCB.push(cb) @@ -259,14 +260,14 @@ export class PluginServices{ handler.onShutdown(() => this.removeSrc(script)) const template = document.createElement('div') - template.insertAdjacentHTML('afterbegin',plugin.template) + template.insertAdjacentHTML('afterbegin', plugin.template) pluginUnit.instance.elementRef.nativeElement.append( template ) - const widgetCompRef = this.widgetService.addNewWidget(pluginUnit,{ + const widgetCompRef = this.widgetService.addNewWidget(pluginUnit, { state : 'floating', exitable : true, persistency: plugin.persistency, - title : plugin.displayName || plugin.name + title : plugin.displayName || plugin.name, }) this.addPluginToLaunchedSet(plugin.name) @@ -276,24 +277,24 @@ export class PluginServices{ const unsubscribeOnPluginDestroy = [] - handler.blink = (sec?:number)=>{ + handler.blink = (sec?: number) => { widgetCompRef.instance.blinkOn = true } handler.setProgressIndicator = (val) => widgetCompRef.instance.progressIndicator = val - handler.shutdown = ()=>{ + handler.shutdown = () => { widgetCompRef.instance.exit() } - handler.onShutdown(()=>{ - unsubscribeOnPluginDestroy.forEach(s=>s.unsubscribe()) + handler.onShutdown(() => { + unsubscribeOnPluginDestroy.forEach(s => s.unsubscribe()) delete this.apiService.interactiveViewer.pluginControl[plugin.name] this.mapPluginNameToWidgetUnit.delete(plugin.name) }) - - pluginUnit.onDestroy(()=>{ - while(shutdownCB.length > 0){ + + pluginUnit.onDestroy(() => { + while (shutdownCB.length > 0) { shutdownCB.pop()() } }) @@ -303,70 +304,14 @@ export class PluginServices{ } } -@Injectable({ - providedIn: 'root' -}) - -export class PluginServiceuseEffect{ - - @Effect() - public initManifests$: Observable<any> - - constructor( - store$: Store<IavRootStoreInterface>, - constantService: AtlasViewerConstantsServices, - pluginService: PluginServices - ){ - this.initManifests$ = store$.pipe( - select('pluginState'), - select('initManifests'), - filter(v => !!v), - startWith([]), - map(arr => { - // only launch plugins that has init manifest src label on it - return arr.filter(([ source ]) => source === PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC) - }), - filter(arr => arr.length > 0), - map((arr: [string, string|null][]) => { - - for (const [source, url] of arr){ - fetch(url, constantService.getFetchOption()) - .then(res => res.json()) - .then(json => pluginService.launchNewWidget(json)) - .catch(console.error) - } - - // clear init manifest - return { - type: PLUGINSTORE_ACTION_TYPES.CLEAR_INIT_PLUGIN - } - }) - ) - } -} - - -export class PluginHandler{ - onShutdown : (callback:()=>void)=>void = (_) => {} - blink : (sec?:number)=>void = (_) => {} - shutdown : ()=>void = () => {} - - initState? : any - initStateUrl? : string - - setInitManifestUrl : (url:string|null)=>void - - setProgressIndicator: (progress:number) => void +export interface IPluginManifest { + name?: string + displayName?: string + templateURL?: string + template?: string + scriptURL?: string + script?: string + initState?: any + initStateUrl?: string + persistency?: boolean } - -export interface PluginManifest{ - name? : string - displayName? : string - templateURL? : string - template? : string - scriptURL? : string - script? : string - initState? : any - initStateUrl? : string - persistency? : boolean -} \ No newline at end of file diff --git a/src/atlasViewer/atlasViewer.urlUtil.spec.ts b/src/atlasViewer/atlasViewer.urlUtil.spec.ts index e36c0c1cb70a7603d1473fac4faba5d039fcaa6d..bbdd2c21e27b3843ba0c8f0d494c14e99fbbad17 100644 --- a/src/atlasViewer/atlasViewer.urlUtil.spec.ts +++ b/src/atlasViewer/atlasViewer.urlUtil.spec.ts @@ -1,6 +1,8 @@ +// tslint:disable:no-empty + import {} from 'jasmine' -import { cvtSearchParamToState, PARSING_SEARCHPARAM_ERROR } from './atlasViewer.urlUtil' import { defaultRootState } from 'src/services/stateStore.service' +import { cvtSearchParamToState, PARSING_SEARCHPARAM_ERROR } from './atlasViewer.urlUtil' const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json') const colin = require('!json-loader!src/res/ext/colin.json') @@ -13,8 +15,8 @@ const fetchedTemplateRootState = { ...rest, viewerState: { ...viewerState, - fetchedTemplates: [ bigbrainJson, colin, mni152, allen, waxholm ] - } + fetchedTemplates: [ bigbrainJson, colin, mni152, allen, waxholm ], + }, } describe('atlasViewer.urlService.service.ts', () => { @@ -64,6 +66,6 @@ describe('atlasViewer.urlService.service.ts', () => { }) describe('cvtStateToSearchParam', () => { - + }) -}) \ No newline at end of file +}) diff --git a/src/atlasViewer/atlasViewer.urlUtil.ts b/src/atlasViewer/atlasViewer.urlUtil.ts index 2a7c38f1f6b886ba8d43bfe3412cc0eb7dbe46d7..40e018c95f3206bc2d031be8e1215c2c5aa0f204 100644 --- a/src/atlasViewer/atlasViewer.urlUtil.ts +++ b/src/atlasViewer/atlasViewer.urlUtil.ts @@ -1,21 +1,21 @@ -import { IavRootStoreInterface, generateLabelIndexId, getNgIdLabelIndexFromRegion } from "../services/stateStore.service"; -import { encodeNumber, separator, decodeToNumber, GLSL_COLORMAP_JET } from "./atlasViewer.constantService.service"; -import { mixNgLayers } from "src/services/state/ngViewerState.store"; import { getGetRegionFromLabelIndexId } from "src/services/effect/effect"; +import { mixNgLayers } from "src/services/state/ngViewerState.store"; import { CONSTANTS as PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.store' +import { generateLabelIndexId, getNgIdLabelIndexFromRegion, IavRootStoreInterface } from "../services/stateStore.service"; +import { decodeToNumber, encodeNumber, GLSL_COLORMAP_JET, separator } from "./atlasViewer.constantService.service"; export const PARSING_SEARCHPARAM_ERROR = { TEMPALTE_NOT_SET: 'TEMPALTE_NOT_SET', TEMPLATE_NOT_FOUND: 'TEMPLATE_NOT_FOUND', - PARCELLATION_NOT_UPDATED: 'PARCELLATION_NOT_UPDATED' + PARCELLATION_NOT_UPDATED: 'PARCELLATION_NOT_UPDATED', } const PARSING_SEARCHPARAM_WARNING = { UNKNOWN_PARCELLATION: 'UNKNOWN_PARCELLATION', - DECODE_CIPHER_ERROR: 'DECODE_CIPHER_ERROR' + DECODE_CIPHER_ERROR: 'DECODE_CIPHER_ERROR', } export const CVT_STATE_TO_SEARCHPARAM_ERROR = { - TEMPLATE_NOT_SELECTED: 'TEMPLATE_NOT_SELECTED' + TEMPLATE_NOT_SELECTED: 'TEMPLATE_NOT_SELECTED', } export const cvtStateToSearchParam = (state: IavRootStoreInterface): URLSearchParams => { @@ -24,7 +24,7 @@ export const cvtStateToSearchParam = (state: IavRootStoreInterface): URLSearchPa const { viewerState, ngViewerState, pluginState } = state const { templateSelected, parcellationSelected, navigation, regionsSelected } = viewerState - if (!templateSelected) throw new Error(CVT_STATE_TO_SEARCHPARAM_ERROR.TEMPLATE_NOT_SELECTED) + if (!templateSelected) { throw new Error(CVT_STATE_TO_SEARCHPARAM_ERROR.TEMPLATE_NOT_SELECTED) } // encoding states searchParam.set('templateSelected', templateSelected.name) @@ -35,11 +35,10 @@ export const cvtStateToSearchParam = (state: IavRootStoreInterface): URLSearchPa for (const region of regionsSelected) { const { ngId, labelIndex } = getNgIdLabelIndexFromRegion({ region }) const existingEntry = accumulatorMap.get(ngId) - if (existingEntry) existingEntry.push(labelIndex) - else accumulatorMap.set(ngId, [ labelIndex ]) + if (existingEntry) { existingEntry.push(labelIndex) } else { accumulatorMap.set(ngId, [ labelIndex ]) } } const cRegionObj = {} - for (const [key, arr] of accumulatorMap){ + for (const [key, arr] of accumulatorMap) { cRegionObj[key] = arr.map(n => encodeNumber(n)).join(separator) } searchParam.set('cRegionsSelected', JSON.stringify(cRegionObj)) @@ -50,20 +49,20 @@ export const cvtStateToSearchParam = (state: IavRootStoreInterface): URLSearchPa orientation.map(n => encodeNumber(n, {float: true})).join(separator), perspectiveOrientation.map(n => encodeNumber(n, {float: true})).join(separator), encodeNumber(Math.floor(perspectiveZoom)), - Array.from(position).map((v:number) => Math.floor(v)).map(n => encodeNumber(n)).join(separator), - encodeNumber(Math.floor(zoom)) + Array.from(position).map((v: number) => Math.floor(v)).map(n => encodeNumber(n)).join(separator), + encodeNumber(Math.floor(zoom)), ].join(`${separator}${separator}`) searchParam.set('cNavigation', cNavString) // encode nifti layers const initialNgState = templateSelected.nehubaConfig.dataset.initialNgState const { layers } = ngViewerState - const additionalLayers = layers.filter(layer => + const additionalLayers = layers.filter(layer => /^blob\:/.test(layer.name) && - Object.keys(initialNgState.layers).findIndex(layerName => layerName === layer.name) < 0 + Object.keys(initialNgState.layers).findIndex(layerName => layerName === layer.name) < 0, ) const niftiLayers = additionalLayers.filter(layer => /^nifti\:\/\//.test(layer.source)) - if (niftiLayers.length > 0) searchParam.set('niftiLayers', niftiLayers.join('__')) + if (niftiLayers.length > 0) { searchParam.set('niftiLayers', niftiLayers.join('__')) } // plugin state const { initManifests } = pluginState @@ -72,52 +71,55 @@ export const cvtStateToSearchParam = (state: IavRootStoreInterface): URLSearchPa .map(([ src, url]) => url) .join('__') - if (initManifests.length > 0) searchParam.set('pluginState', pluginStateParam) + if (initManifests.length > 0) { searchParam.set('pluginState', pluginStateParam) } return searchParam } -export const cvtSearchParamToState = (searchparams: URLSearchParams, state:IavRootStoreInterface, warningCb = (args) => {}): IavRootStoreInterface => { - +export const cvtSearchParamToState = (searchparams: URLSearchParams, state: IavRootStoreInterface, callback?: (error: any) => void): IavRootStoreInterface => { + const returnState = JSON.parse(JSON.stringify(state)) as IavRootStoreInterface + // tslint:disable-next-line:no-empty + const warningCb = callback || (() => {}) + const { TEMPLATE_NOT_FOUND, TEMPALTE_NOT_SET, PARCELLATION_NOT_UPDATED } = PARSING_SEARCHPARAM_ERROR const { UNKNOWN_PARCELLATION, DECODE_CIPHER_ERROR } = PARSING_SEARCHPARAM_WARNING const { fetchedTemplates } = state.viewerState const searchedTemplatename = (() => { const param = searchparams.get('templateSelected') - if (param === 'Allen Mouse') return `Allen adult mouse brain reference atlas V3` - if (param === 'Waxholm Rat V2.0') return 'Waxholm Space rat brain atlas v.2.0' + if (param === 'Allen Mouse') { return `Allen adult mouse brain reference atlas V3` } + if (param === 'Waxholm Rat V2.0') { return 'Waxholm Space rat brain atlas v.2.0' } return param })() const searchedParcellationName = (() => { const param = searchparams.get('parcellationSelected') - if (param === 'Allen Mouse Brain Atlas') return 'Allen adult mouse brain reference atlas V3 Brain Atlas' - if (param === 'Whole Brain (v2.0)') return 'Waxholm Space rat brain atlas v.2.0' + if (param === 'Allen Mouse Brain Atlas') { return 'Allen adult mouse brain reference atlas V3 Brain Atlas' } + if (param === 'Whole Brain (v2.0)') { return 'Waxholm Space rat brain atlas v.2.0' } return param })() - if (!searchedTemplatename) throw new Error(TEMPALTE_NOT_SET) - - const templateToLoad = fetchedTemplates.find(template=>template.name === searchedTemplatename) - if (!templateToLoad) throw new Error(TEMPLATE_NOT_FOUND) + if (!searchedTemplatename) { throw new Error(TEMPALTE_NOT_SET) } + + const templateToLoad = fetchedTemplates.find(template => template.name === searchedTemplatename) + if (!templateToLoad) { throw new Error(TEMPLATE_NOT_FOUND) } /** * TODO if search param of either template or parcellation is incorrect, wrong things are searched */ - const parcellationToLoad = templateToLoad.parcellations.find(parcellation=>parcellation.name === searchedParcellationName) - if (!parcellationToLoad) warningCb({ type: UNKNOWN_PARCELLATION }) - + const parcellationToLoad = templateToLoad.parcellations.find(parcellation => parcellation.name === searchedParcellationName) + if (!parcellationToLoad) { warningCb({ type: UNKNOWN_PARCELLATION }) } + const { viewerState } = returnState viewerState.templateSelected = templateToLoad viewerState.parcellationSelected = parcellationToLoad || templateToLoad.parcellations[0] - + /* selected regions */ // TODO deprecate. Fallback (defaultNgId) (should) already exist // if (!viewerState.parcellationSelected.updated) throw new Error(PARCELLATION_NOT_UPDATED) - + const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({ parcellation: viewerState.parcellationSelected }) /** * either or both parcellationToLoad and .regions maybe empty @@ -126,10 +128,10 @@ export const cvtSearchParamToState = (searchparams: URLSearchParams, state:IavRo * backwards compatibility */ const selectedRegionsParam = searchparams.get('regionsSelected') - if(selectedRegionsParam){ + if (selectedRegionsParam) { const ids = selectedRegionsParam.split('_') - - viewerState.regionsSelected = ids.map(labelIndexId => getRegionFromlabelIndexId({ labelIndexId })) + + viewerState.regionsSelected = ids.map(labelIndexId => getRegionFromlabelIndexId({ labelIndexId })) } const cRegionsSelectedParam = searchparams.get('cRegionsSelected') @@ -139,24 +141,27 @@ export const cvtSearchParamToState = (searchparams: URLSearchParams, state:IavRo const selectRegionIds = [] - for (let ngId in json) { - const val = json[ngId] - const labelIndicies = val.split(separator).map(n =>{ - try{ - return decodeToNumber(n) - } catch (e) { - /** - * TODO poisonsed encoded char, send error message - */ - warningCb({ type: DECODE_CIPHER_ERROR, message: `cRegionSelectionParam is malformed: cannot decode ${n}` }) - return null + for (const ngId in json) { + // see https://palantir.github.io/tslint/rules/forin/ + if (json.hasOwnProperty(ngId)) { + const val = json[ngId] + const labelIndicies = val.split(separator).map(n => { + try { + return decodeToNumber(n) + } catch (e) { + /** + * TODO poisonsed encoded char, send error message + */ + warningCb({ type: DECODE_CIPHER_ERROR, message: `cRegionSelectionParam is malformed: cannot decode ${n}` }) + return null + } + }).filter(v => !!v) + for (const labelIndex of labelIndicies) { + selectRegionIds.push( generateLabelIndexId({ ngId, labelIndex }) ) } - }).filter(v => !!v) - for (let labelIndex of labelIndicies) { - selectRegionIds.push( generateLabelIndexId({ ngId, labelIndex }) ) } } - viewerState.regionsSelected = selectRegionIds.map(labelIndexId => getRegionFromlabelIndexId({ labelIndexId })) + viewerState.regionsSelected = selectRegionIds.map(labelIndexId => getRegionFromlabelIndexId({ labelIndexId })) } catch (e) { /** @@ -171,17 +176,17 @@ export const cvtSearchParamToState = (searchparams: URLSearchParams, state:IavRo // for backwards compatibility const _viewerState = searchparams.get('navigation') - if(_viewerState){ - const [o,po,pz,p,z] = _viewerState.split('__') + if (_viewerState) { + const [o, po, pz, p, z] = _viewerState.split('__') viewerState.navigation = { - orientation : o.split('_').map(n=>Number(n)), - perspectiveOrientation : po.split('_').map(n=>Number(n)), + orientation : o.split('_').map(n => Number(n)), + perspectiveOrientation : po.split('_').map(n => Number(n)), perspectiveZoom : Number(pz), - position : p.split('_').map(n=>Number(n)), + position : p.split('_').map(n => Number(n)), zoom : Number(z), // flag to allow for animation when enabled - animation: {} + animation: {}, } } @@ -200,9 +205,9 @@ export const cvtSearchParamToState = (searchparams: URLSearchParams, state:IavRo perspectiveZoom: pz, position: p, zoom: z, - + // flag to allow for animation when enabled - animation: {} + animation: {}, } } catch (e) { /** @@ -213,7 +218,7 @@ export const cvtSearchParamToState = (searchparams: URLSearchParams, state:IavRo } const niftiLayers = searchparams.get('niftiLayers') - if(niftiLayers){ + if (niftiLayers) { const layers = niftiLayers .split('__') .map(layer => { @@ -221,7 +226,7 @@ export const cvtSearchParamToState = (searchparams: URLSearchParams, state:IavRo name : layer, source : `nifti://${layer}`, mixability : 'nonmixable', - shader : GLSL_COLORMAP_JET + shader : GLSL_COLORMAP_JET, } as any }) const { ngViewerState } = returnState @@ -230,7 +235,7 @@ export const cvtSearchParamToState = (searchparams: URLSearchParams, state:IavRo const { pluginState } = returnState const pluginStates = searchparams.get('pluginStates') - if(pluginStates){ + if (pluginStates) { const arrPluginStates = pluginStates.split('__') pluginState.initManifests = arrPluginStates.map(url => [PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC, url] as [string, string]) } diff --git a/src/atlasViewer/atlasViewer.workerService.service.ts b/src/atlasViewer/atlasViewer.workerService.service.ts index 6f6d352867cc2f1e3a14fc2a5e01976b37745d75..c5e222480facb343ec0d02b1ad8bca75ce486abb 100644 --- a/src/atlasViewer/atlasViewer.workerService.service.ts +++ b/src/atlasViewer/atlasViewer.workerService.service.ts @@ -9,9 +9,9 @@ import '../util/worker.js' export const worker = new Worker('worker.js') @Injectable({ - providedIn:'root' + providedIn: 'root', }) -export class AtlasWorkerService{ +export class AtlasWorkerService { public worker = worker } diff --git a/src/atlasViewer/modalUnit/modalUnit.component.ts b/src/atlasViewer/modalUnit/modalUnit.component.ts index ae5b300707a3ed011268e9cc23733776a94ce89c..eb62a94bb383b8a17e0a3d9d145872e23b0cede0 100644 --- a/src/atlasViewer/modalUnit/modalUnit.component.ts +++ b/src/atlasViewer/modalUnit/modalUnit.component.ts @@ -1,27 +1,27 @@ -import { Component, Input, ViewContainerRef, TemplateRef, ViewChild } from '@angular/core' +import { Component, Input, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core' @Component({ templateUrl : './modalUnit.template.html', styleUrls : [ - './modalUnit.style.css' - ] + './modalUnit.style.css', + ], }) -export class ModalUnit{ - @Input() title : string - @Input() body : string = 'Modal Body Text' - @Input() template: TemplateRef<any> - @Input() footer: string +export class ModalUnit { + @Input() public title: string + @Input() public body: string = 'Modal Body Text' + @Input() public template: TemplateRef<any> + @Input() public footer: string - @ViewChild('templateContainer', {read:ViewContainerRef}) templateContainer : ViewContainerRef + @ViewChild('templateContainer', {read: ViewContainerRef}) public templateContainer: ViewContainerRef + + constructor(public viewContainerRef: ViewContainerRef) { - constructor(public viewContainerRef : ViewContainerRef){ - } - ngAfterViewInit(){ + public ngAfterViewInit() { if (this.templateContainer) { this.templateContainer.createEmbeddedView(this.template) } } -} \ No newline at end of file +} diff --git a/src/atlasViewer/onhoverSegment.pipe.ts b/src/atlasViewer/onhoverSegment.pipe.ts index 640772c63ddc32876f38e8ac708bcf3130d5f73b..5199a582a1ba2e5084d7097996652a492211a342 100644 --- a/src/atlasViewer/onhoverSegment.pipe.ts +++ b/src/atlasViewer/onhoverSegment.pipe.ts @@ -1,24 +1,24 @@ -import { PipeTransform, Pipe, SecurityContext } from "@angular/core"; +import { Pipe, PipeTransform, SecurityContext } from "@angular/core"; import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; @Pipe({ - name: 'transformOnhoverSegment' + name: 'transformOnhoverSegment', }) -export class TransformOnhoverSegmentPipe implements PipeTransform{ - constructor(private sanitizer:DomSanitizer){ +export class TransformOnhoverSegmentPipe implements PipeTransform { + constructor(private sanitizer: DomSanitizer) { } - private sanitizeHtml(inc:string):SafeHtml{ + private sanitizeHtml(inc: string): SafeHtml { return this.sanitizer.sanitize(SecurityContext.HTML, inc) } - private getStatus(text:string) { + private getStatus(text: string) { return ` <span class="text-muted">(${this.sanitizeHtml(text)})</span>` } - public transform(segment: any | number): SafeHtml{ + public transform(segment: any | number): SafeHtml { return this.sanitizer.bypassSecurityTrustHtml(( ( this.sanitizeHtml(segment.name) || segment) + (segment.status @@ -26,4 +26,4 @@ export class TransformOnhoverSegmentPipe implements PipeTransform{ : '') )) } -} \ No newline at end of file +} diff --git a/src/atlasViewer/pluginUnit/pluginUnit.component.ts b/src/atlasViewer/pluginUnit/pluginUnit.component.ts index 5d849eab7fb874a71b9053c8c4ad6e2b4a5a3103..902701b16c29791974171a16841beb85d7ee3fee 100644 --- a/src/atlasViewer/pluginUnit/pluginUnit.component.ts +++ b/src/atlasViewer/pluginUnit/pluginUnit.component.ts @@ -1,24 +1,15 @@ -import { Component, ElementRef, OnDestroy, HostBinding } from "@angular/core"; - +import { Component, ElementRef, HostBinding } from "@angular/core"; @Component({ - templateUrl : `./pluginUnit.template.html` + templateUrl : `./pluginUnit.template.html`, }) -export class PluginUnit implements OnDestroy{ - - elementRef:ElementRef - +export class PluginUnit { + @HostBinding('attr.pluginContainer') - pluginContainer = true + public pluginContainer = true - constructor(er:ElementRef){ - this.elementRef = er - } + constructor(public elementRef: ElementRef) { - ngOnDestroy(){ - if (!PRODUCTION) - console.log('plugin being destroyed') } - -} \ No newline at end of file +} diff --git a/src/atlasViewer/widgetUnit/widgetService.service.ts b/src/atlasViewer/widgetUnit/widgetService.service.ts index a976376cb96c424d48f1cfb5dbe630f3a71dbde5..a8ee57e9c770ed15d3776bdff0f5c33d5c207395 100644 --- a/src/atlasViewer/widgetUnit/widgetService.service.ts +++ b/src/atlasViewer/widgetUnit/widgetService.service.ts @@ -1,36 +1,38 @@ -import { ComponentRef, ComponentFactory, Injectable, ViewContainerRef, ComponentFactoryResolver, Injector, OnDestroy } from "@angular/core"; -import { WidgetUnit } from "./widgetUnit.component"; +import { ComponentFactory, ComponentFactoryResolver, ComponentRef, Injectable, Injector, OnDestroy, ViewContainerRef } from "@angular/core"; +import { BehaviorSubject, Subscription } from "rxjs"; +import { LoggingService } from "src/services/logging.service"; import { AtlasViewerConstantsServices } from "../atlasViewer.constantService.service"; -import { Subscription, BehaviorSubject } from "rxjs"; +import { WidgetUnit } from "./widgetUnit.component"; @Injectable({ - providedIn : 'root' + providedIn : 'root', }) -export class WidgetServices implements OnDestroy{ +export class WidgetServices implements OnDestroy { - public floatingContainer : ViewContainerRef - public dockedContainer : ViewContainerRef - public factoryContainer : ViewContainerRef + public floatingContainer: ViewContainerRef + public dockedContainer: ViewContainerRef + public factoryContainer: ViewContainerRef - private widgetUnitFactory : ComponentFactory<WidgetUnit> - private widgetComponentRefs : Set<ComponentRef<WidgetUnit>> = new Set() + private widgetUnitFactory: ComponentFactory<WidgetUnit> + private widgetComponentRefs: Set<ComponentRef<WidgetUnit>> = new Set() - private clickedListener : Subscription[] = [] + private clickedListener: Subscription[] = [] public minimisedWindow$: BehaviorSubject<Set<WidgetUnit>> - private minimisedWindow: Set<WidgetUnit> = new Set() + private minimisedWindow: Set<WidgetUnit> = new Set() constructor( - private cfr:ComponentFactoryResolver, - private constantServce:AtlasViewerConstantsServices, - private injector : Injector - ){ + private cfr: ComponentFactoryResolver, + private constantServce: AtlasViewerConstantsServices, + private injector: Injector, + private log: LoggingService, + ) { this.widgetUnitFactory = this.cfr.resolveComponentFactory(WidgetUnit) this.minimisedWindow$ = new BehaviorSubject(this.minimisedWindow) this.subscriptions.push( - this.constantServce.useMobileUI$.subscribe(bool => this.useMobileUI = bool) + this.constantServce.useMobileUI$.subscribe(bool => this.useMobileUI = bool), ) } @@ -38,21 +40,21 @@ export class WidgetServices implements OnDestroy{ public useMobileUI: boolean = false - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } - clearAllWidgets(){ - [...this.widgetComponentRefs].forEach((cr:ComponentRef<WidgetUnit>) => { - if(!cr.instance.persistency) cr.destroy() + public clearAllWidgets() { + [...this.widgetComponentRefs].forEach((cr: ComponentRef<WidgetUnit>) => { + if (!cr.instance.persistency) { cr.destroy() } }) - this.clickedListener.forEach(s=>s.unsubscribe()) + this.clickedListener.forEach(s => s.unsubscribe()) } - rename(wu:WidgetUnit, {title, titleHTML}: {title: string, titleHTML: string}){ + public rename(wu: WidgetUnit, {title, titleHTML}: {title: string, titleHTML: string}) { /** * WARNING: always sanitize before pass to rename fn! */ @@ -60,25 +62,25 @@ export class WidgetServices implements OnDestroy{ wu.titleHTML = titleHTML } - minimise(wu:WidgetUnit){ + public minimise(wu: WidgetUnit) { this.minimisedWindow.add(wu) this.minimisedWindow$.next(new Set(this.minimisedWindow)) } - - isMinimised(wu:WidgetUnit){ + + public isMinimised(wu: WidgetUnit) { return this.minimisedWindow.has(wu) } - unminimise(wu:WidgetUnit){ + public unminimise(wu: WidgetUnit) { this.minimisedWindow.delete(wu) this.minimisedWindow$.next(new Set(this.minimisedWindow)) } - addNewWidget(guestComponentRef:ComponentRef<any>,options?:Partial<WidgetOptionsInterface>):ComponentRef<WidgetUnit>{ + public addNewWidget(guestComponentRef: ComponentRef<any>, options?: Partial<IWidgetOptionsInterface>): ComponentRef<WidgetUnit> { const component = this.widgetUnitFactory.create(this.injector) const _option = getOption(options) - if(this.useMobileUI){ + if (this.useMobileUI) { _option.state = 'docked' } @@ -88,12 +90,12 @@ export class WidgetServices implements OnDestroy{ ? this.dockedContainer.insert(component.hostView) : this.floatingContainer.insert(component.hostView) - if(component.constructor === Error){ + if (component.constructor === Error) { throw component - }else{ + } else { const _component = (component as ComponentRef<WidgetUnit>); _component.instance.container.insert( guestComponentRef.hostView ) - + /* programmatic DI */ _component.instance.widgetServices = this @@ -107,12 +109,12 @@ export class WidgetServices implements OnDestroy{ /* internal properties, used for changing state */ _component.instance.guestComponentRef = guestComponentRef - if(_option.state === 'floating'){ + if (_option.state === 'floating') { let position = this.constantServce.floatingWidgetStartingPos - while([...this.widgetComponentRefs].some(widget=> - widget.instance.state === 'floating' && - widget.instance.position.every((v,idx)=>v===position[idx]))){ - position = position.map(v=>v+10) as [number,number] + while ([...this.widgetComponentRefs].some(widget => + widget.instance.state === 'floating' && + widget.instance.position.every((v, idx) => v === position[idx]))) { + position = position.map(v => v + 10) as [number, number] } _component.instance.position = position } @@ -124,77 +126,80 @@ export class WidgetServices implements OnDestroy{ _component.onDestroy(() => this.minimisedWindow.delete(_component.instance)) this.clickedListener.push( - _component.instance.clickedEmitter.subscribe((widgetUnit:WidgetUnit)=>{ + _component.instance.clickedEmitter.subscribe((widgetUnit: WidgetUnit) => { /** - * TODO this operation + * TODO this operation */ - if(widgetUnit.state !== 'floating') + if (widgetUnit.state !== 'floating') { return - const widget = [...this.widgetComponentRefs].find(widget=>widget.instance === widgetUnit) - if(!widget) + } + const foundWidgetCompRef = [...this.widgetComponentRefs].find(wr => wr.instance === widgetUnit) + if (!foundWidgetCompRef) { return - const idx = this.floatingContainer.indexOf(widget.hostView) - if(idx === this.floatingContainer.length - 1 ) + } + const idx = this.floatingContainer.indexOf(foundWidgetCompRef.hostView) + if (idx === this.floatingContainer.length - 1 ) { return + } this.floatingContainer.detach(idx) - this.floatingContainer.insert(widget.hostView) - }) + this.floatingContainer.insert(foundWidgetCompRef.hostView) + }), ) return _component } } - changeState(widgetUnit:WidgetUnit, options : WidgetOptionsInterface){ - const widgetRef = [...this.widgetComponentRefs].find(cr=>cr.instance === widgetUnit) - if(widgetRef){ + public changeState(widgetUnit: WidgetUnit, options: IWidgetOptionsInterface) { + const widgetRef = [...this.widgetComponentRefs].find(cr => cr.instance === widgetUnit) + if (widgetRef) { this.widgetComponentRefs.delete(widgetRef) widgetRef.instance.container.detach( 0 ) const guestComopnent = widgetRef.instance.guestComponentRef - const cr = this.addNewWidget(guestComopnent,options) + const cr = this.addNewWidget(guestComopnent, options) widgetRef.destroy() - }else{ - console.warn('widgetref not found') + } else { + this.log.warn('widgetref not found') } } - exitWidget(widgetUnit:WidgetUnit){ - const widgetRef = [...this.widgetComponentRefs].find(cr=>cr.instance === widgetUnit) - if(widgetRef){ + public exitWidget(widgetUnit: WidgetUnit) { + const widgetRef = [...this.widgetComponentRefs].find(cr => cr.instance === widgetUnit) + if (widgetRef) { widgetRef.destroy() this.widgetComponentRefs.delete(widgetRef) - }else{ - console.warn('widgetref not found') + } else { + this.log.warn('widgetref not found') } } - dockAllWidgets(){ + public dockAllWidgets() { /* nb cannot directly iterate the set, as the set will be updated and create and infinite loop */ [...this.widgetComponentRefs].forEach(cr => cr.instance.dock()) } - floatAllWidgets(){ + public floatAllWidgets() { [...this.widgetComponentRefs].forEach(cr => cr.instance.undock()) } } -function safeGetSingle(obj:any, arg:string){ +function safeGetSingle(obj: any, arg: string) { return typeof obj === 'object' && obj !== null && typeof arg === 'string' ? obj[arg] : null } -function safeGet(obj:any, ...args:string[]){ +function safeGet(obj: any, ...args: string[]) { let _obj = Object.assign({}, obj) - while(args.length > 0){ + while (args.length > 0) { const arg = args.shift() _obj = safeGetSingle(_obj, arg) } return _obj } -function getOption(option?:Partial<WidgetOptionsInterface>):WidgetOptionsInterface{ +function getOption(option?: Partial<IWidgetOptionsInterface>): IWidgetOptionsInterface { return{ exitable : safeGet(option, 'exitable') !== null ? safeGet(option, 'exitable') @@ -202,15 +207,14 @@ function getOption(option?:Partial<WidgetOptionsInterface>):WidgetOptionsInterfa state : safeGet(option, 'state') || 'floating', title : safeGet(option, 'title') || 'Untitled', persistency : safeGet(option, 'persistency') || false, - titleHTML: safeGet(option, 'titleHTML') || null + titleHTML: safeGet(option, 'titleHTML') || null, } } -export interface WidgetOptionsInterface{ - title? : string - state? : 'docked' | 'floating' - exitable? : boolean - persistency? : boolean - titleHTML? : string +export interface IWidgetOptionsInterface { + title?: string + state?: 'docked' | 'floating' + exitable?: boolean + persistency?: boolean + titleHTML?: string } - diff --git a/src/atlasViewer/widgetUnit/widgetUnit.component.ts b/src/atlasViewer/widgetUnit/widgetUnit.component.ts index 5eb6e8d63f1e930930c6f716912f0e5a5f938a6d..de623a93463b4d2cfcdd21ed8f1141e4aa7dd261 100644 --- a/src/atlasViewer/widgetUnit/widgetUnit.component.ts +++ b/src/atlasViewer/widgetUnit/widgetUnit.component.ts @@ -1,46 +1,45 @@ -import { Component, ViewChild, ViewContainerRef,ComponentRef, HostBinding, HostListener, Output, EventEmitter, Input, OnInit, OnDestroy } from "@angular/core"; +import { Component, ComponentRef, EventEmitter, HostBinding, HostListener, Input, OnDestroy, OnInit, Output, ViewChild, ViewContainerRef } from "@angular/core"; -import { WidgetServices } from "./widgetService.service"; -import { AtlasViewerConstantsServices } from "../atlasViewer.constantService.service"; -import { Subscription, Observable } from "rxjs"; +import { Observable, Subscription } from "rxjs"; import { map } from "rxjs/operators"; - +import { AtlasViewerConstantsServices } from "../atlasViewer.constantService.service"; +import { WidgetServices } from "./widgetService.service"; @Component({ templateUrl : './widgetUnit.template.html', styleUrls : [ - `./widgetUnit.style.css` - ] + `./widgetUnit.style.css`, + ], }) -export class WidgetUnit implements OnInit, OnDestroy{ - @ViewChild('container',{read:ViewContainerRef}) container : ViewContainerRef +export class WidgetUnit implements OnInit, OnDestroy { + @ViewChild('container', {read: ViewContainerRef}) public container: ViewContainerRef @HostBinding('attr.state') - public state : 'docked' | 'floating' = 'docked' + public state: 'docked' | 'floating' = 'docked' @HostBinding('style.width') - width : string = this.state === 'docked' ? null : '0px' + public width: string = this.state === 'docked' ? null : '0px' @HostBinding('style.height') - height : string = this.state === 'docked' ? null : '0px' + public height: string = this.state === 'docked' ? null : '0px' @HostBinding('style.display') - isMinimised: string + public isMinimised: string - isMinimised$: Observable<boolean> + public isMinimised$: Observable<boolean> public useMobileUI$: Observable<boolean> public hoverableConfig = { - translateY: -1 + translateY: -1, } /** * Timed alternates of blinkOn property should result in attention grabbing blink behaviour */ private _blinkOn: boolean = false - get blinkOn(){ + get blinkOn() { return this._blinkOn } @@ -48,7 +47,7 @@ export class WidgetUnit implements OnInit, OnDestroy{ this._blinkOn = !!val } - get showProgress(){ + get showProgress() { return this.progressIndicator !== null } @@ -58,11 +57,11 @@ export class WidgetUnit implements OnInit, OnDestroy{ * This value should be between 0 and 1 */ private _progressIndicator: number = null - get progressIndicator(){ + get progressIndicator() { return this._progressIndicator } - set progressIndicator(val:number) { + set progressIndicator(val: number) { if (isNaN(val)) { this._progressIndicator = null return @@ -80,47 +79,47 @@ export class WidgetUnit implements OnInit, OnDestroy{ public canBeDocked: boolean = false @HostListener('mousedown') - clicked(){ + public clicked() { this.clickedEmitter.emit(this) this.blinkOn = false } - @Input() title : string = 'Untitled' + @Input() public title: string = 'Untitled' @Output() - clickedEmitter : EventEmitter<WidgetUnit> = new EventEmitter() + public clickedEmitter: EventEmitter<WidgetUnit> = new EventEmitter() @Input() - public exitable : boolean = true + public exitable: boolean = true @Input() - public titleHTML : string = null + public titleHTML: string = null - public guestComponentRef : ComponentRef<any> - public widgetServices:WidgetServices - public cf : ComponentRef<WidgetUnit> + public guestComponentRef: ComponentRef<any> + public widgetServices: WidgetServices + public cf: ComponentRef<WidgetUnit> private subscriptions: Subscription[] = [] - public id: string - constructor(private constantsService: AtlasViewerConstantsServices){ + public id: string + constructor(private constantsService: AtlasViewerConstantsServices) { this.id = Date.now().toString() this.useMobileUI$ = this.constantsService.useMobileUI$ } - ngOnInit(){ + public ngOnInit() { this.canBeDocked = typeof this.widgetServices.dockedContainer !== 'undefined' this.isMinimised$ = this.widgetServices.minimisedWindow$.pipe( - map(set => set.has(this)) + map(set => set.has(this)), ) this.subscriptions.push( - this.isMinimised$.subscribe(flag => this.isMinimised = flag ? 'none' : null) + this.isMinimised$.subscribe(flag => this.isMinimised = flag ? 'none' : null), ) } - ngOnDestroy(){ - while(this.subscriptions.length > 0){ + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } @@ -131,38 +130,38 @@ export class WidgetUnit implements OnInit, OnDestroy{ * @default false * @TODO does it make sense to tie widget persistency with WidgetUnit class? */ - public persistency : boolean = false + public persistency: boolean = false - undock(event?:Event){ - if(event){ + public undock(event?: Event) { + if (event) { event.stopPropagation() event.preventDefault() } - - this.widgetServices.changeState(this,{ + + this.widgetServices.changeState(this, { title : this.title, - state:'floating', - exitable:this.exitable, - persistency:this.persistency + state: 'floating', + exitable: this.exitable, + persistency: this.persistency, }) } - dock(event?:Event){ - if(event){ + public dock(event?: Event) { + if (event) { event.stopPropagation() event.preventDefault() } - - this.widgetServices.changeState(this,{ + + this.widgetServices.changeState(this, { title : this.title, - state:'docked', - exitable:this.exitable, - persistency:this.persistency + state: 'docked', + exitable: this.exitable, + persistency: this.persistency, }) } - exit(event?:Event){ - if(event){ + public exit(event?: Event) { + if (event) { event.stopPropagation() event.preventDefault() } @@ -170,7 +169,7 @@ export class WidgetUnit implements OnInit, OnDestroy{ this.widgetServices.exitWidget(this) } - setWidthHeight(){ + public setWidthHeight() { this.width = this.state === 'docked' ? null : '0px' this.height = this.state === 'docked' ? null : '0px' } @@ -182,5 +181,5 @@ export class WidgetUnit implements OnInit, OnDestroy{ return this.state === 'floating' ? `translate(${this.position.map(v => v + 'px').join(',')})` : null } - position : [number,number] = [400,100] -} \ No newline at end of file + public position: [number, number] = [400, 100] +} diff --git a/src/atlasViewerExports/export.module.ts b/src/atlasViewerExports/export.module.ts index de4d4be5fba5a0cd4295cccdabb0a5a4c555c3d5..5334cd1da3061b50b9884bfea3f29f396b895f88 100644 --- a/src/atlasViewerExports/export.module.ts +++ b/src/atlasViewerExports/export.module.ts @@ -1,20 +1,16 @@ -import { NgModule, Injector } from "@angular/core"; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations' +import { Injector, NgModule } from "@angular/core"; import { createCustomElement } from '@angular/elements' -import { BrowserModule } from "@angular/platform-browser"; import { FormsModule } from "@angular/forms"; +import { BrowserModule } from "@angular/platform-browser"; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { BsDropdownModule } from "ngx-bootstrap/dropdown"; -import { ReadmoreComponent } from "../components/readmoore/readmore.component"; +import { ComponentsModule } from "../components/components.module"; import { MarkdownDom } from '../components/markdown/markdown.component' -import { SafeHtmlPipe } from "../util/pipes/safeHtml.pipe"; -import { SampleBoxUnit } from "./sampleBox/sampleBox.component"; import { PanelComponent } from "../components/panel/panel.component"; -import { HoverableBlockDirective } from "../components/hoverableBlock.directive"; -import { TreeComponent } from "../components/tree/tree.component"; -import { TreeSearchPipe } from "../util/pipes/treeSearch.pipe"; -import { TreeBaseDirective } from "../components/tree/treeBase.directive"; import { ParseAttributeDirective } from "../components/parseAttribute.directive"; -import { ComponentsModule } from "../components/components.module"; +import { ReadmoreComponent } from "../components/readmoore/readmore.component"; +import { TreeComponent } from "../components/tree/tree.component"; +import { SampleBoxUnit } from "./sampleBox/sampleBox.component"; @NgModule({ imports : [ @@ -22,43 +18,43 @@ import { ComponentsModule } from "../components/components.module"; BrowserAnimationsModule, FormsModule, ComponentsModule, - BsDropdownModule.forRoot() + BsDropdownModule.forRoot(), ], declarations : [ SampleBoxUnit, /* parse element attributes from string to respective datatypes */ - ParseAttributeDirective + ParseAttributeDirective, ], entryComponents : [ SampleBoxUnit, - + ReadmoreComponent, MarkdownDom, TreeComponent, - PanelComponent - ] + PanelComponent, + ], }) -export class ExportModule{ - constructor(public injector:Injector){ - const SampleBox = createCustomElement(SampleBoxUnit,{injector:this.injector}) - customElements.define('sample-box',SampleBox) +export class ExportModule { + constructor(public injector: Injector) { + const sampleBox = createCustomElement(SampleBoxUnit, {injector: this.injector}) + customElements.define('sample-box', sampleBox) - const ReadMore = createCustomElement(ReadmoreComponent,{ injector : this.injector }) - customElements.define('readmore-element',ReadMore) + const readMore = createCustomElement(ReadmoreComponent, { injector : this.injector }) + customElements.define('readmore-element', readMore) - const MarkDown = createCustomElement(MarkdownDom,{injector : this.injector }) - customElements.define('markdown-element',MarkDown) + const markDown = createCustomElement(MarkdownDom, {injector : this.injector }) + customElements.define('markdown-element', markDown) - const Panel = createCustomElement(PanelComponent,{injector : this.injector }) - customElements.define('panel-element',Panel) + const panel = createCustomElement(PanelComponent, {injector : this.injector }) + customElements.define('panel-element', panel) - const Tree = createCustomElement(TreeComponent,{injector : this.injector }) - customElements.define('tree-element',Tree) + const tree = createCustomElement(TreeComponent, {injector : this.injector }) + customElements.define('tree-element', tree) } - ngDoBootstrap(){ - } -} \ No newline at end of file + // tslint:disable-next-line:no-empty + public ngDoBootstrap() {} +} diff --git a/src/atlasViewerExports/main.export.aot.ts b/src/atlasViewerExports/main.export.aot.ts index 754ac3bdb49ee76b3fd29cf2e91c1f04c361b864..c27b91b4ea8d2d86ff7deb6c192f58d7c235ee02 100644 --- a/src/atlasViewerExports/main.export.aot.ts +++ b/src/atlasViewerExports/main.export.aot.ts @@ -3,4 +3,4 @@ import 'zone.js' import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; import { ExportModule } from "./export.module"; -platformBrowserDynamic().bootstrapModule(ExportModule) \ No newline at end of file +platformBrowserDynamic().bootstrapModule(ExportModule) diff --git a/src/atlasViewerExports/main.export.ts b/src/atlasViewerExports/main.export.ts index 6a908339a2f96703a7d3151d0875a83db45a87a4..2bdb1c047520c3ffca666cab10a4f5fba952c510 100644 --- a/src/atlasViewerExports/main.export.ts +++ b/src/atlasViewerExports/main.export.ts @@ -1,6 +1,6 @@ -import 'zone.js' -import 'reflect-metadata' import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; +import 'reflect-metadata' +import 'zone.js' import { ExportModule } from "./export.module"; -platformBrowserDynamic().bootstrapModule(ExportModule) \ No newline at end of file +platformBrowserDynamic().bootstrapModule(ExportModule) diff --git a/src/atlasViewerExports/sampleBox/sampleBox.component.ts b/src/atlasViewerExports/sampleBox/sampleBox.component.ts index bec5619ff388d6dc794a5e770871a09c8116e7b5..3a819430405bf38c6e1951d2910103dec3b5fb4b 100644 --- a/src/atlasViewerExports/sampleBox/sampleBox.component.ts +++ b/src/atlasViewerExports/sampleBox/sampleBox.component.ts @@ -1,44 +1,44 @@ import { - Component, - Input, - ViewChild, + Component, ElementRef, - OnInit, + Input, OnChanges, - Renderer2 + OnInit, + Renderer2, + ViewChild, } from '@angular/core' @Component({ selector : 'sample-box', templateUrl : './sampleBox.template.html', styleUrls : [ - './sampleBox.style.css' - ] + './sampleBox.style.css', + ], }) -export class SampleBoxUnit implements OnInit, OnChanges{ - @Input() sampleBoxTitle = `` - @Input() scriptInput - - @ViewChild('ngContent',{read:ElementRef}) ngContent : ElementRef +export class SampleBoxUnit implements OnInit, OnChanges { + @Input() public sampleBoxTitle = `` + @Input() public scriptInput + + @ViewChild('ngContent', {read: ElementRef}) public ngContent: ElementRef - escapedHtml : string = `` - escapedScript : string = `` + public escapedHtml: string = `` + public escapedScript: string = `` - private scriptEl : HTMLScriptElement + private scriptEl: HTMLScriptElement - constructor(private rd2:Renderer2){ + constructor(private rd2: Renderer2) { this.scriptEl = this.rd2.createElement('script') } - ngOnInit(){ + public ngOnInit() { this.escapedHtml = this.ngContent.nativeElement.innerHTML } - ngOnChanges(){ + public ngOnChanges() { this.escapedScript = this.scriptInput - if( this.scriptInput ){ + if ( this.scriptInput ) { this.scriptEl.innerText = this.scriptInput } } -} \ No newline at end of file +} diff --git a/src/components/components.module.ts b/src/components/components.module.ts index 49785f32d110204bbd4dbe19f4386508956aa94f..bb218722c8118a688e279b8f4ae079371ab6dbfd 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -1,39 +1,38 @@ +import { ScrollingModule } from '@angular/cdk/scrolling' import { NgModule } from '@angular/core' import { FormsModule } from '@angular/forms' -import { ScrollingModule } from '@angular/cdk/scrolling' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { MarkdownDom } from './markdown/markdown.component'; -import { SafeHtmlPipe } from '../util/pipes/safeHtml.pipe' -import { ReadmoreComponent } from './readmoore/readmore.component'; -import { HoverableBlockDirective } from './hoverableBlock.directive'; -import { DropdownComponent } from './dropdown/dropdown.component'; -import { TreeComponent } from './tree/tree.component'; -import { PanelComponent } from './panel/panel.component'; -import { PaginationComponent } from './pagination/pagination.component'; +import { CommonModule } from '@angular/common'; +import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'; +import { UtilModule } from 'src/util/util.module'; import { SearchResultPaginationPipe } from '../util/pipes/pagination.pipe'; -import { ToastComponent } from './toast/toast.component'; +import { SafeHtmlPipe } from '../util/pipes/safeHtml.pipe' import { TreeSearchPipe } from '../util/pipes/treeSearch.pipe'; -import { TreeBaseDirective } from './tree/treeBase.directive'; -import { FlatTreeComponent } from './flatTree/flatTree.component'; -import { FlattenTreePipe } from './flatTree/flattener.pipe'; -import { RenderPipe } from './flatTree/render.pipe'; -import { HighlightPipe } from './flatTree/highlight.pipe'; +import { ConfirmDialogComponent } from './confirmDialog/confirmDialog.component'; +import { DialogComponent } from './dialog/dialog.component'; +import { DropdownComponent } from './dropdown/dropdown.component'; import { AppendSiblingFlagPipe } from './flatTree/appendSiblingFlag.pipe'; import { ClusteringPipe } from './flatTree/clustering.pipe'; -import { TimerComponent } from './timer/timer.component'; -import { PillComponent } from './pill/pill.component'; -import { CommonModule } from '@angular/common'; -import { RadioList } from './radiolist/radiolist.component'; -import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'; import { FilterCollapsePipe } from './flatTree/filterCollapse.pipe'; +import { FlattenTreePipe } from './flatTree/flattener.pipe'; +import { FlatTreeComponent } from './flatTree/flatTree.component'; +import { HighlightPipe } from './flatTree/highlight.pipe'; +import { RenderPipe } from './flatTree/render.pipe'; +import { HoverableBlockDirective } from './hoverableBlock.directive'; +import { PaginationComponent } from './pagination/pagination.component'; +import { PanelComponent } from './panel/panel.component'; +import { PillComponent } from './pill/pill.component'; import { ProgressBar } from './progress/progress.component'; +import { RadioList } from './radiolist/radiolist.component'; +import { ReadmoreComponent } from './readmoore/readmore.component'; import { SleightOfHand } from './sleightOfHand/soh.component'; -import { DialogComponent } from './dialog/dialog.component'; -import { ConfirmDialogComponent } from './confirmDialog/confirmDialog.component'; -import { UtilModule } from 'src/util/util.module'; - +import { TimerComponent } from './timer/timer.component'; +import { ToastComponent } from './toast/toast.component'; +import { TreeComponent } from './tree/tree.component'; +import { TreeBaseDirective } from './tree/treeBase.directive'; @NgModule({ imports : [ @@ -42,7 +41,7 @@ import { UtilModule } from 'src/util/util.module'; FormsModule, BrowserAnimationsModule, AngularMaterialModule, - UtilModule + UtilModule, ], declarations : [ /* components */ @@ -75,11 +74,11 @@ import { UtilModule } from 'src/util/util.module'; HighlightPipe, AppendSiblingFlagPipe, ClusteringPipe, - FilterCollapsePipe + FilterCollapsePipe, ], exports : [ BrowserAnimationsModule, - + MarkdownDom, ReadmoreComponent, DropdownComponent, @@ -100,10 +99,10 @@ import { UtilModule } from 'src/util/util.module'; TreeSearchPipe, HoverableBlockDirective, - TreeBaseDirective - ] + TreeBaseDirective, + ], }) -export class ComponentsModule{ +export class ComponentsModule { -} \ No newline at end of file +} diff --git a/src/components/confirmDialog/confirmDialog.component.ts b/src/components/confirmDialog/confirmDialog.component.ts index 6c16434ac9910839c345d0a5fe9a132411627ba4..c26e524716eac8b4d2c6a3eff12b8780c43452e2 100644 --- a/src/components/confirmDialog/confirmDialog.component.ts +++ b/src/components/confirmDialog/confirmDialog.component.ts @@ -5,10 +5,10 @@ import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material"; selector: 'confirm-dialog-component', templateUrl: './confirmDialog.template.html', styleUrls: [ - './confirmDialog.style.css' - ] + './confirmDialog.style.css', + ], }) -export class ConfirmDialogComponent{ +export class ConfirmDialogComponent { @Input() public title: string = 'Confirm' @@ -16,9 +16,9 @@ export class ConfirmDialogComponent{ @Input() public message: string = 'Would you like to proceed?' - constructor(@Inject(MAT_DIALOG_DATA) data: any){ + constructor(@Inject(MAT_DIALOG_DATA) data: any) { const { title = null, message = null} = data || {} - if (title) this.title = title - if (message) this.message = message + if (title) { this.title = title } + if (message) { this.message = message } } -} \ No newline at end of file +} diff --git a/src/components/dialog/dialog.component.ts b/src/components/dialog/dialog.component.ts index 1e5b2ce32b91e6ed901b5eeffc35a2bd3a28a947..bc8828143a862c28655047cee163f0b6e6baebae 100644 --- a/src/components/dialog/dialog.component.ts +++ b/src/components/dialog/dialog.component.ts @@ -1,49 +1,49 @@ -import { Component, Input, ChangeDetectionStrategy, ViewChild, ElementRef, OnInit, OnDestroy, Inject } from "@angular/core"; -import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material"; -import { Subscription, Observable, fromEvent } from "rxjs"; +import { ChangeDetectionStrategy, Component, ElementRef, Inject, Input, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material"; +import { fromEvent, Observable, Subscription } from "rxjs"; import { filter, share } from "rxjs/operators"; @Component({ selector: 'dialog-component', templateUrl: './dialog.template.html', styleUrls: [ - './dialog.style.css' + './dialog.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DialogComponent implements OnInit, OnDestroy { private subscrptions: Subscription[] = [] - @Input() iconClass: string = `fas fa-save` - - @Input() title: string = 'Message' - @Input() placeholder: string = "Type your response here" - @Input() defaultValue: string = '' - @Input() message: string = '' + @Input() public iconClass: string = `fas fa-save` + + @Input() public title: string = 'Message' + @Input() public placeholder: string = "Type your response here" + @Input() public defaultValue: string = '' + @Input() public message: string = '' @ViewChild('inputField', {read: ElementRef}) private inputField: ElementRef public value: string = '' private keyListener$: Observable<any> constructor( - @Inject(MAT_DIALOG_DATA) public data:any, - private dialogRef: MatDialogRef<DialogComponent> - ){ + @Inject(MAT_DIALOG_DATA) public data: any, + private dialogRef: MatDialogRef<DialogComponent>, + ) { const { title, placeholder, defaultValue, message, iconClass = null } = this.data - if (title) this.title = title - if (placeholder) this.placeholder = placeholder - if (defaultValue) this.value = defaultValue - if (message) this.message = message - if (typeof iconClass !== 'undefined') this.iconClass = iconClass + if (title) { this.title = title } + if (placeholder) { this.placeholder = placeholder } + if (defaultValue) { this.value = defaultValue } + if (message) { this.message = message } + if (typeof iconClass !== 'undefined') { this.iconClass = iconClass } } - ngOnInit(){ + public ngOnInit() { this.keyListener$ = fromEvent(this.inputField.nativeElement, 'keyup').pipe( filter((ev: KeyboardEvent) => ev.key === 'Enter' || ev.key === 'Esc' || ev.key === 'Escape'), - share() + share(), ) this.subscrptions.push( this.keyListener$.subscribe(ev => { @@ -53,21 +53,21 @@ export class DialogComponent implements OnInit, OnDestroy { if (ev.key === 'Esc' || ev.key === 'Escape') { this.dialogRef.close(null) } - }) + }), ) } - confirm(){ + public confirm() { this.dialogRef.close(this.value) } - cancel(){ + public cancel() { this.dialogRef.close(null) } - ngOnDestroy(){ - while(this.subscrptions.length > 0) { + public ngOnDestroy() { + while (this.subscrptions.length > 0) { this.subscrptions.pop().unsubscribe() } } -} \ No newline at end of file +} diff --git a/src/components/dropdown/dropdown.animation.ts b/src/components/dropdown/dropdown.animation.ts index b294e72c7b17f3f7111594fcb4439df815a2dbfa..1a4c0a2a9a952619b893f1db6729066ff2b37628 100644 --- a/src/components/dropdown/dropdown.animation.ts +++ b/src/components/dropdown/dropdown.animation.ts @@ -1,22 +1,21 @@ -import { trigger, state, style, transition, animate } from "@angular/animations"; +import { animate, state, style, transition, trigger } from "@angular/animations"; - -export const dropdownAnimation = trigger('showState',[ +export const dropdownAnimation = trigger('showState', [ state('show', style({ - opacity : '1.0' - }) + opacity : '1.0', + }), ), state('hide', style({ - opacity : '0.0', - 'pointer-events':'none' - }) + "opacity" : '0.0', + 'pointer-events': 'none', + }), ), transition('show => hide', [ - animate('230ms ease-in') + animate('230ms ease-in'), + ]), + transition('hide => show', [ + animate('230ms ease-out'), ]), - transition('hide => show',[ - animate('230ms ease-out') - ]) -]) \ No newline at end of file +]) diff --git a/src/components/dropdown/dropdown.component.spec.ts b/src/components/dropdown/dropdown.component.spec.ts index 16ad858c862d0039de4b5ec57e6b7cfd3a4f3822..6bf288d0648e3b7db0196b3abb14ad61a3ff389b 100644 --- a/src/components/dropdown/dropdown.component.spec.ts +++ b/src/components/dropdown/dropdown.component.spec.ts @@ -1,9 +1,9 @@ +import { async, TestBed } from '@angular/core/testing' import {} from 'jasmine' -import { TestBed, async } from '@angular/core/testing' -import { DropdownComponent } from './dropdown.component'; +import { AngularMaterialModule } from '../../ui/sharedModules/angularMaterial.module' import { HoverableBlockDirective } from '../hoverableBlock.directive' import { RadioList } from '../radiolist/radiolist.component' -import { AngularMaterialModule } from '../../ui/sharedModules/angularMaterial.module' +import { DropdownComponent } from './dropdown.component'; describe('dropdown component', () => { it('jasmine works', () => { @@ -12,19 +12,19 @@ describe('dropdown component', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ - AngularMaterialModule + AngularMaterialModule, ], - declarations : [ + declarations : [ DropdownComponent, HoverableBlockDirective, - RadioList - ] + RadioList, + ], }).compileComponents() })) - it('should create component', async(()=>{ + it('should create component', async(() => { const fixture = TestBed.createComponent(DropdownComponent); const app = fixture.debugElement.componentInstance; expect(app).toBeTruthy(); })) -}) \ No newline at end of file +}) diff --git a/src/components/dropdown/dropdown.component.ts b/src/components/dropdown/dropdown.component.ts index b591d22df432612f72e7ccba9ae0d8d0ffd5c4e8..576ab2410bb92da0dadad1554e36f6d964e5461f 100644 --- a/src/components/dropdown/dropdown.component.ts +++ b/src/components/dropdown/dropdown.component.ts @@ -1,51 +1,52 @@ -import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, HostListener, ViewChild, ElementRef } from "@angular/core"; +import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostListener, Input, Output, ViewChild } from "@angular/core"; +import { IExraBtnClickEvent, IExtraButton, IHasExtraButtons } from '../radiolist/radiolist.component' import { dropdownAnimation } from "./dropdown.animation"; -import { HasExtraButtons, ExraBtnClickEvent, ExtraButton } from '../radiolist/radiolist.component' @Component({ selector : 'dropdown-component', templateUrl : './dropdown.template.html', styleUrls : [ - `./dropdown.style.css` + `./dropdown.style.css`, ], - animations:[ - dropdownAnimation + animations: [ + dropdownAnimation, ], - changeDetection : ChangeDetectionStrategy.OnPush + changeDetection : ChangeDetectionStrategy.OnPush, }) -export class DropdownComponent{ +export class DropdownComponent { - @Input() activeDisplayBtns: ExtraButton[] = [] - @Output() activeDisplayBtnClicked: EventEmitter<{extraBtn: ExtraButton, event: MouseEvent}> = new EventEmitter() + @Input() public activeDisplayBtns: IExtraButton[] = [] + @Output() public activeDisplayBtnClicked: EventEmitter<{extraBtn: IExtraButton, event: MouseEvent}> = new EventEmitter() - @Input() inputArray : HasExtraButtons[] = [] - @Input() selectedItem : any | null = null - @Input() checkSelected: (selectedItem:any, item:any) => boolean = (si,i) => si === i + @Input() public inputArray: IHasExtraButtons[] = [] + @Input() public selectedItem: any | null = null + @Input() public checkSelected: (selectedItem: any, item: any) => boolean = (si, i) => si === i - @Input() listDisplay : (obj:any)=>string = (obj)=>obj.name - @Input() activeDisplay : (obj:any|null)=>string = (obj)=>obj ? obj.name : `Please select an item.` + @Input() public listDisplay: (obj: any) => string = (obj) => obj.name + @Input() public activeDisplay: (obj: any|null) => string = (obj) => obj ? obj.name : `Please select an item.` - @Output() itemSelected : EventEmitter<any> = new EventEmitter() - @Output() extraBtnClicked: EventEmitter<ExraBtnClickEvent> = new EventEmitter() + @Output() public itemSelected: EventEmitter<any> = new EventEmitter() + @Output() public extraBtnClicked: EventEmitter<IExraBtnClickEvent> = new EventEmitter() - @ViewChild('dropdownToggle',{read:ElementRef}) dropdownToggle : ElementRef + @ViewChild('dropdownToggle', {read: ElementRef}) public dropdownToggle: ElementRef - openState : boolean = false + public openState: boolean = false - @HostListener('document:click',['$event']) - close(event:MouseEvent){ + @HostListener('document:click', ['$event']) + public close(event: MouseEvent) { const contains = this.dropdownToggle.nativeElement.contains(event.target) - if(contains) + if (contains) { this.openState = !this.openState - else + } else { this.openState = false; + } } - handleActiveDisplayBtnClick(btn: ExtraButton, event: MouseEvent){ + public handleActiveDisplayBtnClick(btn: IExtraButton, event: MouseEvent) { this.activeDisplayBtnClicked.emit({ extraBtn: btn, - event + event, }) } -} \ No newline at end of file +} diff --git a/src/components/flatTree/appendSiblingFlag.pipe.ts b/src/components/flatTree/appendSiblingFlag.pipe.ts index b927bbdbf9d0bb213427734dafb443ddb8a08a29..c4467283ace0f148a802a537724740ebc40861f4 100644 --- a/src/components/flatTree/appendSiblingFlag.pipe.ts +++ b/src/components/flatTree/appendSiblingFlag.pipe.ts @@ -1,22 +1,22 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name : 'appendSiblingFlagPipe' + name : 'appendSiblingFlagPipe', }) -export class AppendSiblingFlagPipe implements PipeTransform{ - public transform(objs:any[]):any[]{ +export class AppendSiblingFlagPipe implements PipeTransform { + public transform(objs: any[]): any[] { return objs - .reduceRight((acc,curr) => ({ + .reduceRight((acc, curr) => ({ acc : acc.acc.concat(Object.assign({}, curr, { siblingFlags : curr.lvlId.split('_').map((v, idx) => typeof acc.flags[idx] !== 'undefined' ? acc.flags[idx] : false) .slice(1) - .map(v => !v) + .map(v => !v), })), - flags: curr.lvlId.split('_').map((_,idx) => acc.flags[idx] ).slice(0, -1).concat(true) - }), { acc:[], flags : Array(256).fill(false) }) - .acc.reverse() + flags: curr.lvlId.split('_').map((_, idx) => acc.flags[idx] ).slice(0, -1).concat(true), + }), { acc: [], flags : Array(256).fill(false) }) + .acc.reverse() } -} \ No newline at end of file +} diff --git a/src/components/flatTree/clustering.pipe.ts b/src/components/flatTree/clustering.pipe.ts index 9e3486351860ebc5ae35066b3223a3f35e7521aa..2d0d11294b46564d4737087abf5f620b20b56b58 100644 --- a/src/components/flatTree/clustering.pipe.ts +++ b/src/components/flatTree/clustering.pipe.ts @@ -1,13 +1,15 @@ import { Pipe, PipeTransform } from "@angular/core"; +// TODO deprecate? + @Pipe({ - name : 'clusteringPipe' + name : 'clusteringPipe', }) -export class ClusteringPipe implements PipeTransform{ - public transform(arr:any[],num:number = 100):any[][]{ - return arr.reduce((acc,curr,idx,arr) => idx % num === 0 +export class ClusteringPipe implements PipeTransform { + public transform(array: any[], num: number = 100): any[][] { + return array.reduce((acc, curr, idx, arr) => idx % num === 0 ? acc.concat([arr.slice(idx, idx + num)]) - : acc ,[]) + : acc , []) } -} \ No newline at end of file +} diff --git a/src/components/flatTree/filterCollapse.pipe.ts b/src/components/flatTree/filterCollapse.pipe.ts index 00ce53d406e06c316d6b9027b5570f068578e69e..ee3e653a8543d086113b3ff2f18b3e6c0fe05464 100644 --- a/src/components/flatTree/filterCollapse.pipe.ts +++ b/src/components/flatTree/filterCollapse.pipe.ts @@ -1,14 +1,14 @@ -import { PipeTransform, Pipe } from "@angular/core"; +import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name: 'filterCollapsePipe' + name: 'filterCollapsePipe', }) -export class FilterCollapsePipe implements PipeTransform{ - public transform(array: any[], collapsedLevels: Set<string>, uncollapsedLevels: Set<string>, defaultCollapse: boolean ){ +export class FilterCollapsePipe implements PipeTransform { + public transform(array: any[], collapsedLevels: Set<string>, uncollapsedLevels: Set<string>, defaultCollapse: boolean ) { const isCollapsedById = (id) => { - return collapsedLevels.has(id) + return collapsedLevels.has(id) ? true : uncollapsedLevels.has(id) ? false @@ -16,13 +16,13 @@ export class FilterCollapsePipe implements PipeTransform{ } const returnArray = array.filter(item => { return !item.lvlId.split('_') - .filter((v,idx,arr) => idx < arr.length -1 ) - .reduce((acc,curr) => acc - .concat(acc.length === 0 - ? curr - : acc[acc.length -1].concat(`_${curr}`)), []) + .filter((v, idx, arr) => idx < arr.length - 1 ) + .reduce((acc, curr) => acc + .concat(acc.length === 0 + ? curr + : acc[acc.length - 1].concat(`_${curr}`)), []) .some(id => isCollapsedById(id)) }) return returnArray } -} \ No newline at end of file +} diff --git a/src/components/flatTree/filterRowsByVisibility.pipe.ts b/src/components/flatTree/filterRowsByVisibility.pipe.ts index 044e894fca976adfbbcc0419a28a3fdb601abfba..f589fbf8b9a9e05bcfc0a1fce15fee53f2af32d2 100644 --- a/src/components/flatTree/filterRowsByVisibility.pipe.ts +++ b/src/components/flatTree/filterRowsByVisibility.pipe.ts @@ -3,16 +3,16 @@ import { Pipe, PipeTransform } from "@angular/core"; // TODO fix typo @Pipe({ - name : 'filterRowsByVisbilityPipe' + name : 'filterRowsByVisbilityPipe', }) -export class FilterRowsByVisbilityPipe implements PipeTransform{ - public transform(rows:any[], getChildren : (item:any)=>any[], filterFn : (item:any)=>boolean){ - +export class FilterRowsByVisbilityPipe implements PipeTransform { + public transform(rows: any[], getChildren: (item: any) => any[], filterFn: (item: any) => boolean) { + return rows.filter(row => this.recursive(row, getChildren, filterFn) ) } - private recursive(single : any, getChildren : (item:any) => any[], filterFn:(item:any) => boolean):boolean{ + private recursive(single: any, getChildren: (item: any) => any[], filterFn: (item: any) => boolean): boolean { return filterFn(single) || (getChildren && getChildren(single).some(c => this.recursive(c, getChildren, filterFn))) } -} \ No newline at end of file +} diff --git a/src/components/flatTree/flatTree.component.ts b/src/components/flatTree/flatTree.component.ts index 60e0ba44fd00d214d3fb0e9a5997b78d813ffa69..d8bd95fb0d5d780595caa03c1695e2763033ee8e 100644 --- a/src/components/flatTree/flatTree.component.ts +++ b/src/components/flatTree/flatTree.component.ts @@ -1,6 +1,6 @@ -import {EventEmitter, Component, Input, Output, ChangeDetectionStrategy, ViewChild, AfterViewChecked} from "@angular/core"; -import { FlattenedTreeInterface } from "./flattener.pipe"; import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling"; +import {AfterViewChecked, ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild} from "@angular/core"; +import { IFlattenedTreeInterface } from "./flattener.pipe"; /** * TODO to be replaced by virtual scrolling when ivy is in stable @@ -10,44 +10,44 @@ import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling"; selector : 'flat-tree-component', templateUrl : './flatTree.template.html', styleUrls : [ - './flatTree.style.css' + './flatTree.style.css', ], - changeDetection:ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class FlatTreeComponent implements AfterViewChecked { - @Input() inputItem : any = { + @Input() public inputItem: any = { name : 'Untitled', - children : [] + children : [], } - @Input() childrenExpanded : boolean = true + @Input() public childrenExpanded: boolean = true - @Input() useDefaultList: boolean = false + @Input() public useDefaultList: boolean = false - @Output() treeNodeClick : EventEmitter<any> = new EventEmitter() + @Output() public treeNodeClick: EventEmitter<any> = new EventEmitter() /* highly non-performant. rerenders each time on mouseover or mouseout */ // @Output() treeNodeEnter : EventEmitter<any> = new EventEmitter() // @Output() treeNodeLeave : EventEmitter<any> = new EventEmitter() - @Input() renderNode : (item:any)=>string = (item)=>item.name - @Input() findChildren : (item:any)=>any[] = (item)=>item.children ? item.children : [] - @Input() searchFilter : (item:any)=>boolean | null = ()=>true + @Input() public renderNode: (item: any) => string = (item) => item.name + @Input() public findChildren: (item: any) => any[] = (item) => item.children ? item.children : [] + @Input() public searchFilter: (item: any) => boolean | null = () => true - @ViewChild('flatTreeVirtualScrollViewPort') virtualScrollViewPort: CdkVirtualScrollViewport - @Output() totalRenderedListChanged = new EventEmitter<{ previous: number, current: number }>() + @ViewChild('flatTreeVirtualScrollViewPort') public virtualScrollViewPort: CdkVirtualScrollViewport + @Output() public totalRenderedListChanged = new EventEmitter<{ previous: number, current: number }>() private totalDataLength: number = null - public flattenedItems : any[] = [] + public flattenedItems: any[] = [] - getClass(level:number){ - return [...Array(level+1)].map((v,idx) => `render-node-level-${idx}`).join(' ') + public getClass(level: number) { + return [...Array(level + 1)].map((v, idx) => `render-node-level-${idx}`).join(' ') } - collapsedLevels: Set<string> = new Set() - uncollapsedLevels : Set<string> = new Set() + public collapsedLevels: Set<string> = new Set() + public uncollapsedLevels: Set<string> = new Set() - ngAfterViewChecked(){ + public ngAfterViewChecked() { /** * if useDefaultList is true, virtualscrollViewPort will be undefined */ @@ -59,12 +59,12 @@ export class FlatTreeComponent implements AfterViewChecked { this.totalRenderedListChanged.emit({ current: currentTotalDataLength, - previous: previousDataLength + previous: previousDataLength, }) this.totalDataLength = currentTotalDataLength } - toggleCollapse(flattenedItem:FlattenedTreeInterface){ + public toggleCollapse(flattenedItem: IFlattenedTreeInterface) { if (this.isCollapsed(flattenedItem)) { this.collapsedLevels.delete(flattenedItem.lvlId) this.uncollapsedLevels.add(flattenedItem.lvlId) @@ -76,32 +76,32 @@ export class FlatTreeComponent implements AfterViewChecked { this.uncollapsedLevels = new Set(this.uncollapsedLevels) } - isCollapsed(flattenedItem:FlattenedTreeInterface):boolean{ + public isCollapsed(flattenedItem: IFlattenedTreeInterface): boolean { return this.isCollapsedById(flattenedItem.lvlId) } - isCollapsedById(id:string):boolean{ - return this.collapsedLevels.has(id) + public isCollapsedById(id: string): boolean { + return this.collapsedLevels.has(id) ? true : this.uncollapsedLevels.has(id) ? false : !this.childrenExpanded } - collapseRow(flattenedItem:FlattenedTreeInterface):boolean{ + public collapseRow(flattenedItem: IFlattenedTreeInterface): boolean { return flattenedItem.lvlId.split('_') - .filter((v,idx,arr) => idx < arr.length -1 ) - .reduce((acc,curr) => acc - .concat(acc.length === 0 - ? curr - : acc[acc.length -1].concat(`_${curr}`)), []) + .filter((v, idx, arr) => idx < arr.length - 1 ) + .reduce((acc, curr) => acc + .concat(acc.length === 0 + ? curr + : acc[acc.length - 1].concat(`_${curr}`)), []) .some(id => this.isCollapsedById(id)) } - handleTreeNodeClick(event:MouseEvent, inputItem: any){ + public handleTreeNodeClick(event: MouseEvent, inputItem: any) { this.treeNodeClick.emit({ event, - inputItem + inputItem, }) } -} \ No newline at end of file +} diff --git a/src/components/flatTree/flattener.pipe.ts b/src/components/flatTree/flattener.pipe.ts index 3be8cb5c9958580d167fbafe454cc4fa9367ca66..3dfd9ab4862a46269772ba178751cd8badc753e7 100644 --- a/src/components/flatTree/flattener.pipe.ts +++ b/src/components/flatTree/flattener.pipe.ts @@ -1,38 +1,38 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name : 'flattenTreePipe' + name : 'flattenTreePipe', }) -export class FlattenTreePipe implements PipeTransform{ - public transform(root:any, findChildren: (root:any) => any[]):any&FlattenedTreeInterface[]{ - return this.recursiveFlatten(root,findChildren,0, '0') +export class FlattenTreePipe implements PipeTransform { + public transform(root: any, findChildren: (root: any) => any[]): any&IFlattenedTreeInterface[] { + return this.recursiveFlatten(root, findChildren, 0, '0') } - private recursiveFlatten(obj, findChildren, flattenedTreeLevel, lvlId){ + private recursiveFlatten(obj, findChildren, flattenedTreeLevel, lvlId) { return [ this.attachLvlAndLvlIdAndSiblingFlag( obj, - flattenedTreeLevel, - lvlId - ) + flattenedTreeLevel, + lvlId, + ), ].concat( ...findChildren(obj) - .map((c,idx) => this.recursiveFlatten(c,findChildren,flattenedTreeLevel + 1, `${lvlId}_${idx}` )) + .map((c, idx) => this.recursiveFlatten(c, findChildren, flattenedTreeLevel + 1, `${lvlId}_${idx}` )), ) } - private attachLvlAndLvlIdAndSiblingFlag(obj:any, flattenedTreeLevel:number, lvlId:string){ - return Object.assign({}, obj,{ - flattenedTreeLevel, + private attachLvlAndLvlIdAndSiblingFlag(obj: any, flattenedTreeLevel: number, lvlId: string) { + return Object.assign({}, obj, { + flattenedTreeLevel, collapsed : typeof obj.collapsed === 'undefined' ? false : true, - lvlId + lvlId, }) } } -export interface FlattenedTreeInterface{ - flattenedTreeLevel : number - lvlId : string -} \ No newline at end of file +export interface IFlattenedTreeInterface { + flattenedTreeLevel: number + lvlId: string +} diff --git a/src/components/flatTree/highlight.pipe.ts b/src/components/flatTree/highlight.pipe.ts index 15aa8e3bf8db37fae36531bf4ec62de055d2c142..31583a1a98a3b90b139499872c85c07f306fbef6 100644 --- a/src/components/flatTree/highlight.pipe.ts +++ b/src/components/flatTree/highlight.pipe.ts @@ -1,17 +1,17 @@ import { Pipe, PipeTransform, SecurityContext } from "@angular/core"; -import { SafeHtml, DomSanitizer } from "@angular/platform-browser"; +import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; @Pipe({ - name : 'highlightPipe' + name : 'highlightPipe', }) -export class HighlightPipe implements PipeTransform{ - constructor(private sanitizer: DomSanitizer){ - +export class HighlightPipe implements PipeTransform { + constructor(private sanitizer: DomSanitizer) { + } - public transform(input:string, searchTerm:string){ - return searchTerm && searchTerm !== '' + public transform(input: string, searchTerm: string) { + return searchTerm && searchTerm !== '' ? this.sanitizer.bypassSecurityTrustHtml(input.replace(new RegExp( searchTerm, 'gi'), (s) => `<span class = "highlight">${s}</span>`)) : input } -} \ No newline at end of file +} diff --git a/src/components/flatTree/render.pipe.ts b/src/components/flatTree/render.pipe.ts index 39e6a34a7e70e940ded14dc0bdfa715cae15dd4b..0b2707fcd9c135fad134ed9ed3d094eb43d28582 100644 --- a/src/components/flatTree/render.pipe.ts +++ b/src/components/flatTree/render.pipe.ts @@ -1,11 +1,11 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name : 'renderPipe' + name : 'renderPipe', }) -export class RenderPipe implements PipeTransform{ - public transform(node:any, renderFunction:(node:any)=>string):string{ +export class RenderPipe implements PipeTransform { + public transform(node: any, renderFunction: (node: any) => string): string { return renderFunction(node) } -} \ No newline at end of file +} diff --git a/src/components/hoverableBlock.directive.ts b/src/components/hoverableBlock.directive.ts index bc0da29e43f6b99d73108d3db6f63633a659e922..a57001b70aae41db1b4855265726ef7ba9da7175 100644 --- a/src/components/hoverableBlock.directive.ts +++ b/src/components/hoverableBlock.directive.ts @@ -1,30 +1,30 @@ -import { Directive, HostListener, HostBinding, Input } from "@angular/core"; +import { Directive, HostBinding, HostListener, Input } from "@angular/core"; import { DomSanitizer } from "@angular/platform-browser"; @Directive({ selector : '[hoverable]', host : { - 'style':` - transition : - opacity 0.3s ease, - box-shadow 0.3s ease, + style: ` + transition : + opacity 0.3s ease, + box-shadow 0.3s ease, transform 0.3s ease; cursor : default;`, - } + }, }) -export class HoverableBlockDirective{ +export class HoverableBlockDirective { @Input('hoverable') - config:any = { + public config: any = { disable: false, - translateY: -5 + translateY: -5, } private _disable = false private _translateY = -5 - ngOnChanges(){ + public ngOnChanges() { this._disable = this.config && !!this.config.disable /** * 0 is evaluated as falsy, but a valid number @@ -36,17 +36,17 @@ export class HoverableBlockDirective{ } @HostBinding('style.opacity') - opacity : number = 0.9 + public opacity: number = 0.9 @HostBinding('style.transform') - transform = this.sanitizer.bypassSecurityTrustStyle(`translateY(0px)`) + public transform = this.sanitizer.bypassSecurityTrustStyle(`translateY(0px)`) @HostBinding('style.box-shadow') - boxShadow = this.sanitizer.bypassSecurityTrustStyle('0 4px 6px 0 rgba(5,5,5,0.1)') + public boxShadow = this.sanitizer.bypassSecurityTrustStyle('0 4px 6px 0 rgba(5,5,5,0.1)') @HostListener('mouseenter') - onMouseenter(){ - if (this._disable) return + public onMouseenter() { + if (this._disable) { return } this.opacity = 1.0 this.boxShadow = this.sanitizer.bypassSecurityTrustStyle(`0 4px 6px 0 rgba(5,5,5,0.25)`) /** @@ -57,14 +57,14 @@ export class HoverableBlockDirective{ } @HostListener('mouseleave') - onmouseleave(){ - if (this._disable) return + public onmouseleave() { + if (this._disable) { return } this.opacity = 0.9 this.boxShadow = this.sanitizer.bypassSecurityTrustStyle(`0 4px 6px 0 rgba(5,5,5,0.1)`) this.transform = this.sanitizer.bypassSecurityTrustStyle(`translateY(0px)`) } - constructor(private sanitizer:DomSanitizer){ + constructor(private sanitizer: DomSanitizer) { } -} \ No newline at end of file +} diff --git a/src/components/markdown/markdown.component.ts b/src/components/markdown/markdown.component.ts index de6521bf0ee6314b8aac7f2df66e2df234e98108..3fc84636c9df293d65c3caf744ed45c4a71c8a7b 100644 --- a/src/components/markdown/markdown.component.ts +++ b/src/components/markdown/markdown.component.ts @@ -1,35 +1,36 @@ -import { Component, OnChanges, Input, ChangeDetectionStrategy, ViewChild, ElementRef, OnInit } from '@angular/core' +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, OnInit, ViewChild } from '@angular/core' import * as showdown from 'showdown' @Component({ selector : 'markdown-dom', templateUrl : `./markdown.template.html`, styleUrls : [ - `./markdown.style.css` + `./markdown.style.css`, ], - changeDetection : ChangeDetectionStrategy.OnPush + changeDetection : ChangeDetectionStrategy.OnPush, }) -export class MarkdownDom implements OnChanges,OnInit{ +export class MarkdownDom implements OnChanges, OnInit { - @Input() markdown : string = `` - public innerHtml : string = `` + @Input() public markdown: string = `` + public innerHtml: string = `` private converter = new showdown.Converter() - constructor(){ + constructor() { this.converter.setFlavor('github') } - ngOnChanges(){ + public ngOnChanges() { this.innerHtml = this.converter.makeHtml(this.markdown) } - ngOnInit(){ - if(this.contentWrapper.nativeElement.innerHTML.replace(/\w|\n/g,'') !== '') + public ngOnInit() { + if (this.contentWrapper.nativeElement.innerHTML.replace(/\w|\n/g, '') !== '') { this.innerHtml = this.converter.makeHtml(this.contentWrapper.nativeElement.innerHTML) + } } @ViewChild('ngContentWrapper', {read : ElementRef}) - contentWrapper : ElementRef + public contentWrapper: ElementRef } diff --git a/src/components/pagination/pagination.component.ts b/src/components/pagination/pagination.component.ts index 1723d4540cef7683c09f8ca7f5e81986c6003f80..492fb57ca055f8bd65bf5387db49c254e062e3b6 100644 --- a/src/components/pagination/pagination.component.ts +++ b/src/components/pagination/pagination.component.ts @@ -1,23 +1,23 @@ -import { Component, Input, Output, EventEmitter } from '@angular/core' +import { Component, EventEmitter, Input, Output } from '@angular/core' @Component({ selector : 'pagination-component', templateUrl : './pagination.template.html', styleUrls : [ - './pagination.style.css' - ] + './pagination.style.css', + ], }) export class PaginationComponent { - @Input() total : number = 0 - @Input() hitsPerPage : number = 15 - @Input() currentPage : number = 0 + @Input() public total: number = 0 + @Input() public hitsPerPage: number = 15 + @Input() public currentPage: number = 0 - @Output() paginationChange : EventEmitter<number> = new EventEmitter() - @Output() outOfBound: EventEmitter<number> = new EventEmitter() + @Output() public paginationChange: EventEmitter<number> = new EventEmitter() + @Output() public outOfBound: EventEmitter<number> = new EventEmitter() - goto(pgnum:number){ - const emitValue = pgnum < 0 ? + public goto(pgnum: number) { + const emitValue = pgnum < 0 ? 0 : pgnum >= Math.ceil(this.total / this.hitsPerPage) ? Math.ceil(this.total / this.hitsPerPage) - 1 : @@ -26,34 +26,34 @@ export class PaginationComponent { this.paginationChange.emit(emitValue) } - gotoFirst(){ + public gotoFirst() { this.goto(0) } - gotoLast(){ + public gotoLast() { const num = Math.floor(this.total / this.hitsPerPage) + 1 this.goto(num) } - get getPagination(){ + get getPagination() { return Array.from(Array(Math.ceil(this.total / this.hitsPerPage)).keys()).filter((this.hidePagination).bind(this)) } - get getPageLowerBound(){ + get getPageLowerBound() { return this.currentPage * this.hitsPerPage + 1 } - get getPageUpperBound(){ + get getPageUpperBound() { return Math.min( ( this.currentPage + 1 ) * this.hitsPerPage , this.total ) } - hidePagination(idx:number){ - + public hidePagination(idx: number) { + const correctedPagination = this.currentPage < 2 ? 2 : this.currentPage > (Math.ceil(this.total / this.hitsPerPage) - 3) ? Math.ceil(this.total / this.hitsPerPage) - 3 : this.currentPage - return (Math.abs(idx-correctedPagination) < 3) + return (Math.abs(idx - correctedPagination) < 3) } -} \ No newline at end of file +} diff --git a/src/components/panel/panel.animation.ts b/src/components/panel/panel.animation.ts index 5bb5c3fc476e6abb38e4c673a8e2b96ae852df24..1b66a6ad48a989fb16332ef4a7678518decb637a 100644 --- a/src/components/panel/panel.animation.ts +++ b/src/components/panel/panel.animation.ts @@ -1,23 +1,22 @@ -import { trigger, state, style, transition, animate } from "@angular/animations"; +import { animate, state, style, transition, trigger } from "@angular/animations"; - -export const panelAnimations = trigger('collapseState',[ - state('collapsed', - style({ - 'margin-top' : '-{{ fullHeight }}px' +export const panelAnimations = trigger('collapseState', [ + state('collapsed', + style({ + 'margin-top' : '-{{ fullHeight }}px', }), - { params : { fullHeight : 9999 } } + { params : { fullHeight : 9999 } }, ), state('visible', - style({ - 'margin-top' : '0px' + style({ + 'margin-top' : '0px', }), - { params : { fullHeight : 0 } } + { params : { fullHeight : 0 } }, ), - transition('collapsed => visible',[ - animate('250ms ease-out') + transition('collapsed => visible', [ + animate('250ms ease-out'), + ]), + transition('visible => collapsed', [ + animate('250ms ease-in'), ]), - transition('visible => collapsed',[ - animate('250ms ease-in') - ]) -]) \ No newline at end of file +]) diff --git a/src/components/panel/panel.component.ts b/src/components/panel/panel.component.ts index ccfc382fb8297e7b2671bec08bfffb7b694d1a11..a68b760124a05ded217f973a66e6d0edf7cbaf76 100644 --- a/src/components/panel/panel.component.ts +++ b/src/components/panel/panel.component.ts @@ -1,33 +1,33 @@ -import { Component, Input, ViewChild, ElementRef, ChangeDetectionStrategy } from "@angular/core"; +import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild } from "@angular/core"; import { ParseAttributeDirective } from "../parseAttribute.directive"; @Component({ selector : 'panel-component', templateUrl : './panel.template.html', styleUrls : [ - `./panel.style.css` + `./panel.style.css`, ], - changeDetection:ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class PanelComponent extends ParseAttributeDirective { - @Input() showHeading : boolean = true - @Input() showBody : boolean = true - @Input() showFooter : boolean = false + @Input() public showHeading: boolean = true + @Input() public showBody: boolean = true + @Input() public showFooter: boolean = false - @Input() collapseBody : boolean = false - @Input() bodyCollapsable : boolean = false + @Input() public collapseBody: boolean = false + @Input() public bodyCollapsable: boolean = false - @ViewChild('panelBody',{ read : ElementRef }) efPanelBody : ElementRef - @ViewChild('panelFooter',{ read : ElementRef }) efPanelFooter : ElementRef + @ViewChild('panelBody', { read : ElementRef }) public efPanelBody: ElementRef + @ViewChild('panelFooter', { read : ElementRef }) public efPanelFooter: ElementRef - constructor(){ + constructor() { super() } - toggleCollapseBody(_event:Event){ - if(this.bodyCollapsable){ + public toggleCollapseBody(_event: Event) { + if (this.bodyCollapsable) { this.collapseBody = !this.collapseBody this.showBody = !this.showBody this.showFooter = !this.showFooter diff --git a/src/components/parseAttribute.directive.ts b/src/components/parseAttribute.directive.ts index 2576867230924828a9d1bda0d0ef105ab3b56a1b..cb3b2dd942987f862dea0ed09640181b3ce73c4d 100644 --- a/src/components/parseAttribute.directive.ts +++ b/src/components/parseAttribute.directive.ts @@ -1,7 +1,7 @@ import { Directive, OnChanges, SimpleChanges } from "@angular/core"; -function parseAttribute(arg:any,expectedType:string){ - +function parseAttribute(arg: any, expectedType: string) { + // if( // typeof arg === expectedType || // arg === undefined || @@ -15,42 +15,45 @@ function parseAttribute(arg:any,expectedType:string){ // const json = JSON.parse(arg) // return json // }catch(e){ - // console.warn('parseAttribute error, cannot JSON.parse object') + // this.log.warn('parseAttribute error, cannot JSON.parse object') // return arg // } // case 'boolean' : // return arg === 'true' // case 'number': // return isNaN(arg) ? 0 : Number(arg) - + // case 'string': - // default : + // default : // return arg // } /* return if empty string */ - if( + if ( arg === '' || arg === undefined || arg === null - ) + ) { return arg + } - if(!isNaN(arg)){ + if (!isNaN(arg)) { return Number(arg) } - if(arg === 'true') + if (arg === 'true') { return true + } - if(arg === 'false') + if (arg === 'false') { return false + } - try{ + try { const json = JSON.parse(arg) return json - }catch(e){ - // console.warn('parseAttribute, parse JSON, not a json') + } catch (e) { + // this.log.warn('parseAttribute, parse JSON, not a json') /* not a json, continue */ /* probably print in debug mode */ } @@ -62,10 +65,10 @@ function parseAttribute(arg:any,expectedType:string){ selector : '[ivparseattribute]', }) -export class ParseAttributeDirective implements OnChanges{ - ngOnChanges(simpleChanges:SimpleChanges){ - Object.keys(simpleChanges).forEach(key=>{ - this[key] = parseAttribute(simpleChanges[key].currentValue,typeof simpleChanges[key].previousValue) +export class ParseAttributeDirective implements OnChanges { + public ngOnChanges(simpleChanges: SimpleChanges) { + Object.keys(simpleChanges).forEach(key => { + this[key] = parseAttribute(simpleChanges[key].currentValue, typeof simpleChanges[key].previousValue) }) } } diff --git a/src/components/pill/pill.component.ts b/src/components/pill/pill.component.ts index 4764320b46ec6782e56266e4489bf0d66d51b7b6..02764af4b1de00e0ddeb27dcf55ada9b04e921f5 100644 --- a/src/components/pill/pill.component.ts +++ b/src/components/pill/pill.component.ts @@ -1,27 +1,27 @@ -import { Component, Input, Output, EventEmitter } from "@angular/core"; +import { Component, EventEmitter, Input, Output } from "@angular/core"; @Component({ selector: 'pill-component', templateUrl: './pill.template.html', styleUrls: [ - './pill.style.css' - ] + './pill.style.css', + ], }) -export class PillComponent{ - @Input() title: string = 'Untitled Pill' - @Input() showClose: boolean = true - @Output() pillClicked: EventEmitter<boolean> = new EventEmitter() - @Output() closeClicked: EventEmitter<boolean> = new EventEmitter() +export class PillComponent { + @Input() public title: string = 'Untitled Pill' + @Input() public showClose: boolean = true + @Output() public pillClicked: EventEmitter<boolean> = new EventEmitter() + @Output() public closeClicked: EventEmitter<boolean> = new EventEmitter() - @Input() containerStyle: any = { - backgroundColor: 'grey' + @Input() public containerStyle: any = { + backgroundColor: 'grey', } - @Input() closeBtnStyle: any = { - backgroundColor: 'lightgrey' + @Input() public closeBtnStyle: any = { + backgroundColor: 'lightgrey', } - close() { + public close() { this.closeClicked.emit(true) } -} \ No newline at end of file +} diff --git a/src/components/progress/progress.component.ts b/src/components/progress/progress.component.ts index 03e34f9f304f5c6a0b86016c5a73760392434053..7cad6aaebe2e0b891e23bc555129359efebd6ec4 100644 --- a/src/components/progress/progress.component.ts +++ b/src/components/progress/progress.component.ts @@ -1,23 +1,22 @@ -import { Component, Input, ChangeDetectionStrategy } from "@angular/core"; - +import { ChangeDetectionStrategy, Component, Input } from "@angular/core"; @Component({ selector: 'progress-bar', templateUrl: './progress.template.html', styleUrls: [ - './progress.style.css' + './progress.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ProgressBar{ - @Input() progressStyle: any +export class ProgressBar { + @Input() public progressStyle: any private _progress: number = 0 /** * between 0 and 1 */ - @Input() + @Input() set progress(val: number) { if (isNaN(val)) { this._progress = 0 @@ -34,11 +33,11 @@ export class ProgressBar{ this._progress = val } - get progress(){ + get progress() { return this._progress } - get progressPercent(){ + get progressPercent() { return `${this.progress * 100}%` } -} \ No newline at end of file +} diff --git a/src/components/radiolist/radiolist.component.ts b/src/components/radiolist/radiolist.component.ts index e8e25b8daa1176d62a17a055d082a0b5a24139d0..ad1ae5658ac6fbad9c87a690f7a7364526b75e24 100644 --- a/src/components/radiolist/radiolist.component.ts +++ b/src/components/radiolist/radiolist.component.ts @@ -1,59 +1,59 @@ -import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, OnInit, ViewChild, TemplateRef } from "@angular/core"; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output, TemplateRef, ViewChild } from "@angular/core"; @Component({ selector: 'radio-list', templateUrl: './radiolist.template.html', styleUrls: [ - './radiolist.style.css' + './radiolist.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class RadioList{ - @Input() - listDisplay : (item:any) => string = (obj) => obj.name +export class RadioList { + @Input() + public listDisplay: (item: any) => string = (obj) => obj.name @Output() - itemSelected : EventEmitter<any> = new EventEmitter() + public itemSelected: EventEmitter<any> = new EventEmitter() @Input() - selectedItem: any | null = null + public selectedItem: any | null = null @Input() - inputArray: HasExtraButtons[] = [] + public inputArray: IHasExtraButtons[] = [] @Input() - ulClass: string = '' - - @Input() checkSelected: (selectedItem:any, item:any) => boolean = (si,i) => si === i + public ulClass: string = '' + + @Input() public checkSelected: (selectedItem: any, item: any) => boolean = (si, i) => si === i - @Output() extraBtnClicked = new EventEmitter<ExraBtnClickEvent>() + @Output() public extraBtnClicked = new EventEmitter<IExraBtnClickEvent>() - handleExtraBtnClick(extraBtn:ExtraButton, inputItem:any, event:MouseEvent){ + public handleExtraBtnClick(extraBtn: IExtraButton, inputItem: any, event: MouseEvent) { this.extraBtnClicked.emit({ extraBtn, inputItem, - event + event, }) } - overflowText(event) { + public overflowText(event) { return (event.offsetWidth < event.scrollWidth) } } -export interface ExtraButton{ +export interface IExtraButton { name: string, faIcon: string class?: string } -export interface HasExtraButtons{ - extraButtons?: ExtraButton[] +export interface IHasExtraButtons { + extraButtons?: IExtraButton[] } -export interface ExraBtnClickEvent{ - extraBtn:ExtraButton - inputItem:any - event:MouseEvent -} \ No newline at end of file +export interface IExraBtnClickEvent { + extraBtn: IExtraButton + inputItem: any + event: MouseEvent +} diff --git a/src/components/readmoore/readmore.animations.ts b/src/components/readmoore/readmore.animations.ts index aaf355c99adbae6b43191ad4ae749d3bf6200fcd..5187c3d857e55f4a2329c39f3aa24e64f76df3b4 100644 --- a/src/components/readmoore/readmore.animations.ts +++ b/src/components/readmoore/readmore.animations.ts @@ -1,27 +1,27 @@ import { - trigger, + animate, + AnimationTriggerMetadata, state, style, transition, - animate, - AnimationTriggerMetadata + trigger, } from '@angular/animations' -export const readmoreAnimations : AnimationTriggerMetadata = trigger('collapseState',[ +export const readmoreAnimations: AnimationTriggerMetadata = trigger('collapseState', [ state('collapsed', - style({ 'height' : '{{ collapsedHeight }}px' }), - { params : { collapsedHeight : 45, fullHeight : 200, animationLength: 180 } } + style({ height : '{{ collapsedHeight }}px' }), + { params : { collapsedHeight : 45, fullHeight : 200, animationLength: 180 } }, ), state('visible', - style({ 'height' : '*' }), - { params : { collapsedHeight : 45, fullHeight : 200, animationLength: 180 } } + style({ height : '*' }), + { params : { collapsedHeight : 45, fullHeight : 200, animationLength: 180 } }, ), - transition('collapsed => visible',[ + transition('collapsed => visible', [ animate('{{ animationLength }}ms', style({ - 'height' : '{{ fullHeight }}px' - })) + height : '{{ fullHeight }}px', + })), + ]), + transition('visible => collapsed', [ + animate('{{ animationLength }}ms'), ]), - transition('visible => collapsed',[ - animate('{{ animationLength }}ms') - ]) -]) \ No newline at end of file +]) diff --git a/src/components/readmoore/readmore.component.ts b/src/components/readmoore/readmore.component.ts index 260fd13ab2f8a81cd5f1baf3e2f5f7c1b1bbdfca..ccbc0f773b993daaa1ed785b058d1177df5d0f76 100644 --- a/src/components/readmoore/readmore.component.ts +++ b/src/components/readmoore/readmore.component.ts @@ -1,35 +1,35 @@ -import { Component, Input, OnChanges, ViewChild, ElementRef, AfterContentChecked } from "@angular/core"; +import { AfterContentChecked, Component, ElementRef, Input, OnChanges, ViewChild } from "@angular/core"; import { readmoreAnimations } from "./readmore.animations"; @Component({ selector : 'readmore-component', templateUrl : './readmore.template.html', styleUrls : [ - './readmore.style.css' + './readmore.style.css', ], - animations : [ readmoreAnimations ] + animations : [ readmoreAnimations ], }) -export class ReadmoreComponent implements OnChanges, AfterContentChecked{ - @Input() collapsedHeight : number = 45 - @Input() show : boolean = false - @Input() animationLength: number = 180 - @ViewChild('contentContainer') contentContainer : ElementRef - - public fullHeight : number = 200 +export class ReadmoreComponent implements OnChanges, AfterContentChecked { + @Input() public collapsedHeight: number = 45 + @Input() public show: boolean = false + @Input() public animationLength: number = 180 + @ViewChild('contentContainer') public contentContainer: ElementRef - ngAfterContentChecked(){ + public fullHeight: number = 200 + + public ngAfterContentChecked() { this.fullHeight = this.contentContainer.nativeElement.offsetHeight } - ngOnChanges(){ + public ngOnChanges() { this.fullHeight = this.contentContainer.nativeElement.offsetHeight } - public toggle(event:MouseEvent){ - + public toggle(event: MouseEvent) { + this.show = !this.show event.stopPropagation() event.preventDefault() } -} \ No newline at end of file +} diff --git a/src/components/sleightOfHand/soh.component.ts b/src/components/sleightOfHand/soh.component.ts index 4b6fbe5fdb989088e847e10716703dfb4c044c5a..14f80e6888b5fad39f415e68af2d89295a31190d 100644 --- a/src/components/sleightOfHand/soh.component.ts +++ b/src/components/sleightOfHand/soh.component.ts @@ -1,33 +1,33 @@ -import { Component, Input, HostBinding, ChangeDetectionStrategy, HostListener } from "@angular/core"; +import { ChangeDetectionStrategy, Component, HostBinding, HostListener, Input } from "@angular/core"; @Component({ selector: 'sleight-of-hand', templateUrl: './soh.template.html', styleUrls: [ - './soh.style.css' + './soh.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class SleightOfHand{ +export class SleightOfHand { @HostBinding('class.do-not-close') - get doNotCloseClass(){ + get doNotCloseClass() { return this.doNotClose || this.focusInStatus } @HostListener('focusin') - focusInHandler(){ + public focusInHandler() { this.focusInStatus = true } @HostListener('focusout') - focusOutHandler(){ + public focusOutHandler() { this.focusInStatus = false } private focusInStatus: boolean = false @Input() - doNotClose: boolean = false -} \ No newline at end of file + public doNotClose: boolean = false +} diff --git a/src/components/timer/timer.component.ts b/src/components/timer/timer.component.ts index 4c5e6b9715268e68144482a4da9287a57fdf5dc8..eac84af1446d8bc7866a81aa2b9846561cc206be 100644 --- a/src/components/timer/timer.component.ts +++ b/src/components/timer/timer.component.ts @@ -1,35 +1,35 @@ -import { Component, OnInit, Input, OnDestroy, EventEmitter, Output, HostBinding } from "@angular/core"; +import { Component, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output } from "@angular/core"; import { timedValues } from "../../util/generator" @Component({ selector: 'timer-component', templateUrl: './timer.template.html', styleUrls: [ - './timer.style.css' - ] + './timer.style.css', + ], }) -export class TimerComponent implements OnInit, OnDestroy{ - @Input() private timeout:number = 500 - @Input() private pause:boolean = false - @Output() timerEnd : EventEmitter<boolean> = new EventEmitter() +export class TimerComponent implements OnInit, OnDestroy { + @Input() private timeout: number = 500 + @Input() private pause: boolean = false + @Output() public timerEnd: EventEmitter<boolean> = new EventEmitter() - private generator : IterableIterator<any> = null - public progress:number = 0 - private baseProgress:number = 0 + private generator: IterableIterator<any> = null + public progress: number = 0 + private baseProgress: number = 0 - private rafCbId:number + private rafCbId: number private rafCb = () => { - if(this.pause){ + if (this.pause) { this.generator = null this.baseProgress = this.progress - }else{ - if(this.generator === null){ + } else { + if (this.generator === null) { this.generator = timedValues(this.timeout * (1 - this.baseProgress), 'linear') - }else{ + } else { const next = this.generator.next() this.progress = this.baseProgress + (1 - this.baseProgress) * next.value - if(next.done){ + if (next.done) { this.timerEnd.emit(true) return } @@ -38,15 +38,15 @@ export class TimerComponent implements OnInit, OnDestroy{ this.rafCbId = requestAnimationFrame(this.rafCb) } - get transform(){ + get transform() { return `translateX(${this.progress * 100}%)` } - ngOnInit(){ + public ngOnInit() { this.rafCbId = requestAnimationFrame(this.rafCb) } - ngOnDestroy(){ - if(this.rafCbId) cancelAnimationFrame(this.rafCbId) + public ngOnDestroy() { + if (this.rafCbId) { cancelAnimationFrame(this.rafCbId) } } -} \ No newline at end of file +} diff --git a/src/components/toast/toast.animation.ts b/src/components/toast/toast.animation.ts index b3b96abd4ae4824df57af6d51ab3b89e06500344..3ba3986fbcd59d664f4044672c6567c08e2fd6cb 100644 --- a/src/components/toast/toast.animation.ts +++ b/src/components/toast/toast.animation.ts @@ -1,16 +1,16 @@ -import { trigger, state, style, transition, animate } from "@angular/animations"; +import { animate, state, style, transition, trigger } from "@angular/animations"; -export const toastAnimation = trigger('exists',[ - state('*', +export const toastAnimation = trigger('exists', [ + state('*', style({ height : '*', - opacity : 1 + opacity : 1, })), state('void', style({ height: '0em', - opacity : 0 + opacity : 0, })), transition('* => void', animate('180ms ease-in')), - transition('void => *', animate('180ms ease-out')) -]) \ No newline at end of file + transition('void => *', animate('180ms ease-out')), +]) diff --git a/src/components/toast/toast.component.ts b/src/components/toast/toast.component.ts index c0ca259a5e4bbf2da6d61f4027a81a15ad44bd93..8e347fdf1b102a1b72e3486dac9e5cf1be7217ef 100644 --- a/src/components/toast/toast.component.ts +++ b/src/components/toast/toast.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, ViewContainerRef, ViewChild, Output, EventEmitter, HostBinding, ElementRef, ChangeDetectionStrategy, OnInit, HostListener, NgZone } from "@angular/core"; +import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, NgZone, OnInit, Output, ViewChild, ViewContainerRef } from "@angular/core"; import { toastAnimation } from "./toast.animation"; @Component({ @@ -6,30 +6,30 @@ import { toastAnimation } from "./toast.animation"; templateUrl : './toast.template.html', styleUrls : ['./toast.style.css'], animations : [ - toastAnimation - ] + toastAnimation, + ], }) -export class ToastComponent{ - @Input() message : string - @Input() htmlMessage: string - @Input() timeout : number = 0 - @Input() dismissable : boolean = true +export class ToastComponent { + @Input() public message: string + @Input() public htmlMessage: string + @Input() public timeout: number = 0 + @Input() public dismissable: boolean = true - @Output() dismissed : EventEmitter<boolean> = new EventEmitter() + @Output() public dismissed: EventEmitter<boolean> = new EventEmitter() public progress: number = 0 public hover: boolean @HostBinding('@exists') - exists : boolean = true + public exists: boolean = true - @ViewChild('messageContainer',{read:ViewContainerRef}) messageContainer : ViewContainerRef + @ViewChild('messageContainer', {read: ViewContainerRef}) public messageContainer: ViewContainerRef - dismiss(event:MouseEvent){ + public dismiss(event: MouseEvent) { event.preventDefault() event.stopPropagation() this.dismissed.emit(true) } -} \ No newline at end of file +} diff --git a/src/components/tree/tree.animation.ts b/src/components/tree/tree.animation.ts index 9cb6afa3cc7eb823246b30588c54b9c05ceafedb..eca9be9c9530b480c4fdc7e696cfa252c61e2728 100644 --- a/src/components/tree/tree.animation.ts +++ b/src/components/tree/tree.animation.ts @@ -1,23 +1,22 @@ -import { trigger, state, style, transition, animate } from "@angular/animations"; +import { animate, state, style, transition, trigger } from "@angular/animations"; - -export const treeAnimations = trigger('collapseState',[ - state('collapsed', - style({ +export const treeAnimations = trigger('collapseState', [ + state('collapsed', + style({ 'margin-top' : '-{{ fullHeight }}px', }), - { params : { fullHeight : 0 } } + { params : { fullHeight : 0 } }, ), state('visible', - style({ + style({ 'margin-top' : '0px', }), - { params : { fullHeight : 0 } } + { params : { fullHeight : 0 } }, ), - transition('collapsed => visible',[ - animate('180ms') + transition('collapsed => visible', [ + animate('180ms'), + ]), + transition('visible => collapsed', [ + animate('180ms'), ]), - transition('visible => collapsed',[ - animate('180ms') - ]) -]) \ No newline at end of file +]) diff --git a/src/components/tree/tree.component.ts b/src/components/tree/tree.component.ts index 5ec84da8ecfb4ed956d4ccc95bcb32c383004d02..b462dbc2c314626f1052bc552af39ed62ae5b6db 100644 --- a/src/components/tree/tree.component.ts +++ b/src/components/tree/tree.component.ts @@ -1,156 +1,154 @@ -import { Component, Input, Output, EventEmitter, ViewChildren, QueryList, HostBinding, ChangeDetectionStrategy, OnChanges, AfterContentChecked, ViewChild, ElementRef, Optional, OnInit, ChangeDetectorRef, OnDestroy } from "@angular/core"; -import { treeAnimations } from "./tree.animation"; -import { TreeService } from "./treeService.service"; +import { AfterContentChecked, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Input, OnChanges, OnDestroy, OnInit, Optional, Output, QueryList, ViewChild, ViewChildren } from "@angular/core"; import { Subscription } from "rxjs"; import { ParseAttributeDirective } from "../parseAttribute.directive"; - +import { treeAnimations } from "./tree.animation"; +import { TreeService } from "./treeService.service"; @Component({ selector : 'tree-component', templateUrl : './tree.template.html', styleUrls : [ - './tree.style.css' + './tree.style.css', ], animations : [ - treeAnimations + treeAnimations, ], - changeDetection:ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class TreeComponent extends ParseAttributeDirective implements OnChanges,OnInit,OnDestroy,AfterContentChecked{ - @Input() inputItem : any = { +export class TreeComponent extends ParseAttributeDirective implements OnChanges, OnInit, OnDestroy, AfterContentChecked { + @Input() public inputItem: any = { name : 'Untitled', - children : [] + children : [], } - @Input() childrenExpanded : boolean = true + @Input() public childrenExpanded: boolean = true - @Output() mouseentertree : EventEmitter<any> = new EventEmitter() - @Output() mouseleavetree : EventEmitter<any> = new EventEmitter() - @Output() mouseclicktree : EventEmitter<any> = new EventEmitter() + @Output() public mouseentertree: EventEmitter<any> = new EventEmitter() + @Output() public mouseleavetree: EventEmitter<any> = new EventEmitter() + @Output() public mouseclicktree: EventEmitter<any> = new EventEmitter() - @ViewChildren(TreeComponent) treeChildren : QueryList<TreeComponent> - @ViewChild('childrenContainer',{ read : ElementRef }) childrenContainer : ElementRef + @ViewChildren(TreeComponent) public treeChildren: QueryList<TreeComponent> + @ViewChild('childrenContainer', { read : ElementRef }) public childrenContainer: ElementRef - constructor( - private cdr : ChangeDetectorRef, - @Optional() public treeService : TreeService - ){ + constructor( + private cdr: ChangeDetectorRef, + @Optional() public treeService: TreeService, + ) { super() } - - subscriptions : Subscription[] = [] - ngOnInit(){ - if( this.treeService ){ + public subscriptions: Subscription[] = [] + + public ngOnInit() { + if ( this.treeService ) { this.subscriptions.push( - this.treeService.markForCheck.subscribe(()=>this.cdr.markForCheck()) + this.treeService.markForCheck.subscribe(() => this.cdr.markForCheck()), ) } } - - ngOnDestroy(){ - this.subscriptions.forEach(s=>s.unsubscribe()) + public ngOnDestroy() { + this.subscriptions.forEach(s => s.unsubscribe()) } - _fullHeight : number = 9999 + public _fullHeight: number = 9999 - set fullHeight(num:number){ + set fullHeight(num: number) { this._fullHeight = num } - get fullHeight(){ + get fullHeight() { return this._fullHeight } - ngAfterContentChecked(){ + public ngAfterContentChecked() { this.fullHeight = this.childrenContainer ? this.childrenContainer.nativeElement.offsetHeight : 0 this.cdr.detectChanges() } - mouseenter(ev:MouseEvent){ + public mouseenter(ev: MouseEvent) { this.treeService.mouseenter.next({ inputItem : this.inputItem, node : this, - event : ev + event : ev, }) } - mouseleave(ev:MouseEvent){ + public mouseleave(ev: MouseEvent) { this.treeService.mouseleave.next({ inputItem : this.inputItem, node : this, - event : ev + event : ev, }) } - mouseclick(ev:MouseEvent){ + public mouseclick(ev: MouseEvent) { this.treeService.mouseclick.next({ inputItem : this.inputItem, node : this, - event : ev + event : ev, }) } - get chevronClass():string{ - return this.children ? + get chevronClass(): string { + return this.children ? this.children.length > 0 ? - this.childrenExpanded ? + this.childrenExpanded ? 'fa-chevron-down' : 'fa-chevron-right' : 'fa-none' : 'fa-none' } - public handleEv(event:Event){ + public handleEv(event: Event) { event.preventDefault(); event.stopPropagation(); } - public toggleChildrenShow(event:Event){ + public toggleChildrenShow(event: Event) { this.childrenExpanded = !this.childrenExpanded event.stopPropagation() event.preventDefault() } - get children():any[]{ - return this.treeService ? + get children(): any[] { + return this.treeService ? this.treeService.findChildren(this.inputItem) : this.inputItem.children } @HostBinding('attr.filterHidden') - get visibilityOnFilter():boolean{ + get visibilityOnFilter(): boolean { return this.treeService ? this.treeService.searchFilter(this.inputItem) : true } - handleMouseEnter(fullObj:any){ + public handleMouseEnter(fullObj: any) { this.mouseentertree.emit(fullObj) - if(this.treeService){ + if (this.treeService) { this.treeService.mouseenter.next(fullObj) } } - handleMouseLeave(fullObj:any){ - + public handleMouseLeave(fullObj: any) { + this.mouseleavetree.emit(fullObj) - if(this.treeService){ + if (this.treeService) { this.treeService.mouseleave.next(fullObj) } } - handleMouseClick(fullObj:any){ + public handleMouseClick(fullObj: any) { this.mouseclicktree.emit(fullObj) - if(this.treeService){ + if (this.treeService) { this.treeService.mouseclick.next(fullObj) } } - public defaultSearchFilter = ()=>true -} \ No newline at end of file + public defaultSearchFilter = () => true +} diff --git a/src/components/tree/treeBase.directive.ts b/src/components/tree/treeBase.directive.ts index 01f3354064b51d36470c3dc8cf4e6d763051bc52..43fdd5954cb1b4c0145aa5a9ffb10c4d5979f46f 100644 --- a/src/components/tree/treeBase.directive.ts +++ b/src/components/tree/treeBase.directive.ts @@ -1,46 +1,46 @@ -import { Directive, Output, EventEmitter, OnDestroy, Input, OnChanges, ChangeDetectorRef } from "@angular/core"; -import { TreeService } from "./treeService.service"; +import { ChangeDetectorRef, Directive, EventEmitter, Input, OnChanges, OnDestroy, Output } from "@angular/core"; import { Subscription } from "rxjs"; +import { TreeService } from "./treeService.service"; @Directive({ selector : '[treebase]', - host :{ - 'style' : ` + host : { + style : ` - ` + `, }, providers : [ - TreeService - ] + TreeService, + ], }) -export class TreeBaseDirective implements OnDestroy, OnChanges{ - @Output() treeNodeClick : EventEmitter<any> = new EventEmitter() - @Output() treeNodeEnter : EventEmitter<any> = new EventEmitter() - @Output() treeNodeLeave : EventEmitter<any> = new EventEmitter() +export class TreeBaseDirective implements OnDestroy, OnChanges { + @Output() public treeNodeClick: EventEmitter<any> = new EventEmitter() + @Output() public treeNodeEnter: EventEmitter<any> = new EventEmitter() + @Output() public treeNodeLeave: EventEmitter<any> = new EventEmitter() - @Input() renderNode : (item:any)=>string = (item)=>item.name - @Input() findChildren : (item:any)=>any[] = (item)=>item.children - @Input() searchFilter : (item:any)=>boolean | null = ()=>true + @Input() public renderNode: (item: any) => string = (item) => item.name + @Input() public findChildren: (item: any) => any[] = (item) => item.children + @Input() public searchFilter: (item: any) => boolean | null = () => true - private subscriptions : Subscription[] = [] + private subscriptions: Subscription[] = [] constructor( - public changeDetectorRef : ChangeDetectorRef, - public treeService : TreeService - ){ + public changeDetectorRef: ChangeDetectorRef, + public treeService: TreeService, + ) { this.subscriptions.push( - this.treeService.mouseclick.subscribe((obj)=>this.treeNodeClick.emit(obj)) + this.treeService.mouseclick.subscribe((obj) => this.treeNodeClick.emit(obj)), ) this.subscriptions.push( - this.treeService.mouseenter.subscribe((obj)=>this.treeNodeEnter.emit(obj)) + this.treeService.mouseenter.subscribe((obj) => this.treeNodeEnter.emit(obj)), ) this.subscriptions.push( - this.treeService.mouseleave.subscribe((obj)=>this.treeNodeLeave.emit(obj)) + this.treeService.mouseleave.subscribe((obj) => this.treeNodeLeave.emit(obj)), ) } - ngOnChanges(){ + public ngOnChanges() { this.treeService.findChildren = this.findChildren this.treeService.renderNode = this.renderNode this.treeService.searchFilter = this.searchFilter @@ -48,7 +48,7 @@ export class TreeBaseDirective implements OnDestroy, OnChanges{ this.treeService.markForCheck.next(true) } - ngOnDestroy(){ - this.subscriptions.forEach(s=>s.unsubscribe()) + public ngOnDestroy() { + this.subscriptions.forEach(s => s.unsubscribe()) } -} \ No newline at end of file +} diff --git a/src/components/tree/treeService.service.ts b/src/components/tree/treeService.service.ts index dc659b94e9cc274182f2bd9c8e9a6880d91c81f4..2dc6bfc4dc1810038bdb008907a5d2dd5e0c27d9 100644 --- a/src/components/tree/treeService.service.ts +++ b/src/components/tree/treeService.service.ts @@ -2,16 +2,16 @@ import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; @Injectable() -export class TreeService{ - mouseclick : Subject<any> = new Subject() - mouseenter : Subject<any> = new Subject() - mouseleave : Subject<any> = new Subject() +export class TreeService { + public mouseclick: Subject<any> = new Subject() + public mouseenter: Subject<any> = new Subject() + public mouseleave: Subject<any> = new Subject() - findChildren : (item:any)=>any[] = (item)=>item.children - searchFilter : (item:any)=>boolean | null = ()=>true - renderNode : (item:any)=>string = (item)=>item.name + public findChildren: (item: any) => any[] = (item) => item.children + public searchFilter: (item: any) => boolean | null = () => true + public renderNode: (item: any) => string = (item) => item.name - searchTerm : string = `` + public searchTerm: string = `` - markForCheck : Subject<any> = new Subject() -} \ No newline at end of file + public markForCheck: Subject<any> = new Subject() +} diff --git a/src/layouts/floating/floating.component.ts b/src/layouts/floating/floating.component.ts index 9653baccc455f7cc73dc4a81e939961923f6fef9..1d546fd8a339a949e54e230deeeb91acb9c0801e 100644 --- a/src/layouts/floating/floating.component.ts +++ b/src/layouts/floating/floating.component.ts @@ -1,16 +1,15 @@ -import { Component, ViewChild, HostBinding, Input } from "@angular/core"; - +import { Component, HostBinding, Input, ViewChild } from "@angular/core"; @Component({ selector : 'layout-floating-container', templateUrl : './floating.template.html', styleUrls : [ - `./floating.style.css` - ] + `./floating.style.css`, + ], }) -export class FloatingLayoutContainer{ +export class FloatingLayoutContainer { @HostBinding('style.z-index') @Input() - zIndex : number = 5 -} \ No newline at end of file + public zIndex: number = 5 +} diff --git a/src/layouts/layout.module.ts b/src/layouts/layout.module.ts index 703b12e6955f4e8dce267e5c5f07136af4d75422..b10a09b6bb519316c56a22550bc445afc5c5875b 100644 --- a/src/layouts/layout.module.ts +++ b/src/layouts/layout.module.ts @@ -1,31 +1,30 @@ import { NgModule } from "@angular/core"; -import { LayoutMainSide } from "./mainside/mainside.component"; -import { LayoutsExample } from "./layoutsExample/layoutsExample.component"; import { BrowserModule } from "@angular/platform-browser"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { ComponentsModule } from "../components/components.module"; import { FloatingLayoutContainer } from "./floating/floating.component"; -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; - +import { LayoutsExample } from "./layoutsExample/layoutsExample.component"; +import { LayoutMainSide } from "./mainside/mainside.component"; @NgModule({ imports : [ BrowserAnimationsModule, BrowserModule, - ComponentsModule + ComponentsModule, ], declarations : [ LayoutMainSide, FloatingLayoutContainer, - LayoutsExample + LayoutsExample, ], exports : [ BrowserAnimationsModule, LayoutMainSide, FloatingLayoutContainer, - LayoutsExample - ] + LayoutsExample, + ], }) -export class LayoutModule{} +export class LayoutModule {} diff --git a/src/layouts/layoutsExample/layoutsExample.component.ts b/src/layouts/layoutsExample/layoutsExample.component.ts index 1db26a91a0abb6807c9a63921ee8e8581832d5f5..4cc69683628ef3314e0145e598d70f2556aae570 100644 --- a/src/layouts/layoutsExample/layoutsExample.component.ts +++ b/src/layouts/layoutsExample/layoutsExample.component.ts @@ -1,16 +1,15 @@ import { Component } from "@angular/core"; - @Component({ selector : 'layouts-example', templateUrl : './layoutsExample.template.html', styleUrls : [ - `./layoutsExample.style.css` - ] + `./layoutsExample.style.css`, + ], }) -export class LayoutsExample{ - mainsideOverlay : boolean = true - mainsideShowSide : boolean = true - mainsideSideWidth : number = 100 -} \ No newline at end of file +export class LayoutsExample { + public mainsideOverlay: boolean = true + public mainsideShowSide: boolean = true + public mainsideSideWidth: number = 100 +} diff --git a/src/layouts/mainside/mainside.animation.ts b/src/layouts/mainside/mainside.animation.ts index 1cf8e4a3fd8b7a3ca3ef44541f3b96bce3bbb029..cb19cf634e2707115bd7b108829885c646029619 100644 --- a/src/layouts/mainside/mainside.animation.ts +++ b/src/layouts/mainside/mainside.animation.ts @@ -1,25 +1,24 @@ -import { trigger, state, style, transition, animate } from "@angular/animations"; +import { animate, state, style, transition, trigger } from "@angular/animations"; - -export const mainSideAnimation = trigger('collapseSide',[ +export const mainSideAnimation = trigger('collapseSide', [ state('collapsed', style({ 'flex-basis' : '0px', - 'width' : '0px' + 'width' : '0px', }), - { params : { sideWidth : 0, animationTiming: 180 } } + { params : { sideWidth : 0, animationTiming: 180 } }, ), state('visible', style({ 'flex-basis' : '{{ sideWidth }}px', - 'width' : '{{ sideWidth }}px' + 'width' : '{{ sideWidth }}px', }), - { params : { sideWidth : 300, animationTiming: 180 } } + { params : { sideWidth : 300, animationTiming: 180 } }, ), - transition('collapsed => visible',[ - animate('{{ animationTiming }}ms ease-out') + transition('collapsed => visible', [ + animate('{{ animationTiming }}ms ease-out'), + ]), + transition('visible => collapsed', [ + animate('{{ animationTiming }}ms ease-in'), ]), - transition('visible => collapsed',[ - animate('{{ animationTiming }}ms ease-in') - ]) -]) \ No newline at end of file +]) diff --git a/src/layouts/mainside/mainside.component.ts b/src/layouts/mainside/mainside.component.ts index 56824fbe8c371b095ea7b7c266571a3731383088..624c75b69d2c1ceb1bdbc05ce52253b974ecde15 100644 --- a/src/layouts/mainside/mainside.component.ts +++ b/src/layouts/mainside/mainside.component.ts @@ -1,37 +1,37 @@ -import { Component, Input, EventEmitter, Output } from "@angular/core"; +import { Component, EventEmitter, Input, Output } from "@angular/core"; import { mainSideAnimation } from "./mainside.animation"; @Component({ selector : 'layout-mainside', templateUrl : './mainside.template.html', styleUrls : [ - './mainside.style.css' + './mainside.style.css', ], animations : [ - mainSideAnimation - ] + mainSideAnimation, + ], }) -export class LayoutMainSide{ - @Input() showResizeSliver : boolean = true - @Input() showSide : boolean = false - @Input() sideWidth : number = 300 - @Input() animationFlag : boolean = false +export class LayoutMainSide { + @Input() public showResizeSliver: boolean = true + @Input() public showSide: boolean = false + @Input() public sideWidth: number = 300 + @Input() public animationFlag: boolean = false - @Output() panelShowStateChanged : EventEmitter<boolean> = new EventEmitter() - @Output() panelAnimationStart : EventEmitter<boolean> = new EventEmitter() - @Output() panelAnimationEnd : EventEmitter<boolean> = new EventEmitter() + @Output() public panelShowStateChanged: EventEmitter<boolean> = new EventEmitter() + @Output() public panelAnimationStart: EventEmitter<boolean> = new EventEmitter() + @Output() public panelAnimationEnd: EventEmitter<boolean> = new EventEmitter() - togglePanelShow(){ + public togglePanelShow() { this.showSide = !this.showSide this.panelShowStateChanged.emit(this.showSide) } - animationStart(){ + public animationStart() { this.panelAnimationStart.emit(true) } - animationEnd(){ + public animationEnd() { this.panelAnimationEnd.emit(true) } -} \ No newline at end of file +} diff --git a/src/main-aot.ts b/src/main-aot.ts index f50f694558cc500241e9e8ecff71b988d2938fdc..fa498f035dea25a705429a78775d9ed76dcb0674 100644 --- a/src/main-aot.ts +++ b/src/main-aot.ts @@ -2,11 +2,11 @@ import 'zone.js' import 'third_party/testSafari.js' +import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' import { MainModule } from './main.module'; -import { enableProdMode } from '@angular/core'; -const requireAll = (r:any) => {r.keys().forEach(r)} +const requireAll = (r: any) => {r.keys().forEach(r)} requireAll(require.context('./res/ext', false, /\.json$/)) requireAll(require.context('./res/images', true, /\.jpg$|\.png$|\.svg$/)) requireAll(require.context(`./plugin_examples`, true)) @@ -14,6 +14,6 @@ requireAll(require.context(`./plugin_examples`, true)) /* aot === production mode */ enableProdMode() -if(PRODUCTION) console.log(`Interactive Atlas Viewer: ${VERSION}`) +if (PRODUCTION) { this.log.log(`Interactive Atlas Viewer: ${VERSION}`) } -platformBrowserDynamic().bootstrapModule(MainModule) \ No newline at end of file +platformBrowserDynamic().bootstrapModule(MainModule) diff --git a/src/main.module.ts b/src/main.module.ts index d1abd75d386310cb6f49c2a0b6766eb66dc98283..52772356c764f50a8c292e8086460b4f6e52efc0 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -1,61 +1,60 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; -import { ComponentsModule } from "./components/components.module"; import { DragDropModule } from '@angular/cdk/drag-drop' -import { UIModule } from "./ui/ui.module"; -import { LayoutModule } from "./layouts/layout.module"; -import { AtlasViewer } from "./atlasViewer/atlasViewer.component"; -import { StoreModule } from "@ngrx/store"; -import { viewerState, dataStore, uiState, ngViewerState, pluginState, viewerConfigState, userConfigState, UserConfigStateUseEffect } from "./services/stateStore.service"; -import { GetNamesPipe } from "./util/pipes/getNames.pipe"; import { CommonModule } from "@angular/common"; -import { GetNamePipe } from "./util/pipes/getName.pipe"; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; +import { StoreModule } from "@ngrx/store"; import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module' +import { AtlasViewer } from "./atlasViewer/atlasViewer.component"; +import { ComponentsModule } from "./components/components.module"; +import { LayoutModule } from "./layouts/layout.module"; +import { dataStore, ngViewerState, pluginState, uiState, userConfigState, UserConfigStateUseEffect, viewerConfigState, viewerState } from "./services/stateStore.service"; +import { UIModule } from "./ui/ui.module"; +import { GetNamePipe } from "./util/pipes/getName.pipe"; +import { GetNamesPipe } from "./util/pipes/getNames.pipe"; -import { WidgetUnit } from "./atlasViewer/widgetUnit/widgetUnit.component"; -import { WidgetServices } from './atlasViewer/widgetUnit/widgetService.service' -// TODO deprecate -import { fasTooltipScreenshotDirective,fasTooltipInfoSignDirective,fasTooltipLogInDirective,fasTooltipNewWindowDirective,fasTooltipQuestionSignDirective,fasTooltipRemoveDirective,fasTooltipRemoveSignDirective } from "./util/directives/glyphiconTooltip.directive"; -import { TooltipModule } from "ngx-bootstrap/tooltip"; +import {HttpClientModule} from "@angular/common/http"; +import { EffectsModule } from "@ngrx/effects"; import { TabsModule } from 'ngx-bootstrap/tabs' -import { ModalUnit } from "./atlasViewer/modalUnit/modalUnit.component"; -import { ToastComponent } from "./components/toast/toast.component"; +import { TooltipModule } from "ngx-bootstrap/tooltip"; +import {CaptureClickListenerDirective} from "src/util/directives/captureClickListener.directive"; import { AtlasViewerAPIServices } from "./atlasViewer/atlasViewer.apiService.service"; -import { PluginUnit } from "./atlasViewer/pluginUnit/pluginUnit.component"; -import { NewViewerDisctinctViewToLayer } from "./util/pipes/newViewerDistinctViewToLayer.pipe"; import { AtlasWorkerService } from "./atlasViewer/atlasViewer.workerService.service"; -import { DockedContainerDirective } from "./util/directives/dockedContainer.directive"; -import { FloatingContainerDirective } from "./util/directives/floatingContainer.directive"; -import { PluginFactoryDirective } from "./util/directives/pluginFactory.directive"; -import { FloatingMouseContextualContainerDirective } from "./util/directives/floatingMouseContextualContainer.directive"; -import { AuthService } from "./services/auth.service"; -import { DatabrowserService } from "./ui/databrowserModule/databrowser.service"; +import { ModalUnit } from "./atlasViewer/modalUnit/modalUnit.component"; import { TransformOnhoverSegmentPipe } from "./atlasViewer/onhoverSegment.pipe"; -import {HttpClientModule} from "@angular/common/http"; -import { EffectsModule } from "@ngrx/effects"; +import { PluginUnit } from "./atlasViewer/pluginUnit/pluginUnit.component"; +import { WidgetServices } from './atlasViewer/widgetUnit/widgetService.service' +import { WidgetUnit } from "./atlasViewer/widgetUnit/widgetUnit.component"; +import { ConfirmDialogComponent } from "./components/confirmDialog/confirmDialog.component"; +import { DialogComponent } from "./components/dialog/dialog.component"; +import { ToastComponent } from "./components/toast/toast.component"; +import { AuthService } from "./services/auth.service"; +import { DialogService } from "./services/dialogService.service"; import { UseEffects } from "./services/effect/effect"; -import { DragDropDirective } from "./util/directives/dragDrop.directive"; import { LocalFileService } from "./services/localFile.service"; -import { DataBrowserUseEffect } from "./ui/databrowserModule/databrowser.useEffect"; -import { DialogService } from "./services/dialogService.service"; -import { DialogComponent } from "./components/dialog/dialog.component"; -import { ViewerStateControllerUseEffect } from "./ui/viewerStateController/viewerState.useEffect"; -import { ConfirmDialogComponent } from "./components/confirmDialog/confirmDialog.component"; -import { ViewerStateUseEffect } from "./services/state/viewerState.store"; import { NgViewerUseEffect } from "./services/state/ngViewerState.store"; -import { DatabrowserModule } from "./ui/databrowserModule/databrowser.module"; +import { ViewerStateUseEffect } from "./services/state/viewerState.store"; import { UIService } from "./services/uiService.service"; +import { DatabrowserModule } from "./ui/databrowserModule/databrowser.module"; +import { DatabrowserService } from "./ui/databrowserModule/databrowser.service"; +import { DataBrowserUseEffect } from "./ui/databrowserModule/databrowser.useEffect"; +import { ViewerStateControllerUseEffect } from "./ui/viewerStateController/viewerState.useEffect"; +import { DockedContainerDirective } from "./util/directives/dockedContainer.directive"; +import { DragDropDirective } from "./util/directives/dragDrop.directive"; +import { FloatingContainerDirective } from "./util/directives/floatingContainer.directive"; +import { FloatingMouseContextualContainerDirective } from "./util/directives/floatingMouseContextualContainer.directive"; +import { PluginFactoryDirective } from "./util/directives/pluginFactory.directive"; +import { NewViewerDisctinctViewToLayer } from "./util/pipes/newViewerDistinctViewToLayer.pipe"; import { UtilModule } from "./util/util.module"; -import {CaptureClickListenerDirective} from "src/util/directives/captureClickListener.directive"; -import { PluginServiceuseEffect } from "./atlasViewer/atlasViewer.pluginService.service"; import 'hammerjs' +import 'src/res/css/extra_styles.css' import 'src/res/css/version.css' +import {UiStateUseEffect} from "src/services/state/uiState.store"; import 'src/theme.scss' -import 'src/res/css/extra_styles.css' import { AtlasViewerHistoryUseEffect } from "./atlasViewer/atlasViewer.history.service"; -import {UiStateUseEffect} from "src/services/state/uiState.store"; +import { PluginServiceUseEffect } from './services/effect/pluginUseEffect'; +import { LoggingService } from "./services/logging.service"; @NgModule({ imports : [ @@ -68,7 +67,7 @@ import {UiStateUseEffect} from "src/services/state/uiState.store"; DatabrowserModule, AngularMaterialModule, UtilModule, - + TooltipModule.forRoot(), TabsModule.forRoot(), EffectsModule.forRoot([ @@ -78,9 +77,9 @@ import {UiStateUseEffect} from "src/services/state/uiState.store"; ViewerStateControllerUseEffect, ViewerStateUseEffect, NgViewerUseEffect, - PluginServiceuseEffect, + PluginServiceUseEffect, AtlasViewerHistoryUseEffect, - UiStateUseEffect + UiStateUseEffect, ]), StoreModule.forRoot({ pluginState, @@ -89,9 +88,9 @@ import {UiStateUseEffect} from "src/services/state/uiState.store"; viewerState, dataStore, uiState, - userConfigState + userConfigState, }), - HttpClientModule + HttpClientModule, ], declarations : [ AtlasViewer, @@ -100,13 +99,6 @@ import {UiStateUseEffect} from "src/services/state/uiState.store"; PluginUnit, /* directives */ - fasTooltipScreenshotDirective, - fasTooltipInfoSignDirective, - fasTooltipLogInDirective, - fasTooltipNewWindowDirective, - fasTooltipQuestionSignDirective, - fasTooltipRemoveDirective, - fasTooltipRemoveSignDirective, DockedContainerDirective, FloatingContainerDirective, PluginFactoryDirective, @@ -118,7 +110,7 @@ import {UiStateUseEffect} from "src/services/state/uiState.store"; GetNamesPipe, GetNamePipe, TransformOnhoverSegmentPipe, - NewViewerDisctinctViewToLayer + NewViewerDisctinctViewToLayer, ], entryComponents : [ WidgetUnit, @@ -136,23 +128,24 @@ import {UiStateUseEffect} from "src/services/state/uiState.store"; LocalFileService, DialogService, UIService, - + LoggingService, + /** * TODO * once nehubacontainer is separated into viewer + overlay, migrate to nehubaContainer module */ - DatabrowserService + DatabrowserService, ], bootstrap : [ - AtlasViewer + AtlasViewer, ], schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ] + CUSTOM_ELEMENTS_SCHEMA, + ], }) -export class MainModule{ - +export class MainModule { + constructor( authServce: AuthService, @@ -161,8 +154,8 @@ export class MainModule{ * allow for pre fetching of dataentry * TODO only fetch when traffic is idle */ - dbSerivce: DatabrowserService - ){ + dbSerivce: DatabrowserService, + ) { authServce.authReloadState() } } diff --git a/src/main.ts b/src/main.ts index 0e62fe97032b9fbd3d520ba27f36b299fbb9904f..309135b48da57e04375df53e7347ac20e6d9ee34 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,17 +1,17 @@ -import 'zone.js' import 'reflect-metadata' +import 'zone.js' import 'third_party/testSafari.js' import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' -import { MainModule } from './main.module'; import {defineCustomElements} from 'hbp-connectivity-component/dist/loader' +import { MainModule } from './main.module'; -const requireAll = (r:any) => {r.keys().forEach(r)} -requireAll(require.context('./res/ext',false, /\.json$/)) -requireAll(require.context('./res/images',true,/\.jpg$|\.png$|\.svg$/)) +const requireAll = (r: any) => {r.keys().forEach(r)} +requireAll(require.context('./res/ext', false, /\.json$/)) +requireAll(require.context('./res/images', true, /\.jpg$|\.png$|\.svg$/)) requireAll(require.context(`./plugin_examples`, true)) platformBrowserDynamic().bootstrapModule(MainModule) -defineCustomElements(window) \ No newline at end of file +defineCustomElements(window) diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index a74df4bd375db71a5f62e15d3c67dc3ab0f5e7a3..636f02f6249173d0b64471513dcb4c64702656fa 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -1,25 +1,25 @@ +import { HttpClient } from "@angular/common/http"; import { Injectable, OnDestroy } from "@angular/core"; import { Observable, of, Subscription } from "rxjs"; -import { HttpClient } from "@angular/common/http"; import { catchError, shareReplay } from "rxjs/operators"; const IV_REDIRECT_TOKEN = `IV_REDIRECT_TOKEN` @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class AuthService implements OnDestroy{ - public user: User | null +export class AuthService implements OnDestroy { + public user: IUser | null public user$: Observable<any> - public logoutHref: String = 'logout' + public logoutHref: string = 'logout' /** * TODO build it dynamically, or at least possible to configure via env var */ - public loginMethods : AuthMethod[] = [{ + public loginMethods: IAuthMethod[] = [{ name: 'HBP OIDC', - href: 'hbp-oidc/auth' + href: 'hbp-oidc/auth', }] constructor(private httpClient: HttpClient) { @@ -27,38 +27,38 @@ export class AuthService implements OnDestroy{ catchError(err => { return of(null) }), - shareReplay(1) + shareReplay(1), ) this.subscription.push( - this.user$.subscribe(user => this.user = user) + this.user$.subscribe(user => this.user = user), ) } private subscription: Subscription[] = [] - ngOnDestroy(){ - while (this.subscription.length > 0) this.subscription.pop().unsubscribe() + public ngOnDestroy() { + while (this.subscription.length > 0) { this.subscription.pop().unsubscribe() } } - authSaveState() { + public authSaveState() { window.localStorage.setItem(IV_REDIRECT_TOKEN, window.location.href) } - authReloadState() { + public authReloadState() { const redirect = window.localStorage.getItem(IV_REDIRECT_TOKEN) window.localStorage.removeItem(IV_REDIRECT_TOKEN) - if (redirect) window.location.href = redirect + if (redirect) { window.location.href = redirect } } } -export interface User { - name: String - id: String +export interface IUser { + name: string + id: string } -export interface AuthMethod{ - href: String - name: String +export interface IAuthMethod { + href: string + name: string } diff --git a/src/services/dialogService.service.ts b/src/services/dialogService.service.ts index 02fd6b387748c4790465aa64163e82040726ae07..8fda9fc2e309ff56115b0849c3d480dfee4bd03e 100644 --- a/src/services/dialogService.service.ts +++ b/src/services/dialogService.service.ts @@ -1,42 +1,40 @@ import { Injectable } from "@angular/core"; import { MatDialog, MatDialogRef } from "@angular/material"; -import { DialogComponent } from "src/components/dialog/dialog.component"; import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component"; - +import { DialogComponent } from "src/components/dialog/dialog.component"; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class DialogService{ +export class DialogService { private dialogRef: MatDialogRef<DialogComponent> private confirmDialogRef: MatDialogRef<ConfirmDialogComponent> - constructor(private dialog:MatDialog){ + constructor(private dialog: MatDialog) { } - public getUserConfirm(config: Partial<DialogConfig> = {}): Promise<string>{ + public getUserConfirm(config: Partial<DialogConfig> = {}): Promise<string> { this.confirmDialogRef = this.dialog.open(ConfirmDialogComponent, { - data: config + data: config, }) return new Promise((resolve, reject) => this.confirmDialogRef.afterClosed() .subscribe(val => { - if (val) resolve() - else reject('User cancelled') + if (val) { resolve() } else { reject('User cancelled') } }, reject, () => this.confirmDialogRef = null)) } - public getUserInput(config: Partial<DialogConfig> = {}):Promise<string>{ + public getUserInput(config: Partial<DialogConfig> = {}): Promise<string> { const { defaultValue = '', placeholder = 'Type your response here', title = 'Message', message = '', - iconClass + iconClass, } = config this.dialogRef = this.dialog.open(DialogComponent, { data: { @@ -44,8 +42,8 @@ export class DialogService{ placeholder, defaultValue, message, - iconClass - } + iconClass, + }, }) return new Promise((resolve, reject) => { /** @@ -53,18 +51,17 @@ export class DialogService{ * Should not result in leak */ this.dialogRef.afterClosed().subscribe(value => { - if (value) resolve(value) - else reject('User cancelled input') + if (value) { resolve(value) } else { reject('User cancelled input') } this.dialogRef = null }) }) } } -export interface DialogConfig{ +export interface DialogConfig { title: string placeholder: string defaultValue: string message: string iconClass: string -} \ No newline at end of file +} diff --git a/src/services/effect/effect.spec.ts b/src/services/effect/effect.spec.ts index 622639671d92cb8fe9c98c4395f4994a2fa30961..a4b8972fc90e5b72b33368c0f3b46f9239ddaa2f 100644 --- a/src/services/effect/effect.spec.ts +++ b/src/services/effect/effect.spec.ts @@ -3,35 +3,35 @@ import { getGetRegionFromLabelIndexId } from './effect' const colinsJson = require('!json-loader!../../res/ext/colin.json') const hoc1 = { - "name": "Area hOc1 (V1, 17, CalcS) - left hemisphere", - "rgb": [ + name: "Area hOc1 (V1, 17, CalcS) - left hemisphere", + rgb: [ 190, 132, - 147 + 147, ], - "labelIndex": 8, - "ngId": "jubrain colin v18 left", - "children": [], - "status": "publicP", - "position": [ + labelIndex: 8, + ngId: "jubrain colin v18 left", + children: [], + status: "publicP", + position: [ -8533787, -84646549, - 1855106 - ] + 1855106, + ], } describe('effect.ts', () => { describe('getGetRegionFromLabelIndexId', () => { it('translateds hoc1 from labelIndex to region', () => { - const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({ + const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({ parcellation: { ...colinsJson.parcellations[0], - updated: true - } + updated: true, + }, }) const fetchedRegion = getRegionFromlabelIndexId({ labelIndexId: 'jubrain colin v18 left#8' }) expect(fetchedRegion).toEqual(hoc1) }) }) -}) \ No newline at end of file +}) diff --git a/src/services/effect/effect.ts b/src/services/effect/effect.ts index 87cd072181691fe8e4a7a535964c097ea14c52e7..043ff4c116bd86a34155c4048e35d61c8c9b4400 100644 --- a/src/services/effect/effect.ts +++ b/src/services/effect/effect.ts @@ -1,34 +1,36 @@ import { Injectable, OnDestroy } from "@angular/core"; -import { Effect, Actions, ofType } from "@ngrx/effects"; -import { Subscription, merge, fromEvent, Observable } from "rxjs"; -import { withLatestFrom, map, filter, shareReplay, switchMap, take } from "rxjs/operators"; -import { Store, select } from "@ngrx/store"; -import { SELECT_PARCELLATION, SELECT_REGIONS, NEWVIEWER, UPDATE_PARCELLATION, SELECT_REGIONS_WITH_ID, DESELECT_REGIONS, ADD_TO_REGIONS_SELECTION_WITH_IDS } from "../state/viewerState.store"; +import { Actions, Effect, ofType } from "@ngrx/effects"; +import { select, Store } from "@ngrx/store"; +import { fromEvent, merge, Observable, Subscription } from "rxjs"; +import { filter, map, shareReplay, switchMap, take, withLatestFrom } from "rxjs/operators"; import { worker } from 'src/atlasViewer/atlasViewer.workerService.service' -import { getNgIdLabelIndexFromId, generateLabelIndexId, recursiveFindRegionWithLabelIndexId, IavRootStoreInterface } from '../stateStore.service'; +import { LoggingService } from "../logging.service"; +import { ADD_TO_REGIONS_SELECTION_WITH_IDS, DESELECT_REGIONS, NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS, SELECT_REGIONS_WITH_ID, UPDATE_PARCELLATION } from "../state/viewerState.store"; +import { generateLabelIndexId, getNgIdLabelIndexFromId, IavRootStoreInterface, recursiveFindRegionWithLabelIndexId } from '../stateStore.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class UseEffects implements OnDestroy{ +export class UseEffects implements OnDestroy { constructor( private actions$: Actions, - private store$: Store<IavRootStoreInterface> - ){ + private store$: Store<IavRootStoreInterface>, + private log: LoggingService, + ) { this.subscriptions.push( this.newParcellationSelected$.subscribe(parcellation => { worker.postMessage({ type: `PROPAGATE_NG_ID`, - parcellation + parcellation, }) - }) + }), ) this.regionsSelected$ = this.store$.pipe( select('viewerState'), select('regionsSelected'), - shareReplay(1) + shareReplay(1), ) this.onDeselectRegions = this.actions$.pipe( @@ -41,9 +43,9 @@ export class UseEffects implements OnDestroy{ }) return { type: SELECT_REGIONS, - selectRegions + selectRegions, } - }) + }), ) this.onDeselectRegionsWithId$ = this.actions$.pipe( @@ -57,9 +59,10 @@ export class UseEffects implements OnDestroy{ const deselectSet = new Set(deselecRegionIds) return { type: SELECT_REGIONS, - selectRegions: alreadySelectedRegions.filter(({ ngId, labelIndex }) => !deselectSet.has(generateLabelIndexId({ ngId, labelIndex }))) + selectRegions: alreadySelectedRegions + .filter(({ ngId, labelIndex }) => !deselectSet.has(generateLabelIndexId({ ngId, labelIndex }))), } - }) + }), ) this.addToSelectedRegions$ = this.actions$.pipe( @@ -71,80 +74,79 @@ export class UseEffects implements OnDestroy{ switchMap(selectRegionIds => this.updatedParcellation$.pipe( filter(p => !!p), take(1), - map(p => [selectRegionIds, p]) + map(p => [selectRegionIds, p]), )), map(this.convertRegionIdsToRegion), withLatestFrom(this.regionsSelected$), map(([ selectedRegions, alreadySelectedRegions ]) => { return { type: SELECT_REGIONS, - selectRegions: this.removeDuplicatedRegions(selectedRegions, alreadySelectedRegions) + selectRegions: this.removeDuplicatedRegions(selectedRegions, alreadySelectedRegions), } - }) + }), ) } private regionsSelected$: Observable<any[]> - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } private subscriptions: Subscription[] = [] - private parcellationSelected$ = this.actions$.pipe( ofType(SELECT_PARCELLATION), ) private newViewer$ = this.actions$.pipe( - ofType(NEWVIEWER) + ofType(NEWVIEWER), ) private newParcellationSelected$ = merge( this.newViewer$, - this.parcellationSelected$ + this.parcellationSelected$, ).pipe( - map(({selectParcellation}) => selectParcellation) + map(({selectParcellation}) => selectParcellation), ) private updatedParcellation$ = this.store$.pipe( select('viewerState'), select('parcellationSelected'), map(p => p.updated ? p : null), - shareReplay(1) + shareReplay(1), ) @Effect() - onDeselectRegions: Observable<any> + public onDeselectRegions: Observable<any> @Effect() - onDeselectRegionsWithId$: Observable<any> + public onDeselectRegionsWithId$: Observable<any> private convertRegionIdsToRegion = ([selectRegionIds, parcellation]) => { const { ngId: defaultNgId } = parcellation - return (<any[]>selectRegionIds) + return (selectRegionIds as any[]) .map(labelIndexId => getNgIdLabelIndexFromId({ labelIndexId })) .map(({ ngId, labelIndex }) => { return { labelIndexId: generateLabelIndexId({ ngId: ngId || defaultNgId, - labelIndex - }) + labelIndex, + }), } }) .map(({ labelIndexId }) => { - return recursiveFindRegionWithLabelIndexId({ + return recursiveFindRegionWithLabelIndexId({ regions: parcellation.regions, labelIndexId, - inheritedNgId: defaultNgId + inheritedNgId: defaultNgId, }) }) .filter(v => { if (!v) { - console.log(`SELECT_REGIONS_WITH_ID, some ids cannot be parsed intto label index`) + this.log.log(`SELECT_REGIONS_WITH_ID, some ids cannot be parsed intto label index`) } return !!v }) @@ -153,8 +155,8 @@ export class UseEffects implements OnDestroy{ private removeDuplicatedRegions = (...args) => { const set = new Set() const returnArr = [] - for (const regions of args){ - for (const region of regions){ + for (const regions of args) { + for (const region of regions) { if (!set.has(region.name)) { returnArr.push(region) set.add(region.name) @@ -165,15 +167,14 @@ export class UseEffects implements OnDestroy{ } @Effect() - addToSelectedRegions$: Observable<any> - + public addToSelectedRegions$: Observable<any> /** * for backwards compatibility. * older versions of atlas viewer may only have labelIndex as region identifier */ @Effect() - onSelectRegionWithId = this.actions$.pipe( + public onSelectRegionWithId = this.actions$.pipe( ofType(SELECT_REGIONS_WITH_ID), map(action => { const { selectRegionIds } = action @@ -182,39 +183,39 @@ export class UseEffects implements OnDestroy{ switchMap(selectRegionIds => this.updatedParcellation$.pipe( filter(p => !!p), take(1), - map(parcellation => [selectRegionIds, parcellation]) + map(parcellation => [selectRegionIds, parcellation]), )), map(this.convertRegionIdsToRegion), map(selectRegions => { return { type: SELECT_REGIONS, - selectRegions + selectRegions, } - }) + }), ) /** * side effect of selecting a parcellation means deselecting all regions */ @Effect() - onParcellationSelected$ = this.newParcellationSelected$.pipe( + public onParcellationSelected$ = this.newParcellationSelected$.pipe( map(() => ({ type: SELECT_REGIONS, - selectRegions: [] - })) + selectRegions: [], + })), ) /** * calculating propagating ngId from worker thread */ @Effect() - updateParcellation$ = fromEvent(worker, 'message').pipe( + public updateParcellation$ = fromEvent(worker, 'message').pipe( filter((message: MessageEvent) => message && message.data && message.data.type === 'UPDATE_PARCELLATION_REGIONS'), map(({data}) => data.parcellation), withLatestFrom(this.newParcellationSelected$), - filter(([ propagatedP, selectedP ] : [any, any]) => { + filter(([ propagatedP, selectedP ]: [any, any]) => { /** - * TODO + * TODO * use id * but jubrain may have same id for different template spaces */ @@ -223,27 +224,28 @@ export class UseEffects implements OnDestroy{ map(([ propagatedP, _ ]) => propagatedP), map(parcellation => ({ type: UPDATE_PARCELLATION, - updatedParcellation: parcellation - })) + updatedParcellation: parcellation, + })), ) } export const getGetRegionFromLabelIndexId = ({ parcellation }) => { const { ngId: defaultNgId, regions } = parcellation // if (!updated) throw new Error(`parcellation not yet updated`) - return ({ labelIndexId }) => recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId: defaultNgId }) + return ({ labelIndexId }) => + recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId: defaultNgId }) } -export const compareRegions: (r1: any,r2: any) => boolean = (r1, r2) => { - if (!r1) return !r2 - if (!r2) return !r1 +export const compareRegions: (r1: any, r2: any) => boolean = (r1, r2) => { + if (!r1) { return !r2 } + if (!r2) { return !r1 } return r1.ngId === r2.ngId && r1.labelIndex === r2.labelIndex && r1.name === r2.name } const ACTION_TYPES = { - DESELECT_REGIONS_WITH_ID: 'DESELECT_REGIONS_WITH_ID' + DESELECT_REGIONS_WITH_ID: 'DESELECT_REGIONS_WITH_ID', } -export const VIEWER_STATE_ACTION_TYPES = ACTION_TYPES \ No newline at end of file +export const VIEWER_STATE_ACTION_TYPES = ACTION_TYPES diff --git a/src/services/effect/pluginUseEffect.ts b/src/services/effect/pluginUseEffect.ts new file mode 100644 index 0000000000000000000000000000000000000000..a61ae5a8445bb9646e301b907fd096cfb7b72359 --- /dev/null +++ b/src/services/effect/pluginUseEffect.ts @@ -0,0 +1,53 @@ +import { Injectable } from "@angular/core" +import { Effect } from "@ngrx/effects" +import { select, Store } from "@ngrx/store" +import { Observable } from "rxjs" +import { filter, map, startWith } from "rxjs/operators" +import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service" +import { PluginServices } from "src/atlasViewer/atlasViewer.pluginService.service" +import { ACTION_TYPES as PLUGINSTORE_ACTION_TYPES, CONSTANTS as PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.store' +import { LoggingService } from "../logging.service" +import { IavRootStoreInterface } from "../stateStore.service" + +@Injectable({ + providedIn: 'root', +}) + +export class PluginServiceUseEffect { + + @Effect() + public initManifests$: Observable<any> + + constructor( + store$: Store<IavRootStoreInterface>, + constantService: AtlasViewerConstantsServices, + pluginService: PluginServices, + private log: LoggingService, + ) { + this.initManifests$ = store$.pipe( + select('pluginState'), + select('initManifests'), + filter(v => !!v), + startWith([]), + map(arr => { + // only launch plugins that has init manifest src label on it + return arr.filter(([ source ]) => source === PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC) + }), + filter(arr => arr.length > 0), + map((arr: Array<[string, string|null]>) => { + + for (const [source, url] of arr) { + fetch(url, constantService.getFetchOption()) + .then(res => res.json()) + .then(json => pluginService.launchNewWidget(json)) + .catch(this.log.error) + } + + // clear init manifest + return { + type: PLUGINSTORE_ACTION_TYPES.CLEAR_INIT_PLUGIN, + } + }), + ) + } +} diff --git a/src/services/localFile.service.ts b/src/services/localFile.service.ts index 0333438d1b50c59cb4ab0629853e505c33c2fec3..ae9b7dd55f88eb85df350b72d277725f88f447fd 100644 --- a/src/services/localFile.service.ts +++ b/src/services/localFile.service.ts @@ -1,7 +1,7 @@ import { Injectable } from "@angular/core"; import { Store } from "@ngrx/store"; -import { SNACKBAR_MESSAGE } from "./state/uiState.store"; import { KgSingleDatasetService } from "src/ui/databrowserModule/kgSingleDatasetService.service"; +import { SNACKBAR_MESSAGE } from "./state/uiState.store"; import { IavRootStoreInterface } from "./stateStore.service"; /** @@ -9,7 +9,7 @@ import { IavRootStoreInterface } from "./stateStore.service"; */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class LocalFileService { @@ -18,14 +18,14 @@ export class LocalFileService { constructor( private store: Store<IavRootStoreInterface>, - private singleDsService: KgSingleDatasetService - ){ + private singleDsService: KgSingleDatasetService, + ) { } private niiUrl - public handleFileDrop(files: File[]){ + public handleFileDrop(files: File[]) { try { this.validateDrop(files) for (const file of files) { @@ -42,17 +42,17 @@ export class LocalFileService { } catch (e) { this.store.dispatch({ type: SNACKBAR_MESSAGE, - snackbarMessage: `Opening local NIFTI error: ${e.toString()}` + snackbarMessage: `Opening local NIFTI error: ${e.toString()}`, }) } } - private getExtension(filename:string) { + private getExtension(filename: string) { const match = /(\.\w*?)$/i.exec(filename) return (match && match[1]) || '' } - private validateDrop(files: File[]){ + private validateDrop(files: File[]) { if (files.length !== 1) { throw new Error('Interactive atlas viewer currently only supports drag and drop of one file at a time') } @@ -64,14 +64,14 @@ export class LocalFileService { } } - private handleNiiFile(file: File){ + private handleNiiFile(file: File) { if (this.niiUrl) { URL.revokeObjectURL(this.niiUrl) } this.niiUrl = URL.createObjectURL(file) this.singleDsService.showNewNgLayer({ - url: this.niiUrl + url: this.niiUrl, }) this.showLocalWarning() @@ -80,7 +80,7 @@ export class LocalFileService { private showLocalWarning() { this.store.dispatch({ type: SNACKBAR_MESSAGE, - snackbarMessage: `Warning: sharing URL will not share the loaded local file` + snackbarMessage: `Warning: sharing URL will not share the loaded local file`, }) } } @@ -90,5 +90,5 @@ const GII = '.gii' const SUPPORTED_EXT = [ NII, - GII -] \ No newline at end of file + GII, +] diff --git a/src/services/logging.service.ts b/src/services/logging.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..718b96a71d3091c61e74cd9904100317bcac4006 --- /dev/null +++ b/src/services/logging.service.ts @@ -0,0 +1,20 @@ +// tslint:disable:no-console + +import { Injectable } from "@angular/core"; + +@Injectable({ + providedIn: 'root', +}) + +export class LoggingService { + private loggingFlag: boolean = !PRODUCTION + public log(...arg) { + if (this.loggingFlag) { console.log(...arg) } + } + public warn(...arg) { + if (this.loggingFlag) { console.warn(...arg) } + } + public error(...arg) { + if (this.loggingFlag) { console.error(...arg) } + } +} diff --git a/src/services/state/dataStore.store.ts b/src/services/state/dataStore.store.ts index 941d10916773a7e5f6961ecb9e1894868bd38467..237e5cb36127f4f8f12c6f4f36b5b1c3e96adddf 100644 --- a/src/services/state/dataStore.store.ts +++ b/src/services/state/dataStore.store.ts @@ -4,38 +4,38 @@ import { Action } from '@ngrx/store' * TODO merge with databrowser.usereffect.ts */ -export interface StateInterface{ - fetchedDataEntries: DataEntry[] - favDataEntries: DataEntry[] - fetchedSpatialData: DataEntry[] +export interface IStateInterface { + fetchedDataEntries: IDataEntry[] + favDataEntries: IDataEntry[] + fetchedSpatialData: IDataEntry[] } export const defaultState = { fetchedDataEntries: [], favDataEntries: [], - fetchedSpatialData: [] + fetchedSpatialData: [], } -export const getStateStore = ({ state: state = defaultState } = {}) => (prevState:StateInterface = state, action:Partial<ActionInterface>) => { +export const getStateStore = ({ state: state = defaultState } = {}) => (prevState: IStateInterface = state, action: Partial<IActionInterface>) => { - switch (action.type){ + switch (action.type) { case FETCHED_DATAENTRIES: { return { ...prevState, - fetchedDataEntries : action.fetchedDataEntries + fetchedDataEntries : action.fetchedDataEntries, } } - case FETCHED_SPATIAL_DATA :{ + case FETCHED_SPATIAL_DATA : { return { ...prevState, - fetchedSpatialData : action.fetchedDataEntries + fetchedSpatialData : action.fetchedDataEntries, } } case ACTION_TYPES.UPDATE_FAV_DATASETS: { const { favDataEntries = [] } = action return { ...prevState, - favDataEntries + favDataEntries, } } default: return prevState @@ -44,45 +44,45 @@ export const getStateStore = ({ state: state = defaultState } = {}) => (prevStat // must export a named function for aot compilation // see https://github.com/angular/angular/issues/15587 -// https://github.com/amcdnl/ngrx-actions/issues/23 +// https://github.com/amcdnl/ngrx-actions/issues/23 // or just google for: // // angular function expressions are not supported in decorators const defaultStateStore = getStateStore() -export function stateStore(state, action){ +export function stateStore(state, action) { return defaultStateStore(state, action) } -export interface ActionInterface extends Action{ - favDataEntries: DataEntry[] - fetchedDataEntries : DataEntry[] - fetchedSpatialData : DataEntry[] +export interface IActionInterface extends Action { + favDataEntries: IDataEntry[] + fetchedDataEntries: IDataEntry[] + fetchedSpatialData: IDataEntry[] } export const FETCHED_DATAENTRIES = 'FETCHED_DATAENTRIES' export const FETCHED_SPATIAL_DATA = `FETCHED_SPATIAL_DATA` -export interface Activity{ +export interface IActivity { methods: string[] preparation: string[] protocols: string[ ] } -export interface DataEntry{ - activity: Activity[] +export interface IDataEntry { + activity: IActivity[] name: string description: string license: string[] licenseInfo: string[] - parcellationRegion: ParcellationRegion[] + parcellationRegion: IParcellationRegion[] formats: string[] custodians: string[] contributors: string[] - referenceSpaces: ReferenceSpace[] - files : File[] - publications: Publication[] + referenceSpaces: IReferenceSpace[] + files: File[] + publications: IPublication[] embargoStatus: string[] methods: string[] @@ -99,71 +99,71 @@ export interface DataEntry{ fullId: string } -export interface ParcellationRegion { +export interface IParcellationRegion { id?: string name: string } -export interface ReferenceSpace { +export interface IReferenceSpace { name: string } -export interface Publication{ +export interface IPublication { name: string - doi : string - cite : string + doi: string + cite: string } -export interface Property{ - description : string - publications : Publication[] +export interface IProperty { + description: string + publications: IPublication[] } -export interface Landmark{ - type : string //e.g. sEEG recording site, etc - name : string - templateSpace : string // possibily inherited from LandmarkBundle (?) - geometry : PointLandmarkGeometry | PlaneLandmarkGeometry | OtherLandmarkGeometry - properties : Property - files : File[] +export interface ILandmark { + type: string // e.g. sEEG recording site, etc + name: string + templateSpace: string // possibily inherited from LandmarkBundle (?) + geometry: IPointLandmarkGeometry | IPlaneLandmarkGeometry | IOtherLandmarkGeometry + properties: IProperty + files: File[] } -export interface DataStateInterface{ - fetchedDataEntries : DataEntry[] +export interface IDataStateInterface { + fetchedDataEntries: IDataEntry[] /** * Map that maps parcellation name to a Map, which maps datasetname to Property Object */ - fetchedMetadataMap : Map<string,Map<string,{properties:Property}>> + fetchedMetadataMap: Map<string, Map<string, {properties: IProperty}>> } -export interface PointLandmarkGeometry extends LandmarkGeometry{ - position : [number, number, number] +export interface IPointLandmarkGeometry extends ILandmarkGeometry { + position: [number, number, number] } -export interface PlaneLandmarkGeometry extends LandmarkGeometry{ +export interface IPlaneLandmarkGeometry extends ILandmarkGeometry { // corners have to be CW or CCW (no zigzag) - corners : [[number, number, number],[number, number, number],[number, number, number],[number, number, number]] + corners: [[number, number, number], [number, number, number], [number, number, number], [number, number, number]] } -export interface OtherLandmarkGeometry extends LandmarkGeometry{ - vertices: [number, number, number][] - meshIdx: [number,number,number][] +export interface IOtherLandmarkGeometry extends ILandmarkGeometry { + vertices: Array<[number, number, number]> + meshIdx: Array<[number, number, number]> } -interface LandmarkGeometry{ - type : 'point' | 'plane' - space? : 'voxel' | 'real' +interface ILandmarkGeometry { + type: 'point' | 'plane' + space?: 'voxel' | 'real' } -export interface File{ +export interface IFile { name: string absolutePath: string byteSize: number contentType: string, } -export interface ViewerPreviewFile{ +export interface ViewerPreviewFile { name: string filename: string mimetype: string @@ -172,7 +172,7 @@ export interface ViewerPreviewFile{ position?: any } -export interface FileSupplementData{ +export interface IFileSupplementData { data: any } @@ -180,7 +180,7 @@ const ACTION_TYPES = { FAV_DATASET: `FAV_DATASET`, UPDATE_FAV_DATASETS: `UPDATE_FAV_DATASETS`, UNFAV_DATASET: 'UNFAV_DATASET', - TOGGLE_FAV_DATASET: 'TOGGLE_FAV_DATASET' + TOGGLE_FAV_DATASET: 'TOGGLE_FAV_DATASET', } -export const DATASETS_ACTIONS_TYPES = ACTION_TYPES \ No newline at end of file +export const DATASETS_ACTIONS_TYPES = ACTION_TYPES diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts index c0b07d0161acbbe9dff64a5679e5c02cd4417862..4c5df81b315efe45b5cc3ba932ab0fe15dbb6ec0 100644 --- a/src/services/state/ngViewerState.store.ts +++ b/src/services/state/ngViewerState.store.ts @@ -1,18 +1,18 @@ -import { Action, Store, select } from '@ngrx/store' import { Injectable, OnDestroy } from '@angular/core'; -import { Observable, combineLatest, fromEvent, Subscription } from 'rxjs'; -import { Effect, Actions, ofType } from '@ngrx/effects'; -import { withLatestFrom, map, distinctUntilChanged, scan, shareReplay, filter, mapTo } from 'rxjs/operators'; +import { Actions, Effect, ofType } from '@ngrx/effects'; +import { Action, select, Store } from '@ngrx/store' +import { combineLatest, fromEvent, Observable, Subscription } from 'rxjs'; +import { distinctUntilChanged, filter, map, mapTo, scan, shareReplay, withLatestFrom } from 'rxjs/operators'; import { AtlasViewerConstantsServices } from 'src/atlasViewer/atlasViewer.constantService.service'; -import { SNACKBAR_MESSAGE } from './uiState.store'; import { getNgIds, IavRootStoreInterface } from '../stateStore.service'; +import { SNACKBAR_MESSAGE } from './uiState.store'; export const FOUR_PANEL = 'FOUR_PANEL' export const V_ONE_THREE = 'V_ONE_THREE' export const H_ONE_THREE = 'H_ONE_THREE' export const SINGLE_PANEL = 'SINGLE_PANEL' -export function mixNgLayers(oldLayers:NgLayerInterface[], newLayers:NgLayerInterface|NgLayerInterface[]): NgLayerInterface[]{ +export function mixNgLayers(oldLayers: INgLayerInterface[], newLayers: INgLayerInterface|INgLayerInterface[]): INgLayerInterface[] { if (newLayers instanceof Array) { return oldLayers.concat(newLayers) } else { @@ -20,14 +20,14 @@ export function mixNgLayers(oldLayers:NgLayerInterface[], newLayers:NgLayerInter ...newLayers, ...( newLayers.mixability === 'nonmixable' && oldLayers.findIndex(l => l.mixability === 'nonmixable') >= 0 ? {visible: false} - : {}) + : {}), }) } } -export interface StateInterface{ - layers : NgLayerInterface[] - forceShowSegment : boolean | null +export interface StateInterface { + layers: INgLayerInterface[] + forceShowSegment: boolean | null nehubaReady: boolean panelMode: string panelOrder: string @@ -36,43 +36,43 @@ export interface StateInterface{ showZoomlevel: boolean } -export interface ActionInterface extends Action{ - layer : NgLayerInterface - layers : NgLayerInterface[] - forceShowSegment : boolean +export interface ActionInterface extends Action { + layer: INgLayerInterface + layers: INgLayerInterface[] + forceShowSegment: boolean nehubaReady: boolean payload: any } -export const defaultState:StateInterface = { - layers:[], - forceShowSegment:null, +export const defaultState: StateInterface = { + layers: [], + forceShowSegment: null, nehubaReady: false, panelMode: FOUR_PANEL, panelOrder: `0123`, showSubstrate: null, - showZoomlevel: null + showZoomlevel: null, } -export const getStateStore = ({ state = defaultState } = {}) => (prevState:StateInterface = state, action:ActionInterface):StateInterface => { - switch(action.type){ +export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: ActionInterface): StateInterface => { + switch (action.type) { case ACTION_TYPES.SET_PANEL_ORDER: { const { payload } = action const { panelOrder } = payload return { ...prevState, - panelOrder + panelOrder, } } case ACTION_TYPES.SWITCH_PANEL_MODE: { const { payload } = action const { panelMode } = payload - if (SUPPORTED_PANEL_MODES.indexOf(panelMode) < 0) return prevState + if (SUPPORTED_PANEL_MODES.indexOf(panelMode) < 0) { return prevState } return { ...prevState, - panelMode + panelMode, } } case ADD_NG_LAYER: @@ -88,9 +88,9 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State /* this configuration allows the addition of multiple non mixables */ // layers : prevState.layers.map(l => mapLayer(l, action.layer)).concat(action.layer) - layers : mixNgLayers(prevState.layers, action.layer) - - // action.layer.constructor === Array + layers : mixNgLayers(prevState.layers, action.layer), + + // action.layer.constructor === Array // ? prevState.layers.concat(action.layer) // : prevState.layers.concat({ // ...action.layer, @@ -98,25 +98,25 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State // ? {visible: false} // : {}) // }) - } + } case REMOVE_NG_LAYERS: const { layers } = action const layerNameSet = new Set(layers.map(l => l.name)) return { ...prevState, - layers: prevState.layers.filter(l => !layerNameSet.has(l.name)) + layers: prevState.layers.filter(l => !layerNameSet.has(l.name)), } case REMOVE_NG_LAYER: return { ...prevState, - layers : prevState.layers.filter(l => l.name !== action.layer.name) + layers : prevState.layers.filter(l => l.name !== action.layer.name), } case SHOW_NG_LAYER: return { ...prevState, layers : prevState.layers.map(l => l.name === action.layer.name ? { ...l, visible: true } - : l) + : l), } case HIDE_NG_LAYER: return { @@ -124,18 +124,18 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State layers : prevState.layers.map(l => l.name === action.layer.name ? { ...l, visible: false } - : l) + : l), } case FORCE_SHOW_SEGMENT: return { ...prevState, - forceShowSegment : action.forceShowSegment + forceShowSegment : action.forceShowSegment, } - case NEHUBA_READY: + case NEHUBA_READY: const { nehubaReady } = action return { ...prevState, - nehubaReady + nehubaReady, } default: return prevState } @@ -143,22 +143,22 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State // must export a named function for aot compilation // see https://github.com/angular/angular/issues/15587 -// https://github.com/amcdnl/ngrx-actions/issues/23 +// https://github.com/amcdnl/ngrx-actions/issues/23 // or just google for: // // angular function expressions are not supported in decorators const defaultStateStore = getStateStore() -export function stateStore(state, action){ +export function stateStore(state, action) { return defaultStateStore(state, action) } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class NgViewerUseEffect implements OnDestroy{ +export class NgViewerUseEffect implements OnDestroy { @Effect() public toggleMaximiseMode$: Observable<any> @@ -188,11 +188,11 @@ export class NgViewerUseEffect implements OnDestroy{ constructor( private actions: Actions, private store$: Store<IavRootStoreInterface>, - private constantService: AtlasViewerConstantsServices - ){ + private constantService: AtlasViewerConstantsServices, + ) { const toggleMaxmimise$ = this.actions.pipe( ofType(ACTION_TYPES.TOGGLE_MAXIMISE), - shareReplay(1) + shareReplay(1), ) this.panelOrder$ = this.store$.pipe( @@ -214,18 +214,18 @@ export class NgViewerUseEffect implements OnDestroy{ return { type: ACTION_TYPES.SET_PANEL_ORDER, payload: { - panelOrder: [...panelOrder.slice(1), ...panelOrder.slice(0,1)].join('') - } + panelOrder: [...panelOrder.slice(1), ...panelOrder.slice(0, 1)].join(''), + }, } - }) + }), ) this.maximiseOrder$ = toggleMaxmimise$.pipe( withLatestFrom( combineLatest( this.panelOrder$, - this.panelMode$ - ) + this.panelMode$, + ), ), filter(([_action, [_panelOrder, panelMode]]) => panelMode !== SINGLE_PANEL), map(([ action, [ oldPanelOrder ] ]) => { @@ -236,32 +236,32 @@ export class NgViewerUseEffect implements OnDestroy{ return { type: ACTION_TYPES.SET_PANEL_ORDER, payload: { - panelOrder - } + panelOrder, + }, } - }) + }), ) this.unmaximiseOrder$ = toggleMaxmimise$.pipe( withLatestFrom( combineLatest( this.panelOrder$, - this.panelMode$ - ) + this.panelMode$, + ), ), scan((acc, curr) => { const [action, [panelOrders, panelMode]] = curr return [{ - action, + action, panelOrders, - panelMode + panelMode, }, ...acc.slice(0, 1)] }, [] as any[]), filter(([ { panelMode } ]) => panelMode === SINGLE_PANEL), map(arr => { const { action, - panelOrders + panelOrders, } = arr[0] const { @@ -278,17 +278,17 @@ export class NgViewerUseEffect implements OnDestroy{ return { type: ACTION_TYPES.SET_PANEL_ORDER, payload: { - panelOrder - } + panelOrder, + }, } - }) + }), ) - const scanFn = (acc: string[], curr: string):string[] => [curr, ...acc.slice(0,1)] + const scanFn = (acc: string[], curr: string): string[] => [curr, ...acc.slice(0, 1)] this.toggleMaximiseMode$ = toggleMaxmimise$.pipe( withLatestFrom(this.panelMode$.pipe( - scan(scanFn, []) + scan(scanFn, []), )), map(([ _, panelModes ]) => { return { @@ -296,23 +296,23 @@ export class NgViewerUseEffect implements OnDestroy{ payload: { panelMode: panelModes[0] === SINGLE_PANEL ? (panelModes[1] || FOUR_PANEL) - : SINGLE_PANEL - } + : SINGLE_PANEL, + }, } - }) + }), ) this.toggleMaximiseCycleMessage$ = combineLatest( this.toggleMaximiseMode$, - this.constantService.useMobileUI$ + this.constantService.useMobileUI$, ).pipe( filter(([_, useMobileUI]) => !useMobileUI), map(([toggleMaximiseMode, _]) => toggleMaximiseMode), filter(({ payload }) => payload.panelMode && payload.panelMode === SINGLE_PANEL), mapTo({ type: SNACKBAR_MESSAGE, - snackbarMessage: this.constantService.cyclePanelMessage - }) + snackbarMessage: this.constantService.cyclePanelMessage, + }), ) this.spacebarListener$ = fromEvent(document.body, 'keydown', { capture: true }).pipe( @@ -320,8 +320,8 @@ export class NgViewerUseEffect implements OnDestroy{ withLatestFrom(this.panelMode$), filter(([_ , panelMode]) => panelMode === SINGLE_PANEL), mapTo({ - type: ACTION_TYPES.CYCLE_VIEWS - }) + type: ACTION_TYPES.CYCLE_VIEWS, + }), ) /** @@ -331,7 +331,7 @@ export class NgViewerUseEffect implements OnDestroy{ select('viewerState'), select('templateSelected'), map(templateSelected => { - if (!templateSelected) return [] + if (!templateSelected) { return [] } const { ngId , otherNgIds = []} = templateSelected @@ -341,9 +341,9 @@ export class NgViewerUseEffect implements OnDestroy{ ...templateSelected.parcellations.reduce((acc, curr) => { return acc.concat([ curr.ngId, - ...getNgIds(curr.regions) + ...getNgIds(curr.regions), ]) - }, []) + }, []), ] }), /** @@ -353,12 +353,12 @@ export class NgViewerUseEffect implements OnDestroy{ /** * remove falsy values */ - map(arr => arr.filter(v => !!v)) + map(arr => arr.filter(v => !!v)), ) const allLoadedNgLayers$ = this.store$.pipe( select('viewerState'), - select('loadedNgLayers') + select('loadedNgLayers'), ) this.removeAllNonBaseLayers$ = this.actions.pipe( @@ -366,8 +366,8 @@ export class NgViewerUseEffect implements OnDestroy{ withLatestFrom( combineLatest( baseNgLayerName$, - allLoadedNgLayers$ - ) + allLoadedNgLayers$, + ), ), map(([_, [baseNgLayerNames, loadedNgLayers] ]) => { const baseNameSet = new Set(baseNgLayerNames) @@ -376,14 +376,14 @@ export class NgViewerUseEffect implements OnDestroy{ map(layers => { return { type: REMOVE_NG_LAYERS, - layers + layers, } - }) + }), ) } - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } @@ -397,13 +397,13 @@ export const HIDE_NG_LAYER = 'HIDE_NG_LAYER' export const FORCE_SHOW_SEGMENT = `FORCE_SHOW_SEGMENT` export const NEHUBA_READY = `NEHUBA_READY` -export interface NgLayerInterface{ - name : string - source : string - mixability : string // base | mixable | nonmixable - visible? : boolean - shader? : string - transform? : any +export interface INgLayerInterface { + name: string + source: string + mixability: string // base | mixable | nonmixable + visible?: boolean + shader?: string + transform?: any } const ACTION_TYPES = { @@ -413,7 +413,7 @@ const ACTION_TYPES = { TOGGLE_MAXIMISE: 'TOGGLE_MAXIMISE', CYCLE_VIEWS: 'CYCLE_VIEWS', - REMOVE_ALL_NONBASE_LAYERS: `REMOVE_ALL_NONBASE_LAYERS` + REMOVE_ALL_NONBASE_LAYERS: `REMOVE_ALL_NONBASE_LAYERS`, } export const SUPPORTED_PANEL_MODES = [ @@ -423,5 +423,4 @@ export const SUPPORTED_PANEL_MODES = [ SINGLE_PANEL, ] - -export const NG_VIEWER_ACTION_TYPES = ACTION_TYPES \ No newline at end of file +export const NG_VIEWER_ACTION_TYPES = ACTION_TYPES diff --git a/src/services/state/pluginState.store.ts b/src/services/state/pluginState.store.ts index e4319567792737d4474c36301c1a993678481384..ae851e5c669471b5ee6e9d2d972545695ed83c30 100644 --- a/src/services/state/pluginState.store.ts +++ b/src/services/state/pluginState.store.ts @@ -1,46 +1,46 @@ import { Action } from '@ngrx/store' export const defaultState: StateInterface = { - initManifests: [] + initManifests: [], } -export interface StateInterface{ - initManifests : [ string, string|null ][] +export interface StateInterface { + initManifests: Array<[ string, string|null ]> } -export interface ActionInterface extends Action{ +export interface ActionInterface extends Action { manifest: { - name : string, - initManifestUrl : string | null + name: string, + initManifestUrl: string | null, } } export const ACTION_TYPES = { SET_INIT_PLUGIN: `SET_INIT_PLUGIN`, - CLEAR_INIT_PLUGIN: 'CLEAR_INIT_PLUGIN' + CLEAR_INIT_PLUGIN: 'CLEAR_INIT_PLUGIN', } export const CONSTANTS = { - INIT_MANIFEST_SRC: 'INIT_MANIFEST_SRC' + INIT_MANIFEST_SRC: 'INIT_MANIFEST_SRC', } -export const getStateStore = ({ state = defaultState } = {}) => (prevState:StateInterface = state, action:ActionInterface):StateInterface => { - switch(action.type){ +export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: ActionInterface): StateInterface => { + switch (action.type) { case ACTION_TYPES.SET_INIT_PLUGIN: const newMap = new Map(prevState.initManifests ) // reserved source label for init manifest - if (action.manifest.name !== CONSTANTS.INIT_MANIFEST_SRC) newMap.set(action.manifest.name, action.manifest.initManifestUrl) + if (action.manifest.name !== CONSTANTS.INIT_MANIFEST_SRC) { newMap.set(action.manifest.name, action.manifest.initManifestUrl) } return { ...prevState, - initManifests: Array.from(newMap) + initManifests: Array.from(newMap), } case ACTION_TYPES.CLEAR_INIT_PLUGIN: const { initManifests } = prevState const newManifests = initManifests.filter(([source]) => source !== CONSTANTS.INIT_MANIFEST_SRC) return { ...prevState, - initManifests: newManifests + initManifests: newManifests, } default: return prevState @@ -49,13 +49,13 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State // must export a named function for aot compilation // see https://github.com/angular/angular/issues/15587 -// https://github.com/amcdnl/ngrx-actions/issues/23 +// https://github.com/amcdnl/ngrx-actions/issues/23 // or just google for: // // angular function expressions are not supported in decorators const defaultStateStore = getStateStore() -export function stateStore(state, action){ +export function stateStore(state, action) { return defaultStateStore(state, action) } diff --git a/src/services/state/uiState.store.ts b/src/services/state/uiState.store.ts index 8e962f7bd0d2075eee12e39b8a89bc5feaca19a3..4668216887913724442444cd3282de7ecfcb2268 100644 --- a/src/services/state/uiState.store.ts +++ b/src/services/state/uiState.store.ts @@ -1,16 +1,16 @@ -import {Action, select, Store} from '@ngrx/store' import {Injectable, TemplateRef} from '@angular/core'; +import {Action, select, Store} from '@ngrx/store' -import { LOCAL_STORAGE_CONST, COOKIE_VERSION, KG_TOS_VERSION } from 'src/util/constants' -import {GENERAL_ACTION_TYPES, IavRootStoreInterface} from '../stateStore.service' import {Effect} from "@ngrx/effects"; import {Observable} from "rxjs"; import {filter, map, mapTo, scan, startWith} from "rxjs/operators"; +import { COOKIE_VERSION, KG_TOS_VERSION, LOCAL_STORAGE_CONST } from 'src/util/constants' +import {GENERAL_ACTION_TYPES, IavRootStoreInterface} from '../stateStore.service' export const defaultState: StateInterface = { mouseOverSegments: [], mouseOverSegment: null, - + mouseOverLandmark: null, mouseOverUserLandmark: null, @@ -27,33 +27,33 @@ export const defaultState: StateInterface = { * replace with server side logic (?) */ agreedCookies: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_COOKIE) === COOKIE_VERSION, - agreedKgTos: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_KG_TOS) === KG_TOS_VERSION + agreedKgTos: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_KG_TOS) === KG_TOS_VERSION, } -export const getStateStore = ({ state = defaultState } = {}) => (prevState:StateInterface = state,action:ActionInterface) => { - switch(action.type){ +export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: ActionInterface) => { + switch (action.type) { case MOUSE_OVER_SEGMENTS: const { segments } = action return { ...prevState, - mouseOverSegments: segments + mouseOverSegments: segments, } case MOUSE_OVER_SEGMENT: return { ...prevState, - mouseOverSegment : action.segment + mouseOverSegment : action.segment, } case MOUSEOVER_USER_LANDMARK: const { payload = {} } = action const { userLandmark: mouseOverUserLandmark = null } = payload return { ...prevState, - mouseOverUserLandmark + mouseOverUserLandmark, } case MOUSE_OVER_LANDMARK: return { ...prevState, - mouseOverLandmark : action.landmark + mouseOverLandmark : action.landmark, } case SNACKBAR_MESSAGE: const { snackbarMessage } = action @@ -62,45 +62,45 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State */ return { ...prevState, - snackbarMessage: Symbol(snackbarMessage) + snackbarMessage: Symbol(snackbarMessage), } case OPEN_SIDE_PANEL: return { ...prevState, - sidePanelIsOpen: true + sidePanelIsOpen: true, } case CLOSE_SIDE_PANEL: return { ...prevState, - sidePanelIsOpen: false + sidePanelIsOpen: false, } case EXPAND_SIDE_PANEL_CURRENT_VIEW: return { ...prevState, - sidePanelExploreCurrentViewIsOpen: true + sidePanelExploreCurrentViewIsOpen: true, } case COLLAPSE_SIDE_PANEL_CURRENT_VIEW: return { ...prevState, - sidePanelExploreCurrentViewIsOpen: false + sidePanelExploreCurrentViewIsOpen: false, } case SHOW_SIDE_PANEL_DATASET_LIST: return { ...prevState, - sidePanelCurrentViewContent: 'Dataset' + sidePanelCurrentViewContent: 'Dataset', } case SHOW_SIDE_PANEL_CONNECTIVITY: return { ...prevState, - sidePanelCurrentViewContent: 'Connectivity' + sidePanelCurrentViewContent: 'Connectivity', } case HIDE_SIDE_PANEL_CONNECTIVITY: return { ...prevState, - sidePanelCurrentViewContent: 'Dataset' + sidePanelCurrentViewContent: 'Dataset', } case AGREE_COOKIE: /** @@ -109,7 +109,7 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State localStorage.setItem(LOCAL_STORAGE_CONST.AGREE_COOKIE, COOKIE_VERSION) return { ...prevState, - agreedCookies: true + agreedCookies: true, } case AGREE_KG_TOS: /** @@ -118,13 +118,13 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State localStorage.setItem(LOCAL_STORAGE_CONST.AGREE_KG_TOS, KG_TOS_VERSION) return { ...prevState, - agreedKgTos: true + agreedKgTos: true, } case SHOW_BOTTOM_SHEET: const { bottomSheetTemplate } = action return { ...prevState, - bottomSheetTemplate + bottomSheetTemplate, } default: return prevState @@ -140,17 +140,17 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State const defaultStateStore = getStateStore() -export function stateStore(state, action){ +export function stateStore(state, action) { return defaultStateStore(state, action) } -export interface StateInterface{ - mouseOverSegments: { +export interface StateInterface { + mouseOverSegments: Array<{ layer: { - name: string + name: string, } - segment: any | null - }[] + segment: any | null, + }> sidePanelIsOpen: boolean sidePanelCurrentViewContent: 'Connectivity' | 'Dataset' | null sidePanelExploreCurrentViewIsOpen: boolean @@ -169,16 +169,16 @@ export interface StateInterface{ bottomSheetTemplate: TemplateRef<any> } -export interface ActionInterface extends Action{ +export interface ActionInterface extends Action { segment: any | number landmark: any focusedSidePanel?: string - segments?:{ + segments?: Array<{ layer: { - name: string + name: string, } - segment: any | null - }[], + segment: any | null, + }>, snackbarMessage: string bottomSheetTemplate: TemplateRef<any> @@ -187,10 +187,10 @@ export interface ActionInterface extends Action{ } @Injectable({ - providedIn:'root' + providedIn: 'root', }) -export class UiStateUseEffect{ +export class UiStateUseEffect { private numRegionSelectedWithHistory$: Observable<any[]> @@ -206,21 +206,21 @@ export class UiStateUseEffect{ select('regionsSelected'), map(arr => arr.length), startWith(0), - scan((acc, curr) => [curr, ...acc], []) + scan((acc, curr) => [curr, ...acc], []), ) this.sidePanelOpen$ = this.numRegionSelectedWithHistory$.pipe( filter(([curr, prev]) => prev === 0 && curr > 0), mapTo({ - type: OPEN_SIDE_PANEL - }) + type: OPEN_SIDE_PANEL, + }), ) this.viewCurrentOpen$ = this.numRegionSelectedWithHistory$.pipe( filter(([curr, prev]) => prev === 0 && curr > 0), mapTo({ - type: EXPAND_SIDE_PANEL_CURRENT_VIEW - }) + type: EXPAND_SIDE_PANEL_CURRENT_VIEW, + }), ) } } @@ -243,4 +243,4 @@ export const AGREE_KG_TOS = `AGREE_KG_TOS` export const SHOW_KG_TOS = `SHOW_KG_TOS` export const SNACKBAR_MESSAGE = `SNACKBAR_MESSAGE` -export const SHOW_BOTTOM_SHEET = `SHOW_BOTTOM_SHEET` \ No newline at end of file +export const SHOW_BOTTOM_SHEET = `SHOW_BOTTOM_SHEET` diff --git a/src/services/state/userConfigState.store.ts b/src/services/state/userConfigState.store.ts index 95d414f4984a908e8fc9be6a3558e7ff1027fde1..fe2ddb16442f319280ebc075f3a0c8a83de29cb6 100644 --- a/src/services/state/userConfigState.store.ts +++ b/src/services/state/userConfigState.store.ts @@ -1,12 +1,12 @@ -import { Action, Store, select } from "@ngrx/store"; import { Injectable, OnDestroy } from "@angular/core"; import { Actions, Effect, ofType } from "@ngrx/effects"; -import { Observable, combineLatest, Subscription, from, of } from "rxjs"; -import { shareReplay, withLatestFrom, map, distinctUntilChanged, filter, take, switchMap, catchError, share } from "rxjs/operators"; -import { generateLabelIndexId, recursiveFindRegionWithLabelIndexId, IavRootStoreInterface } from "../stateStore.service"; -import { SELECT_REGIONS, NEWVIEWER, SELECT_PARCELLATION } from "./viewerState.store"; -import { DialogService } from "../dialogService.service"; +import { Action, select, Store } from "@ngrx/store"; +import { combineLatest, from, Observable, of, Subscription } from "rxjs"; +import { catchError, distinctUntilChanged, filter, map, share, shareReplay, switchMap, take, withLatestFrom } from "rxjs/operators"; import { LOCAL_STORAGE_CONST } from "src/util//constants"; +import { DialogService } from "../dialogService.service"; +import { generateLabelIndexId, IavRootStoreInterface, recursiveFindRegionWithLabelIndexId } from "../stateStore.service"; +import { NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS } from "./viewerState.store"; // Get around the problem of importing duplicated string (ACTION_TYPES), even using ES6 alias seems to trip up the compiler // TODO file bug and reverse @@ -14,11 +14,11 @@ import * as viewerConfigStore from './viewerConfig.store' const SET_MOBILE_UI = viewerConfigStore.ACTION_TYPES.SET_MOBILE_UI -export interface StateInterface{ +export interface StateInterface { savedRegionsSelection: RegionSelection[] } -export interface RegionSelection{ +export interface RegionSelection { templateSelected: any parcellationSelected: any regionsSelected: any[] @@ -29,7 +29,7 @@ export interface RegionSelection{ /** * for serialisation into local storage/database */ -interface SimpleRegionSelection{ +interface SimpleRegionSelection { id: string, name: string, tName: string, @@ -37,80 +37,80 @@ interface SimpleRegionSelection{ rSelected: string[] } -interface UserConfigAction extends Action{ +interface UserConfigAction extends Action { config?: Partial<StateInterface> payload?: any } export const defaultState: StateInterface = { - savedRegionsSelection: [] + savedRegionsSelection: [], } export const ACTION_TYPES = { UPDATE_REGIONS_SELECTIONS: `UPDATE_REGIONS_SELECTIONS`, - UPDATE_REGIONS_SELECTION:'UPDATE_REGIONS_SELECTION', + UPDATE_REGIONS_SELECTION: 'UPDATE_REGIONS_SELECTION', SAVE_REGIONS_SELECTION: `SAVE_REGIONS_SELECTIONN`, DELETE_REGIONS_SELECTION: 'DELETE_REGIONS_SELECTION', - LOAD_REGIONS_SELECTION: 'LOAD_REGIONS_SELECTION' + LOAD_REGIONS_SELECTION: 'LOAD_REGIONS_SELECTION', } export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: UserConfigAction) => { - switch(action.type) { + switch (action.type) { case ACTION_TYPES.UPDATE_REGIONS_SELECTIONS: const { config = {} } = action const { savedRegionsSelection } = config return { ...prevState, - savedRegionsSelection + savedRegionsSelection, } default: return { - ...prevState + ...prevState, } } } // must export a named function for aot compilation // see https://github.com/angular/angular/issues/15587 -// https://github.com/amcdnl/ngrx-actions/issues/23 +// https://github.com/amcdnl/ngrx-actions/issues/23 // or just google for: // // angular function expressions are not supported in decorators const defaultStateStore = getStateStore() -export function stateStore(state, action){ +export function stateStore(state, action) { return defaultStateStore(state, action) } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class UserConfigStateUseEffect implements OnDestroy{ +export class UserConfigStateUseEffect implements OnDestroy { private subscriptions: Subscription[] = [] constructor( private actions$: Actions, private store$: Store<IavRootStoreInterface>, - private dialogService: DialogService - ){ + private dialogService: DialogService, + ) { const viewerState$ = this.store$.pipe( select('viewerState'), - shareReplay(1) + shareReplay(1), ) this.parcellationSelected$ = viewerState$.pipe( select('parcellationSelected'), distinctUntilChanged(), - share() + share(), ) this.tprSelected$ = combineLatest( viewerState$.pipe( select('templateSelected'), - distinctUntilChanged() + distinctUntilChanged(), ), this.parcellationSelected$, viewerState$.pipe( @@ -119,27 +119,27 @@ export class UserConfigStateUseEffect implements OnDestroy{ * TODO * distinct selectedRegions */ - ) + ), ).pipe( map(([ templateSelected, parcellationSelected, regionsSelected ]) => { return { - templateSelected, parcellationSelected, regionsSelected + templateSelected, parcellationSelected, regionsSelected, } }), - shareReplay(1) + shareReplay(1), ) this.savedRegionsSelections$ = this.store$.pipe( select('userConfigState'), select('savedRegionsSelection'), - shareReplay(1) + shareReplay(1), ) this.onSaveRegionsSelection$ = this.actions$.pipe( ofType(ACTION_TYPES.SAVE_REGIONS_SELECTION), withLatestFrom(this.tprSelected$), withLatestFrom(this.savedRegionsSelections$), - + map(([[action, tprSelected], savedRegionsSelection]) => { const { payload = {} } = action as UserConfigAction const { name = 'Untitled' } = payload @@ -150,15 +150,15 @@ export class UserConfigStateUseEffect implements OnDestroy{ name, templateSelected, parcellationSelected, - regionsSelected + regionsSelected, } return { type: ACTION_TYPES.UPDATE_REGIONS_SELECTIONS, config: { - savedRegionsSelection: savedRegionsSelection.concat([newSavedRegionSelection]) - } + savedRegionsSelection: savedRegionsSelection.concat([newSavedRegionSelection]), + }, } as UserConfigAction - }) + }), ) this.onDeleteRegionsSelection$ = this.actions$.pipe( @@ -170,10 +170,10 @@ export class UserConfigStateUseEffect implements OnDestroy{ return { type: ACTION_TYPES.UPDATE_REGIONS_SELECTIONS, config: { - savedRegionsSelection: savedRegionsSelection.filter(srs => srs.id !== id) - } + savedRegionsSelection: savedRegionsSelection.filter(srs => srs.id !== id), + }, } - }) + }), ) this.onUpdateRegionsSelection$ = this.actions$.pipe( @@ -188,10 +188,10 @@ export class UserConfigStateUseEffect implements OnDestroy{ savedRegionsSelection: savedRegionsSelection .map(srs => srs.id === id ? { ...srs, ...rest } - : { ...srs }) - } + : { ...srs }), + }, } - }) + }), ) this.subscriptions.push( @@ -199,15 +199,15 @@ export class UserConfigStateUseEffect implements OnDestroy{ ofType(ACTION_TYPES.LOAD_REGIONS_SELECTION), map(action => { const { payload = {}} = action as UserConfigAction - const { savedRegionsSelection } : {savedRegionsSelection : RegionSelection} = payload + const { savedRegionsSelection }: {savedRegionsSelection: RegionSelection} = payload return savedRegionsSelection }), filter(val => !!val), withLatestFrom(this.tprSelected$), - switchMap(([savedRegionsSelection, { parcellationSelected, templateSelected, regionsSelected }]) => + switchMap(([savedRegionsSelection, { parcellationSelected, templateSelected, regionsSelected }]) => from(this.dialogService.getUserConfirm({ title: `Load region selection: ${savedRegionsSelection.name}`, - message: `This action would cause the viewer to navigate away from the current view. Proceed?` + message: `This action would cause the viewer to navigate away from the current view. Proceed?`, })).pipe( catchError((e, obs) => of(null)), map(() => { @@ -215,11 +215,11 @@ export class UserConfigStateUseEffect implements OnDestroy{ savedRegionsSelection, parcellationSelected, templateSelected, - regionsSelected + regionsSelected, } }), - filter(val => !!val) - ) + filter(val => !!val), + ), ), switchMap(({ savedRegionsSelection, parcellationSelected, templateSelected, regionsSelected }) => { if (templateSelected.name !== savedRegionsSelection.templateSelected.name ) { @@ -229,54 +229,54 @@ export class UserConfigStateUseEffect implements OnDestroy{ this.store$.dispatch({ type: NEWVIEWER, selectParcellation: savedRegionsSelection.parcellationSelected, - selectTemplate: savedRegionsSelection.templateSelected + selectTemplate: savedRegionsSelection.templateSelected, }) return this.parcellationSelected$.pipe( filter(p => p.updated), take(1), map(() => { return { - regionsSelected: savedRegionsSelection.regionsSelected + regionsSelected: savedRegionsSelection.regionsSelected, } - }) + }), ) } - + if (parcellationSelected.name !== savedRegionsSelection.parcellationSelected.name) { /** * parcellation different, dispatch SELECT_PARCELLATION */ - + this.store$.dispatch({ type: SELECT_PARCELLATION, - selectParcellation: savedRegionsSelection.parcellationSelected + selectParcellation: savedRegionsSelection.parcellationSelected, }) - return this.parcellationSelected$.pipe( + return this.parcellationSelected$.pipe( filter(p => p.updated), take(1), map(() => { return { - regionsSelected: savedRegionsSelection.regionsSelected + regionsSelected: savedRegionsSelection.regionsSelected, } - }) + }), ) } - return of({ - regionsSelected: savedRegionsSelection.regionsSelected + return of({ + regionsSelected: savedRegionsSelection.regionsSelected, }) - }) + }), ).subscribe(({ regionsSelected }) => { this.store$.dispatch({ type: SELECT_REGIONS, - selectRegions: regionsSelected + selectRegions: regionsSelected, }) - }) + }), ) this.subscriptions.push( this.store$.pipe( - select('viewerConfigState') + select('viewerConfigState'), ).subscribe(({ gpuLimit, animation }) => { if (gpuLimit) { @@ -285,7 +285,7 @@ export class UserConfigStateUseEffect implements OnDestroy{ if (typeof animation !== 'undefined' && animation !== null) { window.localStorage.setItem(LOCAL_STORAGE_CONST.ANIMATION, animation.toString()) } - }) + }), ) this.subscriptions.push( @@ -297,15 +297,15 @@ export class UserConfigStateUseEffect implements OnDestroy{ const { useMobileUI } = payload return useMobileUI }), - filter(bool => bool !== null) + filter(bool => bool !== null), ).subscribe((bool: boolean) => { window.localStorage.setItem(LOCAL_STORAGE_CONST.MOBILE_UI, JSON.stringify(bool)) - }) + }), ) this.subscriptions.push( this.actions$.pipe( - ofType(ACTION_TYPES.UPDATE_REGIONS_SELECTIONS) + ofType(ACTION_TYPES.UPDATE_REGIONS_SELECTIONS), ).subscribe(action => { const { config = {} } = action as UserConfigAction const { savedRegionsSelection } = config @@ -315,7 +315,7 @@ export class UserConfigStateUseEffect implements OnDestroy{ name, tName: templateSelected.name, pName: parcellationSelected.name, - rSelected: regionsSelected.map(({ ngId, labelIndex }) => generateLabelIndexId({ ngId, labelIndex })) + rSelected: regionsSelected.map(({ ngId, labelIndex }) => generateLabelIndexId({ ngId, labelIndex })), } as SimpleRegionSelection }) @@ -323,11 +323,11 @@ export class UserConfigStateUseEffect implements OnDestroy{ * TODO save server side on per user basis */ window.localStorage.setItem(LOCAL_STORAGE_CONST.SAVED_REGION_SELECTIONS, JSON.stringify(simpleSRSs)) - }) + }), ) const savedSRSsString = window.localStorage.getItem(LOCAL_STORAGE_CONST.SAVED_REGION_SELECTIONS) - const savedSRSs:SimpleRegionSelection[] = savedSRSsString && JSON.parse(savedSRSsString) + const savedSRSs: SimpleRegionSelection[] = savedSRSsString && JSON.parse(savedSRSsString) this.restoreSRSsFromStorage$ = viewerState$.pipe( filter(() => !!savedSRSs), @@ -342,22 +342,22 @@ export class UserConfigStateUseEffect implements OnDestroy{ parcellationSelected, id, name, - regionsSelected + regionsSelected, } as RegionSelection })), - filter(RSs => RSs.every(rs => rs.regionsSelected && rs.regionsSelected.every(r => !!r))), + filter(restoredSavedRegions => restoredSavedRegions.every(rs => rs.regionsSelected && rs.regionsSelected.every(r => !!r))), take(1), map(savedRegionsSelection => { return { type: ACTION_TYPES.UPDATE_REGIONS_SELECTIONS, - config: { savedRegionsSelection } + config: { savedRegionsSelection }, } - }) + }), ) } - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } @@ -365,7 +365,7 @@ export class UserConfigStateUseEffect implements OnDestroy{ /** * Temmplate Parcellation Regions selected */ - private tprSelected$: Observable<{templateSelected:any, parcellationSelected: any, regionsSelected: any[]}> + private tprSelected$: Observable<{templateSelected: any, parcellationSelected: any, regionsSelected: any[]}> private savedRegionsSelections$: Observable<any[]> private parcellationSelected$: Observable<any> diff --git a/src/services/state/viewerConfig.store.ts b/src/services/state/viewerConfig.store.ts index f2a660e673772e96aec4373de8d16c501d7ea339..425eaf25cc8c1a19e99b9ea66e94fb11cdf42ea8 100644 --- a/src/services/state/viewerConfig.store.ts +++ b/src/services/state/viewerConfig.store.ts @@ -1,13 +1,13 @@ import { Action } from "@ngrx/store"; import { LOCAL_STORAGE_CONST } from "src/util/constants"; -export interface StateInterface{ +export interface StateInterface { gpuLimit: number animation: boolean useMobileUI: boolean } -interface ViewerConfigurationAction extends Action{ +interface ViewerConfigurationAction extends Action { config: Partial<StateInterface>, payload: any } @@ -16,17 +16,17 @@ export const CONFIG_CONSTANTS = { /** * byets */ - gpuLimitMin: 1e8, + gpuLimitMin: 1e8, gpuLimitMax: 1e9, defaultGpuLimit: 1e9, - defaultAnimation: true + defaultAnimation: true, } export const ACTION_TYPES = { SET_ANIMATION: `SET_ANIMATION`, UPDATE_CONFIG: `UPDATE_CONFIG`, CHANGE_GPU_LIMIT: `CHANGE_GPU_LIMIT`, - SET_MOBILE_UI: 'SET_MOBILE_UI' + SET_MOBILE_UI: 'SET_MOBILE_UI', } // get gpu limit @@ -53,38 +53,38 @@ const getIsMobile = () => { /* https://stackoverflow.com/a/25394023/6059235 */ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i.test(ua) } -const useMobileUIStroageValue = window && window.localStorage && window.localStorage.getItem(LOCAL_STORAGE_CONST.MOBILE_UI) +const useMobileUIStroageValue = window && window.localStorage && window.localStorage.getItem(LOCAL_STORAGE_CONST.MOBILE_UI) export const defaultState: StateInterface = { animation, gpuLimit, - useMobileUI: (useMobileUIStroageValue && useMobileUIStroageValue === 'true') || getIsMobile() + useMobileUI: (useMobileUIStroageValue && useMobileUIStroageValue === 'true') || getIsMobile(), } -export const getStateStore = ({ state = defaultState } = {}) => (prevState:StateInterface = state, action:ViewerConfigurationAction) => { +export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: ViewerConfigurationAction) => { switch (action.type) { case ACTION_TYPES.SET_MOBILE_UI: const { payload } = action const { useMobileUI } = payload return { ...prevState, - useMobileUI + useMobileUI, } case ACTION_TYPES.UPDATE_CONFIG: return { ...prevState, - ...action.config + ...action.config, } case ACTION_TYPES.CHANGE_GPU_LIMIT: const newGpuLimit = Math.min( CONFIG_CONSTANTS.gpuLimitMax, Math.max( (prevState.gpuLimit || CONFIG_CONSTANTS.defaultGpuLimit) + action.payload.delta, - CONFIG_CONSTANTS.gpuLimitMin + CONFIG_CONSTANTS.gpuLimitMin, )) return { ...prevState, - gpuLimit: newGpuLimit + gpuLimit: newGpuLimit, } default: return prevState } @@ -92,13 +92,13 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:State // must export a named function for aot compilation // see https://github.com/angular/angular/issues/15587 -// https://github.com/amcdnl/ngrx-actions/issues/23 +// https://github.com/amcdnl/ngrx-actions/issues/23 // or just google for: // // angular function expressions are not supported in decorators const defaultStateStore = getStateStore() -export function stateStore(state, action){ +export function stateStore(state, action) { return defaultStateStore(state, action) } diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts index dc19ff5870977ce4d65c90158b3c5b462f27550a..45ece6088a477198bbd53ef67b559c2ecf5d94da 100644 --- a/src/services/state/viewerState.store.ts +++ b/src/services/state/viewerState.store.ts @@ -1,54 +1,56 @@ -import { Action, Store, select } from '@ngrx/store' -import { UserLandmark } from 'src/atlasViewer/atlasViewer.apiService.service'; -import { NgLayerInterface } from 'src/atlasViewer/atlasViewer.component'; import { Injectable } from '@angular/core'; import { Actions, Effect, ofType } from '@ngrx/effects'; -import { withLatestFrom, map, shareReplay, startWith, filter, distinctUntilChanged } from 'rxjs/operators'; +import { Action, select, Store } from '@ngrx/store' import { Observable } from 'rxjs'; -import { MOUSEOVER_USER_LANDMARK } from './uiState.store'; +import { distinctUntilChanged, filter, map, shareReplay, startWith, withLatestFrom } from 'rxjs/operators'; +import { IUserLandmark } from 'src/atlasViewer/atlasViewer.apiService.service'; +import { INgLayerInterface } from 'src/atlasViewer/atlasViewer.component'; +import { getViewer } from 'src/util/fn'; +import { LoggingService } from '../logging.service'; import { generateLabelIndexId, IavRootStoreInterface } from '../stateStore.service'; import { GENERAL_ACTION_TYPES } from '../stateStore.service' +import { MOUSEOVER_USER_LANDMARK } from './uiState.store'; -export interface StateInterface{ - fetchedTemplates : any[] +export interface StateInterface { + fetchedTemplates: any[] - templateSelected : any | null - parcellationSelected : any | null - regionsSelected : any[] + templateSelected: any | null + parcellationSelected: any | null + regionsSelected: any[] - landmarksSelected : any[] - userLandmarks : UserLandmark[] + landmarksSelected: any[] + userLandmarks: IUserLandmark[] - navigation : any | null - dedicatedView : string[] + navigation: any | null + dedicatedView: string[] - loadedNgLayers: NgLayerInterface[] + loadedNgLayers: INgLayerInterface[] connectivityRegion: string | null } -export interface ActionInterface extends Action{ - fetchedTemplate? : any[] +export interface ActionInterface extends Action { + fetchedTemplate?: any[] - selectTemplate? : any - selectParcellation? : any + selectTemplate?: any + selectParcellation?: any selectRegions?: any[] selectRegionIds: string[] - deselectRegions? : any[] - dedicatedView? : string + deselectRegions?: any[] + dedicatedView?: string - updatedParcellation? : any + updatedParcellation?: any - landmarks : UserLandmark[] - deselectLandmarks : UserLandmark[] + landmarks: IUserLandmark[] + deselectLandmarks: IUserLandmark[] - navigation? : any + navigation?: any payload: any connectivityRegion?: string } -export const defaultState:StateInterface = { +export const defaultState: StateInterface = { landmarksSelected : [], fetchedTemplates : [], @@ -59,11 +61,11 @@ export const defaultState:StateInterface = { navigation: null, parcellationSelected: null, templateSelected: null, - connectivityRegion: '' + connectivityRegion: '', } -export const getStateStore = ({ state = defaultState } = {}) => (prevState:Partial<StateInterface> = state, action:ActionInterface) => { - switch(action.type){ +export const getStateStore = ({ state = defaultState } = {}) => (prevState: Partial<StateInterface> = state, action: ActionInterface) => { + switch (action.type) { /** * TODO may be obsolete. test when nifti become available */ @@ -73,14 +75,14 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:Parti : [action.dedicatedView] return { ...prevState, - dedicatedView + dedicatedView, } case UNLOAD_DEDICATED_LAYER: return { ...prevState, dedicatedView : prevState.dedicatedView ? prevState.dedicatedView.filter(dv => dv !== action.dedicatedView) - : [] + : [], } case NEWVIEWER: const { selectParcellation: parcellation } = action @@ -91,32 +93,32 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:Parti templateSelected : action.selectTemplate, parcellationSelected : { ...parcellationWORegions, - regions: null + regions: null, }, // taken care of by effect.ts // regionsSelected : [], landmarksSelected : [], navigation : {}, - dedicatedView : null + dedicatedView : null, } case FETCHED_TEMPLATE : { return { ...prevState, - fetchedTemplates: prevState.fetchedTemplates.concat(action.fetchedTemplate) + fetchedTemplates: prevState.fetchedTemplates.concat(action.fetchedTemplate), } } case CHANGE_NAVIGATION : { return { ...prevState, - navigation : action.navigation + navigation : action.navigation, } } case SELECT_PARCELLATION : { - const { selectParcellation:parcellation } = action - const { regions, ...parcellationWORegions } = parcellation + const { selectParcellation: sParcellation } = action + const { regions, ...sParcellationWORegions } = sParcellation return { ...prevState, - parcellationSelected: parcellationWORegions, + parcellationSelected: sParcellationWORegions, // taken care of by effect.ts // regionsSelected: [] } @@ -127,53 +129,54 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:Parti ...prevState, parcellationSelected: { ...updatedParcellation, - updated: true - } + updated: true, + }, } } case SELECT_REGIONS: const { selectRegions } = action return { ...prevState, - regionsSelected: selectRegions + regionsSelected: selectRegions, } case DESELECT_LANDMARKS : { return { ...prevState, - landmarksSelected : prevState.landmarksSelected.filter(lm => action.deselectLandmarks.findIndex(dLm => dLm.name === lm.name) < 0) + landmarksSelected : prevState.landmarksSelected.filter(lm => action.deselectLandmarks.findIndex(dLm => dLm.name === lm.name) < 0), } } case SELECT_LANDMARKS : { return { ...prevState, - landmarksSelected : action.landmarks + landmarksSelected : action.landmarks, } } case USER_LANDMARKS : { return { ...prevState, - userLandmarks: action.landmarks - } + userLandmarks: action.landmarks, + } } /** * TODO * duplicated with ngViewerState.layers ? */ case NEHUBA_LAYER_CHANGED: { - if (!window['viewer']) { + const viewer = getViewer() + if (!viewer) { return { ...prevState, - loadedNgLayers: [] + loadedNgLayers: [], } } else { return { ...prevState, - loadedNgLayers: (window['viewer'].layerManager.managedLayers as any[]).map(obj => ({ + loadedNgLayers: (viewer.layerManager.managedLayers as any[]).map(obj => ({ name : obj.name, type : obj.initialSpecification.type, source : obj.sourceUrl, - visible : obj.visible - }) as NgLayerInterface) + visible : obj.visible, + }) as INgLayerInterface), } } } @@ -183,12 +186,12 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:Parti case SET_CONNECTIVITY_REGION: return { ...prevState, - connectivityRegion: action.connectivityRegion + connectivityRegion: action.connectivityRegion, } case CLEAR_CONNECTIVITY_REGION: return { ...prevState, - connectivityRegion: '' + connectivityRegion: '', } default : return prevState @@ -197,14 +200,14 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState:Parti // must export a named function for aot compilation // see https://github.com/angular/angular/issues/15587 -// https://github.com/amcdnl/ngrx-actions/issues/23 +// https://github.com/amcdnl/ngrx-actions/issues/23 // or just google for: // // angular function expressions are not supported in decorators const defaultStateStore = getStateStore() -export function stateStore(state, action){ +export function stateStore(state, action) { return defaultStateStore(state, action) } @@ -233,14 +236,15 @@ export const SET_CONNECTIVITY_REGION = `SET_CONNECTIVITY_REGION` export const CLEAR_CONNECTIVITY_REGION = `CLEAR_CONNECTIVITY_REGION` @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class ViewerStateUseEffect{ +export class ViewerStateUseEffect { constructor( private actions$: Actions, - private store$: Store<IavRootStoreInterface> - ){ + private store$: Store<IavRootStoreInterface>, + private log: LoggingService, + ) { this.currentLandmarks$ = this.store$.pipe( select('viewerState'), select('userLandmarks'), @@ -252,16 +256,16 @@ export class ViewerStateUseEffect{ withLatestFrom(this.currentLandmarks$), map(([action, currentLandmarks]) => { const { landmarkIds } = (action as ActionInterface).payload - for ( const rmId of landmarkIds ){ + for ( const rmId of landmarkIds ) { const idx = currentLandmarks.findIndex(({ id }) => id === rmId) - if (idx < 0) console.warn(`remove userlandmark with id ${rmId} does not exist`) + if (idx < 0) { this.log.warn(`remove userlandmark with id ${rmId} does not exist`) } } const removeSet = new Set(landmarkIds) return { type: USER_LANDMARKS, - landmarks: currentLandmarks.filter(({ id }) => !removeSet.has(id)) + landmarks: currentLandmarks.filter(({ id }) => !removeSet.has(id)), } - }) + }), ) this.addUserLandmarks$ = this.actions$.pipe( @@ -277,7 +281,7 @@ export class ViewerStateUseEffect{ for (const landmark of landmarks) { const { id } = landmark if (landmarkMap.has(id)) { - console.warn(`Attempting to add a landmark that already exists, id: ${id}`) + this.log.warn(`Attempting to add a landmark that already exists, id: ${id}`) } else { landmarkMap.set(id, landmark) } @@ -285,9 +289,9 @@ export class ViewerStateUseEffect{ const userLandmarks = Array.from(landmarkMap).map(([id, landmark]) => landmark) return { type: USER_LANDMARKS, - landmarks: userLandmarks + landmarks: userLandmarks, } - }) + }), ) this.mouseoverUserLandmarks = this.actions$.pipe( @@ -296,31 +300,32 @@ export class ViewerStateUseEffect{ map(([ action, currentLandmarks ]) => { const { payload } = action as any const { label } = payload - if (!label) return { + if (!label) { return { type: MOUSEOVER_USER_LANDMARK, payload: { - userLandmark: null - } + userLandmark: null, + }, + } } const idx = Number(label.replace('label=', '')) if (isNaN(idx)) { - console.warn(`Landmark index could not be parsed as a number: ${idx}`) + this.log.warn(`Landmark index could not be parsed as a number: ${idx}`) return { type: MOUSEOVER_USER_LANDMARK, payload: { - userLandmark: null - } + userLandmark: null, + }, } } return { type: MOUSEOVER_USER_LANDMARK, payload: { - userLandmark: currentLandmarks[idx] - } + userLandmark: currentLandmarks[idx], + }, } - }) + }), ) @@ -331,7 +336,7 @@ export class ViewerStateUseEffect{ const { segments, landmark, userLandmark } = payload return { segments, landmark, userLandmark } }), - shareReplay(1) + shareReplay(1), ) this.doubleClickOnViewerToggleRegions$ = doubleClickOnViewer$.pipe( @@ -340,7 +345,7 @@ export class ViewerStateUseEffect{ select('viewerState'), select('regionsSelected'), distinctUntilChanged(), - startWith([]) + startWith([]), )), map(([{ segments }, regionsSelected]) => { const selectedSet = new Set(regionsSelected.map(generateLabelIndexId)) @@ -348,16 +353,15 @@ export class ViewerStateUseEffect{ const deleteFlag = toggleArr.some(id => selectedSet.has(id)) - for (const id of toggleArr){ - if (deleteFlag) selectedSet.delete(id) - else selectedSet.add(id) + for (const id of toggleArr) { + if (deleteFlag) { selectedSet.delete(id) } else { selectedSet.add(id) } } - + return { type: SELECT_REGIONS_WITH_ID, - selectRegionIds: [...selectedSet] + selectRegionIds: [...selectedSet], } - }) + }), ) this.doubleClickOnViewerToggleLandmark$ = doubleClickOnViewer$.pipe( @@ -365,7 +369,7 @@ export class ViewerStateUseEffect{ withLatestFrom(this.store$.pipe( select('viewerState'), select('landmarksSelected'), - startWith([]) + startWith([]), )), map(([{ landmark }, selectedSpatialDatas]) => { @@ -377,35 +381,35 @@ export class ViewerStateUseEffect{ return { type: SELECT_LANDMARKS, - landmarks: newSelectedSpatialDatas + landmarks: newSelectedSpatialDatas, } - }) + }), ) this.doubleClickOnViewerToogleUserLandmark$ = doubleClickOnViewer$.pipe( - filter(({ userLandmark }) => userLandmark) + filter(({ userLandmark }) => userLandmark), ) } private currentLandmarks$: Observable<any[]> @Effect() - mouseoverUserLandmarks: Observable<any> + public mouseoverUserLandmarks: Observable<any> @Effect() - removeUserLandmarks: Observable<any> + public removeUserLandmarks: Observable<any> @Effect() - addUserLandmarks$: Observable<any> + public addUserLandmarks$: Observable<any> @Effect() - doubleClickOnViewerToggleRegions$: Observable<any> + public doubleClickOnViewerToggleRegions$: Observable<any> @Effect() - doubleClickOnViewerToggleLandmark$: Observable<any> + public doubleClickOnViewerToggleLandmark$: Observable<any> // @Effect() - doubleClickOnViewerToogleUserLandmark$: Observable<any> + public doubleClickOnViewerToogleUserLandmark$: Observable<any> } const ACTION_TYPES = { @@ -414,7 +418,7 @@ const ACTION_TYPES = { MOUSEOVER_USER_LANDMARK_LABEL: 'MOUSEOVER_USER_LANDMARK_LABEL', SINGLE_CLICK_ON_VIEWER: 'SINGLE_CLICK_ON_VIEWER', - DOUBLE_CLICK_ON_VIEWER: 'DOUBLE_CLICK_ON_VIEWER' + DOUBLE_CLICK_ON_VIEWER: 'DOUBLE_CLICK_ON_VIEWER', } -export const VIEWERSTATE_ACTION_TYPES = ACTION_TYPES \ No newline at end of file +export const VIEWERSTATE_ACTION_TYPES = ACTION_TYPES diff --git a/src/services/stateStore.service.ts b/src/services/stateStore.service.ts index 15a2c0016ae8e96b13d63a001878818c2cf10503..c8a69fa0aa6bbe53051f37c64fa3b8b73c1117a3 100644 --- a/src/services/stateStore.service.ts +++ b/src/services/stateStore.service.ts @@ -1,53 +1,53 @@ import { filter } from 'rxjs/operators'; +import { cvtSearchParamToState } from 'src/atlasViewer/atlasViewer.urlUtil'; import { - StateInterface as PluginStateInterface, - stateStore as pluginState, - defaultState as pluginDefaultState, - getStateStore as pluginGetStateStore -} from './state/pluginState.store' -import { - StateInterface as ViewerConfigStateInterface, - stateStore as viewerConfigState, - defaultState as viewerConfigDefaultState, - getStateStore as getViewerConfigStateStore -} from './state/viewerConfig.store' + defaultState as dataStoreDefaultState, + getStateStore as getDatasetStateStore, + IActionInterface as DatasetAction, + IStateInterface as DataStateInterface, + stateStore as dataStore, +} from './state/dataStore.store' import { - StateInterface as NgViewerStateInterface, ActionInterface as NgViewerActionInterface, - stateStore as ngViewerState, defaultState as ngViewerDefaultState, - getStateStore as getNgViewerStateStore + getStateStore as getNgViewerStateStore, + StateInterface as NgViewerStateInterface, + stateStore as ngViewerState, } from './state/ngViewerState.store' import { - StateInterface as ViewerStateInterface, - ActionInterface as ViewerActionInterface, - stateStore as viewerState, - defaultState as viewerDefaultState, - getStateStore as getViewerStateStore -} from './state/viewerState.store' -import { - StateInterface as DataStateInterface, - ActionInterface as DatasetAction, - stateStore as dataStore, - defaultState as dataStoreDefaultState, - getStateStore as getDatasetStateStore -} from './state/dataStore.store' + defaultState as pluginDefaultState, + getStateStore as pluginGetStateStore, + StateInterface as PluginStateInterface, + stateStore as pluginState, +} from './state/pluginState.store' import { - StateInterface as UIStateInterface, ActionInterface as UIActionInterface, - stateStore as uiState, defaultState as uiDefaultState, - getStateStore as getUiStateStore + getStateStore as getUiStateStore, + StateInterface as UIStateInterface, + stateStore as uiState, } from './state/uiState.store' -import{ - stateStore as userConfigState, +import { ACTION_TYPES as USER_CONFIG_ACTION_TYPES, - StateInterface as UserConfigStateInterface, defaultState as userConfigDefaultState, - getStateStore as getuserConfigStateStore + getStateStore as getuserConfigStateStore, + StateInterface as UserConfigStateInterface, + stateStore as userConfigState, } from './state/userConfigState.store' -import { cvtSearchParamToState } from 'src/atlasViewer/atlasViewer.urlUtil'; +import { + defaultState as viewerConfigDefaultState, + getStateStore as getViewerConfigStateStore, + StateInterface as ViewerConfigStateInterface, + stateStore as viewerConfigState, +} from './state/viewerConfig.store' +import { + ActionInterface as ViewerActionInterface, + defaultState as viewerDefaultState, + getStateStore as getViewerStateStore, + StateInterface as ViewerStateInterface, + stateStore as viewerState, +} from './state/viewerState.store' export { pluginState } export { viewerConfigState } @@ -59,23 +59,23 @@ export { userConfigState, USER_CONFIG_ACTION_TYPES} export { ADD_NG_LAYER, FORCE_SHOW_SEGMENT, HIDE_NG_LAYER, REMOVE_NG_LAYER, SHOW_NG_LAYER } from './state/ngViewerState.store' export { CHANGE_NAVIGATION, DESELECT_LANDMARKS, FETCHED_TEMPLATE, NEWVIEWER, SELECT_LANDMARKS, SELECT_PARCELLATION, SELECT_REGIONS, USER_LANDMARKS } from './state/viewerState.store' -export { DataEntry, ParcellationRegion, FETCHED_DATAENTRIES, FETCHED_SPATIAL_DATA, Landmark, OtherLandmarkGeometry, PlaneLandmarkGeometry, PointLandmarkGeometry, Property, Publication, ReferenceSpace, File, FileSupplementData } from './state/dataStore.store' +export { IDataEntry, IParcellationRegion, FETCHED_DATAENTRIES, FETCHED_SPATIAL_DATA, ILandmark, IOtherLandmarkGeometry, IPlaneLandmarkGeometry, IPointLandmarkGeometry, IProperty, IPublication, IReferenceSpace, IFile, IFileSupplementData } from './state/dataStore.store' export { CLOSE_SIDE_PANEL, MOUSE_OVER_LANDMARK, MOUSE_OVER_SEGMENT, OPEN_SIDE_PANEL, SHOW_SIDE_PANEL_CONNECTIVITY, HIDE_SIDE_PANEL_CONNECTIVITY, COLLAPSE_SIDE_PANEL_CURRENT_VIEW, EXPAND_SIDE_PANEL_CURRENT_VIEW } from './state/uiState.store' export { UserConfigStateUseEffect } from './state/userConfigState.store' export const GENERAL_ACTION_TYPES = { ERROR: 'ERROR', - APPLY_STATE: 'APPLY_STATE' + APPLY_STATE: 'APPLY_STATE', } // TODO deprecate -export function safeFilter(key:string){ - return filter((state:any)=> +export function safeFilter(key: string) { + return filter((state: any) => (typeof state !== 'undefined' && state !== null) && - typeof state[key] !== 'undefined' && state[key] !== null) + typeof state[key] !== 'undefined' && state[key] !== null) } -const inheritNgId = (region:any) => { +const inheritNgId = (region: any) => { const {ngId = 'root', children = []} = region return { ngId, @@ -84,32 +84,32 @@ const inheritNgId = (region:any) => { ? { children: children.map(c => inheritNgId({ ngId, - ...c - })) + ...c, + })), } - : {}) + : {}), } } -export function getNgIdLabelIndexFromRegion({ region }){ +export function getNgIdLabelIndexFromRegion({ region }) { const { ngId, labelIndex } = region - if (ngId && labelIndex) return { ngId, labelIndex } + if (ngId && labelIndex) { return { ngId, labelIndex } } throw new Error(`ngId: ${ngId} or labelIndex: ${labelIndex} not defined`) } -export function getMultiNgIdsRegionsLabelIndexMap(parcellation: any = {}):Map<string,Map<number, any>>{ - const map:Map<string,Map<number, any>> = new Map() +export function getMultiNgIdsRegionsLabelIndexMap(parcellation: any = {}): Map<string, Map<number, any>> { + const map: Map<string, Map<number, any>> = new Map() const { ngId = 'root'} = parcellation - const processRegion = (region:any) => { - const { ngId } = region - const existingMap = map.get(ngId) + const processRegion = (region: any) => { + const { ngId: rNgId } = region + const existingMap = map.get(rNgId) const labelIndex = Number(region.labelIndex) if (labelIndex) { if (!existingMap) { const newMap = new Map() newMap.set(labelIndex, region) - map.set(ngId, newMap) + map.set(rNgId, newMap) } else { existingMap.set(labelIndex, region) } @@ -118,8 +118,8 @@ export function getMultiNgIdsRegionsLabelIndexMap(parcellation: any = {}):Map<st if (region.children && region.children.forEach) { region.children.forEach(child => { processRegion({ - ngId, - ...child + ngId: rNgId, + ...child, }) }) } @@ -128,7 +128,7 @@ export function getMultiNgIdsRegionsLabelIndexMap(parcellation: any = {}):Map<st if (parcellation && parcellation.regions && parcellation.regions.forEach) { parcellation.regions.forEach(r => processRegion({ ngId, - ...r + ...r, })) } @@ -139,27 +139,28 @@ export function getMultiNgIdsRegionsLabelIndexMap(parcellation: any = {}):Map<st * labelIndexMap maps label index to region * @TODO deprecate */ -export function getLabelIndexMap(regions:any[]):Map<number,any>{ +export function getLabelIndexMap(regions: any[]): Map<number, any> { const returnMap = new Map() - const reduceRegions = (regions:any[]) => { - regions.forEach(region=>{ - if( region.labelIndex ) returnMap.set(Number(region.labelIndex), - Object.assign({},region,{labelIndex : Number(region.labelIndex)})) - if( region.children && region.children.constructor === Array ) reduceRegions(region.children) + const reduceRegions = (rs: any[]) => { + rs.forEach(region => { + if ( region.labelIndex ) { returnMap.set(Number(region.labelIndex), + Object.assign({}, region, {labelIndex : Number(region.labelIndex)})) + } + if ( region.children && region.children.constructor === Array ) { reduceRegions(region.children) } }) } - if (regions && regions.forEach) reduceRegions(regions) + if (regions && regions.forEach) { reduceRegions(regions) } return returnMap } /** - * + * * @param regions regions to deep iterate to find all ngId 's, filtering out falsy values * n.b. returns non unique list */ -export function getNgIds(regions: any[]): string[]{ +export function getNgIds(regions: any[]): string[] { return regions && regions.map ? regions .map(r => [r.ngId, ...getNgIds(r.children)]) @@ -168,13 +169,12 @@ export function getNgIds(regions: any[]): string[]{ : [] } -export interface DedicatedViewState{ - dedicatedView : string | null +export interface DedicatedViewState { + dedicatedView: string | null } - // @TODO deprecate -export function isDefined(obj){ +export function isDefined(obj) { return typeof obj !== 'undefined' && obj !== null } @@ -198,22 +198,22 @@ export function getNgIdLabelIndexFromId({ labelIndexId } = {labelIndexId: ''}) { const recursiveFlatten = (region, {ngId}) => { return [{ ngId, - ...region + ...region, }].concat( - ...((region.children && region.children.map && region.children.map(c => recursiveFlatten(c, { ngId : region.ngId || ngId })) )|| []) + ...((region.children && region.children.map && region.children.map(c => recursiveFlatten(c, { ngId : region.ngId || ngId })) ) || []), ) } -export function recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId = 'root' }: {regions: any[], labelIndexId: string, inheritedNgId:string}) { +export function recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId = 'root' }: {regions: any[], labelIndexId: string, inheritedNgId: string}) { const { ngId, labelIndex } = getNgIdLabelIndexFromId({ labelIndexId }) - const fr1 = regions.map(r => recursiveFlatten(r,{ ngId: inheritedNgId })) + const fr1 = regions.map(r => recursiveFlatten(r, { ngId: inheritedNgId })) const fr2 = fr1.reduce((acc, curr) => acc.concat(...curr), []) const found = fr2.find(r => r.ngId === ngId && Number(r.labelIndex) === Number(labelIndex)) - if (found) return found + if (found) { return found } return null } -export interface IavRootStoreInterface{ +export interface IavRootStoreInterface { pluginState: PluginStateInterface viewerConfigState: ViewerConfigStateInterface ngViewerState: NgViewerStateInterface @@ -230,11 +230,10 @@ export const defaultRootState: IavRootStoreInterface = { uiState: uiDefaultState, userConfigState: userConfigDefaultState, viewerConfigState: viewerConfigDefaultState, - viewerState: viewerDefaultState + viewerState: viewerDefaultState, } - // export const getInitialState = (): IavRootStoreInterface => { // const search = new URLSearchParams( window.location.search ) // cvtSearchParamToState(search, defaultRootState) -// } \ No newline at end of file +// } diff --git a/src/services/uiService.service.ts b/src/services/uiService.service.ts index 92280bfdf9aa5a3885498d1a8fd5f56d06f00831..937b905793d7ea52d4ed32fac0a03ef51876bac5 100644 --- a/src/services/uiService.service.ts +++ b/src/services/uiService.service.ts @@ -4,14 +4,14 @@ import { AtlasViewerAPIServices } from "src/atlasViewer/atlasViewer.apiService.s import { ToastHandler } from "src/util/pluginHandlerClasses/toastHandler"; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class UIService{ +export class UIService { constructor( private snackbar: MatSnackBar, - private apiService: AtlasViewerAPIServices - ){ + private apiService: AtlasViewerAPIServices, + ) { this.apiService.interactiveViewer.uiHandle.getToastHandler = () => { const toasthandler = new ToastHandler() let handle @@ -19,19 +19,17 @@ export class UIService{ handle = this.showMessage(toasthandler.message, null, { duration: toasthandler.timeout, }) - } toasthandler.hide = () => { - handle && handle.dismiss() + if (!!handle) { handle.dismiss() } handle = null } - return toasthandler - } + } } - showMessage(message: string, actionBtnTxt: string = 'Dismiss', config?: Partial<MatSnackBarConfig>){ + public showMessage(message: string, actionBtnTxt: string = 'Dismiss', config?: Partial<MatSnackBarConfig>) { return this.snackbar.open(message, actionBtnTxt, config) } -} \ No newline at end of file +} diff --git a/src/ui/citation/citations.component.ts b/src/ui/citation/citations.component.ts index c516754468d86ed80ed08dc959643d99136cdc88..177696bbfdc9df6c7f52f32ae6b5369b59666593 100644 --- a/src/ui/citation/citations.component.ts +++ b/src/ui/citation/citations.component.ts @@ -1,15 +1,14 @@ import { Component, Input } from "@angular/core"; -import { Property } from "../../services/stateStore.service"; - +import { IProperty } from "../../services/stateStore.service"; @Component({ selector : 'citations-component', templateUrl : './citations.template.html', styleUrls : [ - './citations.style.css' - ] + './citations.style.css', + ], }) -export class CitationsContainer{ - @Input() properties : Property -} \ No newline at end of file +export class CitationsContainer { + @Input() public properties: IProperty +} diff --git a/src/ui/config/config.component.ts b/src/ui/config/config.component.ts index 152129199ad07d823b5e3cbc8545a79528f03ad8..dd8234c0b814c9a5b5a3ff8a587391d204ff7a35 100644 --- a/src/ui/config/config.component.ts +++ b/src/ui/config/config.component.ts @@ -1,29 +1,29 @@ -import { Component, OnInit, OnDestroy } from '@angular/core' -import { Store, select } from '@ngrx/store'; -import { StateInterface as ViewerConfiguration, ACTION_TYPES as VIEWER_CONFIG_ACTION_TYPES } from 'src/services/state/viewerConfig.store' -import { Observable, Subscription, combineLatest } from 'rxjs'; -import { map, distinctUntilChanged, startWith, debounceTime, tap } from 'rxjs/operators'; -import { MatSlideToggleChange, MatSliderChange } from '@angular/material'; -import { NG_VIEWER_ACTION_TYPES, SUPPORTED_PANEL_MODES } from 'src/services/state/ngViewerState.store'; -import { isIdentityQuat } from '../nehubaContainer/util'; +import { Component, OnDestroy, OnInit } from '@angular/core' +import { MatSliderChange, MatSlideToggleChange } from '@angular/material'; +import { select, Store } from '@ngrx/store'; +import { combineLatest, Observable, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged, map, startWith, tap } from 'rxjs/operators'; import { AtlasViewerConstantsServices } from 'src/atlasViewer/atlasViewer.constantService.service'; +import { NG_VIEWER_ACTION_TYPES, SUPPORTED_PANEL_MODES } from 'src/services/state/ngViewerState.store'; +import { ACTION_TYPES as VIEWER_CONFIG_ACTION_TYPES, StateInterface as ViewerConfiguration } from 'src/services/state/viewerConfig.store' import { IavRootStoreInterface } from 'src/services/stateStore.service'; +import { isIdentityQuat } from '../nehubaContainer/util'; const GPU_TOOLTIP = `Higher GPU usage can cause crashes on lower end machines` const ANIMATION_TOOLTIP = `Animation can cause slowdowns in lower end machines` const MOBILE_UI_TOOLTIP = `Mobile UI enables touch controls` -const ROOT_TEXT_ORDER : [string, string, string, string] = ['Coronal', 'Sagittal', 'Axial', '3D'] -const OBLIQUE_ROOT_TEXT_ORDER : [string, string, string, string] = ['Slice View 1', 'Slice View 2', 'Slice View 3', '3D'] +const ROOT_TEXT_ORDER: [string, string, string, string] = ['Coronal', 'Sagittal', 'Axial', '3D'] +const OBLIQUE_ROOT_TEXT_ORDER: [string, string, string, string] = ['Slice View 1', 'Slice View 2', 'Slice View 3', '3D'] @Component({ selector: 'config-component', templateUrl: './config.template.html', styleUrls: [ - './config.style.css' - ] + './config.style.css', + ], }) -export class ConfigComponent implements OnInit, OnDestroy{ +export class ConfigComponent implements OnInit, OnDestroy { public GPU_TOOLTIP = GPU_TOOLTIP public ANIMATION_TOOLTIP = ANIMATION_TOOLTIP @@ -39,11 +39,11 @@ export class ConfigComponent implements OnInit, OnDestroy{ public animationFlag$: Observable<boolean> private subscriptions: Subscription[] = [] - public gpuMin : number = 100 - public gpuMax : number = 1000 + public gpuMin: number = 100 + public gpuMax: number = 1000 public panelMode$: Observable<string> - + private panelOrder: string private panelOrder$: Observable<string> public panelTexts$: Observable<[string, string, string, string]> @@ -52,34 +52,34 @@ export class ConfigComponent implements OnInit, OnDestroy{ constructor( private store: Store<IavRootStoreInterface>, - private constantService: AtlasViewerConstantsServices + private constantService: AtlasViewerConstantsServices, ) { this.useMobileUI$ = this.constantService.useMobileUI$ this.gpuLimit$ = this.store.pipe( select('viewerConfigState'), - map((config:ViewerConfiguration) => config.gpuLimit), + map((config: ViewerConfiguration) => config.gpuLimit), distinctUntilChanged(), - map(v => v / 1e6) + map(v => v / 1e6), ) this.animationFlag$ = this.store.pipe( select('viewerConfigState'), - map((config:ViewerConfiguration) => config.animation), + map((config: ViewerConfiguration) => config.animation), ) this.panelMode$ = this.store.pipe( select('ngViewerState'), select('panelMode'), - startWith(SUPPORTED_PANEL_MODES[0]) + startWith(SUPPORTED_PANEL_MODES[0]), ) this.panelOrder$ = this.store.pipe( select('ngViewerState'), - select('panelOrder') + select('panelOrder'), ) - + this.viewerObliqueRotated$ = this.store.pipe( select('viewerState'), select('navigation'), @@ -94,63 +94,63 @@ export class ConfigComponent implements OnInit, OnDestroy{ this.panelOrder$.pipe( map(string => string.split('').map(s => Number(s))), ), - this.viewerObliqueRotated$ + this.viewerObliqueRotated$, ).pipe( map(([arr, isObliqueRotated]) => arr.map(idx => (isObliqueRotated ? OBLIQUE_ROOT_TEXT_ORDER : ROOT_TEXT_ORDER)[idx]) as [string, string, string, string]), - startWith(ROOT_TEXT_ORDER) + startWith(ROOT_TEXT_ORDER), ) } - ngOnInit(){ + public ngOnInit() { this.subscriptions.push( - this.panelOrder$.subscribe(panelOrder => this.panelOrder = panelOrder) + this.panelOrder$.subscribe(panelOrder => this.panelOrder = panelOrder), ) } - ngOnDestroy(){ + public ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()) } - public toggleMobileUI(ev: MatSlideToggleChange){ + public toggleMobileUI(ev: MatSlideToggleChange) { const { checked } = ev this.store.dispatch({ type: VIEWER_CONFIG_ACTION_TYPES.SET_MOBILE_UI, payload: { - useMobileUI: checked - } + useMobileUI: checked, + }, }) } - public toggleAnimationFlag(ev: MatSlideToggleChange ){ + public toggleAnimationFlag(ev: MatSlideToggleChange ) { const { checked } = ev this.store.dispatch({ type: VIEWER_CONFIG_ACTION_TYPES.UPDATE_CONFIG, config: { - animation: checked - } + animation: checked, + }, }) } - public handleMatSliderChange(ev:MatSliderChange){ + public handleMatSliderChange(ev: MatSliderChange) { this.store.dispatch({ type: VIEWER_CONFIG_ACTION_TYPES.UPDATE_CONFIG, config: { - gpuLimit: ev.value * 1e6 - } + gpuLimit: ev.value * 1e6, + }, }) } - usePanelMode(panelMode: string){ + public usePanelMode(panelMode: string) { this.store.dispatch({ type: NG_VIEWER_ACTION_TYPES.SWITCH_PANEL_MODE, - payload: { panelMode } + payload: { panelMode }, }) } - handleDrop(event:DragEvent){ + public handleDrop(event: DragEvent) { event.preventDefault() const droppedAttri = (event.target as HTMLElement).getAttribute('panel-order') const draggedAttri = event.dataTransfer.getData('text/plain') - if (droppedAttri === draggedAttri) return + if (droppedAttri === draggedAttri) { return } const idx1 = Number(droppedAttri) const idx2 = Number(draggedAttri) const arr = this.panelOrder.split(''); @@ -158,27 +158,27 @@ export class ConfigComponent implements OnInit, OnDestroy{ [arr[idx1], arr[idx2]] = [arr[idx2], arr[idx1]] this.store.dispatch({ type: NG_VIEWER_ACTION_TYPES.SET_PANEL_ORDER, - payload: { panelOrder: arr.join('') } + payload: { panelOrder: arr.join('') }, }) } - handleDragOver(event:DragEvent){ + public handleDragOver(event: DragEvent) { event.preventDefault() const target = (event.target as HTMLElement) target.classList.add('onDragOver') } - handleDragLeave(event:DragEvent){ + public handleDragLeave(event: DragEvent) { (event.target as HTMLElement).classList.remove('onDragOver') } - handleDragStart(event:DragEvent){ + public handleDragStart(event: DragEvent) { const target = (event.target as HTMLElement) const attri = target.getAttribute('panel-order') event.dataTransfer.setData('text/plain', attri) - + } - handleDragend(event:DragEvent){ + public handleDragend(event: DragEvent) { const target = (event.target as HTMLElement) target.classList.remove('onDragOver') } public stepSize: number = 10 -} \ No newline at end of file +} diff --git a/src/ui/config/currentLayout/currentLayout.component.ts b/src/ui/config/currentLayout/currentLayout.component.ts index f8ed7f581647cccc7234c4acdc448eafbe7e990c..40e8e8c4943de2fdb9139dc21ee62de4856e7ed7 100644 --- a/src/ui/config/currentLayout/currentLayout.component.ts +++ b/src/ui/config/currentLayout/currentLayout.component.ts @@ -1,30 +1,30 @@ import { Component } from "@angular/core"; -import { Store, select } from "@ngrx/store"; +import { select, Store } from "@ngrx/store"; import { Observable } from "rxjs"; -import { SUPPORTED_PANEL_MODES } from "src/services/state/ngViewerState.store"; import { startWith } from "rxjs/operators"; +import { SUPPORTED_PANEL_MODES } from "src/services/state/ngViewerState.store"; import { IavRootStoreInterface } from "src/services/stateStore.service"; @Component({ selector: 'current-layout', templateUrl: './currentLayout.template.html', styleUrls: [ - './currentLayout.style.css' - ] + './currentLayout.style.css', + ], }) -export class CurrentLayout{ +export class CurrentLayout { public supportedPanelModes = SUPPORTED_PANEL_MODES public panelMode$: Observable<string> constructor( private store$: Store<IavRootStoreInterface>, - ){ + ) { this.panelMode$ = this.store$.pipe( select('ngViewerState'), select('panelMode'), - startWith(SUPPORTED_PANEL_MODES[0]) + startWith(SUPPORTED_PANEL_MODES[0]), ) } -} \ No newline at end of file +} diff --git a/src/ui/config/layouts/fourPanel/fourPanel.component.ts b/src/ui/config/layouts/fourPanel/fourPanel.component.ts index c7a22a241e08a3862385b7e7704bf20f0adfded7..dc7a3571acfc615b3a93258fb392f11bee3388d6 100644 --- a/src/ui/config/layouts/fourPanel/fourPanel.component.ts +++ b/src/ui/config/layouts/fourPanel/fourPanel.component.ts @@ -4,10 +4,10 @@ import { Component } from "@angular/core"; selector: 'layout-four-panel', templateUrl: './fourPanel.template.html', styleUrls: [ - './fourPanel.style.css' - ] + './fourPanel.style.css', + ], }) -export class FourPanelLayout{ - -} \ No newline at end of file +export class FourPanelLayout { + +} diff --git a/src/ui/config/layouts/h13/h13.component.ts b/src/ui/config/layouts/h13/h13.component.ts index eccf98f96c6e49993db2a2ac609bfbc05e9a6bc7..a82e12dbc720ba4f71da099d362a9d21c8517101 100644 --- a/src/ui/config/layouts/h13/h13.component.ts +++ b/src/ui/config/layouts/h13/h13.component.ts @@ -4,10 +4,10 @@ import { Component } from "@angular/core"; selector: 'layout-horizontal-one-three', templateUrl: './h13.template.html', styleUrls: [ - './h13.style.css' - ] + './h13.style.css', + ], }) -export class HorizontalOneThree{ - -} \ No newline at end of file +export class HorizontalOneThree { + +} diff --git a/src/ui/config/layouts/single/single.component.ts b/src/ui/config/layouts/single/single.component.ts index 25101d27a9462b37e4072a912bd281126fdb7500..8ab21e8e6bad73af8e0812f0daa81dbf9032d0f1 100644 --- a/src/ui/config/layouts/single/single.component.ts +++ b/src/ui/config/layouts/single/single.component.ts @@ -4,10 +4,10 @@ import { Component } from "@angular/core"; selector: 'layout-single-panel', templateUrl: './single.template.html', styleUrls: [ - './single.style.css' - ] + './single.style.css', + ], }) -export class SinglePanel{ +export class SinglePanel { -} \ No newline at end of file +} diff --git a/src/ui/config/layouts/v13/v13.component.ts b/src/ui/config/layouts/v13/v13.component.ts index c650ac701de3fff14118a9487403410ae89e72ad..2b472f23f07911d870ff674b1748839b904701be 100644 --- a/src/ui/config/layouts/v13/v13.component.ts +++ b/src/ui/config/layouts/v13/v13.component.ts @@ -4,10 +4,10 @@ import { Component } from "@angular/core"; selector: 'layout-vertical-one-three', templateUrl: './v13.template.html', styleUrls: [ - './v13.style.css' - ] + './v13.style.css', + ], }) -export class VerticalOneThree{ - -} \ No newline at end of file +export class VerticalOneThree { + +} diff --git a/src/ui/connectivityBrowser/connectivityBrowser.component.ts b/src/ui/connectivityBrowser/connectivityBrowser.component.ts index 761a9fc0139d6c1fef7580020ef307705dff9786..8db9fe893518abfd82172d6f7ec0fee1baf26459 100644 --- a/src/ui/connectivityBrowser/connectivityBrowser.component.ts +++ b/src/ui/connectivityBrowser/connectivityBrowser.component.ts @@ -3,14 +3,14 @@ import { Component, ElementRef, OnDestroy, - ViewChild + ViewChild, } from "@angular/core"; -import {AtlasViewerConstantsServices} from "src/atlasViewer/atlasViewer.constantService.service"; -import {fromEvent, Observable, Subscription} from "rxjs"; import {select, Store} from "@ngrx/store"; -import {HIDE_SIDE_PANEL_CONNECTIVITY, isDefined, safeFilter} from "src/services/stateStore.service"; +import {fromEvent, Observable, Subscription} from "rxjs"; import {distinctUntilChanged, filter, map} from "rxjs/operators"; +import {AtlasViewerConstantsServices} from "src/atlasViewer/atlasViewer.constantService.service"; import {CLEAR_CONNECTIVITY_REGION, SET_CONNECTIVITY_REGION} from "src/services/state/viewerState.store"; +import {HIDE_SIDE_PANEL_CONNECTIVITY, isDefined, safeFilter} from "src/services/stateStore.service"; @Component({ selector: 'connectivity-browser', @@ -21,7 +21,6 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { public region: string private connectedAreas = [] - private connectivityRegion$: Observable<any> private selectedParcellation$: Observable<any> private subscriptions: Subscription[] = [] @@ -31,28 +30,28 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { public parcellationHasConnectivityData = true private areaHemisphere: string - math = Math + public math = Math - @ViewChild('connectivityComponent', {read: ElementRef}) connectivityComponentElement: ElementRef + @ViewChild('connectivityComponent', {read: ElementRef}) public connectivityComponentElement: ElementRef - constructor(private constantService: AtlasViewerConstantsServices, private store$: Store<any> , private changeDetectionRef : ChangeDetectorRef - ){ + constructor(private constantService: AtlasViewerConstantsServices, private store$: Store<any> , private changeDetectionRef: ChangeDetectorRef, + ) { this.selectedParcellation$ = this.store$.pipe( select('viewerState'), - filter(state=>isDefined(state)&&isDefined(state.parcellationSelected)), - map(state=>state.parcellationSelected), + filter(state => isDefined(state) && isDefined(state.parcellationSelected)), + map(state => state.parcellationSelected), distinctUntilChanged(), ) this.connectivityRegion$ = this.store$.pipe( select('viewerState'), safeFilter('connectivityRegion'), - map(state => state.connectivityRegion) + map(state => state.connectivityRegion), ) } - ngAfterViewInit(): void { + public ngAfterViewInit(): void { this.subscriptions.push( this.selectedParcellation$.subscribe(parcellation => { if (parcellation && parcellation.hasAdditionalViewMode && parcellation.hasAdditionalViewMode.includes('connectivity')) { @@ -70,16 +69,16 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { }), this.connectivityRegion$.subscribe(cr => { this.region = cr - this.areaHemisphere = cr.includes('left hemisphere')? ' - left hemisphere' : ' - right hemisphere' + this.areaHemisphere = cr.includes('left hemisphere') ? ' - left hemisphere' : ' - right hemisphere' this.changeDetectionRef.detectChanges() - }) + }), ) this.subscriptions.push( fromEvent(this.connectivityComponentElement.nativeElement, 'connectivityDataReceived', { capture: true }) .subscribe((e: CustomEvent) => { this.connectedAreas = e.detail - if (this.connectedAreas.length > 0) this.addNewColorMap() + if (this.connectedAreas.length > 0) { this.addNewColorMap() } }), fromEvent(this.connectivityComponentElement.nativeElement, 'collapsedMenuChanged', { capture: true }) .subscribe((e: CustomEvent) => { @@ -89,15 +88,15 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { ) } - ngOnDestroy(): void { + public ngOnDestroy(): void { this.setDefaultMap() this.subscriptions.forEach(s => s.unsubscribe()) } - updateConnevtivityRegion(regionName) { + public updateConnevtivityRegion(regionName) { this.store$.dispatch({ type: SET_CONNECTIVITY_REGION, - connectivityRegion: regionName + connectivityRegion: regionName, }) } @@ -108,11 +107,11 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { type: HIDE_SIDE_PANEL_CONNECTIVITY, }) this.store$.dispatch({ - type: CLEAR_CONNECTIVITY_REGION + type: CLEAR_CONNECTIVITY_REGION, }) } - setDefaultMap() { + public setDefaultMap() { this.allRegions.forEach(r => { if (r && r.ngId && r.rgb) { this.defaultColorMap.get(r.ngId).set(r.labelIndex, {red: r.rgb[0], green: r.rgb[1], blue: r.rgb[2]}) @@ -121,18 +120,18 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { getWindow().interactiveViewer.viewerHandle.applyLayersColourMap(this.defaultColorMap) } - addNewColorMap() { + public addNewColorMap() { this.defaultColorMap = new Map(getWindow().interactiveViewer.viewerHandle.getLayersSegmentColourMap()) const existingMap: Map<string, Map<number, {red: number, green: number, blue: number}>> = (getWindow().interactiveViewer.viewerHandle.getLayersSegmentColourMap()) - const map = new Map(existingMap) + const colorMap = new Map(existingMap) this.allRegions.forEach(r => { if (r.ngId) { - map.get(r.ngId).set(r.labelIndex, {red: 255, green: 255, blue: 255}) + colorMap.get(r.ngId).set(r.labelIndex, {red: 255, green: 255, blue: 255}) } }) @@ -141,26 +140,26 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { .filter(r => r.name === area.name + this.areaHemisphere) .map(r => r) - if (areaAsRegion && areaAsRegion.length && areaAsRegion[0].ngId) + if (areaAsRegion && areaAsRegion.length && areaAsRegion[0].ngId) { // @ts-ignore - map.get(areaAsRegion[0].ngId).set(areaAsRegion[0].labelIndex, {red: area.color.r, green: area.color.g, blue: area.color.b}) + colorMap.get(areaAsRegion[0].ngId).set(areaAsRegion[0].labelIndex, {red: area.color.r, green: area.color.g, blue: area.color.b}) + } }) - getWindow().interactiveViewer.viewerHandle.applyLayersColourMap(map) + getWindow().interactiveViewer.viewerHandle.applyLayersColourMap(colorMap) } - getAllRegionsFromParcellation = (regions) => { - for (let i = 0; i<regions.length; i ++) { - if (regions[i].children && regions[i].children.length) { - this.getAllRegionsFromParcellation(regions[i].children) + public getAllRegionsFromParcellation = (regions) => { + for (const region of regions) { + if (region.children && region.children.length) { + this.getAllRegionsFromParcellation(region.children) } else { - this.allRegions.push(regions[i]) + this.allRegions.push(region) } } } - } -function getWindow (): any { +function getWindow(): any { return window -} \ No newline at end of file +} diff --git a/src/ui/cookieAgreement/cookieAgreement.component.ts b/src/ui/cookieAgreement/cookieAgreement.component.ts index e52f59c5be1878198d2c3321c10daa19486fbeef..0ec8787459f39e26d4ee887ef9e64568f2e2866f 100644 --- a/src/ui/cookieAgreement/cookieAgreement.component.ts +++ b/src/ui/cookieAgreement/cookieAgreement.component.ts @@ -1,13 +1,13 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core' +import { ChangeDetectionStrategy, Component } from '@angular/core' @Component({ selector: 'cookie-agreement', templateUrl: './cookieAgreement.template.html', styleUrls: [ - './cookieAgreement.style.css' + './cookieAgreement.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class CookieAgreement { - public showMore:boolean = false -} \ No newline at end of file + public showMore: boolean = false +} diff --git a/src/ui/databrowserModule/databrowser.module.ts b/src/ui/databrowserModule/databrowser.module.ts index aa37e7e3e6f16a07d1ac358e9ecf9ad882d45a0f..8ee5d1a2e4d839da88b34a075e69cbee31ff0954 100644 --- a/src/ui/databrowserModule/databrowser.module.ts +++ b/src/ui/databrowserModule/databrowser.module.ts @@ -1,42 +1,42 @@ -import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; -import { DataBrowser } from "./databrowser/databrowser.component"; -import { ComponentsModule } from "src/components/components.module"; -import { ModalityPicker } from "./modalityPicker/modalityPicker.component"; +import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; -import { PathToNestedChildren } from "./util/pathToNestedChildren.pipe"; -import { CopyPropertyPipe } from "./util/copyProperty.pipe"; -import { FilterDataEntriesbyMethods } from "./util/filterDataEntriesByMethods.pipe"; -import { FilterDataEntriesByRegion } from "./util/filterDataEntriesByRegion.pipe"; -import { TooltipModule } from "ngx-bootstrap/tooltip"; -import { PreviewComponent } from "./preview/preview.component"; -import { FileViewer } from "./fileviewer/fileviewer.component"; -import { RadarChart } from "./fileviewer/chart/radar/radar.chart.component"; -import { ChartsModule } from "ng2-charts"; -import { LineChart } from "./fileviewer/chart/line/line.chart.component"; -import { DedicatedViewer } from "./fileviewer/dedicated/dedicated.component"; import { Chart } from 'chart.js' -import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; +import { ChartsModule } from "ng2-charts"; import { PopoverModule } from "ngx-bootstrap/popover"; +import { TooltipModule } from "ngx-bootstrap/tooltip"; +import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; +import { ComponentsModule } from "src/components/components.module"; +import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module' +import { DoiParserPipe } from "src/util/pipes/doiPipe.pipe"; import { UtilModule } from "src/util/util.module"; -import { AggregateArrayIntoRootPipe } from "./util/aggregateArrayIntoRoot.pipe"; +import { DataBrowser } from "./databrowser/databrowser.component"; +import { LineChart } from "./fileviewer/chart/line/line.chart.component"; +import { RadarChart } from "./fileviewer/chart/radar/radar.chart.component"; +import { DedicatedViewer } from "./fileviewer/dedicated/dedicated.component"; +import { FileViewer } from "./fileviewer/fileviewer.component"; import { KgSingleDatasetService } from "./kgSingleDatasetService.service" +import { ModalityPicker } from "./modalityPicker/modalityPicker.component"; +import { PreviewComponent } from "./preview/preview.component"; import { SingleDatasetView } from './singleDataset/detailedView/singleDataset.component' -import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module' -import { DoiParserPipe } from "src/util/pipes/doiPipe.pipe"; +import { AggregateArrayIntoRootPipe } from "./util/aggregateArrayIntoRoot.pipe"; +import { CopyPropertyPipe } from "./util/copyProperty.pipe"; import { DatasetIsFavedPipe } from "./util/datasetIsFaved.pipe"; +import { FilterDataEntriesbyMethods } from "./util/filterDataEntriesByMethods.pipe"; +import { FilterDataEntriesByRegion } from "./util/filterDataEntriesByRegion.pipe"; +import { PathToNestedChildren } from "./util/pathToNestedChildren.pipe"; import { RegionBackgroundToRgbPipe } from "./util/regionBackgroundToRgb.pipe"; import { ScrollingModule } from "@angular/cdk/scrolling"; -import { GetKgSchemaIdFromFullIdPipe } from "./util/getKgSchemaIdFromFullId.pipe"; import { PreviewFileIconPipe } from "./preview/previewFileIcon.pipe"; import { PreviewFileTypePipe } from "./preview/previewFileType.pipe"; import { SingleDatasetListView } from "./singleDataset/listView/singleDatasetListView.component"; import { AppendFilerModalityPipe } from "./util/appendFilterModality.pipe"; +import { GetKgSchemaIdFromFullIdPipe } from "./util/getKgSchemaIdFromFullId.pipe"; import { ResetCounterModalityPipe } from "./util/resetCounterModality.pipe"; @NgModule({ - imports:[ + imports: [ ChartsModule, CommonModule, ComponentsModule, @@ -45,7 +45,7 @@ import { ResetCounterModalityPipe } from "./util/resetCounterModality.pipe"; UtilModule, AngularMaterialModule, TooltipModule.forRoot(), - PopoverModule.forRoot() + PopoverModule.forRoot(), ], declarations: [ DataBrowser, @@ -73,9 +73,9 @@ import { ResetCounterModalityPipe } from "./util/resetCounterModality.pipe"; PreviewFileIconPipe, PreviewFileTypePipe, AppendFilerModalityPipe, - ResetCounterModalityPipe + ResetCounterModalityPipe, ], - exports:[ + exports: [ DataBrowser, SingleDatasetView, SingleDatasetListView, @@ -83,27 +83,27 @@ import { ResetCounterModalityPipe } from "./util/resetCounterModality.pipe"; ModalityPicker, FilterDataEntriesbyMethods, FileViewer, - GetKgSchemaIdFromFullIdPipe + GetKgSchemaIdFromFullIdPipe, ], - entryComponents:[ + entryComponents: [ DataBrowser, FileViewer, - SingleDatasetView + SingleDatasetView, ], providers: [ - KgSingleDatasetService - ] + KgSingleDatasetService, + ], /** * shouldn't need bootstrap, so no need for browser module */ }) -export class DatabrowserModule{ +export class DatabrowserModule { constructor( - constantsService:AtlasViewerConstantsServices - ){ + constantsService: AtlasViewerConstantsServices, + ) { /** - * Because there is no easy way to display standard deviation natively, use a plugin + * Because there is no easy way to display standard deviation natively, use a plugin * */ Chart.pluginService.register({ @@ -114,7 +114,7 @@ export class DatabrowserModule{ `rgba(50,50,50,0.8)` : `rgba(255,255,255,0.8)` - if (chart.canvas) ctx.fillRect(0, 0, chart.canvas.width, chart.canvas.height) + if (chart.canvas) { ctx.fillRect(0, 0, chart.canvas.width, chart.canvas.height) } }, @@ -123,15 +123,15 @@ export class DatabrowserModule{ if (chart.config.options && chart.config.options.tooltips) { chart.config.options.tooltips.callbacks = { - label: function (tooltipItem, data) { + label(tooltipItem, data) { let sdValue if (data.datasets && typeof tooltipItem.datasetIndex != 'undefined' && data.datasets[tooltipItem.datasetIndex].label) { const sdLabel = data.datasets[tooltipItem.datasetIndex].label + '_sd' const sd = data.datasets.find(dataset => typeof dataset.label != 'undefined' && dataset.label == sdLabel) - if (sd && sd.data && typeof tooltipItem.index != 'undefined' && typeof tooltipItem.yLabel != 'undefined') sdValue = Number(sd.data[tooltipItem.index]) - Number(tooltipItem.yLabel) + if (sd && sd.data && typeof tooltipItem.index != 'undefined' && typeof tooltipItem.yLabel != 'undefined') { sdValue = Number(sd.data[tooltipItem.index]) - Number(tooltipItem.yLabel) } } return `${tooltipItem.yLabel} ${sdValue ? '(' + sdValue + ')' : ''}` - } + }, } } if (chart.data.datasets) { @@ -142,7 +142,7 @@ export class DatabrowserModule{ if (originalDS) { return Object.assign({}, dataset, { data: (originalDS.data as number[]).map((datapoint, idx) => (Number(datapoint) + Number((dataset.data as number[])[idx]))), - ... constantsService.chartSdStyle + ... constantsService.chartSdStyle, }) } else { return dataset @@ -151,7 +151,7 @@ export class DatabrowserModule{ const sdDS = chart.data.datasets!.find(sdDS => typeof sdDS.label !== 'undefined' && (sdDS.label == dataset.label + '_sd')) if (sdDS) { return Object.assign({}, dataset, { - ...constantsService.chartBaseStyle + ...constantsService.chartBaseStyle, }) } else { return dataset @@ -161,7 +161,7 @@ export class DatabrowserModule{ } }) } - } + }, }) } } diff --git a/src/ui/databrowserModule/databrowser.service.ts b/src/ui/databrowserModule/databrowser.service.ts index 97205dc8c193a9821a9b3428baa83df981d71753..802ca6ac171c938865354af552e5836ceb18ef8c 100644 --- a/src/ui/databrowserModule/databrowser.service.ts +++ b/src/ui/databrowserModule/databrowser.service.ts @@ -1,19 +1,20 @@ +import { HttpClient } from "@angular/common/http"; import { Injectable, OnDestroy } from "@angular/core"; -import { Subscription, Observable, combineLatest, BehaviorSubject, fromEvent, from, of } from "rxjs"; +import { ComponentRef } from "@angular/core/src/render3"; import { select, Store } from "@ngrx/store"; +import { BehaviorSubject, combineLatest, from, fromEvent, Observable, of, Subscription } from "rxjs"; +import { catchError, debounceTime, distinctUntilChanged, filter, map, shareReplay, switchMap, tap, withLatestFrom } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; -import { DataEntry, safeFilter, FETCHED_DATAENTRIES, FETCHED_SPATIAL_DATA, IavRootStoreInterface } from "src/services/stateStore.service"; -import { map, distinctUntilChanged, debounceTime, filter, tap, switchMap, catchError, shareReplay, withLatestFrom } from "rxjs/operators"; import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"; -import { FilterDataEntriesByRegion } from "./util/filterDataEntriesByRegion.pipe"; -import { NO_METHODS } from "./util/filterDataEntriesByMethods.pipe"; -import { ComponentRef } from "@angular/core/src/render3"; -import { DataBrowser } from "./databrowser/databrowser.component"; import { WidgetUnit } from "src/atlasViewer/widgetUnit/widgetUnit.component"; +import { LoggingService } from "src/services/logging.service"; +import { DATASETS_ACTIONS_TYPES } from "src/services/state/dataStore.store"; import { SHOW_KG_TOS } from "src/services/state/uiState.store"; +import { FETCHED_DATAENTRIES, FETCHED_SPATIAL_DATA, IavRootStoreInterface, IDataEntry, safeFilter } from "src/services/stateStore.service"; import { regionFlattener } from "src/util/regionFlattener"; -import { DATASETS_ACTIONS_TYPES } from "src/services/state/dataStore.store"; -import { HttpClient } from "@angular/common/http"; +import { DataBrowser } from "./databrowser/databrowser.component"; +import { NO_METHODS } from "./util/filterDataEntriesByMethods.pipe"; +import { FilterDataEntriesByRegion } from "./util/filterDataEntriesByRegion.pipe"; const noMethodDisplayName = 'No methods described' @@ -29,7 +30,7 @@ const SPATIAL_SEARCH_PRECISION = 6 */ const SPATIAL_SEARCH_DEBOUNCE = 500 -export function temporaryFilterDataentryName(name: string):string{ +export function temporaryFilterDataentryName(name: string): string { return /autoradiography/.test(name) ? 'autoradiography' : NO_METHODS === name @@ -42,24 +43,24 @@ function generateToken() { } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class DatabrowserService implements OnDestroy{ +export class DatabrowserService implements OnDestroy { public kgTos$: Observable<any> - public favedDataentries$: Observable<DataEntry[]> + public favedDataentries$: Observable<IDataEntry[]> public darktheme: boolean = false public instantiatedWidgetUnits: WidgetUnit[] = [] - public queryData: (arg:{regions: any[], template:any, parcellation: any}) => void = (arg) => { + public queryData: (arg: {regions: any[], template: any, parcellation: any}) => void = (arg) => { const { dataBrowser, widgetUnit } = this.createDatabrowser(arg) this.instantiatedWidgetUnits.push(widgetUnit.instance) widgetUnit.onDestroy(() => { this.instantiatedWidgetUnits = this.instantiatedWidgetUnits.filter(db => db !== widgetUnit.instance) }) } - public createDatabrowser: (arg:{regions:any[], template:any, parcellation:any}) => {dataBrowser: ComponentRef<DataBrowser>, widgetUnit:ComponentRef<WidgetUnit>} - public getDataByRegion: ({regions, parcellation, template}:{regions:any[], parcellation:any, template: any}) => Promise<DataEntry[]> = ({regions, parcellation, template}) => new Promise((resolve, reject) => { + public createDatabrowser: (arg: {regions: any[], template: any, parcellation: any}) => {dataBrowser: ComponentRef<DataBrowser>, widgetUnit: ComponentRef<WidgetUnit>} + public getDataByRegion: ({regions, parcellation, template}: {regions: any[], parcellation: any, template: any}) => Promise<IDataEntry[]> = ({regions, parcellation, template}) => new Promise((resolve, reject) => { this.lowLevelQuery(template.name, parcellation.name) .then(de => this.filterDEByRegion.transform(de, regions, parcellation.regions.map(regionFlattener).reduce((acc, item) => acc.concat(item), []))) .then(resolve) @@ -67,7 +68,7 @@ export class DatabrowserService implements OnDestroy{ }) private filterDEByRegion: FilterDataEntriesByRegion = new FilterDataEntriesByRegion() - private dataentries: DataEntry[] = [] + private dataentries: IDataEntry[] = [] private subscriptions: Subscription[] = [] public fetchDataObservable$: Observable<any> @@ -80,33 +81,34 @@ export class DatabrowserService implements OnDestroy{ private workerService: AtlasWorkerService, private constantService: AtlasViewerConstantsServices, private store: Store<IavRootStoreInterface>, - private http: HttpClient - ){ + private http: HttpClient, + private log: LoggingService, + ) { this.kgTos$ = this.http.get(`${this.constantService.backendUrl}datasets/tos`, { - responseType: 'text' + responseType: 'text', }).pipe( - catchError((err,obs) => { - console.warn(`fetching kgTos error`, err) + catchError((err, obs) => { + this.log.warn(`fetching kgTos error`, err) return of(null) }), - shareReplay(1) + shareReplay(1), ) this.favedDataentries$ = this.store.pipe( select('dataStore'), select('favDataEntries'), - shareReplay(1) + shareReplay(1), ) this.subscriptions.push( store.pipe( select('dataStore'), safeFilter('fetchedDataEntries'), - map(v => v.fetchedDataEntries) + map(v => v.fetchedDataEntries), ).subscribe(de => { this.dataentries = de - }) + }), ) this.viewportBoundingBox$ = this.store.pipe( @@ -118,13 +120,13 @@ export class DatabrowserService implements OnDestroy{ map(({ position, zoom }) => { // in mm - const center = position.map(n=>n/1e6) + const center = position.map(n => n / 1e6) const searchWidth = this.constantService.spatialWidth / 4 * zoom / 1e6 const pt1 = center.map(v => (v - searchWidth)) as [number, number, number] const pt2 = center.map(v => (v + searchWidth)) as [number, number, number] return [pt1, pt2] as [Point, Point] - }) + }), ) this.spatialDatasets$ = this.viewportBoundingBox$.pipe( @@ -132,7 +134,7 @@ export class DatabrowserService implements OnDestroy{ select('viewerState'), select('templateSelected'), distinctUntilChanged(), - filter(v => !!v) + filter(v => !!v), )), switchMap(([ bbox, templateSelected ]) => { @@ -140,10 +142,10 @@ export class DatabrowserService implements OnDestroy{ /** * templateSelected and templateSelected.name must be defined for spatial search */ - if (!templateSelected || !templateSelected.name) return from(Promise.reject('templateSelected must not be empty')) + if (!templateSelected || !templateSelected.name) { return from(Promise.reject('templateSelected must not be empty')) } const encodedTemplateName = encodeURIComponent(templateSelected.name) return this.http.get(`${this.constantService.backendUrl}datasets/spatialSearch/templateName/${encodedTemplateName}/bbox/${_bbox[0].join('_')}__${_bbox[1].join("_")}`).pipe( - catchError((err) => (console.log(err), of([]))) + catchError((err) => (this.log.log(err), of([]))), ) }), ) @@ -153,76 +155,76 @@ export class DatabrowserService implements OnDestroy{ select('viewerState'), safeFilter('templateSelected'), tap(({templateSelected}) => this.darktheme = templateSelected.useTheme === 'dark'), - map(({templateSelected})=>(templateSelected.name)), - distinctUntilChanged() + map(({templateSelected}) => (templateSelected.name)), + distinctUntilChanged(), ), this.store.pipe( select('viewerState'), safeFilter('parcellationSelected'), - map(({parcellationSelected})=>(parcellationSelected.name)), - distinctUntilChanged() + map(({parcellationSelected}) => (parcellationSelected.name)), + distinctUntilChanged(), ), - this.manualFetchDataset$ + this.manualFetchDataset$, ) this.subscriptions.push( this.spatialDatasets$.subscribe(arr => { this.store.dispatch({ type: FETCHED_SPATIAL_DATA, - fetchedDataEntries: arr + fetchedDataEntries: arr, }) - }) + }), ) this.subscriptions.push( this.fetchDataObservable$.pipe( - debounceTime(16) - ).subscribe((param : [string, string, null] ) => this.fetchData(param[0], param[1])) + debounceTime(16), + ).subscribe((param: [string, string, null] ) => this.fetchData(param[0], param[1])), ) this.subscriptions.push( fromEvent(this.workerService.worker, 'message').pipe( - filter((message:MessageEvent) => message && message.data && message.data.type === 'RETURN_REBUILT_REGION_SELECTION_TREE'), + filter((message: MessageEvent) => message && message.data && message.data.type === 'RETURN_REBUILT_REGION_SELECTION_TREE'), map(message => message.data), - ).subscribe((payload:any) => { + ).subscribe((payload: any) => { /** - * rebuiltSelectedRegion contains super region that are + * rebuiltSelectedRegion contains super region that are * selected as a result of all of its children that are selectted */ const { rebuiltSelectedRegions, rebuiltSomeSelectedRegions } = payload /** * apply filter and populate databrowser instances */ - }) + }), ) } - ngOnDestroy(){ + public ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()) } - public toggleFav(dataentry: DataEntry){ + public toggleFav(dataentry: IDataEntry) { this.store.dispatch({ type: DATASETS_ACTIONS_TYPES.TOGGLE_FAV_DATASET, - payload: dataentry + payload: dataentry, }) } - public saveToFav(dataentry: DataEntry){ + public saveToFav(dataentry: IDataEntry) { this.store.dispatch({ type: DATASETS_ACTIONS_TYPES.FAV_DATASET, - payload: dataentry + payload: dataentry, }) } - public removeFromFav(dataentry: DataEntry){ + public removeFromFav(dataentry: IDataEntry) { this.store.dispatch({ type: DATASETS_ACTIONS_TYPES.UNFAV_DATASET, - payload: dataentry + payload: dataentry, }) } - public fetchPreviewData(datasetName: string){ + public fetchPreviewData(datasetName: string) { const encodedDatasetName = encodeURIComponent(datasetName) return new Promise((resolve, reject) => { fetch(`${this.constantService.backendUrl}datasets/preview/${encodedDatasetName}`, this.constantService.getFetchOption()) @@ -232,10 +234,10 @@ export class DatabrowserService implements OnDestroy{ }) } - private dispatchData(arr:DataEntry[]){ + private dispatchData(arr: IDataEntry[]) { this.store.dispatch({ type : FETCHED_DATAENTRIES, - fetchedDataEntries : arr + fetchedDataEntries : arr, }) } @@ -244,14 +246,14 @@ export class DatabrowserService implements OnDestroy{ public fetchingFlag: boolean = false private mostRecentFetchToken: any - private lowLevelQuery(templateName: string, parcellationName: string){ + private lowLevelQuery(templateName: string, parcellationName: string) { const encodedTemplateName = encodeURIComponent(templateName) const encodedParcellationName = encodeURIComponent(parcellationName) return Promise.all([ fetch(`${this.constantService.backendUrl}datasets/templateName/${encodedTemplateName}`, this.constantService.getFetchOption()) .then(res => res.json()), fetch(`${this.constantService.backendUrl}datasets/parcellationName/${encodedParcellationName}`, this.constantService.getFetchOption()) - .then(res => res.json()) + .then(res => res.json()), ]) .then(arr => [...arr[0], ...arr[1]]) /** @@ -261,16 +263,16 @@ export class DatabrowserService implements OnDestroy{ const newMap = new Map(acc) return newMap.set(item.name, item) }, new Map())) - .then(map => Array.from(map.values() as DataEntry[])) + .then(map => Array.from(map.values() as IDataEntry[])) } - private fetchData(templateName: string, parcellationName: string){ + private fetchData(templateName: string, parcellationName: string) { this.dispatchData([]) const requestToken = generateToken() this.mostRecentFetchToken = requestToken this.fetchingFlag = true - + this.lowLevelQuery(templateName, parcellationName) .then(array => { if (this.mostRecentFetchToken === requestToken) { @@ -286,7 +288,7 @@ export class DatabrowserService implements OnDestroy{ this.fetchingFlag = false this.mostRecentFetchToken = null this.fetchError = 'Fetching dataset error.' - console.warn('Error fetching dataset', e) + this.log.warn('Error fetching dataset', e) /** * TODO * retry? @@ -295,25 +297,24 @@ export class DatabrowserService implements OnDestroy{ }) } - rebuildRegionTree(selectedRegions, regions){ + public rebuildRegionTree(selectedRegions, regions) { this.workerService.worker.postMessage({ type: 'BUILD_REGION_SELECTION_TREE', selectedRegions, - regions + regions, }) } - public dbComponentInit(db:DataBrowser){ + public dbComponentInit(db: DataBrowser) { this.store.dispatch({ - type: SHOW_KG_TOS + type: SHOW_KG_TOS, }) } public getModalityFromDE = getModalityFromDE } - -export function reduceDataentry(accumulator:{name:string, occurance:number}[], dataentry: DataEntry) { +export function reduceDataentry(accumulator: Array<{name: string, occurance: number}>, dataentry: IDataEntry) { const methods = dataentry.methods .reduce((acc, item) => acc.concat( item.length > 0 @@ -327,7 +328,7 @@ export function reduceDataentry(accumulator:{name:string, occurance:number}[], d return newDE.map(name => { return { name, - occurance: 1 + occurance: 1, } }).concat(accumulator.map(({name, occurance, ...rest}) => { return { @@ -335,26 +336,25 @@ export function reduceDataentry(accumulator:{name:string, occurance:number}[], d name, occurance: methods.some(m => m === name) ? occurance + 1 - : occurance + : occurance, } })) } -export function getModalityFromDE(dataentries:DataEntry[]):CountedDataModality[] { +export function getModalityFromDE(dataentries: IDataEntry[]): CountedDataModality[] { return dataentries.reduce((acc, de) => reduceDataentry(acc, de), []) } -export function getIdFromDataEntry(dataentry: DataEntry){ +export function getIdFromDataEntry(dataentry: IDataEntry) { const { id, fullId } = dataentry const regex = /\/([a-zA-Z0-9\-]*?)$/.exec(fullId) return (regex && regex[1]) || id } - -export interface CountedDataModality{ +export interface CountedDataModality { name: string occurance: number visible: boolean } -type Point = [number, number, number] \ No newline at end of file +type Point = [number, number, number] diff --git a/src/ui/databrowserModule/databrowser.useEffect.ts b/src/ui/databrowserModule/databrowser.useEffect.ts index 8202064ebe1ad983f2e39c0278f31e59854bc905..b448910731a83aa3780277850e35c76574172b40 100644 --- a/src/ui/databrowserModule/databrowser.useEffect.ts +++ b/src/ui/databrowserModule/databrowser.useEffect.ts @@ -1,18 +1,19 @@ import { Injectable, OnDestroy } from "@angular/core"; -import { Store, select } from "@ngrx/store"; -import { Actions, ofType, Effect } from "@ngrx/effects"; -import { DATASETS_ACTIONS_TYPES, DataEntry } from "src/services/state/dataStore.store"; -import { Observable, of, from, merge, Subscription } from "rxjs"; -import { withLatestFrom, map, catchError, filter, switchMap, scan } from "rxjs/operators"; -import { KgSingleDatasetService } from "./kgSingleDatasetService.service"; -import { getIdFromDataEntry } from "./databrowser.service"; -import { LOCAL_STORAGE_CONST } from "src/util/constants"; +import { Actions, Effect, ofType } from "@ngrx/effects"; +import { select, Store } from "@ngrx/store"; +import { from, merge, Observable, of, Subscription } from "rxjs"; +import { catchError, filter, map, scan, switchMap, withLatestFrom } from "rxjs/operators"; +import { LoggingService } from "src/services/logging.service"; +import { DATASETS_ACTIONS_TYPES, IDataEntry } from "src/services/state/dataStore.store"; import { IavRootStoreInterface } from "src/services/stateStore.service"; +import { LOCAL_STORAGE_CONST } from "src/util/constants"; +import { getIdFromDataEntry } from "./databrowser.service"; +import { KgSingleDatasetService } from "./kgSingleDatasetService.service"; const savedFav$ = of(window.localStorage.getItem(LOCAL_STORAGE_CONST.FAV_DATASET)).pipe( map(string => JSON.parse(string)), map(arr => { - if (arr.every(item => item.id )) return arr + if (arr.every(item => item.id )) { return arr } throw new Error('Not every item has id and/or name defined') }), catchError(err => { @@ -21,26 +22,26 @@ const savedFav$ = of(window.localStorage.getItem(LOCAL_STORAGE_CONST.FAV_DATASET * possibly wipe corrupted local stoage here? */ return of(null) - }) + }), ) @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class DataBrowserUseEffect implements OnDestroy{ +export class DataBrowserUseEffect implements OnDestroy { private subscriptions: Subscription[] = [] constructor( private store$: Store<IavRootStoreInterface>, private actions$: Actions<any>, - private kgSingleDatasetService: KgSingleDatasetService - - ){ + private kgSingleDatasetService: KgSingleDatasetService, + private log: LoggingService, + ) { this.favDataEntries$ = this.store$.pipe( select('dataStore'), - select('favDataEntries') + select('favDataEntries'), ) this.toggleDataset$ = this.actions$.pipe( @@ -53,11 +54,11 @@ export class DataBrowserUseEffect implements OnDestroy{ const wasFav = prevFavDataEntries.findIndex(ds => ds.id === id) >= 0 return { type: DATASETS_ACTIONS_TYPES.UPDATE_FAV_DATASETS, - favDataEntries: wasFav + favDataEntries: wasFav ? prevFavDataEntries.filter(ds => ds.id !== id) - : prevFavDataEntries.concat(payload) + : prevFavDataEntries.concat(payload), } - }) + }), ) this.unfavDataset$ = this.actions$.pipe( @@ -69,9 +70,9 @@ export class DataBrowserUseEffect implements OnDestroy{ const { id } = payload return { type: DATASETS_ACTIONS_TYPES.UPDATE_FAV_DATASETS, - favDataEntries: prevFavDataEntries.filter(ds => ds.id !== id) + favDataEntries: prevFavDataEntries.filter(ds => ds.id !== id), } - }) + }), ) this.favDataset$ = this.actions$.pipe( @@ -79,30 +80,29 @@ export class DataBrowserUseEffect implements OnDestroy{ withLatestFrom(this.favDataEntries$), map(([ action, prevFavDataEntries ]) => { const { payload } = action as any - + /** * check duplicate */ const favDataEntries = prevFavDataEntries.find(favDEs => favDEs.id === payload.id) ? prevFavDataEntries : prevFavDataEntries.concat(payload) - + return { type: DATASETS_ACTIONS_TYPES.UPDATE_FAV_DATASETS, - favDataEntries + favDataEntries, } - }) + }), ) - this.subscriptions.push( this.favDataEntries$.pipe( - filter(v => !!v) + filter(v => !!v), ).subscribe(favDataEntries => { /** * only store the minimal data in localstorage/db, hydrate when needed - * for now, only save id - * + * for now, only save id + * * do not save anything else on localstorage. This could potentially be leaking sensitive information */ const serialisedFavDataentries = favDataEntries.map(dataentry => { @@ -110,63 +110,63 @@ export class DataBrowserUseEffect implements OnDestroy{ return { id } }) window.localStorage.setItem(LOCAL_STORAGE_CONST.FAV_DATASET, JSON.stringify(serialisedFavDataentries)) - }) + }), ) this.savedFav$ = savedFav$ this.onInitGetFav$ = this.savedFav$.pipe( filter(v => !!v), - switchMap(arr => + switchMap(arr => merge( - ...arr.map(({ id: kgId }) => + ...arr.map(({ id: kgId }) => from( this.kgSingleDatasetService.getInfoFromKg({ kgId })).pipe( catchError(err => { - console.log(`fetchInfoFromKg error`, err) + this.log.log(`fetchInfoFromKg error`, err) return of(null) }), - switchMap(dataset => + switchMap(dataset => this.kgSingleDatasetService.datasetHasPreview(dataset).pipe( catchError(err => { - console.log(`fetching hasPreview error`, err) + this.log.log(`fetching hasPreview error`, err) return of({}) }), map(resp => { return { ...dataset, - ...resp + ...resp, } - }) - ) - ) - ) - ) + }), + ), + ), + ), + ), ).pipe( filter(v => !!v), - scan((acc, curr) => acc.concat(curr), []) - ) + scan((acc, curr) => acc.concat(curr), []), + ), ), map(favDataEntries => { return { type: DATASETS_ACTIONS_TYPES.UPDATE_FAV_DATASETS, - favDataEntries + favDataEntries, } - }) + }), ) } - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } - private savedFav$: Observable<{id: string, name: string}[] | null> + private savedFav$: Observable<Array<{id: string, name: string}> | null> @Effect() public onInitGetFav$: Observable<any> - private favDataEntries$: Observable<DataEntry[]> + private favDataEntries$: Observable<IDataEntry[]> @Effect() public favDataset$: Observable<any> diff --git a/src/ui/databrowserModule/databrowser/databrowser.component.ts b/src/ui/databrowserModule/databrowser/databrowser.component.ts index f7fcc13e7c0ba71b1f0b636b09d0436e2e282ba5..772f07ec0ab6638b532dd399aa4c3bdbdd9d164f 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.component.ts +++ b/src/ui/databrowserModule/databrowser/databrowser.component.ts @@ -1,11 +1,12 @@ -import { Component, OnDestroy, OnInit, ViewChild, Input, ChangeDetectionStrategy, ChangeDetectorRef, OnChanges, Output,EventEmitter, TemplateRef } from "@angular/core"; -import { DataEntry } from "src/services/stateStore.service"; -import { Subscription, merge, Observable } from "rxjs"; -import { DatabrowserService, CountedDataModality } from "../databrowser.service"; -import { ModalityPicker } from "../modalityPicker/modalityPicker.component"; -import { KgSingleDatasetService } from "../kgSingleDatasetService.service"; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, TemplateRef, ViewChild } from "@angular/core"; +import { merge, Observable, Subscription } from "rxjs"; import { scan, shareReplay } from "rxjs/operators"; +import { LoggingService } from "src/services/logging.service"; import { ViewerPreviewFile } from "src/services/state/dataStore.store"; +import { IDataEntry } from "src/services/stateStore.service"; +import { CountedDataModality, DatabrowserService } from "../databrowser.service"; +import { KgSingleDatasetService } from "../kgSingleDatasetService.service"; +import { ModalityPicker } from "../modalityPicker/modalityPicker.component"; const scanFn: (acc: any[], curr: any) => any[] = (acc, curr) => [curr, ...acc] @@ -13,43 +14,43 @@ const scanFn: (acc: any[], curr: any) => any[] = (acc, curr) => [curr, ...acc] selector : 'data-browser', templateUrl : './databrowser.template.html', styleUrls : [ - `./databrowser.style.css` + `./databrowser.style.css`, ], exportAs: 'dataBrowser', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class DataBrowser implements OnChanges, OnDestroy,OnInit{ +export class DataBrowser implements OnChanges, OnDestroy, OnInit { @Input() public regions: any[] = [] @Input() public template: any - + @Input() public parcellation: any @Output() - dataentriesUpdated: EventEmitter<DataEntry[]> = new EventEmitter() + public dataentriesUpdated: EventEmitter<IDataEntry[]> = new EventEmitter() - public dataentries: DataEntry[] = [] + public dataentries: IDataEntry[] = [] public fetchingFlag: boolean = false public fetchError: boolean = false /** * TODO filter types */ - private subscriptions : Subscription[] = [] + private subscriptions: Subscription[] = [] public countedDataM: CountedDataModality[] = [] public visibleCountedDataM: CountedDataModality[] = [] - public history$: Observable<{file:ViewerPreviewFile, dataset: DataEntry}[]> + public history$: Observable<Array<{file: ViewerPreviewFile, dataset: IDataEntry}>> @ViewChild(ModalityPicker) - modalityPicker: ModalityPicker + public modalityPicker: ModalityPicker - public favDataentries$: Observable<DataEntry[]> + public favDataentries$: Observable<IDataEntry[]> /** * TODO @@ -61,17 +62,18 @@ export class DataBrowser implements OnChanges, OnDestroy,OnInit{ constructor( private dbService: DatabrowserService, - private cdr:ChangeDetectorRef, - private singleDatasetSservice: KgSingleDatasetService - ){ + private cdr: ChangeDetectorRef, + private singleDatasetSservice: KgSingleDatasetService, + private log: LoggingService, + ) { this.favDataentries$ = this.dbService.favedDataentries$ this.history$ = this.singleDatasetSservice.previewingFile$.pipe( scan(scanFn, []), - shareReplay(1) + shareReplay(1), ) } - ngOnChanges(){ + public ngOnChanges() { this.regions = this.regions.map(r => { /** @@ -79,22 +81,22 @@ export class DataBrowser implements OnChanges, OnDestroy,OnInit{ */ return { id: `${this.parcellation.name}/${r.name}`, - ...r + ...r, } }) const { regions, parcellation, template } = this this.fetchingFlag = true // input may be undefined/null - if (!parcellation) return + if (!parcellation) { return } /** * reconstructing parcellation region is async (done on worker thread) - * if parcellation region is not yet defined, return. + * if parcellation region is not yet defined, return. * parccellation will eventually be updated with the correct region */ - if (!parcellation.regions) return - + if (!parcellation.regions) { return } + this.dbService.getDataByRegion({ regions, parcellation, template }) .then(de => { this.dataentries = de @@ -105,7 +107,7 @@ export class DataBrowser implements OnChanges, OnDestroy,OnInit{ this.countedDataM = modalities }) .catch(e => { - console.error(e) + this.log.error(e) this.fetchError = true }) .finally(() => { @@ -116,24 +118,24 @@ export class DataBrowser implements OnChanges, OnDestroy,OnInit{ } - ngOnInit(){ + public ngOnInit() { /** - * TODO gets init'ed everytime when appends to ngtemplateoutlet + * TODO gets init'ed everytime when appends to ngtemplateoutlet */ this.dbService.dbComponentInit(this) this.subscriptions.push( merge( // this.dbService.selectedRegions$, - this.dbService.fetchDataObservable$ + this.dbService.fetchDataObservable$, ).subscribe(() => { /** * Only reset modality picker * resetting all creates infinite loop */ this.clearAll() - }) + }), ) - + /** * TODO fix */ @@ -142,60 +144,60 @@ export class DataBrowser implements OnChanges, OnDestroy,OnInit{ // ) } - ngOnDestroy(){ - this.subscriptions.forEach(s=>s.unsubscribe()) + public ngOnDestroy() { + this.subscriptions.forEach(s => s.unsubscribe()) } - clearAll(){ + public clearAll() { this.countedDataM = this.countedDataM.map(cdm => { return { ...cdm, - visible: false + visible: false, } }) this.visibleCountedDataM = [] } - handleModalityFilterEvent(modalityFilter:CountedDataModality[]){ + public handleModalityFilterEvent(modalityFilter: CountedDataModality[]) { this.countedDataM = modalityFilter this.visibleCountedDataM = modalityFilter.filter(dm => dm.visible) this.cdr.markForCheck() } - retryFetchData(event: MouseEvent){ + public retryFetchData(event: MouseEvent) { event.preventDefault() this.dbService.manualFetchDataset$.next(null) } - toggleFavourite(dataset: DataEntry){ + public toggleFavourite(dataset: IDataEntry) { this.dbService.toggleFav(dataset) } - saveToFavourite(dataset: DataEntry){ + public saveToFavourite(dataset: IDataEntry) { this.dbService.saveToFav(dataset) } - removeFromFavourite(dataset: DataEntry){ + public removeFromFavourite(dataset: IDataEntry) { this.dbService.removeFromFav(dataset) } public showParcellationList: boolean = false - + public filePreviewName: string - onShowPreviewDataset(payload: {datasetName:string, event:MouseEvent}){ + public onShowPreviewDataset(payload: {datasetName: string, event: MouseEvent}) { const { datasetName, event } = payload this.filePreviewName = datasetName } - resetFilters(event?:MouseEvent){ + public resetFilters(event?: MouseEvent) { this.clearAll() } - trackByFn(index:number, dataset:DataEntry){ + public trackByFn(index: number, dataset: IDataEntry) { return dataset.id } } -export interface DataEntryFilter{ - filter: (dataentries:DataEntry[]) => DataEntry[] +export interface IDataEntryFilter { + filter: (dataentries: IDataEntry[]) => IDataEntry[] } diff --git a/src/ui/databrowserModule/fileviewer/chart/chart.base.ts b/src/ui/databrowserModule/fileviewer/chart/chart.base.ts index 98d736d35e6b03a940428e5e1496a409a72427eb..d87209be74093f02e24a2519741d6f9849fd79ae 100644 --- a/src/ui/databrowserModule/fileviewer/chart/chart.base.ts +++ b/src/ui/databrowserModule/fileviewer/chart/chart.base.ts @@ -1,10 +1,10 @@ -import { ViewChild, ElementRef } from "@angular/core"; -import { SafeUrl, DomSanitizer } from "@angular/platform-browser"; -import { Subject, Subscription, Observable, from, interval } from "rxjs"; -import { mapTo, map, shareReplay, switchMapTo, take, switchMap, filter } from "rxjs/operators"; +import { ElementRef, ViewChild } from "@angular/core"; +import { DomSanitizer, SafeUrl } from "@angular/platform-browser"; +import { from, interval, Observable, Subject, Subscription } from "rxjs"; +import { filter, map, mapTo, shareReplay, switchMap, switchMapTo, take } from "rxjs/operators"; -export class ChartBase{ - @ViewChild('canvas') canvas: ElementRef +export class ChartBase { + @ViewChild('canvas') public canvas: ElementRef private _csvData: string @@ -21,16 +21,16 @@ export class ChartBase{ public csvUrl$: Observable<SafeUrl> public pngUrl$: Observable<SafeUrl> - constructor(private sanitizer: DomSanitizer){ + constructor(private sanitizer: DomSanitizer) { this.csvUrl$ = this._newChart$.pipe( mapTo(this._csvData), map(data => { const blob = new Blob([data], { type: 'data:text/csv;charset=utf-8' }) - if (this._csvUrl) window.URL.revokeObjectURL(this._csvUrl) + if (this._csvUrl) { window.URL.revokeObjectURL(this._csvUrl) } this._csvUrl = window.URL.createObjectURL(blob) return this.sanitizer.bypassSecurityTrustUrl(this._csvUrl) }), - shareReplay(1) + shareReplay(1), ) this.pngUrl$ = this._newChart$.pipe( @@ -38,43 +38,43 @@ export class ChartBase{ interval(500).pipe( map(() => this.canvas && this.canvas.nativeElement), filter(v => !!v), - switchMap(el => + switchMap(el => from( - new Promise(rs => el.toBlob(blob => rs(blob), 'image/png')) - ) as Observable<Blob> + new Promise(rs => el.toBlob(blob => rs(blob), 'image/png')), + ) as Observable<Blob>, ), filter(v => !!v), - take(1) - ) + take(1), + ), ), map(blob => { - if (this._pngUrl) window.URL.revokeObjectURL(this._pngUrl) + if (this._pngUrl) { window.URL.revokeObjectURL(this._pngUrl) } this._pngUrl = window.URL.createObjectURL(blob) return this.sanitizer.bypassSecurityTrustUrl(this._pngUrl) }), - shareReplay(1) + shareReplay(1), ) - // necessary + // necessary this._subscriptions.push( - this.pngUrl$.subscribe() + this.pngUrl$.subscribe(), ) this._subscriptions.push( - this.csvUrl$.subscribe() + this.csvUrl$.subscribe(), ) } - superOnDestroy(){ - if (this._csvUrl) window.URL.revokeObjectURL(this._csvUrl) - if (this._pngUrl) window.URL.revokeObjectURL(this._pngUrl) - while(this._subscriptions.length > 0) this._subscriptions.pop().unsubscribe() + public superOnDestroy() { + if (this._csvUrl) { window.URL.revokeObjectURL(this._csvUrl) } + if (this._pngUrl) { window.URL.revokeObjectURL(this._pngUrl) } + while (this._subscriptions.length > 0) { this._subscriptions.pop().unsubscribe() } } - generateNewCsv(csvData: string){ + public generateNewCsv(csvData: string) { this._csvData = csvData this.csvDataUrl = this.sanitizer.bypassSecurityTrustUrl(`data:text/csv;charset=utf-8,${csvData}`) this._newChart$.next(null) } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/fileviewer/chart/chart.interface.ts b/src/ui/databrowserModule/fileviewer/chart/chart.interface.ts index 300eca5c2859e6fd4a47ff1ce8f35f3584c36f0f..510f2a559f707595ed9b7c0b34638fd829dde285 100644 --- a/src/ui/databrowserModule/fileviewer/chart/chart.interface.ts +++ b/src/ui/databrowserModule/fileviewer/chart/chart.interface.ts @@ -1,77 +1,77 @@ import { ChartOptions } from "chart.js"; -import merge from 'lodash.merge' -import { SafeUrl } from "@angular/platform-browser"; import { ElementRef } from "@angular/core"; +import { SafeUrl } from "@angular/platform-browser"; +import merge from 'lodash.merge' -export interface ScaleOptionInterface{ - type? : string - display? : boolean - gridLines? : GridLinesOptionInterface - ticks? :ScaleTickOptionInterface - scaleLabel? : ScaleLabelInterface +export interface ScaleOptionInterface { + type?: string + display?: boolean + gridLines?: GridLinesOptionInterface + ticks?: ScaleTickOptionInterface + scaleLabel?: ScaleLabelInterface } -export interface GridLinesOptionInterface{ - display? : boolean - color? : string +export interface GridLinesOptionInterface { + display?: boolean + color?: string } -export interface ScaleTickOptionInterface{ - min? : number - max? : number - stepSize? : number - backdropColor? : string - showLabelBackdrop? : boolean - suggestedMin? : number - suggestedMax? : number - beginAtZero? : boolean - fontColor? : string +export interface ScaleTickOptionInterface { + min?: number + max?: number + stepSize?: number + backdropColor?: string + showLabelBackdrop?: boolean + suggestedMin?: number + suggestedMax?: number + beginAtZero?: boolean + fontColor?: string } -export interface ChartColor{ - backgroundColor? : string - borderColor? : string - pointBackgroundColor? : string - pointBorderColor? : string - pointHoverBackgroundColor? : string - pointHoverBorderColor? : string +export interface ChartColor { + backgroundColor?: string + borderColor?: string + pointBackgroundColor?: string + pointBorderColor?: string + pointHoverBackgroundColor?: string + pointHoverBorderColor?: string } -export interface DatasetInterface{ - labels : string[] | number[] - datasets : Dataset[] +export interface DatasetInterface { + labels: string[] | number[] + datasets: Dataset[] } -export interface Dataset{ - data : number[] - label? : string - borderWidth? : number - borderDash? : number[] - fill? :string|number|boolean - backgroundColor : string +export interface Dataset { + data: number[] + label?: string + borderWidth?: number + borderDash?: number[] + fill?: string|number|boolean + backgroundColor: string } -export interface LegendInterface{ - display? : boolean - labels? : { - fontColor? : string +export interface LegendInterface { + display?: boolean + labels?: { + fontColor?: string, } } -export interface TitleInterfacce{ - display? : boolean - text? : string - fontColor? : string +export interface TitleInterfacce { + display?: boolean + text?: string + fontColor?: string } -export interface ScaleLabelInterface{ - labelString? : string - fontColor? : string - display? : boolean +export interface ScaleLabelInterface { + labelString?: string + fontColor?: string + display?: boolean } -export interface CommonChartInterface{ +export interface CommonChartInterface { csvDataUrl: SafeUrl csvTitle: string imageTitle: string @@ -79,6 +79,6 @@ export interface CommonChartInterface{ canvas: ElementRef } -export const applyOption = (defaultOption:ChartOptions,option?:Partial<ChartOptions>)=>{ - merge(defaultOption,option) -} \ No newline at end of file +export const applyOption = (defaultOption: ChartOptions, option?: Partial<ChartOptions>) => { + merge(defaultOption, option) +} diff --git a/src/ui/databrowserModule/fileviewer/chart/line/line.chart.component.ts b/src/ui/databrowserModule/fileviewer/chart/line/line.chart.component.ts index 15b387dd893b28d9a80adb4c0db0d57195556c82..a90751cb14a64c4e08961d8c663faf39e9d165b1 100644 --- a/src/ui/databrowserModule/fileviewer/chart/line/line.chart.component.ts +++ b/src/ui/databrowserModule/fileviewer/chart/line/line.chart.component.ts @@ -1,40 +1,39 @@ import { Component, Input, OnChanges } from '@angular/core' -import { DatasetInterface, ChartColor, ScaleOptionInterface, LegendInterface, TitleInterfacce, applyOption, CommonChartInterface } from '../chart.interface' +import { applyOption, ChartColor, CommonChartInterface, DatasetInterface, LegendInterface, ScaleOptionInterface, TitleInterfacce } from '../chart.interface' -import { ChartOptions, LinearTickOptions,ChartDataSets } from 'chart.js'; -import { Color } from 'ng2-charts'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { ChartDataSets, ChartOptions, LinearTickOptions } from 'chart.js'; +import { Color } from 'ng2-charts'; import { ChartBase } from '../chart.base'; @Component({ selector : `line-chart`, templateUrl : './line.chart.template.html', - styleUrls : [ - `./line.chart.style.css` + styleUrls : [ + `./line.chart.style.css`, ], - exportAs: 'iavLineChart' + exportAs: 'iavLineChart', }) -export class LineChart extends ChartBase implements OnChanges, CommonChartInterface{ - +export class LineChart extends ChartBase implements OnChanges, CommonChartInterface { /** * labels of each of the columns, spider web edges */ - @Input() labels: string[] + @Input() public labels: string[] /** * shown on the legend, different lines */ - @Input() lineDatasets: LineDatasetInputInterface[] = [] + @Input() public lineDatasets: LineDatasetInputInterface[] = [] /** * colors of the datasetes */ - @Input() colors: ChartColor[] = [] + @Input() public colors: ChartColor[] = [] - @Input() options: any + @Input() public options: any - mousescroll(_ev:MouseWheelEvent){ + public mousescroll(_ev: MouseWheelEvent) { /** * temporarily disabled @@ -42,30 +41,30 @@ export class LineChart extends ChartBase implements OnChanges, CommonChartInterf } - maxY: number + public maxY: number - xAxesTicks: LinearTickOptions = { + public xAxesTicks: LinearTickOptions = { stepSize: 20, - fontColor: 'white' + fontColor: 'white', } - chartOption: Partial<LineChartOption> = { + public chartOption: Partial<LineChartOption> = { responsive: true, scales: { xAxes: [{ type: 'linear', gridLines: { - color: 'rgba(128,128,128,0.5)' + color: 'rgba(128,128,128,0.5)', }, ticks: this.xAxesTicks, scaleLabel: { display: true, labelString: 'X Axes label', - fontColor: 'rgba(200,200,200,1.0)' - } + fontColor: 'rgba(200,200,200,1.0)', + }, }], yAxes: [{ gridLines: { - color: 'rgba(128,128,128,0.5)' + color: 'rgba(128,128,128,0.5)', }, ticks: { fontColor: 'white', @@ -73,76 +72,76 @@ export class LineChart extends ChartBase implements OnChanges, CommonChartInterf scaleLabel: { display: true, labelString: 'Y Axes label', - fontColor: 'rgba(200,200,200,1.0)' - } + fontColor: 'rgba(200,200,200,1.0)', + }, }], }, legend: { - display: true + display: true, }, title: { display: true, text: 'Title', - fontColor: 'rgba(255,255,255,1.0)' + fontColor: 'rgba(255,255,255,1.0)', }, color: [{ - backgroundColor: `rgba(255,255,255,0.2)` + backgroundColor: `rgba(255,255,255,0.2)`, }], - animation :undefined, - elements: + animation : undefined, + elements: { point: { radius: 0, hoverRadius: 8, - hitRadius: 4 - } - } - + hitRadius: 4, + }, + }, + } - chartDataset: DatasetInterface = { + public chartDataset: DatasetInterface = { labels: [], - datasets: [] + datasets: [], } - shapedLineChartDatasets: ChartDataSets[] - - constructor(sanitizer:DomSanitizer){ + public shapedLineChartDatasets: ChartDataSets[] + + constructor(sanitizer: DomSanitizer) { super(sanitizer) } - ngOnChanges(){ - this.shapedLineChartDatasets = this.lineDatasets.map(lineDataset=>({ - data: lineDataset.data.map((v,idx)=>({ + public ngOnChanges() { + this.shapedLineChartDatasets = this.lineDatasets.map(lineDataset => ({ + data: lineDataset.data.map((v, idx) => ({ x: idx, - y: v + y: v, })), - fill: 'origin' + fill: 'origin', })) - this.maxY = this.chartDataset.datasets.reduce((max,dataset)=>{ + this.maxY = this.chartDataset.datasets.reduce((max, dataset) => { return Math.max( max, - dataset.data.reduce((max,number)=>{ - return Math.max(number,max) - },0)) - },0) + dataset.data.reduce((max, number) => { + return Math.max(number, max) + }, 0)) + }, 0) - applyOption(this.chartOption,this.options) + applyOption(this.chartOption, this.options) this.generateDataUrl() } - getDataPointString(input:any):string{ + public getDataPointString(input: any): string { return typeof input === 'number' || typeof input === 'string' ? input.toString() : this.getDataPointString(input.y) } - private generateDataUrl(){ + private generateDataUrl() { const row0 = [this.chartOption.scales.xAxes[0].scaleLabel.labelString].concat(this.chartOption.scales.yAxes[0].scaleLabel.labelString).join(',') const maxRows = this.lineDatasets.reduce((acc, lds) => Math.max(acc, lds.data.length), 0) - const rows = Array.from(Array(maxRows)).map((_,idx) => [idx.toString()].concat(this.shapedLineChartDatasets.map(v => v.data[idx] ? this.getDataPointString(v.data[idx]) : '').join(','))).join('\n') + const rows = Array.from(Array(maxRows)).map((_, idx) => [idx.toString()].concat(this.shapedLineChartDatasets.map(v => v.data[idx] ? this.getDataPointString(v.data[idx]) : '').join(','))).join('\n') const csvData = `${row0}\n${rows}` this.generateNewCsv(csvData) @@ -151,33 +150,32 @@ export class LineChart extends ChartBase implements OnChanges, CommonChartInterf this.imageTitle = `${this.getGraphTitleAsString().replace(/\s/g, '_')}.png` } - private getGraphTitleAsString():string{ - try{ + private getGraphTitleAsString(): string { + try { return this.chartOption.title.text.constructor === Array ? (this.chartOption.title.text as string[]).join(' ') : this.chartOption.title.text as string - }catch(e){ + } catch (e) { return `Untitled` } } } - -export interface LineDatasetInputInterface{ +export interface LineDatasetInputInterface { label?: string data: number[] } -export interface LinearChartOptionInterface{ +export interface LinearChartOptionInterface { scales?: { xAxes?: ScaleOptionInterface[] - yAxes?: ScaleOptionInterface[] + yAxes?: ScaleOptionInterface[], } legend?: LegendInterface title?: TitleInterfacce color?: Color[] } -interface LineChartOption extends ChartOptions{ +interface LineChartOption extends ChartOptions { color?: Color[] } diff --git a/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.component.ts b/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.component.ts index df4c7edfd04f802c33894a44201f8a122e1c0ea6..3201db85b4644c36c9bc6a8afc737a517b6857ca 100644 --- a/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.component.ts +++ b/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.component.ts @@ -1,38 +1,38 @@ -import { Component, Input, OnChanges, ViewChild, ElementRef, OnInit, OnDestroy } from '@angular/core' +import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, ViewChild } from '@angular/core' -import { DatasetInterface, ChartColor, ScaleOptionInterface, TitleInterfacce, LegendInterface, applyOption, CommonChartInterface } from '../chart.interface'; -import { Color } from 'ng2-charts'; import { DomSanitizer } from '@angular/platform-browser'; import { RadialChartOptions } from 'chart.js' +import { Color } from 'ng2-charts'; import { ChartBase } from '../chart.base'; +import { applyOption, ChartColor, CommonChartInterface, DatasetInterface, LegendInterface, ScaleOptionInterface, TitleInterfacce } from '../chart.interface'; @Component({ selector : `radar-chart`, templateUrl : './radar.chart.template.html', - styleUrls : [ - `./radar.chart.style.css` + styleUrls : [ + `./radar.chart.style.css`, ], - exportAs: 'iavRadarChart' + exportAs: 'iavRadarChart', }) export class RadarChart extends ChartBase implements OnDestroy, OnChanges, CommonChartInterface { /** * labels of each of the columns, spider web edges */ - @Input() labels : string[] = [] + @Input() public labels: string[] = [] /** * shown on the legend, different lines */ - @Input() radarDatasets : RadarDatasetInputInterface[] = [] + @Input() public radarDatasets: RadarDatasetInputInterface[] = [] /** * colors of the datasetes */ - @Input() colors : ChartColor[] = [] + @Input() public colors: ChartColor[] = [] - @Input() options : any + @Input() public options: any - mousescroll(_ev:MouseWheelEvent){ + public mousescroll(_ev: MouseWheelEvent) { /** * mouse wheel zooming disabled for now @@ -55,73 +55,73 @@ export class RadarChart extends ChartBase implements OnDestroy, OnChanges, Commo // this.chartOption = Object.assign({},this.chartOption,{scale : combineScale,animation : false}) } - maxY : number + public maxY: number - chartOption : Partial<RadialChartOptions> = { + public chartOption: Partial<RadialChartOptions> = { responsive: true, scale : { gridLines : { - color : 'rgba(128,128,128,0.5)' + color : 'rgba(128,128,128,0.5)', }, ticks : { showLabelBackdrop : false, - fontColor : 'white' + fontColor : 'white', }, angleLines : { - color : 'rgba(128,128,128,0.2)' + color : 'rgba(128,128,128,0.2)', }, pointLabels : { - fontColor : 'white' - } + fontColor : 'white', + }, }, legend : { display: true, labels : { - fontColor : 'white' - } + fontColor : 'white', + }, }, - title :{ + title : { text : 'Radar graph', display : true, - fontColor : 'rgba(255,255,255,1.0)' + fontColor : 'rgba(255,255,255,1.0)', }, - animation: null + animation: null, } - chartDataset : DatasetInterface = { + public chartDataset: DatasetInterface = { labels : [], - datasets : [] + datasets : [], } - constructor(sanitizer: DomSanitizer){ + constructor(sanitizer: DomSanitizer) { super(sanitizer) } - ngOnDestroy(){ + public ngOnDestroy() { this.superOnDestroy() } - - ngOnChanges(){ + + public ngOnChanges() { this.chartDataset = { labels : this.labels, - datasets : this.radarDatasets.map(ds=>Object.assign({},ds,{backgroundColor : 'rgba(255,255,255,0.2)'})) + datasets : this.radarDatasets.map(ds => Object.assign({}, ds, {backgroundColor : 'rgba(255,255,255,0.2)'})), } // this.chartDataset.datasets[0] - this.maxY = this.chartDataset.datasets.reduce((max,dataset)=>{ + this.maxY = this.chartDataset.datasets.reduce((max, dataset) => { return Math.max( max, - dataset.data.reduce((max,number)=>{ - return Math.max(number,max) - },0)) - },0) + dataset.data.reduce((max, number) => { + return Math.max(number, max) + }, 0)) + }, 0) - applyOption(this.chartOption,this.options) + applyOption(this.chartOption, this.options) this.generateDataUrl() } - private generateDataUrl(){ + private generateDataUrl() { const row0 = ['Receptors', ...this.chartDataset.datasets.map(ds => ds.label || 'no label')].join(',') const otherRows = (this.chartDataset.labels as string[]) .map((label, index) => [ label, ...this.chartDataset.datasets.map(ds => ds.data[index]) ].join(',')).join('\n') @@ -130,42 +130,42 @@ export class RadarChart extends ChartBase implements OnDestroy, OnChanges, Commo this.generateNewCsv(csvData) this.csvTitle = `${this.getGraphTitleAsString().replace(/\s/g, '_')}.csv` - this.imageTitle = `${this.getGraphTitleAsString().replace(/\s/g, '_')}.png` + this.imageTitle = `${this.getGraphTitleAsString().replace(/\s/g, '_')}.png` } - private getGraphTitleAsString():string{ - try{ + private getGraphTitleAsString(): string { + try { return this.chartOption.title.text as string - }catch(e){ + } catch (e) { return `Untitled` } } } -export interface RadarDatasetInputInterface{ - label : string - data : number[] +export interface RadarDatasetInputInterface { + label: string + data: number[] } -export interface RadarChartOptionInterface{ - scale? : ScaleOptionInterface&RadarScaleOptionAdditionalInterface - animation? : any - legend? : LegendInterface - title? : TitleInterfacce - color? :Color[] +export interface RadarChartOptionInterface { + scale?: ScaleOptionInterface&RadarScaleOptionAdditionalInterface + animation?: any + legend?: LegendInterface + title?: TitleInterfacce + color?: Color[] } -interface RadarScaleOptionAdditionalInterface{ - angleLines? :AngleLineInterface - pointLabels?:PointLabelInterface +interface RadarScaleOptionAdditionalInterface { + angleLines?: AngleLineInterface + pointLabels?: PointLabelInterface } -interface AngleLineInterface{ - display? : boolean - color? : string - lineWidth? : number +interface AngleLineInterface { + display?: boolean + color?: string + lineWidth?: number } -interface PointLabelInterface{ - fontColor? : string -} \ No newline at end of file +interface PointLabelInterface { + fontColor?: string +} diff --git a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.component.ts b/src/ui/databrowserModule/fileviewer/dedicated/dedicated.component.ts index ce27a423d9f547aa9323fce09d8a1bd3b88f9b49..6f917e3164ba3428f5dcc84032268b35d633df76 100644 --- a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.component.ts +++ b/src/ui/databrowserModule/fileviewer/dedicated/dedicated.component.ts @@ -7,32 +7,32 @@ import { KgSingleDatasetService } from "../../kgSingleDatasetService.service"; selector : 'dedicated-viewer', templateUrl : './dedicated.template.html', styleUrls : [ - `./dedicated.style.css` - ] + `./dedicated.style.css`, + ], }) -export class DedicatedViewer{ - @Input() previewFile : ViewerPreviewFile +export class DedicatedViewer { + @Input() public previewFile: ViewerPreviewFile constructor( - private singleKgDsService:KgSingleDatasetService, - ){ + private singleKgDsService: KgSingleDatasetService, + ) { } - get isShowing(){ + get isShowing() { return this.singleKgDsService.ngLayers.has(this.previewFile.url) } - showDedicatedView(){ + public showDedicatedView() { this.singleKgDsService.showNewNgLayer({ url: this.previewFile.url }) } - removeDedicatedView(){ + public removeDedicatedView() { this.singleKgDsService.removeNgLayer({ url: this.previewFile.url }) } - - click(event:MouseEvent){ + + public click(event: MouseEvent) { event.preventDefault() this.isShowing ? this.removeDedicatedView() diff --git a/src/ui/databrowserModule/fileviewer/fileviewer.component.ts b/src/ui/databrowserModule/fileviewer/fileviewer.component.ts index 6425465324e6dcb313b2823f06cb5715e4386857..97588bf5f09c2ac230a7b0338f9b496fe90415bd 100644 --- a/src/ui/databrowserModule/fileviewer/fileviewer.component.ts +++ b/src/ui/databrowserModule/fileviewer/fileviewer.component.ts @@ -1,37 +1,36 @@ -import { Component, Input, Inject, Optional, OnChanges, ViewChild, ChangeDetectorRef } from '@angular/core' +import { ChangeDetectorRef, Component, Inject, Input, OnChanges, Optional, ViewChild } from '@angular/core' -import { ViewerPreviewFile } from 'src/services/state/dataStore.store'; import { MAT_DIALOG_DATA } from '@angular/material'; +import { ViewerPreviewFile } from 'src/services/state/dataStore.store'; import { ChartBase } from './chart/chart.base'; - @Component({ selector : 'file-viewer', templateUrl : './fileviewer.template.html' , - styleUrls : [ - './fileviewer.style.css' - ] + styleUrls : [ + './fileviewer.style.css', + ], }) -export class FileViewer implements OnChanges{ +export class FileViewer implements OnChanges { - childChart: ChartBase + public childChart: ChartBase - @ViewChild('childChart') - set setChildChart(childChart:ChartBase){ + @ViewChild('childChart') + set setChildChart(childChart: ChartBase) { this.childChart = childChart this.cdr.detectChanges() - } + } /** * fetched directly from KG */ - @Input() previewFile : ViewerPreviewFile + @Input() public previewFile: ViewerPreviewFile constructor( private cdr: ChangeDetectorRef, - @Optional() @Inject(MAT_DIALOG_DATA) data - ){ + @Optional() @Inject(MAT_DIALOG_DATA) data, + ) { if (data) { this.previewFile = data.previewFile this.downloadUrl = this.previewFile.url @@ -39,7 +38,7 @@ export class FileViewer implements OnChanges{ } public downloadUrl: string - ngOnChanges(){ + public ngOnChanges() { this.downloadUrl = this.previewFile.url } } diff --git a/src/ui/databrowserModule/kgSingleDatasetService.service.ts b/src/ui/databrowserModule/kgSingleDatasetService.service.ts index 3efd76eabee157f862ab4fbd8e83b661d1262fce..8c8a3998fda6ad3301403e0a10d6fd6daf187788 100644 --- a/src/ui/databrowserModule/kgSingleDatasetService.service.ts +++ b/src/ui/databrowserModule/kgSingleDatasetService.service.ts @@ -1,24 +1,24 @@ -import { Injectable, TemplateRef, OnDestroy } from "@angular/core"; +import { HttpClient } from "@angular/common/http"; +import { Injectable, OnDestroy, TemplateRef } from "@angular/core"; +import { MatDialog, MatSnackBar } from "@angular/material"; +import { select, Store } from "@ngrx/store"; +import { Subject, Subscription } from "rxjs"; +import { filter } from "rxjs/operators"; import { AtlasViewerConstantsServices, GLSL_COLORMAP_JET } from "src/atlasViewer/atlasViewer.constantService.service" -import { Store, select } from "@ngrx/store"; +import { IDataEntry, ViewerPreviewFile } from "src/services/state/dataStore.store"; import { SHOW_BOTTOM_SHEET } from "src/services/state/uiState.store"; -import { ViewerPreviewFile, DataEntry } from "src/services/state/dataStore.store"; -import { determinePreviewFileType, PREVIEW_FILE_TYPES } from "./preview/previewFileIcon.pipe"; -import { MatDialog, MatSnackBar } from "@angular/material"; +import { ADD_NG_LAYER, CHANGE_NAVIGATION, IavRootStoreInterface, REMOVE_NG_LAYER } from "src/services/stateStore.service"; import { FileViewer } from "./fileviewer/fileviewer.component"; -import { ADD_NG_LAYER, REMOVE_NG_LAYER, CHANGE_NAVIGATION, IavRootStoreInterface } from "src/services/stateStore.service"; -import { Subscription, Subject } from "rxjs"; -import { HttpClient } from "@angular/common/http"; +import { determinePreviewFileType, PREVIEW_FILE_TYPES } from "./preview/previewFileIcon.pipe"; import { GetKgSchemaIdFromFullIdPipe } from "./util/getKgSchemaIdFromFullId.pipe"; -import { filter } from "rxjs/operators"; @Injectable({ providedIn: 'root' }) -export class KgSingleDatasetService implements OnDestroy{ +export class KgSingleDatasetService implements OnDestroy { - public previewingFile$: Subject<{file:ViewerPreviewFile, dataset: DataEntry}> = new Subject() + public previewingFile$: Subject<{file: ViewerPreviewFile, dataset: IDataEntry}> = new Subject() private subscriptions: Subscription[] = [] - public ngLayers : Set<string> = new Set() + public ngLayers: Set<string> = new Set() private getKgSchemaIdFromFullIdPipe: GetKgSchemaIdFromFullIdPipe = new GetKgSchemaIdFromFullIdPipe() @@ -27,27 +27,27 @@ export class KgSingleDatasetService implements OnDestroy{ private store$: Store<IavRootStoreInterface>, private dialog: MatDialog, private http: HttpClient, - private snackBar: MatSnackBar + private snackBar: MatSnackBar, ) { this.subscriptions.push( this.store$.pipe( select('ngViewerState'), - filter(v => !!v) + filter(v => !!v), ).subscribe(layersInterface => { this.ngLayers = new Set(layersInterface.layers.map(l => l.source.replace(/^nifti\:\/\//, ''))) - }) + }), ) } - ngOnDestroy(){ + public ngOnDestroy() { while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } - public datasetHasPreview({ name } : { name: string } = { name: null }){ - if (!name) throw new Error('kgSingleDatasetService#datasetHashPreview name must be defined') + public datasetHasPreview({ name }: { name: string } = { name: null }) { + if (!name) { throw new Error('kgSingleDatasetService#datasetHashPreview name must be defined') } const _url = new URL(`datasets/hasPreview`, this.constantService.backendUrl ) const searchParam = _url.searchParams searchParam.set('datasetName', name) @@ -61,12 +61,12 @@ export class KgSingleDatasetService implements OnDestroy{ searchParam.set('kgId', kgId) return fetch(_url.toString()) .then(res => { - if (res.status >= 400) throw new Error(res.status.toString()) + if (res.status >= 400) { throw new Error(res.status.toString()) } return res.json() }) } - public getDownloadZipFromKgHref({ kgSchema = 'minds/core/dataset/v1.0.0', kgId }){ + public getDownloadZipFromKgHref({ kgSchema = 'minds/core/dataset/v1.0.0', kgId }) { const _url = new URL(`datasets/downloadKgFiles`, this.constantService.backendUrl) const searchParam = _url.searchParams searchParam.set('kgSchema', kgSchema) @@ -74,23 +74,23 @@ export class KgSingleDatasetService implements OnDestroy{ return _url.toString() } - public showPreviewList(template: TemplateRef<any>){ + public showPreviewList(template: TemplateRef<any>) { this.store$.dispatch({ type: SHOW_BOTTOM_SHEET, - bottomSheetTemplate: template + bottomSheetTemplate: template, }) } - public previewFile(file:ViewerPreviewFile, dataset: DataEntry) { + public previewFile(file: ViewerPreviewFile, dataset: IDataEntry) { this.previewingFile$.next({ file, - dataset + dataset, }) const { position, name } = file if (position) { this.snackBar.open(`Postion of interest found.`, 'Go there', { - duration: 5000 + duration: 5000, }) .afterDismissed() .subscribe(({ dismissedByAction }) => { @@ -99,8 +99,8 @@ export class KgSingleDatasetService implements OnDestroy{ type: CHANGE_NAVIGATION, navigation: { position, - animation: {} - } + animation: {}, + }, }) } }) @@ -110,55 +110,54 @@ export class KgSingleDatasetService implements OnDestroy{ if (type === PREVIEW_FILE_TYPES.NIFTI) { this.store$.dispatch({ type: SHOW_BOTTOM_SHEET, - bottomSheetTemplate: null + bottomSheetTemplate: null, }) const { url } = file this.showNewNgLayer({ url }) return } - this.dialog.open(FileViewer, { data: { - previewFile: file + previewFile: file, }, - autoFocus: false + autoFocus: false, }) } - public showNewNgLayer({ url }):void{ + public showNewNgLayer({ url }): void { const layer = { name : url, source : `nifti://${url}`, mixability : 'nonmixable', - shader : GLSL_COLORMAP_JET + shader : GLSL_COLORMAP_JET, } this.store$.dispatch({ type: ADD_NG_LAYER, - layer + layer, }) } - removeNgLayer({ url }) { + public removeNgLayer({ url }) { this.store$.dispatch({ type : REMOVE_NG_LAYER, layer : { - name : url - } + name : url, + }, }) } - getKgSchemaKgIdFromFullId(fullId: string){ + public getKgSchemaKgIdFromFullId(fullId: string) { const match = this.getKgSchemaIdFromFullIdPipe.transform(fullId) return match && { kgSchema: match[0], - kgId: match[1] + kgId: match[1], } } } -interface KgQueryInterface{ +interface KgQueryInterface { kgSchema: string kgId: string } diff --git a/src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts b/src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts index 01bca9eb65e9c2408932916af8f4ae4352239fb2..f7505efcd99b7c8224e14574e7d62564e2f32dd2 100644 --- a/src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts +++ b/src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts @@ -1,22 +1,21 @@ -import { Component, EventEmitter, Input, Output, OnChanges } from "@angular/core"; +import { Component, EventEmitter, Input, OnChanges, Output } from "@angular/core"; import { CountedDataModality } from "../databrowser.service"; @Component({ selector: 'modality-picker', templateUrl: './modalityPicker.template.html', styleUrls: [ - './modalityPicker.style.css' - ] + './modalityPicker.style.css', + ], }) -export class ModalityPicker implements OnChanges{ +export class ModalityPicker implements OnChanges { public modalityVisibility: Set<string> = new Set() @Input() public countedDataM: CountedDataModality[] = [] - public checkedModality: CountedDataModality[] = [] @Output() @@ -28,7 +27,7 @@ export class ModalityPicker implements OnChanges{ // : dataentries.filter(de => de.activity.some(a => a.methods.some(m => this.modalityVisibility.has(this.dbService.temporaryFilterDataentryName(m))))) // } - ngOnChanges(){ + public ngOnChanges() { this.checkedModality = this.countedDataM.filter(d => d.visible) } @@ -36,29 +35,29 @@ export class ModalityPicker implements OnChanges{ * TODO * togglemodailty should emit event, and let parent handle state */ - toggleModality(modality: Partial<CountedDataModality>){ + public toggleModality(modality: Partial<CountedDataModality>) { this.modalityFilterEmitter.emit( this.countedDataM.map(d => d.name === modality.name ? { ...d, - visible: !d.visible + visible: !d.visible, } - : d) + : d), ) } - uncheckModality(modality:string){ + public uncheckModality(modality: string) { this.toggleModality({name: modality}) } - clearAll(){ + public clearAll() { this.modalityFilterEmitter.emit( this.countedDataM.map(d => { return { ...d, - visible: false + visible: false, } - }) + }), ) } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/preview/preview.component.ts b/src/ui/databrowserModule/preview/preview.component.ts index bb2e06485eee70e053130a03922f4ad82a2690f6..4f19643b246bd2394c674c98df89426cb953fa8d 100644 --- a/src/ui/databrowserModule/preview/preview.component.ts +++ b/src/ui/databrowserModule/preview/preview.component.ts @@ -1,6 +1,6 @@ -import { Component, Input, OnInit, Output, EventEmitter, ChangeDetectorRef, ChangeDetectionStrategy } from "@angular/core"; -import { DatabrowserService } from "../databrowser.service"; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; import { ViewerPreviewFile } from "src/services/state/dataStore.store"; +import { DatabrowserService } from "../databrowser.service"; const getRenderNodeFn = ({name : activeFileName = ''} = {}) => ({name = '', path = 'unpathed'}) => name ? activeFileName === name @@ -12,14 +12,14 @@ const getRenderNodeFn = ({name : activeFileName = ''} = {}) => ({name = '', path selector: 'preview-component', templateUrl: './previewList.template.html', styleUrls: [ - './preview.style.css' + './preview.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PreviewComponent implements OnInit{ - @Input() datasetName: string - @Output() previewFile: EventEmitter<ViewerPreviewFile> = new EventEmitter() +export class PreviewComponent implements OnInit { + @Input() public datasetName: string + @Output() public previewFile: EventEmitter<ViewerPreviewFile> = new EventEmitter() public fetchCompleteFlag: boolean = false @@ -28,20 +28,20 @@ export class PreviewComponent implements OnInit{ private error: string constructor( - private dbrService:DatabrowserService, - private cdr: ChangeDetectorRef - ){ + private dbrService: DatabrowserService, + private cdr: ChangeDetectorRef, + ) { this.renderNode = getRenderNodeFn() } - previewFileClick(ev, el){ - + public previewFileClick(ev, el) { + ev.event.preventDefault() ev.event.stopPropagation() - if(ev.inputItem.children.length > 0){ + if (ev.inputItem.children.length > 0) { el.toggleCollapse(ev.inputItem) - }else{ + } else { this.activeFile = ev.inputItem this.renderNode = getRenderNodeFn(this.activeFile) } @@ -49,9 +49,9 @@ export class PreviewComponent implements OnInit{ this.cdr.markForCheck() } - public renderNode: (obj:any) => string + public renderNode: (obj: any) => string - ngOnInit(){ + public ngOnInit() { if (this.datasetName) { this.dbrService.fetchPreviewData(this.datasetName) .then(json => { diff --git a/src/ui/databrowserModule/preview/previewFileIcon.pipe.ts b/src/ui/databrowserModule/preview/previewFileIcon.pipe.ts index 717147da325568c890f55b0b9240cd40ab12fa30..038f48a980771893863f23e9faec1eefd7cdb112 100644 --- a/src/ui/databrowserModule/preview/previewFileIcon.pipe.ts +++ b/src/ui/databrowserModule/preview/previewFileIcon.pipe.ts @@ -2,30 +2,33 @@ import { Pipe, PipeTransform } from "@angular/core"; import { ViewerPreviewFile } from "src/services/state/dataStore.store"; @Pipe({ - name: 'previewFileIconPipe' + name: 'previewFileIconPipe', }) -export class PreviewFileIconPipe implements PipeTransform{ - public transform(previewFile: ViewerPreviewFile):{fontSet: string, fontIcon:string}{ +export class PreviewFileIconPipe implements PipeTransform { + public transform(previewFile: ViewerPreviewFile): {fontSet: string, fontIcon: string} { const type = determinePreviewFileType(previewFile) - if (type === PREVIEW_FILE_TYPES.NIFTI) return { + if (type === PREVIEW_FILE_TYPES.NIFTI) { return { fontSet: 'fas', - fontIcon: 'fa-brain' + fontIcon: 'fa-brain', + } } - if (type === PREVIEW_FILE_TYPES.IMAGE) return { + if (type === PREVIEW_FILE_TYPES.IMAGE) { return { fontSet: 'fas', - fontIcon: 'fa-image' + fontIcon: 'fa-image', + } } - if (type === PREVIEW_FILE_TYPES.CHART) return { + if (type === PREVIEW_FILE_TYPES.CHART) { return { fontSet: 'far', - fontIcon: 'fa-chart-bar' + fontIcon: 'fa-chart-bar', + } } return { fontSet: 'fas', - fontIcon: 'fa-file' + fontIcon: 'fa-file', } } } @@ -33,9 +36,9 @@ export class PreviewFileIconPipe implements PipeTransform{ export const determinePreviewFileType = (previewFile: ViewerPreviewFile) => { const { mimetype, data } = previewFile const { chartType = null } = data || {} - if ( mimetype === 'application/nifti' ) return PREVIEW_FILE_TYPES.NIFTI - if ( /^image/.test(mimetype)) return PREVIEW_FILE_TYPES.IMAGE - if ( /application\/json/.test(mimetype) && (chartType === 'line' || chartType === 'radar')) return PREVIEW_FILE_TYPES.CHART + if ( mimetype === 'application/nifti' ) { return PREVIEW_FILE_TYPES.NIFTI } + if ( /^image/.test(mimetype)) { return PREVIEW_FILE_TYPES.IMAGE } + if ( /application\/json/.test(mimetype) && (chartType === 'line' || chartType === 'radar')) { return PREVIEW_FILE_TYPES.CHART } return PREVIEW_FILE_TYPES.OTHER } @@ -43,5 +46,5 @@ export const PREVIEW_FILE_TYPES = { NIFTI: 'NIFTI', IMAGE: 'IMAGE', CHART: 'CHART', - OTHER: 'OTHER' + OTHER: 'OTHER', } diff --git a/src/ui/databrowserModule/preview/previewFileType.pipe.ts b/src/ui/databrowserModule/preview/previewFileType.pipe.ts index 6bba2c39f45564cfe3f4ac1f8f00529c143297f3..61241f1118c9faf5fa773fc6b3ebb2aeb7a79477 100644 --- a/src/ui/databrowserModule/preview/previewFileType.pipe.ts +++ b/src/ui/databrowserModule/preview/previewFileType.pipe.ts @@ -3,11 +3,11 @@ import { ViewerPreviewFile } from "src/services/state/dataStore.store"; import { determinePreviewFileType } from "./previewFileIcon.pipe"; @Pipe({ - name: 'previewFileTypePipe' + name: 'previewFileTypePipe', }) -export class PreviewFileTypePipe implements PipeTransform{ - public transform(file: ViewerPreviewFile): string{ +export class PreviewFileTypePipe implements PipeTransform { + public transform(file: ViewerPreviewFile): string { return determinePreviewFileType(file) } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts index f13e912815bbf039e07802e6471827e330631933..06228a4fc6cb8320a042e0f3129884655498a694 100644 --- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts +++ b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts @@ -1,22 +1,22 @@ -import { Component, ChangeDetectionStrategy, ChangeDetectorRef, Optional, Inject} from "@angular/core"; -import { - SingleDatasetBase, +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Optional} from "@angular/core"; +import { MAT_DIALOG_DATA } from "@angular/material"; +import { + AtlasViewerConstantsServices, DatabrowserService, KgSingleDatasetService, - AtlasViewerConstantsServices + SingleDatasetBase, } from "../singleDataset.base"; -import { MAT_DIALOG_DATA } from "@angular/material"; @Component({ selector: 'single-dataset-view', templateUrl: './singleDataset.template.html', styleUrls: [ - `./singleDataset.style.css` + `./singleDataset.style.css`, ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class SingleDatasetView extends SingleDatasetBase{ +export class SingleDatasetView extends SingleDatasetBase { constructor( dbService: DatabrowserService, @@ -24,8 +24,8 @@ export class SingleDatasetView extends SingleDatasetBase{ cdr: ChangeDetectorRef, constantService: AtlasViewerConstantsServices, - @Optional() @Inject(MAT_DIALOG_DATA) data: any - ){ + @Optional() @Inject(MAT_DIALOG_DATA) data: any, + ) { super(dbService, singleDatasetService, cdr, constantService, data) } } diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts index 44ee85ca5cc0a8e115b641c22bab450a5b64b884..59a931934d054847ef9e49862af3ead35a6a7f2e 100644 --- a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts +++ b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts @@ -1,8 +1,8 @@ -import { Component,ChangeDetectionStrategy, ChangeDetectorRef } from "@angular/core";import { +import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from "@angular/core"; import { SingleDatasetBase, DatabrowserService, KgSingleDatasetService, - AtlasViewerConstantsServices + AtlasViewerConstantsServices, } from "../singleDataset.base"; import { MatDialog, MatSnackBar } from "@angular/material"; import { SingleDatasetView } from "../detailedView/singleDataset.component"; @@ -11,9 +11,9 @@ import { SingleDatasetView } from "../detailedView/singleDataset.component"; selector: 'single-dataset-list-view', templateUrl: './singleDatasetListView.template.html', styleUrls: [ - './singleDatasetListView.style.css' + './singleDatasetListView.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class SingleDatasetListView extends SingleDatasetBase { @@ -23,21 +23,21 @@ export class SingleDatasetListView extends SingleDatasetBase { singleDatasetService: KgSingleDatasetService, cdr: ChangeDetectorRef, constantService: AtlasViewerConstantsServices, - private dialog:MatDialog, - private snackBar: MatSnackBar - ){ + private dialog: MatDialog, + private snackBar: MatSnackBar, + ) { super(_dbService, singleDatasetService, cdr, constantService) } - showDetailInfo(){ + public showDetailInfo() { this.dialog.open(SingleDatasetView, { - data: this.dataset + data: this.dataset, }) } - undoableRemoveFav(){ + public undoableRemoveFav() { this.snackBar.open(`Unpinned dataset: ${this.dataset.name}`, 'Undo', { - duration: 5000 + duration: 5000, }) .afterDismissed() .subscribe(({ dismissedByAction }) => { @@ -48,9 +48,9 @@ export class SingleDatasetListView extends SingleDatasetBase { this._dbService.removeFromFav(this.dataset) } - undoableAddFav(){ + public undoableAddFav() { this.snackBar.open(`Pin dataset: ${this.dataset.name}`, 'Undo', { - duration: 5000 + duration: 5000, }) .afterDismissed() .subscribe(({ dismissedByAction }) => { @@ -60,4 +60,4 @@ export class SingleDatasetListView extends SingleDatasetBase { }) this._dbService.saveToFav(this.dataset) } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.base.ts b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts index d0a8341adf725e656f086cfd8d4af590a5a9e808..9b07f874e454fb2fb98b5816beb8bdea8587670e 100644 --- a/src/ui/databrowserModule/singleDataset/singleDataset.base.ts +++ b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts @@ -1,37 +1,37 @@ -import { Input, OnInit, ChangeDetectorRef, TemplateRef, Output, EventEmitter } from "@angular/core"; -import { KgSingleDatasetService } from "../kgSingleDatasetService.service"; -import { Publication, File, DataEntry, ViewerPreviewFile } from 'src/services/state/dataStore.store' +import { ChangeDetectorRef, EventEmitter, Input, OnInit, Output, TemplateRef } from "@angular/core"; +import { Observable } from "rxjs"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; +import { IDataEntry, IFile, IPublication, ViewerPreviewFile } from 'src/services/state/dataStore.store' import { HumanReadableFileSizePipe } from "src/util/pipes/humanReadableFileSize.pipe"; import { DatabrowserService } from "../databrowser.service"; -import { Observable } from "rxjs"; +import { KgSingleDatasetService } from "../kgSingleDatasetService.service"; export { DatabrowserService, KgSingleDatasetService, ChangeDetectorRef, - AtlasViewerConstantsServices + AtlasViewerConstantsServices, } export class SingleDatasetBase implements OnInit { - @Input() ripple: boolean = false + @Input() public ripple: boolean = false /** * the name/desc/publications are placeholder/fallback entries * while the actual data is being loaded from KG with kgSchema and kgId */ - @Input() name?: string - @Input() description?: string - @Input() publications?: Publication[] + @Input() public name?: string + @Input() public description?: string + @Input() public publications?: IPublication[] - @Input() kgSchema?: string - @Input() kgId?: string + @Input() public kgSchema?: string + @Input() public kgId?: string - @Input() dataset: any = null - @Input() simpleMode: boolean = false + @Input() public dataset: any = null + @Input() public simpleMode: boolean = false - @Output() previewingFile: EventEmitter<ViewerPreviewFile> = new EventEmitter() + @Output() public previewingFile: EventEmitter<ViewerPreviewFile> = new EventEmitter() public preview: boolean = false private humanReadableFileSizePipe: HumanReadableFileSizePipe = new HumanReadableFileSizePipe() @@ -40,12 +40,12 @@ export class SingleDatasetBase implements OnInit { * sic! */ public kgReference: string[] = [] - public files: File[] = [] + public files: IFile[] = [] private methods: string[] = [] /** * sic! */ - private parcellationRegion: { name: string }[] + private parcellationRegion: Array<{ name: string }> private error: string = null @@ -54,15 +54,15 @@ export class SingleDatasetBase implements OnInit { public dlFromKgHref: string = null - public favedDataentries$: Observable<DataEntry[]> + public favedDataentries$: Observable<IDataEntry[]> constructor( private dbService: DatabrowserService, private singleDatasetService: KgSingleDatasetService, private cdr: ChangeDetectorRef, private constantService: AtlasViewerConstantsServices, - dataset?: any - ){ + dataset?: any, + ) { this.favedDataentries$ = this.dbService.favedDataentries$ if (dataset) { this.dataset = dataset @@ -76,7 +76,7 @@ export class SingleDatasetBase implements OnInit { } } - ngOnInit() { + public ngOnInit() { const { kgId, kgSchema, dataset } = this this.dlFromKgHref = this.singleDatasetService.getDownloadZipFromKgHref({ kgSchema, kgId }) if ( dataset ) { @@ -87,14 +87,14 @@ export class SingleDatasetBase implements OnInit { this.publications = publications this.files = files this.preview = preview - + return } - if (!kgSchema || !kgId) return + if (!kgSchema || !kgId) { return } this.fetchingSingleInfoInProgress = true this.singleDatasetService.getInfoFromKg({ kgId, - kgSchema + kgSchema, }) .then(json => { /** @@ -124,37 +124,37 @@ export class SingleDatasetBase implements OnInit { return this.kgSchema && this.kgId } - get numOfFiles(){ + get numOfFiles() { return this.files ? this.files.length : null } - get totalFileByteSize(){ + get totalFileByteSize() { return this.files ? this.files.reduce((acc, curr) => acc + curr.byteSize, 0) : null } - get tooltipText(){ + get tooltipText() { return `${this.numOfFiles} files ~ ${this.humanReadableFileSizePipe.transform(this.totalFileByteSize)}` } - get showFooter(){ + get showFooter() { return (this.kgReference && this.kgReference.length > 0) || (this.publications && this.publications.length > 0) || (this.files && this.files.length > 0) } - toggleFav() { + public toggleFav() { this.dbService.toggleFav(this.dataset) } - showPreviewList(templateRef: TemplateRef<any>){ + public showPreviewList(templateRef: TemplateRef<any>) { this.singleDatasetService.showPreviewList(templateRef) } - handlePreviewFile(file: ViewerPreviewFile){ + public handlePreviewFile(file: ViewerPreviewFile) { this.previewingFile.emit(file) this.singleDatasetService.previewFile(file, this.dataset) } diff --git a/src/ui/databrowserModule/util/aggregateArrayIntoRoot.pipe.ts b/src/ui/databrowserModule/util/aggregateArrayIntoRoot.pipe.ts index 513da12531b9531fd5618f23ba5fe1efffdd56aa..67ff69a94bc815f0d14d13c581fade63dad0f4da 100644 --- a/src/ui/databrowserModule/util/aggregateArrayIntoRoot.pipe.ts +++ b/src/ui/databrowserModule/util/aggregateArrayIntoRoot.pipe.ts @@ -1,16 +1,15 @@ import { Pipe, PipeTransform } from "@angular/core"; - @Pipe({ - name: 'aggregateArrayIntoRootPipe' + name: 'aggregateArrayIntoRootPipe', }) -export class AggregateArrayIntoRootPipe implements PipeTransform{ - public transform(array: any[], rootName: string = 'Root Element', childrenPropertyName: string = 'children'){ +export class AggregateArrayIntoRootPipe implements PipeTransform { + public transform(array: any[], rootName: string = 'Root Element', childrenPropertyName: string = 'children') { const returnObj = { - name: rootName + name: rootName, } returnObj[childrenPropertyName] = array return returnObj } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/util/appendFilterModality.pipe.ts b/src/ui/databrowserModule/util/appendFilterModality.pipe.ts index ef7e740c2ebd398729d6fe9797deb7e4e11b9e99..7894ca1bed066cb3eb9fef91a6c43212d6cee282 100644 --- a/src/ui/databrowserModule/util/appendFilterModality.pipe.ts +++ b/src/ui/databrowserModule/util/appendFilterModality.pipe.ts @@ -2,23 +2,23 @@ import { Pipe, PipeTransform } from "@angular/core"; import { CountedDataModality } from "../databrowser.service"; @Pipe({ - name: 'appendFilterModalityPipe' + name: 'appendFilterModalityPipe', }) -export class AppendFilerModalityPipe implements PipeTransform{ - public transform(root: CountedDataModality[], appending: CountedDataModality[][]): CountedDataModality[]{ - let returnArr:CountedDataModality[] = [...root] - for (const mods of appending){ - for (const mod of mods){ +export class AppendFilerModalityPipe implements PipeTransform { + public transform(root: CountedDataModality[], appending: CountedDataModality[][]): CountedDataModality[] { + let returnArr: CountedDataModality[] = [...root] + for (const mods of appending) { + for (const mod of mods) { // preserve the visibility const { visible } = returnArr.find(({ name }) => name === mod.name) || mod returnArr = returnArr.filter(({ name }) => name !== mod.name) returnArr = returnArr.concat({ ...mod, - visible + visible, }) } } return returnArr } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/util/copyProperty.pipe.spec.ts b/src/ui/databrowserModule/util/copyProperty.pipe.spec.ts index 548df650ab385b60a615859327d9c4b8605c82ca..56e40c339621ec7b84c1acaf50dd4e96f8bfecd6 100644 --- a/src/ui/databrowserModule/util/copyProperty.pipe.spec.ts +++ b/src/ui/databrowserModule/util/copyProperty.pipe.spec.ts @@ -1,19 +1,19 @@ -import { CopyPropertyPipe } from './copyProperty.pipe' import {} from 'jasmine' +import { CopyPropertyPipe } from './copyProperty.pipe' const array = [{ name : 'name1', - key1 : 'value1' -},{ + key1 : 'value1', +}, { name : 'name2', - key1 : 'value2' -},{ + key1 : 'value2', +}, { name : 'name3', key1 : 'value3', - key2 : 'oldvalue3' -},{ + key2 : 'oldvalue3', +}, { name : 'name4', - key2 : 'oldValue4' + key2 : 'oldValue4', }] describe('copyProperty.pipe works as expected', () => { @@ -22,45 +22,45 @@ describe('copyProperty.pipe works as expected', () => { }) it('copyProperty pipe should copy value of key1 as value of key2, even means overwriting old value', () => { const pipe = new CopyPropertyPipe() - const newItem = pipe.transform(array,'key1','key2') - + const newItem = pipe.transform(array, 'key1', 'key2') + expect(newItem[0]).toEqual({ name : 'name1', key1 : 'value1', - key2 : 'value1' + key2 : 'value1', }) expect(newItem[1]).toEqual({ name : 'name2', key1 : 'value2', - key2 : 'value2' + key2 : 'value2', }) expect(newItem[2]).toEqual({ name : 'name3', key1 : 'value3', - key2 : 'value3' + key2 : 'value3', }) expect(newItem[3]).toEqual({ name : 'name4', - key2 : undefined + key2 : undefined, }) }) it('if given undefined or null as array input, will return an emtpy array', () => { const pipe = new CopyPropertyPipe() - const nullItem = pipe.transform(null,'key1','key2') + const nullItem = pipe.transform(null, 'key1', 'key2') expect(nullItem).toEqual([]) - const undefinedItem = pipe.transform(undefined, 'key1','key2') + const undefinedItem = pipe.transform(undefined, 'key1', 'key2') expect(undefinedItem).toEqual([]) }) it('if either keys are undefined, return the original array, or emtpy array if original array is undefined', () => { const pipe = new CopyPropertyPipe() const nokey1 = pipe.transform(array, null, 'key2') expect(nokey1).toEqual(array) - + const nokey2 = pipe.transform(array, 'key1', null) expect(nokey2).toEqual(array) }) -}) \ No newline at end of file +}) diff --git a/src/ui/databrowserModule/util/copyProperty.pipe.ts b/src/ui/databrowserModule/util/copyProperty.pipe.ts index 0d9aec8897794536271d882d872b303ae577cfcb..9bc1bb6709f5f30d09ab8ba4819c6306b11188ba 100644 --- a/src/ui/databrowserModule/util/copyProperty.pipe.ts +++ b/src/ui/databrowserModule/util/copyProperty.pipe.ts @@ -1,25 +1,27 @@ -import { PipeTransform, Pipe } from "@angular/core"; +import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name : 'copyProperty' + name : 'copyProperty', }) -export class CopyPropertyPipe implements PipeTransform{ - private isDefined(obj){ +export class CopyPropertyPipe implements PipeTransform { + private isDefined(obj) { return typeof obj !== 'undefined' && obj !== null } - public transform(inputArray:any[],src:string,dest:string):any[]{ - if(!this.isDefined(inputArray)) + public transform(inputArray: any[], src: string, dest: string): any[] { + if (!this.isDefined(inputArray)) { return [] - if(!this.isDefined(src) || !this.isDefined(dest) ) + } + if (!this.isDefined(src) || !this.isDefined(dest) ) { return this.isDefined(inputArray) ? inputArray : [] + } - return inputArray.map(item=>{ - const newObj = Object.assign({},item) + return inputArray.map(item => { + const newObj = Object.assign({}, item) newObj[dest] = item[src] return newObj }) } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/util/datasetIsFaved.pipe.ts b/src/ui/databrowserModule/util/datasetIsFaved.pipe.ts index 35ec1bea374c1e7e46a894a854fca01b5f291873..a7ca19ef1c0e712e46a895ff73ea06952ab3ff25 100644 --- a/src/ui/databrowserModule/util/datasetIsFaved.pipe.ts +++ b/src/ui/databrowserModule/util/datasetIsFaved.pipe.ts @@ -1,12 +1,12 @@ -import { PipeTransform, Pipe } from "@angular/core"; -import { DataEntry } from "src/services/stateStore.service"; +import { Pipe, PipeTransform } from "@angular/core"; +import { IDataEntry } from "src/services/stateStore.service"; @Pipe({ - name: 'datasetIsFaved' + name: 'datasetIsFaved', }) -export class DatasetIsFavedPipe implements PipeTransform{ - public transform(favedDataEntry: DataEntry[], dataentry: DataEntry):boolean{ - if (!dataentry) return false +export class DatasetIsFavedPipe implements PipeTransform { + public transform(favedDataEntry: IDataEntry[], dataentry: IDataEntry): boolean { + if (!dataentry) { return false } return favedDataEntry.findIndex(ds => ds.id === dataentry.id) >= 0 } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/util/filterDataEntriesByMethods.pipe.ts b/src/ui/databrowserModule/util/filterDataEntriesByMethods.pipe.ts index 3c3db25513fe1663c8c9e8bc7308d85a9a290583..e323975a62d6c6c49eef2926a18ebae1a8d88638 100644 --- a/src/ui/databrowserModule/util/filterDataEntriesByMethods.pipe.ts +++ b/src/ui/databrowserModule/util/filterDataEntriesByMethods.pipe.ts @@ -1,15 +1,15 @@ -import { PipeTransform, Pipe } from "@angular/core"; -import { DataEntry } from "src/services/stateStore.service"; -import { temporaryFilterDataentryName, CountedDataModality } from '../databrowser.service' +import { Pipe, PipeTransform } from "@angular/core"; +import { IDataEntry } from "src/services/stateStore.service"; +import { CountedDataModality, temporaryFilterDataentryName } from '../databrowser.service' export const NO_METHODS = `NO_METHODS` @Pipe({ - name : 'filterDataEntriesByMethods' + name : 'filterDataEntriesByMethods', }) -export class FilterDataEntriesbyMethods implements PipeTransform{ - public transform(dataEntries:DataEntry[],dataModalities:CountedDataModality[]):DataEntry[]{ +export class FilterDataEntriesbyMethods implements PipeTransform { + public transform(dataEntries: IDataEntry[], dataModalities: CountedDataModality[]): IDataEntry[] { const noMethodDisplayName = temporaryFilterDataentryName(NO_METHODS) const includeEmpty = dataModalities.some(d => d.name === noMethodDisplayName) return dataEntries && dataModalities && dataModalities.length > 0 diff --git a/src/ui/databrowserModule/util/filterDataEntriesByRegion.pipe.ts b/src/ui/databrowserModule/util/filterDataEntriesByRegion.pipe.ts index 4efe62a4c80a893f1020279d9ce080467ed9f1dd..afa527a672e0dc5d4c35bffb00c7769352f86f79 100644 --- a/src/ui/databrowserModule/util/filterDataEntriesByRegion.pipe.ts +++ b/src/ui/databrowserModule/util/filterDataEntriesByRegion.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from "@angular/core"; -import { DataEntry } from "src/services/stateStore.service"; +import { IDataEntry } from "src/services/stateStore.service"; const isSubRegion = (high, low) => (high.id && low.id && high.id === low.id) || high.name === low.name ? true @@ -7,15 +7,15 @@ const isSubRegion = (high, low) => (high.id && low.id && high.id === low.id) || ? high.children.some(r => isSubRegion(r, low)) : false -const filterSubSelect = (dataEntry, selectedRegions) => - dataEntry.parcellationRegion.some(pr => selectedRegions.some(sr => isSubRegion(pr,sr))) +const filterSubSelect = (dataEntry, selectedRegions) => + dataEntry.parcellationRegion.some(pr => selectedRegions.some(sr => isSubRegion(pr, sr))) @Pipe({ - name: 'filterDataEntriesByRegion' + name: 'filterDataEntriesByRegion', }) -export class FilterDataEntriesByRegion implements PipeTransform{ - public transform(dataentries: DataEntry[], selectedRegions: any[], flattenedAllRegions: any[]) { +export class FilterDataEntriesByRegion implements PipeTransform { + public transform(dataentries: IDataEntry[], selectedRegions: any[], flattenedAllRegions: any[]) { return dataentries && selectedRegions && selectedRegions.length > 0 ? dataentries .map(de => { @@ -30,7 +30,7 @@ export class FilterDataEntriesByRegion implements PipeTransform{ */ return (r.id && id && r.id === id) || r.name === name - || r.relatedAreas && r.relatedAreas.length && r.relatedAreas.some(syn => syn === name) + || r.relatedAreas && r.relatedAreas.length && r.relatedAreas.some(syn => syn === name) }) return found ? { name, id, ...rest, ...found } @@ -38,10 +38,10 @@ export class FilterDataEntriesByRegion implements PipeTransform{ }) return { ...de, - parcellationRegion: newParcellationRegion + parcellationRegion: newParcellationRegion, } }) .filter(de => filterSubSelect(de, selectedRegions)) : dataentries } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/util/getKgSchemaIdFromFullId.pipe.ts b/src/ui/databrowserModule/util/getKgSchemaIdFromFullId.pipe.ts index 446a22789f3d2a05b7c0602de10fd47ef4a3be3f..8872ee88f6a07ec4e06b5ebeaabf559445aca1b5 100644 --- a/src/ui/databrowserModule/util/getKgSchemaIdFromFullId.pipe.ts +++ b/src/ui/databrowserModule/util/getKgSchemaIdFromFullId.pipe.ts @@ -1,14 +1,14 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name: 'getKgSchemaIdFromFullIdPipe' + name: 'getKgSchemaIdFromFullIdPipe', }) -export class GetKgSchemaIdFromFullIdPipe implements PipeTransform{ - public transform(fullId: string):[string, string]{ - if (!fullId) return [null, null] +export class GetKgSchemaIdFromFullIdPipe implements PipeTransform { + public transform(fullId: string): [string, string] { + if (!fullId) { return [null, null] } const match = /([\w\-\.]*\/[\w\-\.]*\/[\w\-\.]*\/[\w\-\.]*)\/([\w\-\.]*)$/.exec(fullId) - if (!match) return [null, null] + if (!match) { return [null, null] } return [match[1], match[2]] } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/util/pathToNestedChildren.pipe.spec.ts b/src/ui/databrowserModule/util/pathToNestedChildren.pipe.spec.ts index 3810225a2cd8830bb1e1b2b0417377c79c304dd0..d6532838faee3f8451c8add166afba1d94dd04bf 100644 --- a/src/ui/databrowserModule/util/pathToNestedChildren.pipe.spec.ts +++ b/src/ui/databrowserModule/util/pathToNestedChildren.pipe.spec.ts @@ -8,74 +8,74 @@ import {} from 'jasmine' const array1 = [{ pizza : 'pineapple', - path : 'root1' -},{ - path: 'root2' + path : 'root1', +}, { + path: 'root2', }] const expectedArray1 = [{ pizza : 'pineapple', path : 'root1', - children : [] -},{ + children : [], +}, { path : 'root2', - children : [] + children : [], }] const array2 = [{ - path : 'root' -},{ - path : 'root/dir' + path : 'root', +}, { + path : 'root/dir', }] const expectedArray2 = [{ path : 'root', children : [{ path : 'dir', - children : [] - }] + children : [], + }], }] const array3 = [{ name : 'eagle', - path : 'root1/dir1' -},{ - path : 'root1/dir2' -},{ - path : 'root2/dir3' -},{ - path : 'root2/dir4' + path : 'root1/dir1', +}, { + path : 'root1/dir2', +}, { + path : 'root2/dir3', +}, { + path : 'root2/dir4', }] const expectedArray3 = [{ path : 'root1', children : [{ name : 'eagle', path : 'dir1', - children : [] - },{ + children : [], + }, { path : 'dir2', - children : [] - }] -},{ - path :'root2', - children :[{ + children : [], + }], +}, { + path : 'root2', + children : [{ path : 'dir3', - children : [] - },{ + children : [], + }, { path : 'dir4', - children : [] - }] + children : [], + }], }] const array4 = [{ - path : 'root1/3\\/4' + path : 'root1/3\\/4', }] const expectedArray4 = [{ path : 'root1', children : [{ path : '3\\/4', - children : [] - }] + children : [], + }], }] const pipe = new PathToNestedChildren() @@ -91,7 +91,7 @@ describe('path to nested children', () => { }) it('transforms nested hierachy correctly', () => { - + const transformed2 = pipe.transform(array2) expect(transformed2).toEqual(expectedArray2) @@ -103,4 +103,4 @@ describe('path to nested children', () => { const transformed4 = pipe.transform(array4) expect(transformed4).toEqual(expectedArray4) }) -}) \ No newline at end of file +}) diff --git a/src/ui/databrowserModule/util/pathToNestedChildren.pipe.ts b/src/ui/databrowserModule/util/pathToNestedChildren.pipe.ts index 5bb9194e63afb51e532f2abe535f7525341fb8e8..5aec2dbee8ec9af49f8225760671357bbde7d26e 100644 --- a/src/ui/databrowserModule/util/pathToNestedChildren.pipe.ts +++ b/src/ui/databrowserModule/util/pathToNestedChildren.pipe.ts @@ -1,118 +1,119 @@ -import { PipeTransform, Pipe } from "@angular/core"; +import { Pipe, PipeTransform } from "@angular/core"; /** * The pipe transforms a flat array to a nested array, based on the path property, following Unix file path rule */ @Pipe({ - name : 'pathToNestedChildren' + name : 'pathToNestedChildren', }) -export class PathToNestedChildren implements PipeTransform{ - public transform(array:HasPathProperty[],prop?:string):NestedChildren[]{ - if(!array) +export class PathToNestedChildren implements PipeTransform { + public transform(array: HasPathProperty[], prop?: string): NestedChildren[] { + if (!array) { return [] + } return this.constructDoubleHeirachy(array) } - private constructDoubleHeirachy(array:HasPathProperty[]):NestedChildren[]{ - return array.reduce((acc:NestedChildren[],curr:HasPathProperty)=>{ - return this.checkEntryExist(acc,curr) ? - acc.map(item=> - this.checkEntryExist([item],curr) ? - this.concatItem(item,curr) : + private constructDoubleHeirachy(array: HasPathProperty[]): NestedChildren[] { + return array.reduce((acc: NestedChildren[], curr: HasPathProperty) => { + return this.checkEntryExist(acc, curr) ? + acc.map(item => + this.checkEntryExist([item], curr) ? + this.concatItem(item, curr) : item ) as NestedChildren[] : acc.concat( - this.constructNewItem(curr) + this.constructNewItem(curr), ) as NestedChildren[] - },[] as NestedChildren[]) + }, [] as NestedChildren[]) } - private constructNewItem(curr:HasPathProperty):NestedChildren{ + private constructNewItem(curr: HasPathProperty): NestedChildren { const finalPath = this.getCurrentPath(curr) === curr.path return Object.assign({}, - finalPath ? - curr : - {},{ + finalPath ? + curr : + {}, { path : this.getCurrentPath(curr), - children : finalPath ? + children : finalPath ? [] : this.constructDoubleHeirachy( - [ this.getNextLevelHeirachy(curr) ] - ) + [ this.getNextLevelHeirachy(curr) ], + ), }) } - private concatItem(item:NestedChildren,curr:HasPathProperty):NestedChildren{ + private concatItem(item: NestedChildren, curr: HasPathProperty): NestedChildren { const finalPath = this.getCurrentPath(curr) === curr.path return Object.assign({}, item, - finalPath ? + finalPath ? curr : {}, { children : item.children.concat( - finalPath ? + finalPath ? [] : this.constructDoubleHeirachy( - [ this.getNextLevelHeirachy(curr) ] - ) - ) + [ this.getNextLevelHeirachy(curr) ], + ), + ), }) } - private checkEntryExist(acc:NestedChildren[],curr:HasPathProperty):boolean{ + private checkEntryExist(acc: NestedChildren[], curr: HasPathProperty): boolean { const path = this.getCurrentPath(curr) - return acc.findIndex(it=>it.path === path) >= 0 + return acc.findIndex(it => it.path === path) >= 0 } - private getNextLevelHeirachy(file:HasPathProperty):HasPathProperty{ - return Object.assign({},file, + private getNextLevelHeirachy(file: HasPathProperty): HasPathProperty { + return Object.assign({}, file, { - path: file.path - ? file.path.slice(this.findRealIndex(file.path)+1) - : ' ' + path: file.path + ? file.path.slice(this.findRealIndex(file.path) + 1) + : ' ', }) } - private getNextPath(curr:HasPathProperty):string{ + private getNextPath(curr: HasPathProperty): string { const realIdx = this.findRealIndex(curr.path) - return realIdx > 0 ? + return realIdx > 0 ? curr.path.slice(realIdx + 1) : - realIdx == 0 ? + realIdx == 0 ? ' ' : curr.path } - private getCurrentPath(curr:HasPathProperty):string{ + private getCurrentPath(curr: HasPathProperty): string { const realIdx = this.findRealIndex(curr.path) - return realIdx > 0 ? - curr.path.slice(0,realIdx) : - realIdx == 0 ? + return realIdx > 0 ? + curr.path.slice(0, realIdx) : + realIdx == 0 ? ' ' : curr.path } - private findRealIndex(path:string):number{ + private findRealIndex(path: string): number { - if(!path){ + if (!path) { return 0 } let idx = path.indexOf('/') - while(path[idx-1] === '\\' && idx >= 0){ - idx = path.indexOf('/',idx + 1) + while (path[idx - 1] === '\\' && idx >= 0) { + idx = path.indexOf('/', idx + 1) } return idx } } -export interface HasPathProperty{ - path : string +export interface HasPathProperty { + path: string } -export interface NestedChildren{ - path : string - children : NestedChildren[] -} \ No newline at end of file +export interface NestedChildren { + path: string + children: NestedChildren[] +} diff --git a/src/ui/databrowserModule/util/regionBackgroundToRgb.pipe.ts b/src/ui/databrowserModule/util/regionBackgroundToRgb.pipe.ts index 4a03cd9dc7230bfbab69b5a4f19866e249914c80..c55e54fb914b96dbd3f8827e17630f84866b83f1 100644 --- a/src/ui/databrowserModule/util/regionBackgroundToRgb.pipe.ts +++ b/src/ui/databrowserModule/util/regionBackgroundToRgb.pipe.ts @@ -1,13 +1,13 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name: 'regionBackgroundToRgbPipe' + name: 'regionBackgroundToRgbPipe', }) -export class RegionBackgroundToRgbPipe implements PipeTransform{ - public transform(region = null): string{ +export class RegionBackgroundToRgbPipe implements PipeTransform { + public transform(region = null): string { return region && region.rgb ? `rgb(${region.rgb.join(',')})` : 'white' } -} \ No newline at end of file +} diff --git a/src/ui/databrowserModule/util/resetCounterModality.pipe.ts b/src/ui/databrowserModule/util/resetCounterModality.pipe.ts index 8f65af67ebc15bf25a02ef0fa05342d5a6ec068b..a44882f4ad3b09806c9ca05903698b8cf9faab4b 100644 --- a/src/ui/databrowserModule/util/resetCounterModality.pipe.ts +++ b/src/ui/databrowserModule/util/resetCounterModality.pipe.ts @@ -2,16 +2,16 @@ import { Pipe, PipeTransform } from "@angular/core"; import { CountedDataModality } from "../databrowser.service"; @Pipe({ - name: 'resetcounterModalityPipe' + name: 'resetcounterModalityPipe', }) -export class ResetCounterModalityPipe implements PipeTransform{ - public transform(inc: CountedDataModality[]):CountedDataModality[]{ +export class ResetCounterModalityPipe implements PipeTransform { + public transform(inc: CountedDataModality[]): CountedDataModality[] { return inc.map(({ occurance, ...rest }) => { return { occurance: 0, - ...rest + ...rest, } }) } -} \ No newline at end of file +} diff --git a/src/ui/help/help.component.ts b/src/ui/help/help.component.ts index 4c022e511bc73476243fc8e5e9ee4db773235ae6..8b9b8bf74110f04096c4ffdefb19e5c0a9380b3d 100644 --- a/src/ui/help/help.component.ts +++ b/src/ui/help/help.component.ts @@ -1,16 +1,16 @@ import { Component } from '@angular/core' -import { AtlasViewerConstantsServices } from 'src/atlasViewer/atlasViewer.constantService.service'; import { DomSanitizer } from '@angular/platform-browser'; +import { AtlasViewerConstantsServices } from 'src/atlasViewer/atlasViewer.constantService.service'; @Component({ selector: 'help-component', templateUrl: './help.template.html', styleUrls: [ - './help.style.css' - ] + './help.style.css', + ], }) -export class HelpComponent{ +export class HelpComponent { public generalHelp public sliceviewHelp @@ -23,9 +23,9 @@ export class HelpComponent{ public userDoc: string = `https://interactive-viewer-user-documentation.apps-dev.hbp.eu` constructor( - private constantService:AtlasViewerConstantsServices, - private sanitizer:DomSanitizer - ){ + private constantService: AtlasViewerConstantsServices, + private sanitizer: DomSanitizer, + ) { this.generalHelp = this.constantService.showHelpGeneralMap this.sliceviewHelp = this.constantService.showHelpSliceViewMap this.perspectiveviewHelp = this.constantService.showHelpPerspectiveViewMap @@ -34,4 +34,4 @@ export class HelpComponent{ this.contactEmailHref = `mailto:${this.constantService.supportEmailAddress}?Subject=[InteractiveAtlasViewer]%20Queries` this.contactEmail = this.constantService.supportEmailAddress } -} \ No newline at end of file +} diff --git a/src/ui/kgEntryViewer/kgentry.component.ts b/src/ui/kgEntryViewer/kgentry.component.ts index df948f272a2e3b5f75fd51059ae31b785d032c53..fada06ab098aafc41acc8f27848455897c1258e0 100644 --- a/src/ui/kgEntryViewer/kgentry.component.ts +++ b/src/ui/kgEntryViewer/kgentry.component.ts @@ -1,29 +1,29 @@ import { Component, Input } from "@angular/core"; -import { DataEntry } from "src/services/stateStore.service"; +import { IDataEntry } from "src/services/stateStore.service"; @Component({ selector : 'kg-entry-viewer', templateUrl : './kgentry.template.html', styleUrls : [ - './kgentry.style.css' - ] + './kgentry.style.css', + ], }) export class KgEntryViewer { - @Input() dataset: DataEntry + @Input() public dataset: IDataEntry - public kgData : any = null - public kgError : any = null + public kgData: any = null + public kgError: any = null - get tableColClass1(){ + get tableColClass1() { return `col-xs-4 col-lg-4 tableEntry` } - get tableColClass2(){ + get tableColClass2() { return `col-xs-8 col-lg-8 tableEntry` } - public isArray(v){ + public isArray(v) { return v.constructor === Array } } diff --git a/src/ui/kgEntryViewer/subjectViewer/subjectViewer.component.ts b/src/ui/kgEntryViewer/subjectViewer/subjectViewer.component.ts index ab291c490c5b460b80d6d92a6d1b4312ada9ba4f..6fe19573473bfd3089c102244d09324c456d6acb 100644 --- a/src/ui/kgEntryViewer/subjectViewer/subjectViewer.component.ts +++ b/src/ui/kgEntryViewer/subjectViewer/subjectViewer.component.ts @@ -1,36 +1,36 @@ -import { Component, Input, ChangeDetectionStrategy } from "@angular/core"; +import { ChangeDetectionStrategy, Component, Input } from "@angular/core"; @Component({ selector : 'kg-entry-viewer-subject-viewer', templateUrl : './subjectViewer.template.html', styleUrls : ['./subjectViewer.style.css'], - changeDetection : ChangeDetectionStrategy.OnPush + changeDetection : ChangeDetectionStrategy.OnPush, }) -export class SubjectViewer{ - @Input() subjects: any = [] +export class SubjectViewer { + @Input() public subjects: any = [] - get isSingle():boolean{ + get isSingle(): boolean { return this.subjects.constructor !== Array } - get species():string[]{ + get species(): string[] { return this.isSingle ? [this.subjects.children.species.value] - : this.subjects.reduce((acc:string[],curr:any) => + : this.subjects.reduce((acc: string[], curr: any) => acc.findIndex(species => species === curr.children.species.value) >= 0 ? acc : acc.concat(curr.children.species.value) , []) } - get groupBySex(){ + get groupBySex() { return this.isSingle ? [{ name : this.subjects.children.sex.value, - count : 1 + count : 1, }] - : this.subjects.reduce((acc:any[],curr) => + : this.subjects.reduce((acc: any[], curr) => acc.findIndex(item => item.name === curr.children.sex.value) >= 0 ? acc.map(item => item.name === curr.children.sex.value ? Object.assign({}, item, { count: item.count + 1 }) @@ -38,4 +38,4 @@ export class SubjectViewer{ : acc.concat({name: curr.children.sex.value, count: 1}) , []) } -} \ No newline at end of file +} diff --git a/src/ui/kgtos/kgtos.component.ts b/src/ui/kgtos/kgtos.component.ts index 53edea2a8ec08f93674f856bebe07b4c23c48c0c..e887d95f297b0e848df8f087169b09a14bd76444 100644 --- a/src/ui/kgtos/kgtos.component.ts +++ b/src/ui/kgtos/kgtos.component.ts @@ -1,22 +1,22 @@ import { Component } from "@angular/core"; -import { DatabrowserService } from "../databrowserModule/databrowser.service"; import { Observable } from "rxjs"; +import { DatabrowserService } from "../databrowserModule/databrowser.service"; @Component({ selector: 'kgtos-component', templateUrl: './kgtos.template.html', styleUrls: [ - './kgtos.style.css' - ] + './kgtos.style.css', + ], }) -export class KGToS{ +export class KGToS { public kgTos$: Observable<string> constructor( - private dbService: DatabrowserService - ){ + private dbService: DatabrowserService, + ) { this.kgTos$ = this.dbService.kgTos$ } -} \ No newline at end of file +} diff --git a/src/ui/layerbrowser/layerbrowser.component.ts b/src/ui/layerbrowser/layerbrowser.component.ts index 12cba26e28f78c39905f3af3be3d2efd619397be..8002cfd69a8fe6b3c334be4b5b0a994ba7cc2dc0 100644 --- a/src/ui/layerbrowser/layerbrowser.component.ts +++ b/src/ui/layerbrowser/layerbrowser.component.ts @@ -1,60 +1,64 @@ -import { Component, OnDestroy, Input, Pipe, PipeTransform, Output, EventEmitter, OnInit } from "@angular/core"; -import { NgLayerInterface } from "../../atlasViewer/atlasViewer.component"; -import { Store, select } from "@ngrx/store"; -import { ViewerStateInterface, isDefined, REMOVE_NG_LAYER, FORCE_SHOW_SEGMENT, safeFilter, getNgIds } from "../../services/stateStore.service"; -import { Subscription, Observable, combineLatest } from "rxjs"; -import { filter, map, shareReplay, distinctUntilChanged, throttleTime, debounceTime } from "rxjs/operators"; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, Pipe, PipeTransform } from "@angular/core"; +import { select, Store } from "@ngrx/store"; +import { combineLatest, Observable, Subscription } from "rxjs"; +import { debounceTime, distinctUntilChanged, filter, map, shareReplay, throttleTime } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; +import { LoggingService } from "src/services/logging.service"; import { NG_VIEWER_ACTION_TYPES } from "src/services/state/ngViewerState.store"; +import { getViewer } from "src/util/fn"; +import { INgLayerInterface } from "../../atlasViewer/atlasViewer.component"; +import { FORCE_SHOW_SEGMENT, getNgIds, isDefined, REMOVE_NG_LAYER, safeFilter, ViewerStateInterface } from "../../services/stateStore.service"; @Component({ selector : 'layer-browser', templateUrl : './layerbrowser.template.html', - styleUrls : [ + styleUrls : [ './layerbrowser.style.css', - '../btnShadow.style.css' - ] + '../btnShadow.style.css', + ], }) -export class LayerBrowser implements OnInit, OnDestroy{ +export class LayerBrowser implements OnInit, OnDestroy { - @Output() nonBaseLayersChanged: EventEmitter<NgLayerInterface[]> = new EventEmitter() + @Output() public nonBaseLayersChanged: EventEmitter<INgLayerInterface[]> = new EventEmitter() /** * TODO make untangle nglayernames and its dependency on ng */ - public loadedNgLayers$: Observable<NgLayerInterface[]> - public lockedLayers : string[] = [] + public loadedNgLayers$: Observable<INgLayerInterface[]> + public lockedLayers: string[] = [] - public nonBaseNgLayers$: Observable<NgLayerInterface[]> + public nonBaseNgLayers$: Observable<INgLayerInterface[]> + + public forceShowSegmentCurrentState: boolean | null = null + public forceShowSegment$: Observable<boolean|null> - public forceShowSegmentCurrentState : boolean | null = null - public forceShowSegment$ : Observable<boolean|null> - public ngLayers$: Observable<string[]> public advancedMode: boolean = false - private subscriptions : Subscription[] = [] - private disposeHandler : any - + private subscriptions: Subscription[] = [] + private disposeHandler: any + /* TODO temporary measure. when datasetID can be used, will use */ - public fetchedDataEntries$ : Observable<any> + public fetchedDataEntries$: Observable<any> @Input() - showPlaceholder: boolean = true + public showPlaceholder: boolean = true - darktheme$: Observable<boolean> + public darktheme$: Observable<boolean> constructor( - private store : Store<ViewerStateInterface>, - private constantsService: AtlasViewerConstantsServices){ + private store: Store<ViewerStateInterface>, + private constantsService: AtlasViewerConstantsServices, + private log: LoggingService, + ) { this.ngLayers$ = store.pipe( select('viewerState'), select('templateSelected'), map(templateSelected => { - if (!templateSelected) return [] - if (this.advancedMode) return [] + if (!templateSelected) { return [] } + if (this.advancedMode) { return [] } const { ngId , otherNgIds = []} = templateSelected @@ -64,9 +68,9 @@ export class LayerBrowser implements OnInit, OnDestroy{ ...templateSelected.parcellations.reduce((acc, curr) => { return acc.concat([ curr.ngId, - ...getNgIds(curr.regions) + ...getNgIds(curr.regions), ]) - }, []) + }, []), ] }), /** @@ -76,23 +80,23 @@ export class LayerBrowser implements OnInit, OnDestroy{ /** * remove falsy values */ - map(arr => arr.filter(v => !!v)) + map(arr => arr.filter(v => !!v)), ) this.loadedNgLayers$ = this.store.pipe( select('viewerState'), - select('loadedNgLayers') + select('loadedNgLayers'), ) this.nonBaseNgLayers$ = combineLatest( this.ngLayers$, - this.loadedNgLayers$ + this.loadedNgLayers$, ).pipe( map(([baseNgLayerNames, loadedNgLayers]) => { const baseNameSet = new Set(baseNgLayerNames) return loadedNgLayers.filter(l => !baseNameSet.has(l.name)) }), - distinctUntilChanged() + distinctUntilChanged(), ) /** @@ -102,69 +106,69 @@ export class LayerBrowser implements OnInit, OnDestroy{ this.fetchedDataEntries$ = this.store.pipe( select('dataStore'), safeFilter('fetchedDataEntries'), - map(v=>v.fetchedDataEntries) + map(v => v.fetchedDataEntries), ) this.forceShowSegment$ = this.store.pipe( select('ngViewerState'), filter(state => isDefined(state) && typeof state.forceShowSegment !== 'undefined'), - map(state => state.forceShowSegment) + map(state => state.forceShowSegment), ) - this.darktheme$ = this.constantsService.darktheme$.pipe( - shareReplay(1) + shareReplay(1), ) } - ngOnInit(){ + public ngOnInit() { this.subscriptions.push( this.nonBaseNgLayers$.pipe( - // on switching template, non base layer will fire + // on switching template, non base layer will fire // debounce to ensure that the non base layer is indeed an extra layer - debounceTime(160) - ).subscribe(layers => this.nonBaseLayersChanged.emit(layers)) + debounceTime(160), + ).subscribe(layers => this.nonBaseLayersChanged.emit(layers)), ) this.subscriptions.push( - this.forceShowSegment$.subscribe(state => this.forceShowSegmentCurrentState = state) + this.forceShowSegment$.subscribe(state => this.forceShowSegmentCurrentState = state), ) } - ngOnDestroy(){ + public ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()) } - public classVisible(layer:any):boolean{ + public classVisible(layer: any): boolean { return typeof layer.visible === 'undefined' ? true : layer.visible } - checkLocked(ngLayer:NgLayerInterface):boolean{ - if(!this.lockedLayers){ + public checkLocked(ngLayer: INgLayerInterface): boolean { + if (!this.lockedLayers) { /* locked layer undefined. always return false */ return false - }else{ + } else { return this.lockedLayers.findIndex(l => l === ngLayer.name) >= 0 } } - toggleVisibility(layer:any){ + public toggleVisibility(layer: any) { + const viewer = getViewer() const layerName = layer.name - if(!layerName){ - console.error('layer name not defined', layer) + if (!layerName) { + this.log.error('layer name not defined', layer) return } - const ngLayer = window['viewer'].layerManager.getLayerByName(layerName) - if(!ngLayer){ - console.error('ngLayer could not be found', layerName, window['viewer'].layerManager.managedLayers) + const ngLayer = viewer.layerManager.getLayerByName(layerName) + if (!ngLayer) { + this.log.error('ngLayer could not be found', layerName, viewer.layerManager.managedLayers) } ngLayer.setVisible(!ngLayer.visible) } - toggleForceShowSegment(ngLayer:any){ - if(!ngLayer || ngLayer.type !== 'segmentation'){ + public toggleForceShowSegment(ngLayer: any) { + if (!ngLayer || ngLayer.type !== 'segmentation') { /* toggle only on segmentation layer */ return } @@ -178,56 +182,56 @@ export class LayerBrowser implements OnInit, OnDestroy{ ? true : this.forceShowSegmentCurrentState === true ? false - : null + : null, }) } - removeAllNonBasicLayer(){ + public removeAllNonBasicLayer() { this.store.dispatch({ - type: NG_VIEWER_ACTION_TYPES.REMOVE_ALL_NONBASE_LAYERS + type: NG_VIEWER_ACTION_TYPES.REMOVE_ALL_NONBASE_LAYERS, }) } - removeLayer(layer:any){ - if(this.checkLocked(layer)){ - console.warn('this layer is locked and cannot be removed') + public removeLayer(layer: any) { + if (this.checkLocked(layer)) { + this.log.warn('this layer is locked and cannot be removed') return } this.store.dispatch({ type : REMOVE_NG_LAYER, layer : { - name : layer.name - } + name : layer.name, + }, }) } /** * TODO use observable and pipe to make this more perf */ - segmentationTooltip(){ - return `toggle segments visibility: + public segmentationTooltip() { + return `toggle segments visibility: ${this.forceShowSegmentCurrentState === true ? 'always show' : this.forceShowSegmentCurrentState === false ? 'always hide' : 'auto'}` } - get segmentationAdditionalClass(){ + get segmentationAdditionalClass() { return this.forceShowSegmentCurrentState === null ? 'blue' : this.forceShowSegmentCurrentState === true ? 'normal' : this.forceShowSegmentCurrentState === false ? 'muted' - : 'red' + : 'red' } public matTooltipPosition: string = 'below' } @Pipe({ - name: 'lockedLayerBtnClsPipe' + name: 'lockedLayerBtnClsPipe', }) -export class LockedLayerBtnClsPipe implements PipeTransform{ - public transform(ngLayer:NgLayerInterface, lockedLayers?: string[]): boolean{ +export class LockedLayerBtnClsPipe implements PipeTransform { + public transform(ngLayer: INgLayerInterface, lockedLayers?: string[]): boolean { return (lockedLayers && new Set(lockedLayers).has(ngLayer.name)) || false } -} \ No newline at end of file +} diff --git a/src/ui/logoContainer/logoContainer.component.ts b/src/ui/logoContainer/logoContainer.component.ts index 31eec2ab88fc61f9dffa4629938fd53b952b37a5..15b1607a15ad28181776107ee2c6fb69b84d0ab6 100644 --- a/src/ui/logoContainer/logoContainer.component.ts +++ b/src/ui/logoContainer/logoContainer.component.ts @@ -4,17 +4,17 @@ import { Component, HostBinding } from "@angular/core"; selector : 'logo-container', templateUrl : './logoContainer.template.html', styleUrls : [ - './logoContainer.style.css' - ] + './logoContainer.style.css', + ], }) -export class LogoContainer{ +export class LogoContainer { // only used to define size public imgSrc = USE_LOGO === 'hbp' ? 'res/image/HBP_Primary_RGB_WhiteText.png' : USE_LOGO === 'ebrains' ? `res/image/ebrains-logo-light.svg` : null - + public useLogo = USE_LOGO -} \ No newline at end of file +} diff --git a/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.component.ts b/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.component.ts index 77c4992fda331347cd90be8d5e8af289118a743e..f5a0da571abdab38017c7424b89029f30d4d449c 100644 --- a/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.component.ts +++ b/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.component.ts @@ -1,90 +1,89 @@ -import { Component, Input, HostBinding, ChangeDetectionStrategy, OnChanges } from "@angular/core"; - +import { ChangeDetectionStrategy, Component, HostBinding, Input, OnChanges } from "@angular/core"; @Component({ selector : 'nehuba-2dlandmark-unit', templateUrl : './landmarkUnit.template.html', styleUrls : [ - `./landmarkUnit.style.css` + `./landmarkUnit.style.css`, ], - changeDetection : ChangeDetectionStrategy.OnPush + changeDetection : ChangeDetectionStrategy.OnPush, }) -export class LandmarkUnit implements OnChanges{ - @Input() positionX : number = 0 - @Input() positionY : number = 0 - @Input() positionZ : number = 0 - - @Input() highlight : boolean = false - @Input() flatProjection : boolean = false +export class LandmarkUnit implements OnChanges { + @Input() public positionX: number = 0 + @Input() public positionY: number = 0 + @Input() public positionZ: number = 0 + + @Input() public highlight: boolean = false + @Input() public flatProjection: boolean = false - @Input() fasClass : string = 'fa-map-marker' + @Input() public fasClass: string = 'fa-map-marker' @HostBinding('style.transform') - transform : string = `translate(${this.positionX}px, ${this.positionY}px)` + public transform: string = `translate(${this.positionX}px, ${this.positionY}px)` get className() { return `fas ${this.fasClass}` } - styleNode(){ + public styleNode() { return({ 'color' : `rgb(${this.highlight ? HOVER_COLOR : NORMAL_COLOR})`, - 'z-index' : this.positionZ >= 0 ? 0 : -2 + 'z-index' : this.positionZ >= 0 ? 0 : -2, }) } - ngOnChanges(){ + public ngOnChanges() { this.transform = `translate(${this.positionX}px, ${this.positionY}px)` } - calcOpacity():number{ - return this.flatProjection ? + public calcOpacity(): number { + return this.flatProjection ? this.calcOpacityFlatMode() : - this.positionZ >= 0 ? + this.positionZ >= 0 ? 1 : - 0.4 + 0.4 } - calcOpacityFlatMode():number{ + public calcOpacityFlatMode(): number { return this.highlight ? 1.0 : 10 / (Math.abs(this.positionZ) + 10) } - styleShadow(){ - + public styleShadow() { + return ({ - 'background':`radial-gradient( + background: `radial-gradient( circle at center, - rgba(${this.highlight ? HOVER_COLOR + ',0.3' : NORMAL_COLOR + ',0.3'}) 10%, + rgba(${this.highlight ? HOVER_COLOR + ',0.3' : NORMAL_COLOR + ',0.3'}) 10%, rgba(${this.highlight ? HOVER_COLOR + ',0.8' : NORMAL_COLOR + ',0.8'}) 30%, rgba(0,0,0,0.8))`, - 'transform' : `scale(3,3)` + transform : `scale(3,3)`, }) } - - get markerTransform(){ - return `translate(0px, ${-1*this.positionZ}px)` + + get markerTransform() { + return `translate(0px, ${-1 * this.positionZ}px)` } - get beamTransform(){ - return `translate(0px, ${-1*this.positionZ/2}px) scale(1,${Math.abs(this.positionZ)})` + get beamTransform() { + return `translate(0px, ${-1 * this.positionZ / 2}px) scale(1,${Math.abs(this.positionZ)})` } - styleBeamDashedColor(){ + public styleBeamDashedColor() { return({ - 'border-left-color' :`rgba(${this.highlight ? HOVER_COLOR + ',0.8' : NORMAL_COLOR + ',0.8'})` + 'border-left-color' : `rgba(${this.highlight ? HOVER_COLOR + ',0.8' : NORMAL_COLOR + ',0.8'})`, }) } - styleBeamColor(inner:boolean){ + public styleBeamColor(inner: boolean) { return inner ? ({ - transform : `scale(1.0,1.0)`, - 'border-top-color' : `rgba(${this.highlight ? HOVER_COLOR + ',0.8' : NORMAL_COLOR + ',0.8'})` + "transform" : `scale(1.0,1.0)`, + 'border-top-color' : `rgba(${this.highlight ? HOVER_COLOR + ',0.8' : NORMAL_COLOR + ',0.8'})`, }) : ({ - transform : `scale(1.5,1.0)`, - 'border-top-color' : 'rgb(0,0,0)' + "transform" : `scale(1.5,1.0)`, + 'border-top-color' : 'rgb(0,0,0)', }) } } -const NORMAL_COLOR : string = '201,54,38' -const HOVER_COLOR : string = '250,150,80' \ No newline at end of file +const NORMAL_COLOR: string = '201,54,38' +const HOVER_COLOR: string = '250,150,80' diff --git a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts index 61ff6805c70c0fbcbdac2d2f877490d0eaeb7b79..7187071b9950bcfc6df08d956f3fbd7dbecb67f2 100644 --- a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts +++ b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from "@angular/core"; -import { Store, select } from "@ngrx/store"; +import { select, Store } from "@ngrx/store"; import { Observable } from "rxjs"; import { distinctUntilChanged, map } from "rxjs/operators"; import { SINGLE_PANEL } from "src/services/state/ngViewerState.store"; @@ -9,13 +9,13 @@ import { IavRootStoreInterface } from "src/services/stateStore.service"; selector: 'maximise-panel-button', templateUrl: './maximisePanelButton.template.html', styleUrls: [ - './maximisePanelButton.style.css' - ] + './maximisePanelButton.style.css', + ], }) -export class MaximmisePanelButton{ - - @Input() panelIndex: number +export class MaximmisePanelButton { + + @Input() public panelIndex: number private panelMode$: Observable<string> private panelOrder$: Observable<string> @@ -24,21 +24,21 @@ export class MaximmisePanelButton{ constructor( private store$: Store<IavRootStoreInterface>, - ){ + ) { this.panelMode$ = this.store$.pipe( select('ngViewerState'), select('panelMode'), - distinctUntilChanged() + distinctUntilChanged(), ) this.panelOrder$ = this.store$.pipe( select('ngViewerState'), select('panelOrder'), - distinctUntilChanged() + distinctUntilChanged(), ) this.isMaximised$ = this.panelMode$.pipe( - map(panelMode => panelMode === SINGLE_PANEL) + map(panelMode => panelMode === SINGLE_PANEL), ) } -} \ No newline at end of file +} diff --git a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts index 9971a1b4bb7d3b6515cba9d503028b939f6cbaa3..71405db1c4634b25112fa21df7d4b495ceb66cff 100644 --- a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts +++ b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts @@ -1,13 +1,13 @@ -import { Component, Input, Output,EventEmitter, ElementRef, ViewChild, AfterViewInit, ChangeDetectionStrategy, OnDestroy, OnInit, OnChanges } from "@angular/core"; -import { fromEvent, Subject, Observable, merge, concat, of, combineLatest } from "rxjs"; -import { map, switchMap, takeUntil, filter, scan, take, tap } from "rxjs/operators"; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, ViewChild } from "@angular/core"; +import { combineLatest, concat, fromEvent, merge, Observable, of, Subject } from "rxjs"; +import { filter, map, scan, switchMap, take, takeUntil, tap } from "rxjs/operators"; import { clamp } from "src/util/generator"; @Component({ selector : 'mobile-overlay', templateUrl : './mobileOverlay.template.html', styleUrls : [ - './mobileOverlay.style.css' + './mobileOverlay.style.css', ], styles : [ ` @@ -24,50 +24,50 @@ div:not(.active) > span:before width : 1em; display: inline-block; } - ` - ] + `, + ], }) -export class MobileOverlay implements OnInit, OnDestroy{ - @Input() tunableProperties : string [] = [] - @Output() deltaValue : EventEmitter<{delta:number, selectedProp : string}> = new EventEmitter() - @ViewChild('initiator', {read: ElementRef}) initiator : ElementRef - @ViewChild('mobileMenuContainer', {read: ElementRef}) menuContainer : ElementRef - @ViewChild('intersector', {read: ElementRef}) intersector: ElementRef +export class MobileOverlay implements OnInit, OnDestroy { + @Input() public tunableProperties: string [] = [] + @Output() public deltaValue: EventEmitter<{delta: number, selectedProp: string}> = new EventEmitter() + @ViewChild('initiator', {read: ElementRef}) public initiator: ElementRef + @ViewChild('mobileMenuContainer', {read: ElementRef}) public menuContainer: ElementRef + @ViewChild('intersector', {read: ElementRef}) public intersector: ElementRef - private _onDestroySubject : Subject<boolean> = new Subject() + private _onDestroySubject: Subject<boolean> = new Subject() - private _focusedProperties : string - get focusedProperty(){ + private _focusedProperties: string + get focusedProperty() { return this._focusedProperties ? this._focusedProperties : this.tunableProperties[0] } - get focusedIndex(){ + get focusedIndex() { return this._focusedProperties ? this.tunableProperties.findIndex(p => p === this._focusedProperties) : 0 } - public showScreen$ : Observable<boolean> - public showProperties$ : Observable<boolean> + public showScreen$: Observable<boolean> + public showProperties$: Observable<boolean> public showDelta$: Observable<boolean> public showInitiator$: Observable<boolean> - private _drag$ : Observable<any> + private _drag$: Observable<any> private intersectionObserver: IntersectionObserver - - ngOnDestroy(){ + + public ngOnDestroy() { this._onDestroySubject.next(true) this._onDestroySubject.complete() } - ngOnInit(){ + public ngOnInit() { const itemCount = this.tunableProperties.length const config = { root: this.intersector.nativeElement, - threshold: [...[...Array(itemCount)].map((_, k) => k / itemCount), 1] + threshold: [...[...Array(itemCount)].map((_, k) => k / itemCount), 1], } this.intersectionObserver = new IntersectionObserver((arg) => { @@ -78,115 +78,114 @@ export class MobileOverlay implements OnInit, OnDestroy{ }, config) this.intersectionObserver.observe(this.menuContainer.nativeElement) - - const scanDragScanAccumulator: (acc:TouchEvent[], item: TouchEvent, idx: number) => TouchEvent[] = (acc,curr) => acc.length < 2 + + const scanDragScanAccumulator: (acc: TouchEvent[], item: TouchEvent, idx: number) => TouchEvent[] = (acc, curr) => acc.length < 2 ? acc.concat(curr) : acc.slice(1).concat(curr) - + this._drag$ = fromEvent(this.initiator.nativeElement, 'touchmove').pipe( takeUntil(fromEvent(this.initiator.nativeElement, 'touchend').pipe( - filter((ev:TouchEvent) => ev.touches.length === 0) + filter((ev: TouchEvent) => ev.touches.length === 0), )), - map((ev:TouchEvent) => (ev.preventDefault(), ev.stopPropagation(), ev)), - filter((ev:TouchEvent) => ev.touches.length === 1), + map((ev: TouchEvent) => (ev.preventDefault(), ev.stopPropagation(), ev)), + filter((ev: TouchEvent) => ev.touches.length === 1), scan(scanDragScanAccumulator, []), - filter(ev => ev.length === 2) + filter(ev => ev.length === 2), ) this.showProperties$ = concat( of(false), - fromEvent(this.initiator.nativeElement, 'touchstart').pipe( + fromEvent(this.initiator.nativeElement, 'touchstart').pipe( switchMap(() => concat( this._drag$.pipe( map(double => ({ deltaX : double[1].touches[0].screenX - double[0].touches[0].screenX, - deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY + deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY, })), scan((acc, _curr) => acc), - map(v => v.deltaY ** 2 > v.deltaX ** 2) + map(v => v.deltaY ** 2 > v.deltaX ** 2), ), - of(false) - )) - ) + of(false), + )), + ), ) - this.showDelta$ = concat( of(false), - fromEvent(this.initiator.nativeElement, 'touchstart').pipe( + fromEvent(this.initiator.nativeElement, 'touchstart').pipe( switchMap(() => concat( this._drag$.pipe( map(double => ({ deltaX : double[1].touches[0].screenX - double[0].touches[0].screenX, - deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY + deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY, })), scan((acc, _curr) => acc), - map(v => v.deltaX ** 2 > v.deltaY ** 2) + map(v => v.deltaX ** 2 > v.deltaY ** 2), ), - of(false) - )) - ) + of(false), + )), + ), ) this.showInitiator$ = combineLatest( this.showProperties$, - this.showDelta$ + this.showDelta$, ).pipe( - map(([flag1, flag2]) => !flag1 && !flag2) + map(([flag1, flag2]) => !flag1 && !flag2), ) this.showScreen$ = combineLatest( merge( fromEvent(this.initiator.nativeElement, 'touchstart'), - fromEvent(this.initiator.nativeElement, 'touchend') + fromEvent(this.initiator.nativeElement, 'touchend'), ), - this.showInitiator$ + this.showInitiator$, ).pipe( - map(([ev, showInitiator] : [TouchEvent, boolean]) => showInitiator && ev.touches.length === 1) + map(([ev, showInitiator]: [TouchEvent, boolean]) => showInitiator && ev.touches.length === 1), ) fromEvent(this.initiator.nativeElement, 'touchstart').pipe( switchMap(() => this._drag$.pipe( map(double => ({ deltaX : double[1].touches[0].screenX - double[0].touches[0].screenX, - deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY + deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY, })), - scan((acc, curr:any) => ({ + scan((acc, curr: any) => ({ pass: acc.pass === null ? curr.deltaX ** 2 > curr.deltaY ** 2 : acc.pass, - delta: curr.deltaX + delta: curr.deltaX, }), { pass: null, - delta : null + delta : null, }), filter(ev => ev.pass), - map(ev => ev.delta) + map(ev => ev.delta), )), - takeUntil(this._onDestroySubject) + takeUntil(this._onDestroySubject), ).subscribe(ev => this.deltaValue.emit({ delta : ev, - selectedProp : this.focusedProperty + selectedProp : this.focusedProperty, })) const offsetObs$ = fromEvent(this.initiator.nativeElement, 'touchstart').pipe( switchMap(() => concat( this._drag$.pipe( - scan((acc,curr) => [acc[0], curr[1]]), + scan((acc, curr) => [acc[0], curr[1]]), map(double => ({ deltaX : double[1].touches[0].screenX - double[0].touches[0].screenX, - deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY + deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY, })), - ) - )) + ), + )), ) combineLatest( this.showProperties$, - offsetObs$ + offsetObs$, ).pipe( filter(v => v[0]), map(v => v[1]), - takeUntil(this._onDestroySubject) + takeUntil(this._onDestroySubject), ).subscribe(v => { const deltaY = v.deltaY const cellHeight = this.menuContainer && this.tunableProperties && this.tunableProperties.length > 0 && this.menuContainer.nativeElement.offsetHeight / this.tunableProperties.length @@ -200,17 +199,17 @@ export class MobileOverlay implements OnInit, OnDestroy{ this.showProperties$.pipe( takeUntil(this._onDestroySubject), - filter(v => !v) + filter(v => !v), ).subscribe(() => { - if(this.focusItemIndex >= 0){ + if (this.focusItemIndex >= 0) { this._focusedProperties = this.tunableProperties[this.focusItemIndex] } }) - + } public menuTransform = `translate(0px, 0px)` public focusItemIndex: number = 0 -} \ No newline at end of file +} diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 5b312c5794d474e5e3da488c500564a292b122ad..d8cfb72b3f6b3551cfb04ef079daba7effc9f02d 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -1,21 +1,22 @@ -import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, ComponentRef, OnInit, OnDestroy, ElementRef, Input, OnChanges } from "@angular/core"; -import { NehubaViewerUnit, computeDistance } from "./nehubaViewer/nehubaViewer.component"; -import { Store, select } from "@ngrx/store"; -import { ViewerStateInterface, safeFilter, CHANGE_NAVIGATION, isDefined, ADD_NG_LAYER, REMOVE_NG_LAYER, NgViewerStateInterface, MOUSE_OVER_LANDMARK, Landmark, PointLandmarkGeometry, PlaneLandmarkGeometry, OtherLandmarkGeometry, getNgIds, getMultiNgIdsRegionsLabelIndexMap, generateLabelIndexId, DataEntry } from "src/services/stateStore.service"; -import { Observable, Subscription, fromEvent, combineLatest, merge, of } from "rxjs"; -import { filter,map, take, scan, debounceTime, distinctUntilChanged, switchMap, skip, buffer, tap, switchMapTo, shareReplay, mapTo, takeUntil, throttleTime, withLatestFrom, startWith } from "rxjs/operators"; -import { AtlasViewerAPIServices, UserLandmark } from "../../atlasViewer/atlasViewer.apiService.service"; -import { timedValues } from "../../util/generator"; -import { AtlasViewerConstantsServices } from "../../atlasViewer/atlasViewer.constantService.service"; -import { StateInterface as ViewerConfigStateInterface } from "src/services/state/viewerConfig.store"; +import { Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Input, OnChanges, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; +import { select, Store } from "@ngrx/store"; +import { combineLatest, fromEvent, merge, Observable, of, Subscription } from "rxjs"; import { pipeFromArray } from "rxjs/internal/util/pipe"; -import { NEHUBA_READY, H_ONE_THREE, V_ONE_THREE, FOUR_PANEL, SINGLE_PANEL, NG_VIEWER_ACTION_TYPES } from "src/services/state/ngViewerState.store"; +import { buffer, debounceTime, distinctUntilChanged, filter, map, mapTo, scan, shareReplay, skip, startWith, switchMap, switchMapTo, take, takeUntil, tap, throttleTime, withLatestFrom } from "rxjs/operators"; +import { LoggingService } from "src/services/logging.service"; +import { FOUR_PANEL, H_ONE_THREE, NEHUBA_READY, NG_VIEWER_ACTION_TYPES, SINGLE_PANEL, V_ONE_THREE } from "src/services/state/ngViewerState.store"; import { MOUSE_OVER_SEGMENTS } from "src/services/state/uiState.store"; -import { getHorizontalOneThree, getVerticalOneThree, getFourPanel, getSinglePanel } from "./util"; -import { SELECT_REGIONS_WITH_ID, NEHUBA_LAYER_CHANGED, VIEWERSTATE_ACTION_TYPES } from "src/services/state/viewerState.store"; -import { isSame } from "src/util/fn"; +import { StateInterface as ViewerConfigStateInterface } from "src/services/state/viewerConfig.store"; +import { NEHUBA_LAYER_CHANGED, SELECT_REGIONS_WITH_ID, VIEWERSTATE_ACTION_TYPES } from "src/services/state/viewerState.store"; +import { ADD_NG_LAYER, CHANGE_NAVIGATION, generateLabelIndexId, getMultiNgIdsRegionsLabelIndexMap, getNgIds, ILandmark, IOtherLandmarkGeometry, IPlaneLandmarkGeometry, IPointLandmarkGeometry, isDefined, MOUSE_OVER_LANDMARK, NgViewerStateInterface, REMOVE_NG_LAYER, safeFilter, ViewerStateInterface } from "src/services/stateStore.service"; +import { getExportNehuba, isSame } from "src/util/fn"; +import { AtlasViewerAPIServices, IUserLandmark } from "../../atlasViewer/atlasViewer.apiService.service"; +import { AtlasViewerConstantsServices } from "../../atlasViewer/atlasViewer.constantService.service"; +import { timedValues } from "../../util/generator"; +import { computeDistance, NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component"; +import { getFourPanel, getHorizontalOneThree, getSinglePanel, getVerticalOneThree } from "./util"; -const getProxyUrl = (ngUrl) => `nifti://${BACKEND_URL}preview/file?fileUrl=${encodeURIComponent(ngUrl.replace(/^nifti:\/\//,''))}` +const getProxyUrl = (ngUrl) => `nifti://${BACKEND_URL}preview/file?fileUrl=${encodeURIComponent(ngUrl.replace(/^nifti:\/\//, ''))}` const getProxyOther = ({source}) => /AUTH_227176556f3c4bb38df9feea4b91200c/.test(source) ? { transform: [ @@ -23,42 +24,42 @@ const getProxyOther = ({source}) => /AUTH_227176556f3c4bb38df9feea4b91200c/.test 1e6, 0, 0, - 0 + 0, ], [ 0, 1e6, 0, - 0 + 0, ], [ 0, 0, 1e6, - 0 + 0, ], [ 0, 0, 0, - 1 - ] - ] -}: {} + 1, + ], + ], +} : {} const isFirstRow = (cell: HTMLElement) => { - const { parentElement:row } = cell - const { parentElement:container } = row + const { parentElement: row } = cell + const { parentElement: container } = row return container.firstElementChild === row } -const isFirstCell = (cell:HTMLElement) => { - const { parentElement:row } = cell +const isFirstCell = (cell: HTMLElement) => { + const { parentElement: row } = cell return row.firstElementChild === cell } -const scanFn : (acc:[boolean, boolean, boolean], curr: CustomEvent) => [boolean, boolean, boolean] = (acc, curr) => { +const scanFn: (acc: [boolean, boolean, boolean], curr: CustomEvent) => [boolean, boolean, boolean] = (acc, curr) => { - const target = <HTMLElement>curr.target + const target = curr.target as HTMLElement const targetIsFirstRow = isFirstRow(target) const targetIsFirstCell = isFirstCell(target) const idx = targetIsFirstRow @@ -80,17 +81,17 @@ const scanFn : (acc:[boolean, boolean, boolean], curr: CustomEvent) => [boolean, selector : 'ui-nehuba-container', templateUrl : './nehubaContainer.template.html', styleUrls : [ - `./nehubaContainer.style.css` - ] + `./nehubaContainer.style.css`, + ], }) -export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ +export class NehubaContainer implements OnInit, OnChanges, OnDestroy { - @ViewChild('container',{read:ViewContainerRef}) container : ViewContainerRef + @ViewChild('container', {read: ViewContainerRef}) public container: ViewContainerRef - private nehubaViewerFactory : ComponentFactory<NehubaViewerUnit> + private nehubaViewerFactory: ComponentFactory<NehubaViewerUnit> - public viewerLoaded : boolean = false + public viewerLoaded: boolean = false private viewerPerformanceConfig$: Observable<ViewerConfigStateInterface> @@ -101,50 +102,50 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ public perspectiveViewLoading$: Observable<string|null> private templateSelected$: Observable<any> - private newViewer$ : Observable<any> - private selectedParcellation$ : Observable<any> - private selectedRegions$ : Observable<any[]> - public selectedLandmarks$ : Observable<any[]> - public selectedPtLandmarks$ : Observable<any[]> - private hideSegmentations$ : Observable<boolean> - - private fetchedSpatialDatasets$ : Observable<Landmark[]> - private userLandmarks$ : Observable<UserLandmark[]> - - public onHoverSegment$ : Observable<any> + private newViewer$: Observable<any> + private selectedParcellation$: Observable<any> + private selectedRegions$: Observable<any[]> + public selectedLandmarks$: Observable<any[]> + public selectedPtLandmarks$: Observable<any[]> + private hideSegmentations$: Observable<boolean> + + private fetchedSpatialDatasets$: Observable<ILandmark[]> + private userLandmarks$: Observable<IUserLandmark[]> + + public onHoverSegment$: Observable<any> @Input() - private currentOnHover: {segments:any, landmark:any, userLandmark: any} + private currentOnHover: {segments: any, landmark: any, userLandmark: any} @Input() - private currentOnHoverObs$: Observable<{segments:any, landmark:any, userLandmark: any}> + private currentOnHoverObs$: Observable<{segments: any, landmark: any, userLandmark: any}> public onHoverSegments$: Observable<any[]> - private navigationChanges$ : Observable<any> - public spatialResultsVisible$ : Observable<boolean> - private spatialResultsVisible : boolean = false + private navigationChanges$: Observable<any> + public spatialResultsVisible$: Observable<boolean> + private spatialResultsVisible: boolean = false - private selectedTemplate : any | null - private selectedRegionIndexSet : Set<string> = new Set() - public fetchedSpatialData : Landmark[] = [] + private selectedTemplate: any | null + private selectedRegionIndexSet: Set<string> = new Set() + public fetchedSpatialData: ILandmark[] = [] - private ngLayersRegister : Partial<NgViewerStateInterface> = {layers : [], forceShowSegment: null} - private ngLayers$ : Observable<NgViewerStateInterface> - - public selectedParcellation : any | null + private ngLayersRegister: Partial<NgViewerStateInterface> = {layers : [], forceShowSegment: null} + private ngLayers$: Observable<NgViewerStateInterface> - private cr : ComponentRef<NehubaViewerUnit> - public nehubaViewer : NehubaViewerUnit + public selectedParcellation: any | null + + private cr: ComponentRef<NehubaViewerUnit> + public nehubaViewer: NehubaViewerUnit private multiNgIdsRegionsLabelIndexMap: Map<string, Map<number, any>> = new Map() - private landmarksLabelIndexMap : Map<number, any> = new Map() - private landmarksNameMap : Map<string,number> = new Map() - - private subscriptions : Subscription[] = [] - private nehubaViewerSubscriptions : Subscription[] = [] + private landmarksLabelIndexMap: Map<number, any> = new Map() + private landmarksNameMap: Map<string, number> = new Map() + + private subscriptions: Subscription[] = [] + private nehubaViewerSubscriptions: Subscription[] = [] - public nanometersToOffsetPixelsFn : Function[] = [] - private viewerConfig : Partial<ViewerConfigStateInterface> = {} + public nanometersToOffsetPixelsFn: Array<(...arg) => any> = [] + private viewerConfig: Partial<ViewerConfigStateInterface> = {} private viewPanels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement] = [null, null, null, null] public panelMode$: Observable<string> @@ -158,12 +159,15 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ private ngPanelTouchMove$: Observable<any> constructor( - private constantService : AtlasViewerConstantsServices, - private apiService :AtlasViewerAPIServices, - private csf:ComponentFactoryResolver, - private store : Store<ViewerStateInterface>, - private elementRef : ElementRef - ){ + private constantService: AtlasViewerConstantsServices, + private apiService: AtlasViewerAPIServices, + private csf: ComponentFactoryResolver, + private store: Store<ViewerStateInterface>, + private elementRef: ElementRef, + private log: LoggingService, + ) { + + this.exportNehuba = getExportNehuba() this.useMobileUI$ = this.constantService.useMobileUI$ @@ -176,14 +180,14 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ distinctUntilChanged(), debounceTime(200), tap(viewerConfig => this.viewerConfig = viewerConfig ), - filter(() => isDefined(this.nehubaViewer) && isDefined(this.nehubaViewer.nehubaViewer)) + filter(() => isDefined(this.nehubaViewer) && isDefined(this.nehubaViewer.nehubaViewer)), ) this.panelMode$ = this.store.pipe( select('ngViewerState'), select('panelMode'), distinctUntilChanged(), - shareReplay(1) + shareReplay(1), ) this.panelOrder$ = this.store.pipe( @@ -191,9 +195,9 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ select('panelOrder'), distinctUntilChanged(), shareReplay(1), - tap(panelOrder => this.panelOrder = panelOrder) + tap(panelOrder => this.panelOrder = panelOrder), ) - + this.redrawLayout$ = this.store.pipe( select('ngViewerState'), select('nehubaReady'), @@ -201,8 +205,8 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ filter(v => !!v), switchMapTo(combineLatest( this.panelMode$, - this.panelOrder$ - )) + this.panelOrder$, + )), ) this.nehubaViewerFactory = this.csf.resolveComponentFactory(NehubaViewerUnit) @@ -210,34 +214,34 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ this.templateSelected$ = this.store.pipe( select('viewerState'), select('templateSelected'), - distinctUntilChanged(isSame) + distinctUntilChanged(isSame), ) this.newViewer$ = this.templateSelected$.pipe( - filter(v => !!v) + filter(v => !!v), ) this.selectedParcellation$ = this.store.pipe( select('viewerState'), safeFilter('parcellationSelected'), - map(state=>state.parcellationSelected), - distinctUntilChanged() + map(state => state.parcellationSelected), + distinctUntilChanged(), ) this.selectedRegions$ = this.store.pipe( select('viewerState'), select('regionsSelected'), - filter(rs => !!rs) + filter(rs => !!rs), ) this.selectedLandmarks$ = this.store.pipe( select('viewerState'), safeFilter('landmarksSelected'), - map(state => state.landmarksSelected) + map(state => state.landmarksSelected), ) this.selectedPtLandmarks$ = this.selectedLandmarks$.pipe( - map(lms => lms.filter(lm => lm.geometry.type === 'point')) + map(lms => lms.filter(lm => lm.geometry.type === 'point')), ) this.fetchedSpatialDatasets$ = this.store.pipe( @@ -251,43 +255,43 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ this.navigationChanges$ = this.store.pipe( select('viewerState'), safeFilter('navigation'), - map(state=>state.navigation) + map(state => state.navigation), ) this.spatialResultsVisible$ = this.store.pipe( select('spatialSearchState'), - map(state=> isDefined(state) ? + map(state => isDefined(state) ? isDefined(state.spatialDataVisible) ? state.spatialDataVisible : true : true), - distinctUntilChanged() + distinctUntilChanged(), ) this.userLandmarks$ = this.store.pipe( select('viewerState'), select('userLandmarks'), - distinctUntilChanged() + distinctUntilChanged(), ) this.sliceViewLoadingMain$ = fromEvent(this.elementRef.nativeElement, 'sliceRenderEvent').pipe( scan(scanFn, [null, null, null]), - shareReplay(1) + shareReplay(1), ) this.sliceViewLoading0$ = this.sliceViewLoadingMain$ .pipe( - map(arr => arr[0]) + map(arr => arr[0]), ) this.sliceViewLoading1$ = this.sliceViewLoadingMain$ .pipe( - map(arr => arr[1]) + map(arr => arr[1]), ) this.sliceViewLoading2$ = this.sliceViewLoadingMain$ .pipe( - map(arr => arr[2]) + map(arr => arr[2]), ) /* missing chunk perspective view */ @@ -311,33 +315,33 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ // : this.regionsLabelIndexMap.get(lastLoadedIdNum) // ? `Loading ${this.regionsLabelIndexMap.get(lastLoadedIdNum).name}` : 'Loading meshes ...' - }) + }), ) this.ngLayers$ = this.store.pipe( - select('ngViewerState') + select('ngViewerState'), ) this.hideSegmentations$ = this.ngLayers$.pipe( map(state => isDefined(state) ? state.layers.findIndex(l => l.mixability === 'nonmixable') >= 0 - : false) + : false), ) this.ngPanelTouchMove$ = fromEvent(this.elementRef.nativeElement, 'touchstart').pipe( - switchMap((touchStartEv:TouchEvent) => fromEvent(this.elementRef.nativeElement, 'touchmove').pipe( + switchMap((touchStartEv: TouchEvent) => fromEvent(this.elementRef.nativeElement, 'touchmove').pipe( tap((ev: TouchEvent) => ev.preventDefault()), - scan((acc, curr: TouchEvent) => [curr, ...acc.slice(0,1)], []), - map((touchMoveEvs:TouchEvent[]) => { + scan((acc, curr: TouchEvent) => [curr, ...acc.slice(0, 1)], []), + map((touchMoveEvs: TouchEvent[]) => { return { touchStartEv, - touchMoveEvs + touchMoveEvs, } }), takeUntil(fromEvent(this.elementRef.nativeElement, 'touchend').pipe( - filter((ev: TouchEvent) => ev.touches.length === 0)) - ) - )) + filter((ev: TouchEvent) => ev.touches.length === 0)), + ), + )), ) } @@ -353,7 +357,9 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ private findPanelIndex = (panel: HTMLElement) => this.viewPanels.findIndex(p => p === panel) - ngOnInit(){ + private exportNehuba: any + + public ngOnInit() { // translation on mobile this.subscriptions.push( @@ -366,32 +372,25 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ const deltaY = touchMoveEvs[1].touches[0].screenY - touchMoveEvs[0].touches[0].screenY // figure out the target of touch start - const panelIdx = this.findPanelIndex(touchStartEv.target as HTMLElement) + const panelIdx = this.findPanelIndex(touchStartEv.target as HTMLElement) // translate if panelIdx < 3 - if (panelIdx >=0 && panelIdx < 3) { + if (panelIdx >= 0 && panelIdx < 3) { const { position } = this.nehubaViewer.nehubaViewer.ngviewer.navigationState const pos = position.spatialCoordinates - window['export_nehuba'].vec3.set(pos, deltaX, deltaY, 0) - window['export_nehuba'].vec3.transformMat4(pos, pos, this.nehubaViewer.viewportToDatas[panelIdx]) + this.exportNehuba.vec3.set(pos, deltaX, deltaY, 0) + this.exportNehuba.vec3.transformMat4(pos, pos, this.nehubaViewer.viewportToDatas[panelIdx]) position.changed.dispatch() - } - - // rotate 3D if panelIdx === 3 - - else if (panelIdx === 3) { + } else if (panelIdx === 3) { const {perspectiveNavigationState} = this.nehubaViewer.nehubaViewer.ngviewer - const { vec3 } = window['export_nehuba'] + const { vec3 } = this.exportNehuba perspectiveNavigationState.pose.rotateRelative(vec3.fromValues(0, 1, 0), -deltaX / 4.0 * Math.PI / 180.0) perspectiveNavigationState.pose.rotateRelative(vec3.fromValues(1, 0, 0), deltaY / 4.0 * Math.PI / 180.0) this.nehubaViewer.nehubaViewer.ngviewer.perspectiveNavigationState.changed.dispatch() + } else { + this.log.warn(`panelIdx not found`) } - - // this shoudn't happen? - else { - console.warn(`panelIdx not found`) - } - }) + }), ) // perspective reorientation on mobile @@ -402,69 +401,66 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ const d1 = computeDistance( [touchMoveEvs[1].touches[0].screenX, touchMoveEvs[1].touches[0].screenY], - [touchMoveEvs[1].touches[1].screenX, touchMoveEvs[1].touches[1].screenY] + [touchMoveEvs[1].touches[1].screenX, touchMoveEvs[1].touches[1].screenY], ) const d2 = computeDistance( [touchMoveEvs[0].touches[0].screenX, touchMoveEvs[0].touches[0].screenY], - [touchMoveEvs[0].touches[1].screenX, touchMoveEvs[0].touches[1].screenY] + [touchMoveEvs[0].touches[1].screenX, touchMoveEvs[0].touches[1].screenY], ) - const factor = d1/d2 + const factor = d1 / d2 // figure out the target of touch start - const panelIdx = this.findPanelIndex(touchStartEv.target as HTMLElement) + const panelIdx = this.findPanelIndex(touchStartEv.target as HTMLElement) // zoom slice view if slice - if (panelIdx >=0 && panelIdx < 3) { + if (panelIdx >= 0 && panelIdx < 3) { this.nehubaViewer.nehubaViewer.ngviewer.navigationState.zoomBy(factor) - } - - // zoom perspective view if on perspective - else if (panelIdx === 3) { + } else if (panelIdx === 3) { const { minZoom = null, maxZoom = null } = (this.selectedTemplate.nehubaConfig && this.selectedTemplate.nehubaConfig.layout && this.selectedTemplate.nehubaConfig.layout.useNehubaPerspective && this.selectedTemplate.nehubaConfig.layout.useNehubaPerspective.restrictZoomLevel) || {} - + const { zoomFactor } = this.nehubaViewer.nehubaViewer.ngviewer.perspectiveNavigationState - if (!!minZoom && zoomFactor.value * factor < minZoom) return - if (!!maxZoom && zoomFactor.value * factor > maxZoom) return + if (!!minZoom && zoomFactor.value * factor < minZoom) { return } + if (!!maxZoom && zoomFactor.value * factor > maxZoom) { return } zoomFactor.zoomBy(factor) } - }) + }), ) this.hoveredPanelIndices$ = fromEvent(this.elementRef.nativeElement, 'mouseover').pipe( - switchMap((ev:MouseEvent) => merge( + switchMap((ev: MouseEvent) => merge( of(this.findPanelIndex(ev.target as HTMLElement)), fromEvent(this.elementRef.nativeElement, 'mouseout').pipe( - mapTo(null) - ) + mapTo(null), + ), )), debounceTime(20), - shareReplay(1) + shareReplay(1), ) // TODO deprecate /* each time a new viewer is initialised, take the first event to get the translation function */ this.subscriptions.push( this.newViewer$.pipe( - switchMap(() => pipeFromArray([...takeOnePipe])(fromEvent(this.elementRef.nativeElement, 'sliceRenderEvent'))) - ).subscribe((events)=>{ - for (const idx in [0,1,2]) { + switchMap(() => pipeFromArray([...takeOnePipe])(fromEvent(this.elementRef.nativeElement, 'sliceRenderEvent'))), + ).subscribe((events) => { + for (const idx in [0, 1, 2]) { const ev = events[idx] as CustomEvent this.viewPanels[idx] = ev.target as HTMLElement this.nanometersToOffsetPixelsFn[idx] = ev.detail.nanometersToOffsetPixels } - }) + }), ) this.subscriptions.push( this.newViewer$.pipe( switchMapTo(fromEvent(this.elementRef.nativeElement, 'perpspectiveRenderEvent').pipe( - take(1) + take(1), )), - ).subscribe(ev => this.viewPanels[3] = ((ev as CustomEvent).target) as HTMLElement) + ).subscribe(ev => this.viewPanels[3] = ((ev as CustomEvent).target) as HTMLElement), ) this.subscriptions.push( @@ -473,21 +469,21 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ /** * TODO be smarter with event stream */ - if (!this.nehubaViewer) return + if (!this.nehubaViewer) { return } /** * TODO smarter with event stream */ - if (!viewPanels.every(v => !!v)) return + if (!viewPanels.every(v => !!v)) { return } switch (mode) { - case H_ONE_THREE:{ + case H_ONE_THREE: { const element = this.removeExistingPanels() const newEl = getHorizontalOneThree(viewPanels) element.appendChild(newEl) break; } - case V_ONE_THREE:{ + case V_ONE_THREE: { const element = this.removeExistingPanels() const newEl = getVerticalOneThree(viewPanels) element.appendChild(newEl) @@ -505,9 +501,9 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ element.appendChild(newEl) break; } - default: + default: } - for (const panel of viewPanels){ + for (const panel of viewPanels) { (panel as HTMLElement).classList.add('neuroglancer-panel') } @@ -515,69 +511,69 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ // see https://trello.com/c/oJOnlc6v/60-enlarge-panel-allow-user-rearrange-panel-position // further investigaation required this.nehubaViewer.redraw() - }) + }), ) this.subscriptions.push( this.viewerPerformanceConfig$.subscribe(config => { this.nehubaViewer.applyPerformanceConfig(config) - }) + }), ) this.subscriptions.push( this.fetchedSpatialDatasets$.subscribe(datasets => { - this.landmarksLabelIndexMap = new Map(datasets.map((v,idx) => [idx, v]) as [number, any][]) - this.landmarksNameMap = new Map(datasets.map((v,idx) => [v.name, idx] as [string, number])) - }) + this.landmarksLabelIndexMap = new Map(datasets.map((v, idx) => [idx, v]) as Array<[number, any]>) + this.landmarksNameMap = new Map(datasets.map((v, idx) => [v.name, idx] as [string, number])) + }), ) this.subscriptions.push( combineLatest( this.fetchedSpatialDatasets$, - this.spatialResultsVisible$ - ).subscribe(([fetchedSpatialData,visible])=>{ + this.spatialResultsVisible$, + ).subscribe(([fetchedSpatialData, visible]) => { this.fetchedSpatialData = fetchedSpatialData - if(visible && this.fetchedSpatialData && this.fetchedSpatialData.length > 0){ + if (visible && this.fetchedSpatialData && this.fetchedSpatialData.length > 0) { this.nehubaViewer.addSpatialSearch3DLandmarks( this.fetchedSpatialData - .map(data=> data.geometry.type === 'point' - ? (data.geometry as PointLandmarkGeometry).position + .map(data => data.geometry.type === 'point' + ? (data.geometry as IPointLandmarkGeometry).position : data.geometry.type === 'plane' ? [ - (data.geometry as PlaneLandmarkGeometry).corners, - [[0,1,2], [0,2,3]] - ] + (data.geometry as IPlaneLandmarkGeometry).corners, + [[0, 1, 2], [0, 2, 3]], + ] : data.geometry.type === 'mesh' ? [ - (data.geometry as OtherLandmarkGeometry).vertices, - (data.geometry as OtherLandmarkGeometry).meshIdx + (data.geometry as IOtherLandmarkGeometry).vertices, + (data.geometry as IOtherLandmarkGeometry).meshIdx, ] - : null) + : null), ) - }else{ + } else { if (this.nehubaViewer && this.nehubaViewer.removeSpatialSearch3DLandmarks instanceof Function) { this.nehubaViewer.removeSpatialSearch3DLandmarks() } } - }) + }), ) this.subscriptions.push( this.userLandmarks$.subscribe(landmarks => { - if(this.nehubaViewer){ + if (this.nehubaViewer) { this.nehubaViewer.updateUserLandmarks(landmarks) } - }) + }), ) - + this.subscriptions.push( - this.spatialResultsVisible$.subscribe(visible => this.spatialResultsVisible = visible) + this.spatialResultsVisible$.subscribe(visible => this.spatialResultsVisible = visible), ) this.subscriptions.push( this.newViewer$.pipe( - skip(1) + skip(1), ).subscribe(() => { /* on selecting of new template, remove additional nglayers */ @@ -589,15 +585,15 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ this.store.dispatch({ type : REMOVE_NG_LAYER, layer : { - name : layerName - } + name : layerName, + }, }) }) - }) + }), ) this.subscriptions.push( - this.templateSelected$.subscribe(() => this.destroynehuba()) + this.templateSelected$.subscribe(() => this.destroynehuba()), ) /* order of subscription will determine the order of execution */ @@ -614,25 +610,25 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ return deepCopiedState }), withLatestFrom(this.selectedParcellation$.pipe( - startWith(null) - )) - ).subscribe(([templateSelected, parcellationSelected])=>{ + startWith(null), + )), + ).subscribe(([templateSelected, parcellationSelected]) => { this.store.dispatch({ type: NEHUBA_READY, - nehubaReady: false + nehubaReady: false, }) - this.nehubaViewerSubscriptions.forEach(s=>s.unsubscribe()) + this.nehubaViewerSubscriptions.forEach(s => s.unsubscribe()) this.selectedTemplate = templateSelected this.createNewNehuba(templateSelected) const foundParcellation = parcellationSelected - && templateSelected.parcellations.find(parcellation=> parcellationSelected.name === parcellation.name) + && templateSelected.parcellations.find(parcellation => parcellationSelected.name === parcellation.name) this.handleParcellation(foundParcellation || templateSelected.parcellations[0]) const nehubaConfig = templateSelected.nehubaConfig const initialSpec = nehubaConfig.dataset.initialNgState const {layers} = initialSpec - + const dispatchLayers = Object.keys(layers).map(key => { const layer = { name : key, @@ -645,7 +641,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ : layers[key].visible, transform : typeof layers[key].transform === 'undefined' ? null - : layers[key].transform + : layers[key].transform, } this.ngLayersRegister.layers.push(layer) return layer @@ -653,38 +649,38 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ this.store.dispatch({ type : ADD_NG_LAYER, - layer : dispatchLayers + layer : dispatchLayers, }) - }) + }), ) this.subscriptions.push( - this.selectedParcellation$.subscribe((this.handleParcellation).bind(this)) + this.selectedParcellation$.subscribe((this.handleParcellation).bind(this)), ) this.subscriptions.push( combineLatest( this.selectedRegions$.pipe( - distinctUntilChanged() + distinctUntilChanged(), ), this.hideSegmentations$.pipe( - distinctUntilChanged() + distinctUntilChanged(), ), this.ngLayers$.pipe( - map(state => state.forceShowSegment) + map(state => state.forceShowSegment), ), - this.selectedParcellation$ + this.selectedParcellation$, ) - .subscribe(([regions,hideSegmentFlag,forceShowSegment, selectedParcellation])=>{ - if(!this.nehubaViewer) return + .subscribe(([regions, hideSegmentFlag, forceShowSegment, selectedParcellation]) => { + if (!this.nehubaViewer) { return } const { ngId: defaultNgId } = selectedParcellation /* selectedregionindexset needs to be updated regardless of forceshowsegment */ - this.selectedRegionIndexSet = new Set(regions.map(({ngId = defaultNgId, labelIndex})=>generateLabelIndexId({ ngId, labelIndex }))) + this.selectedRegionIndexSet = new Set(regions.map(({ngId = defaultNgId, labelIndex}) => generateLabelIndexId({ ngId, labelIndex }))) - if( forceShowSegment === false || (forceShowSegment === null && hideSegmentFlag) ){ + if ( forceShowSegment === false || (forceShowSegment === null && hideSegmentFlag) ) { this.nehubaViewer.hideAllSeg() return } @@ -692,44 +688,44 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ this.selectedRegionIndexSet.size > 0 ? this.nehubaViewer.showSegs([...this.selectedRegionIndexSet]) : this.nehubaViewer.showAllSeg() - } - ) + }, + ), ) - this.subscriptions.push( this.ngLayers$.subscribe(ngLayersInterface => { - if(!this.nehubaViewer) return + if (!this.nehubaViewer) { return } const newLayers = ngLayersInterface.layers.filter(l => this.ngLayersRegister.layers.findIndex(ol => ol.name === l.name) < 0) const removeLayers = this.ngLayersRegister.layers.filter(l => ngLayersInterface.layers.findIndex(nl => nl.name === l.name) < 0) - - if(newLayers.length > 0){ - const newLayersObj:any = {} + + if (newLayers.length > 0) { + const newLayersObj: any = {} newLayers.forEach(({ name, source, ...rest }) => newLayersObj[name] = { ...rest, - source + source, // source: getProxyUrl(source), // ...getProxyOther({source}) }) - if(!this.nehubaViewer.nehubaViewer || !this.nehubaViewer.nehubaViewer.ngviewer){ + if (!this.nehubaViewer.nehubaViewer || !this.nehubaViewer.nehubaViewer.ngviewer) { this.nehubaViewer.initNiftiLayers.push(newLayersObj) - }else{ + } else { this.nehubaViewer.loadLayer(newLayersObj) } this.ngLayersRegister.layers = this.ngLayersRegister.layers.concat(newLayers) } - if(removeLayers.length > 0){ + if (removeLayers.length > 0) { removeLayers.forEach(l => { - if(this.nehubaViewer.removeLayer({ - name : l.name - })) + if (this.nehubaViewer.removeLayer({ + name : l.name, + })) { this.ngLayersRegister.layers = this.ngLayersRegister.layers.filter(rl => rl.name !== l.name) + } }) } - }) + }), ) /* setup init view state */ @@ -737,17 +733,17 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ this.navigationChanges$, this.selectedRegions$, ).pipe( - filter(() => !!this.nehubaViewer) - ).subscribe(([navigation,regions])=>{ + filter(() => !!this.nehubaViewer), + ).subscribe(([navigation, regions]) => { this.nehubaViewer.initNav = { ...navigation, - positionReal: true + positionReal: true, } this.nehubaViewer.initRegions = regions.map(({ ngId, labelIndex }) => generateLabelIndexId({ ngId, labelIndex })) }) this.subscriptions.push( - this.navigationChanges$.subscribe(this.handleDispatchedNavigationChange.bind(this)) + this.navigationChanges$.subscribe(this.handleDispatchedNavigationChange.bind(this)), ) /* handler to open/select landmark */ @@ -757,30 +753,30 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ clickObs$.pipe( buffer( clickObs$.pipe( - debounceTime(200) - ) + debounceTime(200), + ), ), - filter(arr => arr.length >= 2) + filter(arr => arr.length >= 2), ) .subscribe(() => { const { currentOnHover } = this this.store.dispatch({ type : VIEWERSTATE_ACTION_TYPES.DOUBLE_CLICK_ON_VIEWER, - payload: { ...currentOnHover } + payload: { ...currentOnHover }, }) - }) + }), ) this.subscriptions.push( this.selectedLandmarks$.pipe( map(lms => lms.map(lm => this.landmarksNameMap.get(lm.name))), - debounceTime(16) + debounceTime(16), ).subscribe(indices => { const filteredIndices = indices.filter(v => typeof v !== 'undefined' && v !== null) - if(this.nehubaViewer) { + if (this.nehubaViewer) { this.nehubaViewer.spatialLandmarkSelectionChanged(filteredIndices) } - }) + }), ) // To get WebGL content when taking screenshot @@ -794,37 +790,37 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ } // datasetViewerRegistry : Set<string> = new Set() - public showObliqueScreen$ : Observable<boolean> - public showObliqueSelection$ : Observable<boolean> - public showObliqueRotate$ : Observable<boolean> + public showObliqueScreen$: Observable<boolean> + public showObliqueSelection$: Observable<boolean> + public showObliqueRotate$: Observable<boolean> - ngOnChanges(){ + public ngOnChanges() { if (this.currentOnHoverObs$) { this.onHoverSegments$ = this.currentOnHoverObs$.pipe( - map(({ segments }) => segments) + map(({ segments }) => segments), ) const sortByFreshness: (acc: any[], curr: any[]) => any[] = (acc, curr) => { - const getLayerName = ({layer} = {layer:{}}) => { - const { name } = <any>layer + const getLayerName = ({layer} = {layer: {}}) => { + const { name } = layer as any return name } - + const newEntries = (curr && curr.filter(entry => { const name = getLayerName(entry) return acc.map(getLayerName).indexOf(name) < 0 })) || [] - + const entryChanged: (itemPrevState, newArr) => boolean = (itemPrevState, newArr) => { const layerName = getLayerName(itemPrevState) const { segment } = itemPrevState const foundItem = newArr.find((_item) => getLayerName(_item) === layerName) - + if (foundItem) { - const { segment:foundSegment } = foundItem - return segment !== foundSegment + const { segment: foundSegment } = foundItem + return segment !== foundSegment } else { /** * if item was not found in the new array, meaning hovering nothing @@ -832,30 +828,30 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ return segment !== null } } - + const getItemFromLayerName = (item, arr) => { const foundItem = arr.find(i => getLayerName(i) === getLayerName(item)) return foundItem ? foundItem : { layer: item.layer, - segment: null + segment: null, } } - + const getReduceExistingLayers = (newArr) => ([changed, unchanged], _curr) => { const changedFlag = entryChanged(_curr, newArr) return changedFlag ? [ changed.concat( getItemFromLayerName(_curr, newArr) ), unchanged ] : [ changed, unchanged.concat(_curr) ] } - + /** * now, for all the previous layers, separate into changed and unchanged layers */ const [changed, unchanged] = acc.reduce(getReduceExistingLayers(curr), [[], []]) return [...newEntries, ...changed, ...unchanged] - } + } // TODO to be deprected soon @@ -872,84 +868,84 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ map(({ segment }) => { return { labelIndex: (isNaN(segment) && Number(segment.labelIndex)) || null, - foundRegion: (isNaN(segment) && segment) || null + foundRegion: (isNaN(segment) && segment) || null, } - }) + }), ) } } - ngOnDestroy(){ - this.subscriptions.forEach(s=>s.unsubscribe()) + public ngOnDestroy() { + this.subscriptions.forEach(s => s.unsubscribe()) } - toggleMaximiseMinimise(index: number){ + public toggleMaximiseMinimise(index: number) { this.store.dispatch({ type: NG_VIEWER_ACTION_TYPES.TOGGLE_MAXIMISE, payload: { - index - } + index, + }, }) } public tunableMobileProperties = ['Oblique Rotate X', 'Oblique Rotate Y', 'Oblique Rotate Z', 'Remove extra layers'] public selectedProp = null - handleMobileOverlayTouchEnd(focusItemIndex){ + public handleMobileOverlayTouchEnd(focusItemIndex) { if (this.tunableMobileProperties[focusItemIndex] === 'Remove extra layers') { this.store.dispatch({ - type: NG_VIEWER_ACTION_TYPES.REMOVE_ALL_NONBASE_LAYERS + type: NG_VIEWER_ACTION_TYPES.REMOVE_ALL_NONBASE_LAYERS, }) } } - handleMobileOverlayEvent(obj:any){ + public handleMobileOverlayEvent(obj: any) { const {delta, selectedProp} = obj this.selectedProp = selectedProp const idx = this.tunableMobileProperties.findIndex(p => p === selectedProp) - if (idx === 0) this.nehubaViewer.obliqueRotateX(delta) - if (idx === 1) this.nehubaViewer.obliqueRotateY(delta) - if (idx === 2) this.nehubaViewer.obliqueRotateZ(delta) + if (idx === 0) { this.nehubaViewer.obliqueRotateX(delta) } + if (idx === 1) { this.nehubaViewer.obliqueRotateY(delta) } + if (idx === 2) { this.nehubaViewer.obliqueRotateZ(delta) } } - returnTruePos(quadrant:number,data:any){ + public returnTruePos(quadrant: number, data: any) { const pos = quadrant > 2 ? - [0,0,0] : + [0, 0, 0] : this.nanometersToOffsetPixelsFn && this.nanometersToOffsetPixelsFn[quadrant] ? - this.nanometersToOffsetPixelsFn[quadrant](data.geometry.position.map(n=>n*1e6)) : - [0,0,0] + this.nanometersToOffsetPixelsFn[quadrant](data.geometry.position.map(n => n * 1e6)) : + [0, 0, 0] return pos } - getPositionX(quadrant:number,data:any){ - return this.returnTruePos(quadrant,data)[0] + public getPositionX(quadrant: number, data: any) { + return this.returnTruePos(quadrant, data)[0] } - getPositionY(quadrant:number,data:any){ - return this.returnTruePos(quadrant,data)[1] + public getPositionY(quadrant: number, data: any) { + return this.returnTruePos(quadrant, data)[1] } - getPositionZ(quadrant:number,data:any){ - return this.returnTruePos(quadrant,data)[2] + public getPositionZ(quadrant: number, data: any) { + return this.returnTruePos(quadrant, data)[2] } // handles mouse enter/leave landmarks in 2D - handleMouseEnterLandmark(spatialData:any){ + public handleMouseEnterLandmark(spatialData: any) { spatialData.highlight = true this.store.dispatch({ type : MOUSE_OVER_LANDMARK, - landmark : spatialData._label + landmark : spatialData._label, }) } - handleMouseLeaveLandmark(spatialData:any){ + public handleMouseLeaveLandmark(spatialData: any) { spatialData.highlight = false this.store.dispatch({ - type :MOUSE_OVER_LANDMARK, - landmark : null + type : MOUSE_OVER_LANDMARK, + landmark : null, }) } - private handleParcellation(parcellation:any){ + private handleParcellation(parcellation: any) { /** * parcellaiton may be undefined */ @@ -965,7 +961,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ this.multiNgIdsRegionsLabelIndexMap = getMultiNgIdsRegionsLabelIndexMap(parcellation) this.nehubaViewer.multiNgIdsLabelIndexMap = this.multiNgIdsRegionsLabelIndexMap - this.nehubaViewer.auxilaryMeshIndices = parcellation.auxillaryMeshIndices || [] + this.nehubaViewer.auxilaryMeshIndices = parcellation.auxillaryMeshIndices || [] /* TODO replace with proper KG id */ /** @@ -976,23 +972,23 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ } /* related spatial search */ - oldNavigation : any = {} - spatialSearchPagination : number = 0 + public oldNavigation: any = {} + public spatialSearchPagination: number = 0 - private destroynehuba(){ + private destroynehuba() { /** * TODO if plugin subscribes to viewerHandle, and then new template is selected, changes willl not be be sent - * could be considered as a bug. + * could be considered as a bug. */ this.apiService.interactiveViewer.viewerHandle = null - if( this.cr ) this.cr.destroy() + if ( this.cr ) { this.cr.destroy() } this.container.clear() this.viewerLoaded = false this.nehubaViewer = null } - private createNewNehuba(template:any){ + private createNewNehuba(template: any) { this.viewerLoaded = true this.cr = this.container.createComponent(this.nehubaViewerFactory) @@ -1012,50 +1008,50 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ perspectiveOrientation, perspectiveZoom, position: [0, 1, 2].map(idx => voxelSize[idx] * voxelCoordinates[idx]), - zoom: zoomFactor + zoom: zoomFactor, } this.handleEmittedNavigationChange(initNavigation) this.oldNavigation = initNavigation - + if (gpuLimit) { const initialNgState = nehubaConfig && nehubaConfig.dataset && nehubaConfig.dataset.initialNgState - initialNgState['gpuLimit'] = gpuLimit + initialNgState.gpuLimit = gpuLimit } - + this.nehubaViewer.config = nehubaConfig /* TODO replace with id from KG */ this.nehubaViewer.templateId = template.name this.nehubaViewerSubscriptions.push( - this.nehubaViewer.debouncedViewerPositionChange.subscribe(this.handleEmittedNavigationChange.bind(this)) + this.nehubaViewer.debouncedViewerPositionChange.subscribe(this.handleEmittedNavigationChange.bind(this)), ) this.nehubaViewerSubscriptions.push( this.nehubaViewer.layersChanged.subscribe(() => { this.store.dispatch({ - type: NEHUBA_LAYER_CHANGED + type: NEHUBA_LAYER_CHANGED, }) - }) + }), ) this.nehubaViewerSubscriptions.push( /** - * TODO when user selects new template, window.viewer + * TODO when user selects new template, window.viewer */ this.nehubaViewer.nehubaReady.subscribe(() => { this.store.dispatch({ type: NEHUBA_READY, - nehubaReady: true + nehubaReady: true, }) - }) + }), ) - + const accumulatorFn: ( - acc:Map<string, { segment: string | null, segmentId: number | null }>, - arg: {layer: {name: string}, segmentId: number|null, segment: string | null} + acc: Map<string, { segment: string | null, segmentId: number | null }>, + arg: {layer: {name: string}, segmentId: number|null, segment: string | null}, ) => Map<string, {segment: string | null, segmentId: number|null}> = (acc, arg) => { const { layer, segment, segmentId } = arg @@ -1066,10 +1062,10 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ } this.nehubaViewerSubscriptions.push( - + this.nehubaViewer.mouseoverSegmentEmitter.pipe( scan(accumulatorFn, new Map()), - map(map => Array.from(map.entries()).filter(([_ngId, { segmentId }]) => segmentId)) + map(map => Array.from(map.entries()).filter(([_ngId, { segmentId }]) => segmentId)), ).subscribe(arrOfArr => { this.store.dispatch({ type: MOUSE_OVER_SEGMENTS, @@ -1078,65 +1074,65 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ layer: { name: ngId, }, - segment: segment || `${ngId}#${segmentId}` + segment: segment || `${ngId}#${segmentId}`, } - } ) + } ), }) - }) + }), ) this.nehubaViewerSubscriptions.push( this.nehubaViewer.mouseoverLandmarkEmitter.pipe( - throttleTime(100) + throttleTime(100), ).subscribe(label => { this.store.dispatch({ type : MOUSE_OVER_LANDMARK, - landmark : label + landmark : label, }) - }) + }), ) this.nehubaViewerSubscriptions.push( this.nehubaViewer.mouseoverUserlandmarkEmitter.pipe( - throttleTime(160) + throttleTime(160), ).subscribe(label => { this.store.dispatch({ type: VIEWERSTATE_ACTION_TYPES.MOUSEOVER_USER_LANDMARK_LABEL, payload: { - label - } + label, + }, }) - }) + }), ) this.setupViewerHandleApi() } - private setupViewerHandleApi(){ + private setupViewerHandleApi() { this.apiService.interactiveViewer.viewerHandle = { - setNavigationLoc : (coord,realSpace?)=>this.nehubaViewer.setNavigationState({ + setNavigationLoc : (coord, realSpace?) => this.nehubaViewer.setNavigationState({ position : coord, - positionReal : typeof realSpace !== 'undefined' ? realSpace : true + positionReal : typeof realSpace !== 'undefined' ? realSpace : true, }), /* TODO introduce animation */ - moveToNavigationLoc : (coord,realSpace?)=>this.nehubaViewer.setNavigationState({ + moveToNavigationLoc : (coord, realSpace?) => this.nehubaViewer.setNavigationState({ position : coord, - positionReal : typeof realSpace !== 'undefined' ? realSpace : true + positionReal : typeof realSpace !== 'undefined' ? realSpace : true, }), - setNavigationOri : (quat)=>this.nehubaViewer.setNavigationState({ - orientation : quat + setNavigationOri : (quat) => this.nehubaViewer.setNavigationState({ + orientation : quat, }), /* TODO introduce animation */ - moveToNavigationOri : (quat)=>this.nehubaViewer.setNavigationState({ - orientation : quat + moveToNavigationOri : (quat) => this.nehubaViewer.setNavigationState({ + orientation : quat, }), showSegment : (labelIndex) => { /** * TODO reenable with updated select_regions api */ - console.warn(`showSegment is temporarily disabled`) + this.log.warn(`showSegment is temporarily disabled`) - // if(!this.selectedRegionIndexSet.has(labelIndex)) + // if(!this.selectedRegionIndexSet.has(labelIndex)) // this.store.dispatch({ // type : SELECT_REGIONS, // selectRegions : [labelIndex, ...this.selectedRegionIndexSet] @@ -1144,30 +1140,33 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ }, add3DLandmarks : landmarks => { // TODO check uniqueness of ID - if(!landmarks.every(l => isDefined(l.id))) + if (!landmarks.every(l => isDefined(l.id))) { throw new Error('every landmarks needs to be identified with the id field') - if(!landmarks.every(l=> isDefined(l.position))) + } + if (!landmarks.every(l => isDefined(l.position))) { throw new Error('every landmarks needs to have position defined') - if(!landmarks.every(l => l.position.constructor === Array) || !landmarks.every(l => l.position.every(v => !isNaN(v))) || !landmarks.every(l => l.position.length == 3)) + } + if (!landmarks.every(l => l.position.constructor === Array) || !landmarks.every(l => l.position.every(v => !isNaN(v))) || !landmarks.every(l => l.position.length == 3)) { throw new Error('position needs to be a length 3 tuple of numbers ') + } this.store.dispatch({ type: VIEWERSTATE_ACTION_TYPES.ADD_USERLANDMARKS, - landmarks : landmarks + landmarks, }) }, remove3DLandmarks : landmarkIds => { this.store.dispatch({ type: VIEWERSTATE_ACTION_TYPES.REMOVE_USER_LANDMARKS, payload: { - landmarkIds - } + landmarkIds, + }, }) }, hideSegment : (labelIndex) => { /** * TODO reenable with updated select_regions api */ - console.warn(`hideSegment is temporarily disabled`) + this.log.warn(`hideSegment is temporarily disabled`) // if(this.selectedRegionIndexSet.has(labelIndex)){ // this.store.dispatch({ @@ -1185,32 +1184,32 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ }) this.store.dispatch({ type : SELECT_REGIONS_WITH_ID, - selectRegionIds + selectRegionIds, }) }, hideAllSegments : () => { this.store.dispatch({ type : SELECT_REGIONS_WITH_ID, - selectRegions : [] + selectRegions : [], }) }, segmentColourMap : new Map(), getLayersSegmentColourMap: () => this.nehubaViewer.multiNgIdColorMap, - applyColourMap : (map)=>{ + applyColourMap : (map) => { throw new Error(`apply color map has been deprecated. use applyLayersColourMap instead`) }, applyLayersColourMap: (map) => { this.nehubaViewer.setColorMap(map) }, - loadLayer : (layerObj)=>this.nehubaViewer.loadLayer(layerObj), - removeLayer : (condition)=>this.nehubaViewer.removeLayer(condition), - setLayerVisibility : (condition,visible)=>this.nehubaViewer.setLayerVisibility(condition,visible), + loadLayer : (layerObj) => this.nehubaViewer.loadLayer(layerObj), + removeLayer : (condition) => this.nehubaViewer.removeLayer(condition), + setLayerVisibility : (condition, visible) => this.nehubaViewer.setLayerVisibility(condition, visible), mouseEvent : merge( - fromEvent(this.elementRef.nativeElement,'click').pipe( - map((ev:MouseEvent)=>({eventName :'click',event:ev})) + fromEvent(this.elementRef.nativeElement, 'click').pipe( + map((ev: MouseEvent) => ({eventName : 'click', event: ev})), ), - fromEvent(this.elementRef.nativeElement,'mousemove').pipe( - map((ev:MouseEvent)=>({eventName :'mousemove',event:ev})) + fromEvent(this.elementRef.nativeElement, 'mousemove').pipe( + map((ev: MouseEvent) => ({eventName : 'mousemove', event: ev})), ), /** * neuroglancer prevents propagation, so use capture instead @@ -1218,49 +1217,49 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ Observable.create(observer => { this.elementRef.nativeElement.addEventListener('mousedown', event => observer.next({eventName: 'mousedown', event}), true) }) as Observable<{eventName: string, event: MouseEvent}>, - fromEvent(this.elementRef.nativeElement,'mouseup').pipe( - map((ev:MouseEvent)=>({eventName :'mouseup',event:ev})) + fromEvent(this.elementRef.nativeElement, 'mouseup').pipe( + map((ev: MouseEvent) => ({eventName : 'mouseup', event: ev})), ), ) , mouseOverNehuba : this.onHoverSegment$.pipe( - tap(() => console.warn('mouseOverNehuba observable is becoming deprecated. use mouseOverNehubaLayers instead.')) + tap(() => this.log.warn('mouseOverNehuba observable is becoming deprecated. use mouseOverNehubaLayers instead.')), ), mouseOverNehubaLayers: this.onHoverSegments$, - getNgHash : this.nehubaViewer.getNgHash + getNgHash : this.nehubaViewer.getNgHash, } } - /* because the navigation can be changed from two sources, - either dynamically (e.g. navigation panel in the UI or plugins etc) - or actively (via user interaction with the viewer) + /* because the navigation can be changed from two sources, + either dynamically (e.g. navigation panel in the UI or plugins etc) + or actively (via user interaction with the viewer) or lastly, set on init - + This handler function is meant to handle anytime viewer's navigation changes from either sources */ - handleEmittedNavigationChange(navigation){ + public handleEmittedNavigationChange(navigation) { /* If the navigation is changed dynamically, this.oldnavigation is set prior to the propagation of the navigation state to the viewer. - As the viewer updates the dynamically changed navigation, it will emit the navigation state. + As the viewer updates the dynamically changed navigation, it will emit the navigation state. The emitted navigation state should be identical to this.oldnavigation */ - const navigationChangedActively: boolean = Object.keys(this.oldNavigation).length === 0 || !Object.keys(this.oldNavigation).every(key=>{ + const navigationChangedActively: boolean = Object.keys(this.oldNavigation).length === 0 || !Object.keys(this.oldNavigation).every(key => { return this.oldNavigation[key].constructor === Number || this.oldNavigation[key].constructor === Boolean ? this.oldNavigation[key] === navigation[key] : - this.oldNavigation[key].every((_,idx)=>this.oldNavigation[key][idx] === navigation[key][idx]) + this.oldNavigation[key].every((_, idx) => this.oldNavigation[key][idx] === navigation[key][idx]) }) /* if navigation is changed dynamically (ie not actively), the state would have been propagated to the store already. Hence return */ - if( !navigationChangedActively ) return - + if ( !navigationChangedActively ) { return } + /* navigation changed actively (by user interaction with the viewer) probagate the changes to the store */ this.store.dispatch({ type : CHANGE_NAVIGATION, - navigation + navigation, }) } - handleDispatchedNavigationChange(navigation){ + public handleDispatchedNavigationChange(navigation) { /* extract the animation object */ const { animation, ..._navigation } = navigation @@ -1269,65 +1268,65 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy{ * remove keys that are falsy */ Object.keys(_navigation).forEach(key => (!_navigation[key]) && delete _navigation[key]) - + const { animation: globalAnimationFlag } = this.viewerConfig - if( globalAnimationFlag && animation ){ + if ( globalAnimationFlag && animation ) { /* animated */ const gen = timedValues() - const dest = Object.assign({},_navigation) + const dest = Object.assign({}, _navigation) /* this.oldNavigation is old */ - const delta = Object.assign({}, ...Object.keys(dest).filter(key=>key !== 'positionReal').map(key=>{ + const delta = Object.assign({}, ...Object.keys(dest).filter(key => key !== 'positionReal').map(key => { const returnObj = {} returnObj[key] = typeof dest[key] === 'number' ? dest[key] - this.oldNavigation[key] : typeof dest[key] === 'object' ? - dest[key].map((val,idx)=>val - this.oldNavigation[key][idx]) : + dest[key].map((val, idx) => val - this.oldNavigation[key][idx]) : true return returnObj })) - const animate = ()=>{ + const animate = () => { const next = gen.next() const d = next.value - + this.nehubaViewer.setNavigationState( - Object.assign({}, ...Object.keys(dest).filter(k=>k !== 'positionReal').map(key=>{ + Object.assign({}, ...Object.keys(dest).filter(k => k !== 'positionReal').map(key => { const returnObj = {} returnObj[key] = typeof dest[key] === 'number' ? dest[key] - ( delta[key] * ( 1 - d ) ) : - dest[key].map((val,idx)=>val - ( delta[key][idx] * ( 1 - d ) ) ) + dest[key].map((val, idx) => val - ( delta[key][idx] * ( 1 - d ) ) ) return returnObj - }),{ - positionReal : true - }) + }), { + positionReal : true, + }), ) - if( !next.done ){ - requestAnimationFrame(()=>animate()) - }else{ + if ( !next.done ) { + requestAnimationFrame(() => animate()) + } else { /* set this.oldnavigation to represent the state of the store */ /* animation done, set this.oldNavigation */ - this.oldNavigation = Object.assign({},this.oldNavigation,dest) + this.oldNavigation = Object.assign({}, this.oldNavigation, dest) } } - requestAnimationFrame(()=>animate()) + requestAnimationFrame(() => animate()) } else { /* not animated */ /* set this.oldnavigation to represent the state of the store */ /* since the emitted change of navigation state is debounced, we can safely set this.oldNavigation to the destination */ - this.oldNavigation = Object.assign({},this.oldNavigation,_navigation) + this.oldNavigation = Object.assign({}, this.oldNavigation, _navigation) - this.nehubaViewer.setNavigationState(Object.assign({},_navigation,{ - positionReal : true + this.nehubaViewer.setNavigationState(Object.assign({}, _navigation, { + positionReal : true, })) } } } -export const identifySrcElement = (element:HTMLElement) => { +export const identifySrcElement = (element: HTMLElement) => { const elementIsFirstRow = isFirstRow(element) const elementIsFirstCell = isFirstCell(element) @@ -1343,35 +1342,35 @@ export const identifySrcElement = (element:HTMLElement) => { } export const takeOnePipe = [ - scan((acc:Event[],event:Event)=>{ + scan((acc: Event[], event: Event) => { const target = (event as Event).target as HTMLElement /** * 0 | 1 * 2 | 3 - * + * * 4 ??? */ const key = identifySrcElement(target) const _ = {} _[key] = event - return Object.assign({},acc,_) - },[]), - filter(v=>{ + return Object.assign({}, acc, _) + }, []), + filter(v => { const isdefined = (obj) => typeof obj !== 'undefined' && obj !== null - return (isdefined(v[0]) && isdefined(v[1]) && isdefined(v[2])) + return (isdefined(v[0]) && isdefined(v[1]) && isdefined(v[2])) }), - take(1) + take(1), ] -export const singleLmUnchanged = (lm:{id:string,position:[number,number,number]}, map: Map<string,[number,number,number]>) => - map.has(lm.id) && map.get(lm.id).every((value,idx) => value === lm.position[idx]) +export const singleLmUnchanged = (lm: {id: string, position: [number, number, number]}, map: Map<string, [number, number, number]>) => + map.has(lm.id) && map.get(lm.id).every((value, idx) => value === lm.position[idx]) export const userLmUnchanged = (oldlms, newlms) => { const oldmap = new Map(oldlms.map(lm => [lm.id, lm.position])) const newmap = new Map(newlms.map(lm => [lm.id, lm.position])) - return oldlms.every(lm => singleLmUnchanged(lm, newmap as Map<string,[number,number,number]>)) - && newlms.every(lm => singleLmUnchanged(lm, oldmap as Map<string, [number,number,number]>)) + return oldlms.every(lm => singleLmUnchanged(lm, newmap as Map<string, [number, number, number]>)) + && newlms.every(lm => singleLmUnchanged(lm, oldmap as Map<string, [number, number, number]>)) } export const calculateSliceZoomFactor = (originalZoom) => originalZoom diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts index 28ecbe2987beccfb6d4c18f5798968ef3ac04567..980bcbdd4d5d6b9faa699b8df0c5f4858fefd47c 100644 --- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts +++ b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts @@ -1,35 +1,37 @@ -import { Component, OnDestroy, Output, EventEmitter, ElementRef, NgZone, Renderer2, OnInit } from "@angular/core"; -import { fromEvent, Subscription, Subject } from 'rxjs' -import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"; -import { map, filter, debounceTime, scan } from "rxjs/operators"; +import { Component, ElementRef, EventEmitter, NgZone, OnDestroy, OnInit, Output, Renderer2 } from "@angular/core"; +import { fromEvent, Subject, Subscription } from 'rxjs' +import { pipeFromArray } from "rxjs/internal/util/pipe"; +import { debounceTime, filter, map, scan } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; -import { takeOnePipe } from "../nehubaContainer.component"; +import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"; import { StateInterface as ViewerConfiguration } from "src/services/state/viewerConfig.store"; -import { pipeFromArray } from "rxjs/internal/util/pipe"; import { getNgIdLabelIndexFromId } from "src/services/stateStore.service"; +import { takeOnePipe } from "../nehubaContainer.component"; -import 'third_party/export_nehuba/main.bundle.js' +import { LoggingService } from "src/services/logging.service"; +import { getExportNehuba, getViewer, setNehubaViewer } from "src/util/fn"; import 'third_party/export_nehuba/chunk_worker.bundle.js' +import 'third_party/export_nehuba/main.bundle.js' interface LayerLabelIndex { - layer: { - name: string + layer: { + name: string, } labelIndicies: number[] } -const scanFn : (acc: LayerLabelIndex[], curr: LayerLabelIndex) => LayerLabelIndex[] = (acc: LayerLabelIndex[], curr: LayerLabelIndex) => { +const scanFn: (acc: LayerLabelIndex[], curr: LayerLabelIndex) => LayerLabelIndex[] = (acc: LayerLabelIndex[], curr: LayerLabelIndex) => { const { layer } = curr const { name } = layer const foundIndex = acc.findIndex(({ layer }) => layer.name === name) - if (foundIndex < 0) return acc.concat(curr) - else return acc.map((item, idx) => idx === foundIndex + if (foundIndex < 0) { return acc.concat(curr) } else { return acc.map((item, idx) => idx === foundIndex ? { ...item, - labelIndicies: [...new Set([...item.labelIndicies, ...curr.labelIndicies])] + labelIndicies: [...new Set([...item.labelIndicies, ...curr.labelIndicies])], } : item) + } } /** @@ -38,59 +40,62 @@ const scanFn : (acc: LayerLabelIndex[], curr: LayerLabelIndex) => LayerLabelInde @Component({ templateUrl : './nehubaViewer.template.html', styleUrls : [ - './nehubaViewer.style.css' - ] + './nehubaViewer.style.css', + ], }) -export class NehubaViewerUnit implements OnInit, OnDestroy{ +export class NehubaViewerUnit implements OnInit, OnDestroy { + + private exportNehuba: any + private viewer: any private subscriptions: Subscription[] = [] - - @Output() nehubaReady: EventEmitter<null> = new EventEmitter() - @Output() layersChanged: EventEmitter<null> = new EventEmitter() + + @Output() public nehubaReady: EventEmitter<null> = new EventEmitter() + @Output() public layersChanged: EventEmitter<null> = new EventEmitter() private layersChangedHandler: any - @Output() debouncedViewerPositionChange : EventEmitter<any> = new EventEmitter() - @Output() mouseoverSegmentEmitter: + @Output() public debouncedViewerPositionChange: EventEmitter<any> = new EventEmitter() + @Output() public mouseoverSegmentEmitter: EventEmitter<{ segmentId: number | null, - segment:string | null, - layer:{ + segment: string | null, + layer: { name?: string, - url?: string - } + url?: string, + }, }> = new EventEmitter() - @Output() mouseoverLandmarkEmitter : EventEmitter<number | null> = new EventEmitter() - @Output() mouseoverUserlandmarkEmitter: EventEmitter<number | null> = new EventEmitter() - @Output() regionSelectionEmitter : EventEmitter<{segment:number, layer:{name?: string, url?: string}}> = new EventEmitter() - @Output() errorEmitter : EventEmitter<any> = new EventEmitter() + @Output() public mouseoverLandmarkEmitter: EventEmitter<number | null> = new EventEmitter() + @Output() public mouseoverUserlandmarkEmitter: EventEmitter<number | null> = new EventEmitter() + @Output() public regionSelectionEmitter: EventEmitter<{segment: number, layer: {name?: string, url?: string}}> = new EventEmitter() + @Output() public errorEmitter: EventEmitter<any> = new EventEmitter() public auxilaryMeshIndices: number[] = [] /* only used to set initial navigation state */ - initNav : any - initRegions : any[] - initNiftiLayers : any[] = [] + public initNav: any + public initRegions: any[] + public initNiftiLayers: any[] = [] - config : any - nehubaViewer : any + public config: any + public nehubaViewer: any private _dim: [number, number, number] - get dim(){ + get dim() { return this._dim ? this._dim : [1.5e9, 1.5e9, 1.5e9] } - _s1$ : any - _s2$ : any - _s3$ : any - _s4$ : any - _s5$ : any - _s6$ : any - _s7$ : any - _s8$ : any - _s9$ : any + public _s1$: any + public _s2$: any + public _s3$: any + public _s4$: any + public _s5$: any + public _s6$: any + public _s7$: any + public _s8$: any + public _s9$: any - _s$ : any[] = [ + public _s$: any[] = [ this._s1$, this._s2$, this._s3$, @@ -99,20 +104,23 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ this._s6$, this._s7$, this._s8$, - this._s9$ + this._s9$, ] - ondestroySubscriptions: Subscription[] = [] + public ondestroySubscriptions: Subscription[] = [] constructor( private rd: Renderer2, - public elementRef:ElementRef, - private workerService : AtlasWorkerService, - private zone : NgZone, - public constantService : AtlasViewerConstantsServices - ){ + public elementRef: ElementRef, + private workerService: AtlasWorkerService, + private zone: NgZone, + public constantService: AtlasViewerConstantsServices, + private log: LoggingService, + ) { - if(!this.constantService.loadExportNehubaPromise){ + this.exportNehuba = getExportNehuba() + + if (!this.constantService.loadExportNehubaPromise) { this.constantService.loadExportNehubaPromise = new Promise((resolve, reject) => { const scriptEl = this.rd.createElement('script') scriptEl.src = 'main.bundle.js' @@ -140,21 +148,21 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ this.ondestroySubscriptions.push( fromEvent(this.workerService.worker, 'message').pipe( - filter((message:any) => { + filter((message: any) => { - if(!message){ - // console.error('worker response message is undefined', message) + if (!message) { + // this.log.error('worker response message is undefined', message) return false } - if(!message.data){ - // console.error('worker response message.data is undefined', message.data) + if (!message.data) { + // this.log.error('worker response message.data is undefined', message.data) return false } - if(message.data.type !== 'ASSEMBLED_LANDMARKS_VTK'){ + if (message.data.type !== 'ASSEMBLED_LANDMARKS_VTK') { /* worker responded with not assembled landmark, no need to act */ return false } - if(!message.data.url){ + if (!message.data.url) { /* file url needs to be defined */ return false } @@ -162,32 +170,32 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ }), debounceTime(100), filter(e => this.templateId === e.data.template), - map(e => e.data.url) + map(e => e.data.url), ).subscribe(url => { this.removeSpatialSearch3DLandmarks() const _ = {} _[this.constantService.ngLandmarkLayerName] = { - type :'mesh', + type : 'mesh', source : `vtk://${url}`, - shader : FRAGMENT_MAIN_WHITE + shader : FRAGMENT_MAIN_WHITE, } this.loadLayer(_) - }) + }), ) this.ondestroySubscriptions.push( fromEvent(this.workerService.worker, 'message').pipe( - filter((message:any) => { + filter((message: any) => { - if(!message){ - // console.error('worker response message is undefined', message) + if (!message) { + // this.log.error('worker response message is undefined', message) return false } - if(!message.data){ - // console.error('worker response message.data is undefined', message.data) + if (!message.data) { + // this.log.error('worker response message.data is undefined', message.data) return false } - if(message.data.type !== 'ASSEMBLED_USERLANDMARKS_VTK'){ + if (message.data.type !== 'ASSEMBLED_USERLANDMARKS_VTK') { /* worker responded with not assembled landmark, no need to act */ return false } @@ -195,38 +203,38 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ * nb url may be undefined * if undefined, user have removed all user landmarks, and all that needs to be done * is remove the user landmark layer - * + * * message.data.url */ return true }), debounceTime(100), - map(e => e.data.url) + map(e => e.data.url), ).subscribe(url => { this.removeuserLandmarks() /** * url may be null if user removes all landmarks */ - if (!url) return + if (!url) { return } const _ = {} _[this.constantService.ngUserLandmarkLayerName] = { - type :'mesh', + type : 'mesh', source : `vtk://${url}`, - shader : FRAGMENT_MAIN_WHITE + shader : FRAGMENT_MAIN_WHITE, } this.loadLayer(_) - }) + }), ) } - private _baseUrlToParcellationIdMap:Map<string, string> = new Map() + private _baseUrlToParcellationIdMap: Map<string, string> = new Map() private _baseUrls: string[] = [] public numMeshesToBeLoaded: number = 0 - public applyPerformanceConfig ({ gpuLimit }: Partial<ViewerConfiguration>) { + public applyPerformanceConfig({ gpuLimit }: Partial<ViewerConfiguration>) { if (gpuLimit && this.nehubaViewer) { const limit = this.nehubaViewer.ngviewer.state.children.get('gpuMemoryLimit') if (limit && limit.restoreState) { @@ -236,69 +244,68 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ } /* required to check if correct landmarks are loaded */ - private _templateId : string - get templateId(){ + private _templateId: string + get templateId() { return this._templateId } - set templateId(id:string){ + set templateId(id: string) { this._templateId = id } /** compatible with multiple parcellation id selection */ private _ngIds: string[] = [] - get ngIds(){ + get ngIds() { return this._ngIds } - set ngIds(val:string[]){ + set ngIds(val: string[]) { - if(this.nehubaViewer){ + if (this.nehubaViewer) { this._ngIds.forEach(id => { const oldlayer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(id) - if(oldlayer)oldlayer.setVisible(false) - else console.warn('could not find old layer', id) + if (oldlayer) {oldlayer.setVisible(false) } else { this.log.warn('could not find old layer', id) } }) } this._ngIds = val - - if(this.nehubaViewer){ + + if (this.nehubaViewer) { this.loadNewParcellation() this.showAllSeg() } } - spatialLandmarkSelectionChanged(labels:number[]){ - const getCondition = (label:number) => `if(label > ${label - 0.1} && label < ${label + 0.1} ){${FRAGMENT_EMIT_RED}}` + public spatialLandmarkSelectionChanged(labels: number[]) { + const getCondition = (label: number) => `if(label > ${label - 0.1} && label < ${label + 0.1} ){${FRAGMENT_EMIT_RED}}` const newShader = `void main(){ ${labels.map(getCondition).join('else ')}else {${FRAGMENT_EMIT_WHITE}} }` - if(!this.nehubaViewer){ - if(!PRODUCTION || window['__debug__']) console.warn('setting special landmark selection changed failed ... nehubaViewer is not yet defined') + if (!this.nehubaViewer) { + if (!PRODUCTION) { this.log.warn('setting special landmark selection changed failed ... nehubaViewer is not yet defined') } return } const landmarkLayer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(this.constantService.ngLandmarkLayerName) - if(!landmarkLayer){ - if(!PRODUCTION || window['__debug__']) console.warn('landmark layer could not be found ... will not update colour map') + if (!landmarkLayer) { + if (!PRODUCTION) { this.log.warn('landmark layer could not be found ... will not update colour map') } return } - if(labels.length === 0){ - landmarkLayer.layer.displayState.fragmentMain.restoreState(FRAGMENT_MAIN_WHITE) - }else{ + if (labels.length === 0) { + landmarkLayer.layer.displayState.fragmentMain.restoreState(FRAGMENT_MAIN_WHITE) + } else { landmarkLayer.layer.displayState.fragmentMain.restoreState(newShader) } } - multiNgIdsLabelIndexMap: Map<string, Map<number, any>> + public multiNgIdsLabelIndexMap: Map<string, Map<number, any>> - navPosReal : [number,number,number] = [0,0,0] - navPosVoxel : [number,number,number] = [0,0,0] + public navPosReal: [number, number, number] = [0, 0, 0] + public navPosVoxel: [number, number, number] = [0, 0, 0] - mousePosReal : [number,number,number] = [0,0,0] - mousePosVoxel : [number,number,number] = [0,0,0] + public mousePosReal: [number, number, number] = [0, 0, 0] + public mousePosVoxel: [number, number, number] = [0, 0, 0] - viewerState : ViewerState + public viewerState: ViewerState - private _multiNgIdColorMap: Map<string, Map<number, {red: number, green:number, blue: number}>> - get multiNgIdColorMap(){ + private _multiNgIdColorMap: Map<string, Map<number, {red: number, green: number, blue: number}>> + get multiNgIdColorMap() { return this._multiNgIdColorMap } @@ -307,30 +314,30 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ } private loadMeshes$: Subject<{labelIndicies: number[], layer: { name: string }}> = new Subject() - private loadMeshes(labelIndicies: number[], { name }){ + private loadMeshes(labelIndicies: number[], { name }) { this.loadMeshes$.next({ labelIndicies, - layer: { name } + layer: { name }, }) } public mouseOverSegment: number | null - public mouseOverLayer: {name:string,url:string}| null + public mouseOverLayer: {name: string, url: string}| null - public viewportToDatas : [any, any, any] = [null, null, null] + public viewportToDatas: [any, any, any] = [null, null, null] - public getNgHash : () => string = () => window['export_nehuba'] - ? window['export_nehuba'].getNgHash() + public getNgHash: () => string = () => this.exportNehuba + ? this.exportNehuba.getNgHash() : null - redraw(){ + public redraw() { this.nehubaViewer.redraw() } - loadNehuba(){ - this.nehubaViewer = window['export_nehuba'].createNehubaViewer(this.config, (err)=>{ + public loadNehuba() { + this.nehubaViewer = this.exportNehuba.createNehubaViewer(this.config, (err) => { /* print in debug mode */ - console.log(err) + this.log.log(err) }) /** @@ -338,26 +345,25 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ * Then show the layers referenced in multiNgIdLabelIndexMap */ const managedLayers = this.nehubaViewer.ngviewer.layerManager.managedLayers - managedLayers.slice(1).forEach(layer=>layer.setVisible(false)) + managedLayers.slice(1).forEach(layer => layer.setVisible(false)) Array.from(this.multiNgIdsLabelIndexMap.keys()).forEach(ngId => { const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(ngId) - if (layer) layer.setVisible(true) - else console.log('layer unavailable', ngId) + if (layer) { layer.setVisible(true) } else { this.log.log('layer unavailable', ngId) } }) this.redraw() /* creation of the layout is done on next frame, hence the settimeout */ setTimeout(() => { - window['viewer'].display.panels.forEach(patchSliceViewPanel) + getViewer().display.panels.forEach(patchSliceViewPanel) this.nehubaReady.emit(null) }) - + this.newViewerInit() this.loadNewParcellation() - window['nehubaViewer'] = this.nehubaViewer + setNehubaViewer(this.nehubaViewer) - this.onDestroyCb.push(() => window['nehubaViewer'] = null) + this.onDestroyCb.push(() => setNehubaViewer(null)) /** * TODO @@ -371,59 +377,59 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ // [0,1,2].forEach(idx => this.viewportToDatas[idx] = events[idx].detail.viewportToData) // }) pipeFromArray([...takeOnePipe])(fromEvent(this.elementRef.nativeElement, 'viewportToData')) - .subscribe((events:CustomEvent[]) => { - [0,1,2].forEach(idx => this.viewportToDatas[idx] = events[idx].detail.viewportToData) - }) + .subscribe((events: CustomEvent[]) => { + [0, 1, 2].forEach(idx => this.viewportToDatas[idx] = events[idx].detail.viewportToData) + }), ) } - ngOnInit(){ + public ngOnInit() { this.subscriptions.push( this.loadMeshes$.pipe( - scan(scanFn, []) + scan(scanFn, []), ).subscribe(layersLabelIndex => { let totalMeshes = 0 - for (const layerLayerIndex of layersLabelIndex){ + for (const layerLayerIndex of layersLabelIndex) { const { layer, labelIndicies } = layerLayerIndex totalMeshes += labelIndicies.length this.nehubaViewer.setMeshesToLoad(labelIndicies, layer) } // TODO implement total mesh to be loaded and mesh loading UI // this.numMeshesToBeLoaded = totalMeshes - }) + }), ) } - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } - this._s$.forEach(_s$=>{ - if(_s$) _s$.unsubscribe() + this._s$.forEach(_s$ => { + if (_s$) { _s$.unsubscribe() } }) this.ondestroySubscriptions.forEach(s => s.unsubscribe()) - while(this.onDestroyCb.length > 0){ + while (this.onDestroyCb.length > 0) { this.onDestroyCb.pop()() } this.nehubaViewer.dispose() } - private onDestroyCb : (()=>void)[] = [] + private onDestroyCb: Array<() => void> = [] + + private patchNG() { - private patchNG(){ + const { LayerManager, UrlHashBinding } = this.exportNehuba.getNgPatchableObj() - const { LayerManager, UrlHashBinding } = window['export_nehuba'].getNgPatchableObj() - UrlHashBinding.prototype.setUrlHash = () => { - // console.log('seturl hash') - // console.log('setting url hash') + // this.log.log('seturl hash') + // this.log.log('setting url hash') } UrlHashBinding.prototype.updateFromUrlHash = () => { - // console.log('update hash binding') + // this.log.log('update hash binding') } - + /* TODO find a more permanent fix to disable double click */ LayerManager.prototype.invokeAction = (arg) => { @@ -441,15 +447,15 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ this.onDestroyCb.push(() => LayerManager.prototype.invokeAction = (arg) => {}) } - private filterLayers(l:any,layerObj:any):boolean{ + private filterLayers(l: any, layerObj: any): boolean { /** * if selector is an empty object, select all layers */ - return layerObj instanceof Object && Object.keys(layerObj).every(key => + return layerObj instanceof Object && Object.keys(layerObj).every(key => /** * the property described by the selector must exist and ... */ - !!l[key] && + !!l[key] && /** * if the selector is regex, test layer property */ @@ -463,174 +469,176 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ /** * otherwise do not filter */ - : false - ) + : false + ), ) } // TODO single landmark for user landmark - public updateUserLandmarks(landmarks:any[]){ - if(!this.nehubaViewer) + public updateUserLandmarks(landmarks: any[]) { + if (!this.nehubaViewer) { return + } this.workerService.worker.postMessage({ type : 'GET_USERLANDMARKS_VTK', scale: Math.min(...this.dim.map(v => v * this.constantService.nehubaLandmarkConstant)), - landmarks : landmarks.map(lm => lm.position.map(coord => coord * 1e6)) + landmarks : landmarks.map(lm => lm.position.map(coord => coord * 1e6)), }) } - public removeSpatialSearch3DLandmarks(){ + public removeSpatialSearch3DLandmarks() { this.removeLayer({ - name : this.constantService.ngLandmarkLayerName + name : this.constantService.ngLandmarkLayerName, }) } - public removeuserLandmarks(){ + public removeuserLandmarks() { this.removeLayer({ - name : this.constantService.ngUserLandmarkLayerName + name : this.constantService.ngUserLandmarkLayerName, }) } - //pos in mm - public addSpatialSearch3DLandmarks(geometries: any[],scale?:number,type?:'icosahedron'){ + // pos in mm + public addSpatialSearch3DLandmarks(geometries: any[], scale?: number, type?: 'icosahedron') { this.workerService.worker.postMessage({ type : 'GET_LANDMARKS_VTK', template : this.templateId, scale: Math.min(...this.dim.map(v => v * this.constantService.nehubaLandmarkConstant)), - landmarks : geometries.map(geometry => + landmarks : geometries.map(geometry => geometry === null ? null - //gemoetry : [number,number,number] | [ [number,number,number][], [number,number,number][] ] + // gemoetry : [number,number,number] | [ [number,number,number][], [number,number,number][] ] : isNaN(geometry[0]) ? [geometry[0].map(triplets => triplets.map(coord => coord * 1e6)), geometry[1]] - : geometry.map(coord => coord * 1e6) - ) + : geometry.map(coord => coord * 1e6), + ), }) } - public setLayerVisibility(condition:{name:string|RegExp},visible:boolean){ - if(!this.nehubaViewer) + public setLayerVisibility(condition: {name: string|RegExp}, visible: boolean) { + if (!this.nehubaViewer) { return false + } const viewer = this.nehubaViewer.ngviewer viewer.layerManager.managedLayers - .filter(l=>this.filterLayers(l,condition)) - .map(layer=>layer.setVisible(visible)) + .filter(l => this.filterLayers(l, condition)) + .map(layer => layer.setVisible(visible)) } - public removeLayer(layerObj:any){ - if(!this.nehubaViewer) + public removeLayer(layerObj: any) { + if (!this.nehubaViewer) { return false + } const viewer = this.nehubaViewer.ngviewer - const removeLayer = (i) => (viewer.layerManager.removeManagedLayer(i),i.name) + const removeLayer = (i) => (viewer.layerManager.removeManagedLayer(i), i.name) return viewer.layerManager.managedLayers - .filter(l=>this.filterLayers(l,layerObj)) + .filter(l => this.filterLayers(l, layerObj)) .map(removeLayer) } - public loadLayer(layerObj:any){ + public loadLayer(layerObj: any) { const viewer = this.nehubaViewer.ngviewer return Object.keys(layerObj) - .filter(key=> + .filter(key => /* if the layer exists, it will not be loaded */ !viewer.layerManager.getLayerByName(key)) - .map(key=>{ + .map(key => { viewer.layerManager.addManagedLayer( - viewer.layerSpecification.getLayer(key,layerObj[key])) + viewer.layerSpecification.getLayer(key, layerObj[key])) return layerObj[key] }) } - public hideAllSeg(){ - if(!this.nehubaViewer) return + public hideAllSeg() { + if (!this.nehubaViewer) { return } Array.from(this.multiNgIdsLabelIndexMap.keys()).forEach(ngId => { - + Array.from(this.multiNgIdsLabelIndexMap.get(ngId).keys()).forEach(idx => { this.nehubaViewer.hideSegment(idx, { - name: ngId + name: ngId, }) }) this.nehubaViewer.showSegment(0, { - name: ngId + name: ngId, }) }) } - public showAllSeg(){ - if(!this.nehubaViewer) return + public showAllSeg() { + if (!this.nehubaViewer) { return } this.hideAllSeg() Array.from(this.multiNgIdsLabelIndexMap.keys()).forEach(ngId => { - this.nehubaViewer.hideSegment(0,{ - name: ngId + this.nehubaViewer.hideSegment(0, { + name: ngId, }) }) } - public showSegs(array: number[] | string[]){ + public showSegs(array: number[] | string[]) { - if(!this.nehubaViewer) return + if (!this.nehubaViewer) { return } this.hideAllSeg() - - if (array.length === 0) return + if (array.length === 0) { return } /** * TODO tobe deprecated */ if (typeof array[0] === 'number') { - console.warn(`show seg with number indices has been deprecated`) + this.log.warn(`show seg with number indices has been deprecated`) return - } + } - const reduceFn:(acc:Map<string,number[]>,curr:string)=>Map<string,number[]> = (acc, curr) => { + const reduceFn: (acc: Map<string, number[]>, curr: string) => Map<string, number[]> = (acc, curr) => { const newMap = new Map(acc) const { ngId, labelIndex } = getNgIdLabelIndexFromId({ labelIndexId: curr }) const exist = newMap.get(ngId) - if (!exist) newMap.set(ngId, [Number(labelIndex)]) - else newMap.set(ngId, [...exist, Number(labelIndex)]) + if (!exist) { newMap.set(ngId, [Number(labelIndex)]) } else { newMap.set(ngId, [...exist, Number(labelIndex)]) } return newMap } - + /** - * TODO + * TODO * AAAAAAARG TYPESCRIPT WHY YOU SO MAD */ - //@ts-ignore - const newMap:Map<string, number[]> = array.reduce(reduceFn, new Map()) - + // @ts-ignore + const newMap: Map<string, number[]> = array.reduce(reduceFn, new Map()) + /** * TODO * ugh, ugly code. cleanify */ /** - * TODO + * TODO * sometimes, ngId still happends to be undefined */ newMap.forEach((segs, ngId) => { this.nehubaViewer.hideSegment(0, { - name: ngId + name: ngId, }) segs.forEach(seg => { this.nehubaViewer.showSegment(seg, { - name: ngId + name: ngId, }) }) }) } - private vec3(pos:[number,number,number]){ - return window['export_nehuba'].vec3.fromValues(...pos) + private vec3(pos: [number, number, number]) { + return this.exportNehuba.vec3.fromValues(...pos) } - public setNavigationState(newViewerState:Partial<ViewerState>){ + public setNavigationState(newViewerState: Partial<ViewerState>) { - if(!this.nehubaViewer){ - if(!PRODUCTION || window['__debug__']) - console.warn('setNavigationState > this.nehubaViewer is not yet defined') + if (!this.nehubaViewer) { + if (!PRODUCTION) { + this.log.warn('setNavigationState > this.nehubaViewer is not yet defined') + } return } @@ -640,85 +648,90 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ perspectiveZoom, position, positionReal, - zoom + zoom, } = newViewerState - if( perspectiveZoom ) + if ( perspectiveZoom ) { this.nehubaViewer.ngviewer.perspectiveNavigationState.zoomFactor.restoreState(perspectiveZoom) - if( zoom ) + } + if ( zoom ) { this.nehubaViewer.ngviewer.navigationState.zoomFactor.restoreState(zoom) - if( perspectiveOrientation ) + } + if ( perspectiveOrientation ) { this.nehubaViewer.ngviewer.perspectiveNavigationState.pose.orientation.restoreState( perspectiveOrientation ) - if( orientation ) + } + if ( orientation ) { this.nehubaViewer.ngviewer.navigationState.pose.orientation.restoreState( orientation ) - if( position ) + } + if ( position ) { this.nehubaViewer.setPosition( this.vec3(position) , positionReal ? true : false ) + } } - public obliqueRotateX(amount:number){ + public obliqueRotateX(amount: number) { this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([0, 1, 0]), -amount / 4.0 * Math.PI / 180.0) } - public obliqueRotateY(amount:number){ + public obliqueRotateY(amount: number) { this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([1, 0, 0]), amount / 4.0 * Math.PI / 180.0) } - public obliqueRotateZ(amount:number){ + public obliqueRotateZ(amount: number) { this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([0, 0, 1]), amount / 4.0 * Math.PI / 180.0) } /** - * + * * @param arrayIdx label indices of the shown segment(s) * @param ngId segmentation layer name */ - private updateColorMap(arrayIdx:number[], ngId: string){ + private updateColorMap(arrayIdx: number[], ngId: string) { const set = new Set(arrayIdx) const newColorMap = new Map( Array.from(this.multiNgIdColorMap.get(ngId).entries()) - .map(v=> set.has(v[0]) || set.size === 0 ? + .map(v => set.has(v[0]) || set.size === 0 ? v : - [v[0],{red:255,green:255,blue:255}]) as any + [v[0], {red: 255, green: 255, blue: 255}]) as any, ) - this.nehubaViewer.batchAddAndUpdateSegmentColors(newColorMap,{ - name: ngId + this.nehubaViewer.batchAddAndUpdateSegmentColors(newColorMap, { + name: ngId, }) } - private newViewerInit(){ + private newViewerInit() { /* isn't this layer specific? */ /* TODO this is layer specific. need a way to distinguish between different segmentation layers */ this._s2$ = this.nehubaViewer.mouseOver.segment - .subscribe(({ segment, layer })=>{ + .subscribe(({ segment, layer }) => { this.mouseOverSegment = segment this.mouseOverLayer = { ...layer } }) - if(this.initNav){ + if (this.initNav) { this.setNavigationState(this.initNav) } - if(this.initRegions){ + if (this.initRegions) { this.hideAllSeg() this.showSegs(this.initRegions) } - if(this.initNiftiLayers.length > 0){ + if (this.initNiftiLayers.length > 0) { this.initNiftiLayers.forEach(layer => this.loadLayer(layer)) this.hideAllSeg() } - this._s8$ = this.nehubaViewer.mouseOver.segment.subscribe(({segment: segmentId, layer, ...rest})=>{ - + this._s8$ = this.nehubaViewer.mouseOver.segment.subscribe(({segment: segmentId, layer, ...rest}) => { + const {name = 'unnamed'} = layer const map = this.multiNgIdsLabelIndexMap.get(name) const region = map && map.get(segmentId) this.mouseoverSegmentEmitter.emit({ layer, segment: region, - segmentId + segmentId, }) }) @@ -731,96 +744,95 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ perspectiveOrientation: po1, perspectiveZoom: pz1, position: p1, - zoom: z1 + zoom: z1, } = a const { orientation: o2, perspectiveOrientation: po2, perspectiveZoom: pz2, position: p2, - zoom: z2 + zoom: z2, } = b - return [0,1,2,3].every(idx => o1[idx] === o2[idx]) && - [0,1,2,3].every(idx => po1[idx] === po2[idx]) && + return [0, 1, 2, 3].every(idx => o1[idx] === o2[idx]) && + [0, 1, 2, 3].every(idx => po1[idx] === po2[idx]) && pz1 === pz2 && - [0,1,2].every(idx => p1[idx] === p2[idx]) && + [0, 1, 2].every(idx => p1[idx] === p2[idx]) && z1 === z2 }) - .subscribe(({ orientation, perspectiveOrientation, perspectiveZoom, position, zoom })=>{ + .subscribe(({ orientation, perspectiveOrientation, perspectiveZoom, position, zoom }) => { this.viewerState = { orientation, perspectiveOrientation, perspectiveZoom, zoom, position, - positionReal : false + positionReal : false, } - + this.debouncedViewerPositionChange.emit({ orientation : Array.from(orientation), perspectiveOrientation : Array.from(perspectiveOrientation), perspectiveZoom, zoom, position, - positionReal : true + positionReal : true, }) }) this.ondestroySubscriptions.push( this.nehubaViewer.mouseOver.layer .filter(obj => obj.layer.name === this.constantService.ngLandmarkLayerName) - .subscribe(obj => this.mouseoverLandmarkEmitter.emit(obj.value)) + .subscribe(obj => this.mouseoverLandmarkEmitter.emit(obj.value)), ) this.ondestroySubscriptions.push( this.nehubaViewer.mouseOver.layer .filter(obj => obj.layer.name === this.constantService.ngUserLandmarkLayerName) - .subscribe(obj => this.mouseoverUserlandmarkEmitter.emit(obj.value)) + .subscribe(obj => this.mouseoverUserlandmarkEmitter.emit(obj.value)), ) this._s4$ = this.nehubaViewer.navigationState.position.inRealSpace - .filter(v=>typeof v !== 'undefined' && v !== null) - .subscribe(v=>this.navPosReal=v) + .filter(v => typeof v !== 'undefined' && v !== null) + .subscribe(v => this.navPosReal = v) this._s5$ = this.nehubaViewer.navigationState.position.inVoxels - .filter(v=>typeof v !== 'undefined' && v !== null) - .subscribe(v=>this.navPosVoxel=v) + .filter(v => typeof v !== 'undefined' && v !== null) + .subscribe(v => this.navPosVoxel = v) this._s6$ = this.nehubaViewer.mousePosition.inRealSpace - .filter(v=>typeof v !== 'undefined' && v !== null) - .subscribe(v=>(this.mousePosReal=v)) + .filter(v => typeof v !== 'undefined' && v !== null) + .subscribe(v => (this.mousePosReal = v)) this._s7$ = this.nehubaViewer.mousePosition.inVoxels - .filter(v=>typeof v !== 'undefined' && v !== null) - .subscribe(v=>(this.mousePosVoxel=v)) + .filter(v => typeof v !== 'undefined' && v !== null) + .subscribe(v => (this.mousePosVoxel = v)) } - private loadNewParcellation(){ + private loadNewParcellation() { /* show correct segmentation layer */ this._baseUrls = [] this.ngIds.map(id => { const newlayer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(id) - if(newlayer)newlayer.setVisible(true) - else console.warn('could not find new layer',id) + if (newlayer) {newlayer.setVisible(true) } else { this.log.warn('could not find new layer', id) } const regex = /^(\S.*?)\:\/\/(.*?)$/.exec(newlayer.sourceUrl) - - if(!regex || !regex[2]){ - console.error('could not parse baseUrl') + + if (!regex || !regex[2]) { + this.log.error('could not parse baseUrl') return } - if(regex[1] !== 'precomputed'){ - console.error('sourceUrl is not precomputed') + if (regex[1] !== 'precomputed') { + this.log.error('sourceUrl is not precomputed') return } - + const baseUrl = regex[2] this._baseUrls.push(baseUrl) this._baseUrlToParcellationIdMap.set(baseUrl, id) const indicies = [ ...Array.from(this.multiNgIdsLabelIndexMap.get(id).keys()), - ...this.auxilaryMeshIndices + ...this.auxilaryMeshIndices, ] this.loadMeshes(indicies, { name: id }) @@ -828,49 +840,49 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ const obj = Array.from(this.multiNgIdsLabelIndexMap.keys()).map(ngId => { return [ - ngId, + ngId, new Map(Array.from( [ ...this.multiNgIdsLabelIndexMap.get(ngId).entries(), ...this.auxilaryMeshIndices.map(val => { return [val, {}] - }) - ] - ).map((val:[number,any])=>([val[0],this.getRgb(val[0],val[1].rgb)])) as any) + }), + ], + ).map((val: [number, any]) => ([val[0], this.getRgb(val[0], val[1].rgb)])) as any), ] - }) as [string, Map<number, {red:number, green: number, blue: number}>][] + }) as Array<[string, Map<number, {red: number, green: number, blue: number}>]> const multiNgIdColorMap = new Map(obj) /* load colour maps */ - + this.setColorMap(multiNgIdColorMap) - this._s$.forEach(_s$=>{ - if(_s$) _s$.unsubscribe() + this._s$.forEach(_s$ => { + if (_s$) { _s$.unsubscribe() } }) - if(this._s1$)this._s1$.unsubscribe() - if(this._s9$)this._s9$.unsubscribe() + if (this._s1$) {this._s1$.unsubscribe() } + if (this._s9$) {this._s9$.unsubscribe() } const arr = Array.from(this.multiNgIdsLabelIndexMap.keys()).map(ngId => { return this.nehubaViewer.getShownSegmentsObservable({ - name: ngId + name: ngId, }).subscribe(arrayIdx => this.updateColorMap(arrayIdx, ngId)) }) this._s9$ = { unsubscribe: () => { - while(arr.length > 0) { + while (arr.length > 0) { arr.pop().unsubscribe() } - } + }, } } - public setColorMap(map: Map<string, Map<number,{red:number, green:number, blue:number}>>){ + public setColorMap(map: Map<string, Map<number, {red: number, green: number, blue: number}>>) { this.multiNgIdColorMap = map - + Array.from(map.entries()).forEach(([ngId, map]) => { this.nehubaViewer.batchAddAndUpdateSegmentColors( @@ -879,33 +891,33 @@ export class NehubaViewerUnit implements OnInit, OnDestroy{ }) } - private getRgb(labelIndex:number,rgb?:number[]):{red:number,green:number,blue:number}{ - if(typeof rgb === 'undefined' || rgb === null){ + private getRgb(labelIndex: number, rgb?: number[]): {red: number, green: number, blue: number} { + if (typeof rgb === 'undefined' || rgb === null) { const arr = intToColour(Number(labelIndex)) return { red : arr[0], green: arr[1], - blue : arr[2] + blue : arr[2], } } return { red : rgb[0], green: rgb[1], - blue : rgb[2] + blue : rgb[2], } } } const patchSliceViewPanel = (sliceViewPanel: any) => { const originalDraw = sliceViewPanel.draw - sliceViewPanel.draw = function (this) { - - if(this.sliceView){ + sliceViewPanel.draw = function(this) { + + if (this.sliceView) { const viewportToDataEv = new CustomEvent('viewportToData', { bubbles: true, detail: { - viewportToData : this.sliceView.viewportToData - } + viewportToData : this.sliceView.viewportToData, + }, }) this.element.dispatchEvent(viewportToDataEv) } @@ -915,14 +927,14 @@ const patchSliceViewPanel = (sliceViewPanel: any) => { } /** - * + * * https://stackoverflow.com/a/16348977/6059235 */ const intToColour = function(int) { - if(int >= 65500){ - return [255,255,255] + if (int >= 65500) { + return [255, 255, 255] } - const str = String(int*65535) + const str = String(int * 65535) let hash = 0 for (let i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); @@ -935,13 +947,13 @@ const intToColour = function(int) { return returnV } -export interface ViewerState{ - orientation : [number,number,number,number] - perspectiveOrientation : [number,number,number,number] - perspectiveZoom : number - position : [number,number,number] - positionReal : boolean - zoom : number +export interface ViewerState { + orientation: [number, number, number, number] + perspectiveOrientation: [number, number, number, number] + perspectiveZoom: number + position: [number, number, number] + positionReal: boolean + zoom: number } export const ICOSAHEDRON = `# vtk DataFile Version 2.0 @@ -986,9 +998,9 @@ POLYGONS 20 80 declare const TextEncoder export const _encoder = new TextEncoder() -export const ICOSAHEDRON_VTK_URL = URL.createObjectURL( new Blob([ _encoder.encode(ICOSAHEDRON) ],{type : 'application/octet-stream'} )) +export const ICOSAHEDRON_VTK_URL = URL.createObjectURL( new Blob([ _encoder.encode(ICOSAHEDRON) ], {type : 'application/octet-stream'} )) export const FRAGMENT_MAIN_WHITE = `void main(){emitRGB(vec3(1.0,1.0,1.0));}` export const FRAGMENT_EMIT_WHITE = `emitRGB(vec3(1.0, 1.0, 1.0));` export const FRAGMENT_EMIT_RED = `emitRGB(vec3(1.0, 0.1, 0.12));` -export const computeDistance = (pt1:[number, number], pt2:[number,number]) => ((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) ** 0.5 \ No newline at end of file +export const computeDistance = (pt1: [number, number], pt2: [number, number]) => ((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) ** 0.5 diff --git a/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts b/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts index 4566c470506588e39edc79f005843a24be80c748..8c07d7f65e57c6a34524af8c98051d331585bca2 100644 --- a/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts +++ b/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts @@ -1,30 +1,30 @@ -import { PipeTransform, Pipe } from "@angular/core"; -import { FOUR_PANEL, H_ONE_THREE, V_ONE_THREE, SINGLE_PANEL } from "src/services/state/ngViewerState.store"; +import { Pipe, PipeTransform } from "@angular/core"; +import { FOUR_PANEL, H_ONE_THREE, SINGLE_PANEL, V_ONE_THREE } from "src/services/state/ngViewerState.store"; @Pipe({ - name: 'mobileControlNubStylePipe' + name: 'mobileControlNubStylePipe', }) -export class MobileControlNubStylePipe implements PipeTransform{ - public transform(panelMode: string): any{ +export class MobileControlNubStylePipe implements PipeTransform { + public transform(panelMode: string): any { switch (panelMode) { case SINGLE_PANEL: return { top: '80%', - left: '95%' + left: '95%', } case V_ONE_THREE: case H_ONE_THREE: return { top: '66.66%', - left: '66.66%' + left: '66.66%', } - case FOUR_PANEL: + case FOUR_PANEL: default: return { top: '50%', - left: '50%' + left: '50%', } } } -} \ No newline at end of file +} diff --git a/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts b/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts index 3077fb30211614615da0e98120c22576aa07b209..3e76f4d0234a6942743f9eeff983eb065b938298 100644 --- a/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts +++ b/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts @@ -1,14 +1,13 @@ import { Pipe, PipeTransform } from "@angular/core"; - @Pipe({ - name: 'reorderPanelIndexPipe' + name: 'reorderPanelIndexPipe', }) -export class ReorderPanelIndexPipe implements PipeTransform{ - public transform(panelOrder: string, uncorrectedIndex: number){ +export class ReorderPanelIndexPipe implements PipeTransform { + public transform(panelOrder: string, uncorrectedIndex: number) { return uncorrectedIndex === null ? null : panelOrder.indexOf(uncorrectedIndex.toString()) } -} \ No newline at end of file +} diff --git a/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts b/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts index 2b4fe6c2e0bd3573deb7cddaf942513ae265bfe6..f20cb3bc477f31979b168d8624aacf1161dbe299 100644 --- a/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts +++ b/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts @@ -1,40 +1,39 @@ -import { Component, Pipe, PipeTransform, ElementRef, ViewChild, AfterViewInit } from "@angular/core"; -import { Observable, fromEvent, Subscription, Subject } from "rxjs"; -import { Store, select } from "@ngrx/store"; -import { switchMap, bufferTime, take, filter, withLatestFrom, map } from 'rxjs/operators' -import { ViewerStateInterface, NEWVIEWER } from "src/services/stateStore.service"; +import { AfterViewInit, Component, ElementRef, Pipe, PipeTransform, ViewChild } from "@angular/core"; +import { select, Store } from "@ngrx/store"; +import { fromEvent, Observable, Subject, Subscription } from "rxjs"; +import { bufferTime, filter, map, switchMap, take, withLatestFrom } from 'rxjs/operators' import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; - +import { NEWVIEWER, ViewerStateInterface } from "src/services/stateStore.service"; @Component({ selector : 'ui-splashscreen', templateUrl : './splashScreen.template.html', styleUrls : [ - `./splashScreen.style.css` - ] + `./splashScreen.style.css`, + ], }) -export class SplashScreen implements AfterViewInit{ +export class SplashScreen implements AfterViewInit { - public loadedTemplate$ : Observable<any[]> - @ViewChild('parentContainer', {read:ElementRef}) + public loadedTemplate$: Observable<any[]> + @ViewChild('parentContainer', {read: ElementRef}) private parentContainer: ElementRef private activatedTemplate$: Subject<any> = new Subject() private subscriptions: Subscription[] = [] constructor( - private store:Store<ViewerStateInterface>, + private store: Store<ViewerStateInterface>, private constanceService: AtlasViewerConstantsServices, private constantsService: AtlasViewerConstantsServices, - ){ + ) { this.loadedTemplate$ = this.store.pipe( select('viewerState'), - select('fetchedTemplates') + select('fetchedTemplates'), ) } - ngAfterViewInit(){ + public ngAfterViewInit() { /** * instead of blindly listening to click event, this event stream waits to see if user mouseup within 200ms @@ -45,56 +44,56 @@ export class SplashScreen implements AfterViewInit{ fromEvent(this.parentContainer.nativeElement, 'mousedown').pipe( switchMap(() => fromEvent(this.parentContainer.nativeElement, 'mouseup').pipe( bufferTime(200), - take(1) + take(1), )), filter(arr => arr.length > 0), withLatestFrom(this.activatedTemplate$), - map(([_, template]) => template) - ).subscribe(template => this.selectTemplate(template)) + map(([_, template]) => template), + ).subscribe(template => this.selectTemplate(template)), ) } - selectTemplateParcellation(template, parcellation){ + public selectTemplateParcellation(template, parcellation) { this.store.dispatch({ type : NEWVIEWER, selectTemplate : template, - selectParcellation : parcellation + selectParcellation : parcellation, }) } - selectTemplate(template:any){ + public selectTemplate(template: any) { this.store.dispatch({ type : NEWVIEWER, selectTemplate : template, - selectParcellation : template.parcellations[0] + selectParcellation : template.parcellations[0], }) } - get totalTemplates(){ + get totalTemplates() { return this.constanceService.templateUrls.length } } @Pipe({ - name: 'getTemplateImageSrcPipe' + name: 'getTemplateImageSrcPipe', }) -export class GetTemplateImageSrcPipe implements PipeTransform{ - public transform(name:string):string{ +export class GetTemplateImageSrcPipe implements PipeTransform { + public transform(name: string): string { return `./res/image/${name.replace(/[|&;$%@()+,\s./]/g, '')}.png` } } @Pipe({ - name: 'imgSrcSetPipe' + name: 'imgSrcSetPipe', }) -export class ImgSrcSetPipe implements PipeTransform{ - public transform(src:string):string{ +export class ImgSrcSetPipe implements PipeTransform { + public transform(src: string): string { const regex = /^(.*?)(\.\w*?)$/.exec(src) - if (!regex) throw new Error(`cannot find filename, ext ${src}`) + if (!regex) { throw new Error(`cannot find filename, ext ${src}`) } const filename = regex[1] const ext = regex[2] return [100, 200, 300, 400].map(val => `${filename}-${val}${ext} ${val}w`).join(',') } -} \ No newline at end of file +} diff --git a/src/ui/nehubaContainer/statusCard/statusCard.component.ts b/src/ui/nehubaContainer/statusCard/statusCard.component.ts index ece941ab7752d6bacd9bb8471e968b1d099c5588..ec04fc976b73aa142d6fb6290a0092ba223ff1f2 100644 --- a/src/ui/nehubaContainer/statusCard/statusCard.component.ts +++ b/src/ui/nehubaContainer/statusCard/statusCard.component.ts @@ -1,74 +1,76 @@ import { Component, Input } from "@angular/core"; -import { CHANGE_NAVIGATION, ViewerStateInterface } from "src/services/stateStore.service"; import { Store } from "@ngrx/store"; +import { LoggingService } from "src/services/logging.service"; +import { CHANGE_NAVIGATION, ViewerStateInterface } from "src/services/stateStore.service"; import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component"; @Component({ selector : 'ui-status-card', templateUrl : './statusCard.template.html', - styleUrls : ['./statusCard.style.css'] - }) -export class StatusCardComponent{ + styleUrls : ['./statusCard.style.css'], + }) +export class StatusCardComponent { - @Input() selectedTemplate: any; - @Input() isMobile: boolean; - @Input() nehubaViewer: NehubaViewerUnit; - @Input() onHoverSegmentName: String; + @Input() public selectedTemplate: any; + @Input() public isMobile: boolean; + @Input() public nehubaViewer: NehubaViewerUnit; + @Input() public onHoverSegmentName: string; constructor( - private store : Store<ViewerStateInterface>, + private store: Store<ViewerStateInterface>, + private log: LoggingService, ) {} - statusPanelRealSpace : boolean = true + public statusPanelRealSpace: boolean = true - get mouseCoord():string{ + get mouseCoord(): string { return this.nehubaViewer ? - this.statusPanelRealSpace ? - this.nehubaViewer.mousePosReal ? - Array.from(this.nehubaViewer.mousePosReal.map(n=> isNaN(n) ? 0 : n/1e6)) - .map(n=>n.toFixed(3)+'mm').join(' , ') : + this.statusPanelRealSpace ? + this.nehubaViewer.mousePosReal ? + Array.from(this.nehubaViewer.mousePosReal.map(n => isNaN(n) ? 0 : n / 1e6)) + .map(n => n.toFixed(3) + 'mm').join(' , ') : '0mm , 0mm , 0mm (mousePosReal not yet defined)' : - this.nehubaViewer.mousePosVoxel ? + this.nehubaViewer.mousePosVoxel ? this.nehubaViewer.mousePosVoxel.join(' , ') : '0 , 0 , 0 (mousePosVoxel not yet defined)' : '0 , 0 , 0 (nehubaViewer not defined)' } - editingNavState : boolean = false + public editingNavState: boolean = false - textNavigateTo(string:string){ - if(string.split(/[\s|,]+/).length>=3 && string.split(/[\s|,]+/).slice(0,3).every(entry=>!isNaN(Number(entry.replace(/mm/,''))))){ - const pos = (string.split(/[\s|,]+/).slice(0,3).map((entry)=>Number(entry.replace(/mm/,''))*(this.statusPanelRealSpace ? 1000000 : 1))) + public textNavigateTo(string: string) { + if (string.split(/[\s|,]+/).length >= 3 && string.split(/[\s|,]+/).slice(0, 3).every(entry => !isNaN(Number(entry.replace(/mm/, ''))))) { + const pos = (string.split(/[\s|,]+/).slice(0, 3).map((entry) => Number(entry.replace(/mm/, '')) * (this.statusPanelRealSpace ? 1000000 : 1))) this.nehubaViewer.setNavigationState({ - position : (pos as [number,number,number]), - positionReal : this.statusPanelRealSpace + position : (pos as [number, number, number]), + positionReal : this.statusPanelRealSpace, }) - }else{ - console.log('input did not parse to coordinates ',string) + } else { + this.log.log('input did not parse to coordinates ', string) } } - navigationValue(){ - return this.nehubaViewer ? - this.statusPanelRealSpace ? - Array.from(this.nehubaViewer.navPosReal.map(n=> isNaN(n) ? 0 : n/1e6)) - .map(n=>n.toFixed(3)+'mm').join(' , ') : - Array.from(this.nehubaViewer.navPosVoxel.map(n=> isNaN(n) ? 0 : n)).join(' , ') : + public navigationValue() { + return this.nehubaViewer ? + this.statusPanelRealSpace ? + Array.from(this.nehubaViewer.navPosReal.map(n => isNaN(n) ? 0 : n / 1e6)) + .map(n => n.toFixed(3) + 'mm').join(' , ') : + Array.from(this.nehubaViewer.navPosVoxel.map(n => isNaN(n) ? 0 : n)).join(' , ') : `[0,0,0] (neubaViewer is undefined)` } - + /** * TODO * maybe have a nehuba manager service * so that reset navigation logic can stay there - * + * * When that happens, we don't even need selectTemplate input - * + * * the info re: nehubaViewer can stay there, too */ - resetNavigation({rotation: rotationFlag = false, position: positionFlag = false, zoom : zoomFlag = false} : {rotation?: boolean, position?: boolean, zoom?: boolean}){ + public resetNavigation({rotation: rotationFlag = false, position: positionFlag = false, zoom : zoomFlag = false}: {rotation?: boolean, position?: boolean, zoom?: boolean}) { const initialNgState = this.selectedTemplate.nehubaConfig.dataset.initialNgState - + const perspectiveZoom = initialNgState ? initialNgState.perspectiveZoom : undefined const perspectiveOrientation = initialNgState ? initialNgState.perspectiveOrientation : undefined const zoom = (zoomFlag @@ -86,7 +88,7 @@ export class StatusCardComponent{ || undefined const orientation = rotationFlag - ? [0,0,0,1] + ? [0, 0, 0, 1] : undefined this.store.dispatch({ @@ -97,17 +99,13 @@ export class StatusCardComponent{ perspectiveOrientation, zoom, position, - orientation + orientation, }, ...{ positionReal : false, - animation : {} - } - } + animation : {}, + }, + }, }) } } - - - - diff --git a/src/ui/nehubaContainer/touchSideClass.directive.ts b/src/ui/nehubaContainer/touchSideClass.directive.ts index b08d47eee4c64c1469147bae63d251843706e43e..f10e11d71845c5f3e61b62ec5140b80079975961 100644 --- a/src/ui/nehubaContainer/touchSideClass.directive.ts +++ b/src/ui/nehubaContainer/touchSideClass.directive.ts @@ -1,16 +1,16 @@ -import { Directive, Input, ElementRef, OnDestroy, OnInit } from "@angular/core"; -import { Store, select } from "@ngrx/store"; +import { Directive, ElementRef, Input, OnDestroy, OnInit } from "@angular/core"; +import { select, Store } from "@ngrx/store"; import { Observable, Subscription } from "rxjs"; import { distinctUntilChanged, tap } from "rxjs/operators"; -import { removeTouchSideClasses, addTouchSideClasses } from "./util"; import { IavRootStoreInterface } from "src/services/stateStore.service"; +import { addTouchSideClasses, removeTouchSideClasses } from "./util"; @Directive({ selector: '[touch-side-class]', - exportAs: 'touchSideClass' + exportAs: 'touchSideClass', }) -export class TouchSideClass implements OnDestroy, OnInit{ +export class TouchSideClass implements OnDestroy, OnInit { @Input('touch-side-class') public panelNativeIndex: number @@ -21,30 +21,30 @@ export class TouchSideClass implements OnDestroy, OnInit{ constructor( private store$: Store<IavRootStoreInterface>, - private el: ElementRef - ){ + private el: ElementRef, + ) { this.panelMode$ = this.store$.pipe( select('ngViewerState'), select('panelMode'), distinctUntilChanged(), - tap(mode => this.panelMode = mode) + tap(mode => this.panelMode = mode), ) } - ngOnInit(){ + public ngOnInit() { this.subscriptions.push( this.panelMode$.subscribe(panelMode => { removeTouchSideClasses(this.el.nativeElement) addTouchSideClasses(this.el.nativeElement, this.panelNativeIndex, panelMode) - }) + }), ) } - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } -} \ No newline at end of file +} diff --git a/src/ui/nehubaContainer/util.ts b/src/ui/nehubaContainer/util.ts index d3d1f78e8b7e1761a51f611b3a77b635e19343f9..14c416a4b0bf55e0e44cb4635a0ed053c4118825 100644 --- a/src/ui/nehubaContainer/util.ts +++ b/src/ui/nehubaContainer/util.ts @@ -1,20 +1,20 @@ -import { FOUR_PANEL, SINGLE_PANEL, H_ONE_THREE, V_ONE_THREE } from "src/services/state/ngViewerState.store"; +import { FOUR_PANEL, H_ONE_THREE, SINGLE_PANEL, V_ONE_THREE } from "src/services/state/ngViewerState.store"; const flexContCmnCls = ['w-100', 'h-100', 'd-flex', 'justify-content-center', 'align-items-stretch'] -const makeRow = (...els:HTMLElement[]) => { +const makeRow = (...els: HTMLElement[]) => { const container = document.createElement('div') container.classList.add(...flexContCmnCls, 'flex-row') - for (const el of els){ + for (const el of els) { container.appendChild(el) } return container } -const makeCol = (...els:HTMLElement[]) => { +const makeCol = (...els: HTMLElement[]) => { const container = document.createElement('div') container.classList.add(...flexContCmnCls, 'flex-column') - for (const el of els){ + for (const el of els) { container.appendChild(el) } return container @@ -22,7 +22,7 @@ const makeCol = (...els:HTMLElement[]) => { const washPanels = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { for (const panel of panels) { - if (panel) panel.className = `position-relative` + if (panel) { panel.className = `position-relative` } } return panels } @@ -38,28 +38,28 @@ mapModeIdxClass.set(FOUR_PANEL, new Map([ [0, { top, left }], [1, { top, right }], [2, { bottom, left }], - [3, { right, bottom }] + [3, { right, bottom }], ])) mapModeIdxClass.set(SINGLE_PANEL, new Map([ [0, { top, left, right, bottom }], [1, {}], [2, {}], - [3, {}] + [3, {}], ])) mapModeIdxClass.set(H_ONE_THREE, new Map([ [0, { top, left, bottom }], [1, { top, right }], [2, { right }], - [3, { bottom, right }] + [3, { bottom, right }], ])) mapModeIdxClass.set(V_ONE_THREE, new Map([ [0, { top, left, right }], [1, { bottom, left }], [2, { bottom }], - [3, { bottom, right }] + [3, { bottom, right }], ])) export const removeTouchSideClasses = (panel: HTMLElement) => { @@ -74,44 +74,44 @@ export const removeTouchSideClasses = (panel: HTMLElement) => { /** * gives a clue of the approximate location of the panel, allowing position of checkboxes/scale bar to be placed in unobtrustive places */ -export const panelTouchSide = (panel: HTMLElement, { top, left, right, bottom }: any) => { - if (top) panel.classList.add(`touch-top`) - if (left) panel.classList.add(`touch-left`) - if (right) panel.classList.add(`touch-right`) - if (bottom) panel.classList.add(`touch-bottom`) +export const panelTouchSide = (panel: HTMLElement, { top: touchTop, left: touchLeft, right: touchRight, bottom: touchBottom }: any) => { + if (touchTop) { panel.classList.add(`touch-top`) } + if (touchLeft) { panel.classList.add(`touch-left`) } + if (touchRight) { panel.classList.add(`touch-right`) } + if (touchBottom) { panel.classList.add(`touch-bottom`) } return panel } export const addTouchSideClasses = (panel: HTMLElement, actualOrderIndex: number, panelMode: string) => { - - if (actualOrderIndex < 0) return panel + + if (actualOrderIndex < 0) { return panel } const mapIdxClass = mapModeIdxClass.get(panelMode) - if (!mapIdxClass) return panel + if (!mapIdxClass) { return panel } const classArg = mapIdxClass.get(actualOrderIndex) - if (!classArg) return panel + if (!classArg) { return panel } return panelTouchSide(panel, classArg) } -export const getHorizontalOneThree = (panels:[HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { +export const getHorizontalOneThree = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { washPanels(panels) panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, H_ONE_THREE)) - + const majorContainer = makeCol(panels[0]) const minorContainer = makeCol(panels[1], panels[2], panels[3]) majorContainer.style.flexBasis = '67%' minorContainer.style.flexBasis = '33%' - + return makeRow(majorContainer, minorContainer) } -export const getVerticalOneThree = (panels:[HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { +export const getVerticalOneThree = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { washPanels(panels) - + panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, V_ONE_THREE)) const majorContainer = makeRow(panels[0]) @@ -119,14 +119,13 @@ export const getVerticalOneThree = (panels:[HTMLElement, HTMLElement, HTMLElemen majorContainer.style.flexBasis = '67%' minorContainer.style.flexBasis = '33%' - + return makeCol(majorContainer, minorContainer) } - -export const getFourPanel = (panels:[HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { +export const getFourPanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { washPanels(panels) - + panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, FOUR_PANEL)) const majorContainer = makeRow(panels[0], panels[1]) @@ -134,13 +133,13 @@ export const getFourPanel = (panels:[HTMLElement, HTMLElement, HTMLElement, HTML majorContainer.style.flexBasis = '50%' minorContainer.style.flexBasis = '50%' - + return makeCol(majorContainer, minorContainer) } -export const getSinglePanel = (panels:[HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { +export const getSinglePanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { washPanels(panels) - + panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, SINGLE_PANEL)) const majorContainer = makeRow(panels[0]) @@ -158,4 +157,3 @@ export const isIdentityQuat = ori => Math.abs(ori[0]) < 1e-6 && Math.abs(ori[1]) < 1e-6 && Math.abs(ori[2]) < 1e-6 && Math.abs(ori[3] - 1) < 1e-6 - \ No newline at end of file diff --git a/src/ui/parcellationRegion/region.base.ts b/src/ui/parcellationRegion/region.base.ts index db802685088ca59b985f41ba0eb7423fc2efbcac..2632b9d0e3d6dd7882ee9533d47c78f5ad5cc927 100644 --- a/src/ui/parcellationRegion/region.base.ts +++ b/src/ui/parcellationRegion/region.base.ts @@ -1,14 +1,14 @@ -import { Store } from "@ngrx/store"; import {EventEmitter, Input, Output} from "@angular/core"; -import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "../viewerStateController/viewerState.base"; +import { Store } from "@ngrx/store"; +import {SET_CONNECTIVITY_REGION} from "src/services/state/viewerState.store"; import { EXPAND_SIDE_PANEL_CURRENT_VIEW, IavRootStoreInterface, OPEN_SIDE_PANEL, - SHOW_SIDE_PANEL_CONNECTIVITY + SHOW_SIDE_PANEL_CONNECTIVITY, } from "src/services/stateStore.service"; -import {SET_CONNECTIVITY_REGION} from "src/services/state/viewerState.store"; +import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "../viewerStateController/viewerState.base"; -export class RegionBase{ +export class RegionBase { @Input() public region: any @@ -16,40 +16,40 @@ export class RegionBase{ @Input() public isSelected: boolean = false - @Input() hasConnectivity: boolean - @Output() exploreConnectivity: EventEmitter<string> = new EventEmitter() + @Input() public hasConnectivity: boolean + @Output() public exploreConnectivity: EventEmitter<string> = new EventEmitter() constructor( private store$: Store<IavRootStoreInterface>, - ){ - + ) { + } - navigateToRegion(){ + public navigateToRegion() { const { region } = this this.store$.dispatch({ type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.NAVIGATETO_REGION, - payload: { region } + payload: { region }, }) } - toggleRegionSelected(){ + public toggleRegionSelected() { const { region } = this this.store$.dispatch({ type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.TOGGLE_REGION_SELECT, - payload: { region } + payload: { region }, }) } - showConnectivity(regionName) { - //ToDo trigger side panel opening with effect + public showConnectivity(regionName) { + // ToDo trigger side panel opening with effect this.store$.dispatch({type: OPEN_SIDE_PANEL}) this.store$.dispatch({type: EXPAND_SIDE_PANEL_CURRENT_VIEW}) this.store$.dispatch({type: SHOW_SIDE_PANEL_CONNECTIVITY}) this.store$.dispatch({ type: SET_CONNECTIVITY_REGION, - connectivityRegion: regionName + connectivityRegion: regionName, }) } -} \ No newline at end of file +} diff --git a/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts b/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts index 693435f1083bb77c0ec269dff6804470b5c915b4..487d2bc7fd1867582acff52b44df985c46387f75 100644 --- a/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts +++ b/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts @@ -1,28 +1,28 @@ import { Component, Input } from "@angular/core"; -import { RegionBase } from '../region.base' import { Store } from "@ngrx/store"; import { IavRootStoreInterface } from "src/services/stateStore.service"; +import { RegionBase } from '../region.base' @Component({ selector: 'region-list-simple-view', templateUrl: './regionListSimpleView.template.html', styleUrls: [ - './regionListSimpleView.style.css' - ] + './regionListSimpleView.style.css', + ], }) -export class RegionListSimpleViewComponent extends RegionBase{ - +export class RegionListSimpleViewComponent extends RegionBase { + @Input() - showBrainIcon: boolean = false + public showBrainIcon: boolean = false @Input() - showDesc: boolean = false + public showDesc: boolean = false constructor( - store$: Store<IavRootStoreInterface> - ){ + store$: Store<IavRootStoreInterface>, + ) { super(store$) } -} \ No newline at end of file +} diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts b/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts index 565d5081cae4aec15dfceff52956bd6af0311fab..e2022355c35cc75b67c4bd23d33508b153a18d87 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts @@ -1,19 +1,19 @@ import { Component } from "@angular/core"; import { Store } from "@ngrx/store"; -import { RegionBase } from '../region.base' import { IavRootStoreInterface } from "src/services/stateStore.service"; +import { RegionBase } from '../region.base' @Component({ selector: 'region-menu', templateUrl: './regionMenu.template.html', - styleUrls: ['./regionMenu.style.css'] + styleUrls: ['./regionMenu.style.css'], }) export class RegionMenuComponent extends RegionBase { constructor( - store$: Store<IavRootStoreInterface> + store$: Store<IavRootStoreInterface>, ) { super(store$) } -} \ No newline at end of file +} diff --git a/src/ui/parcellationRegion/regionSimple/regionSimple.component.ts b/src/ui/parcellationRegion/regionSimple/regionSimple.component.ts index 45a42c46c83fa3e4b6f9e174aea0424ccd7472d6..76859b9e5341053c37c14c3a57115cc1a6b5f190 100644 --- a/src/ui/parcellationRegion/regionSimple/regionSimple.component.ts +++ b/src/ui/parcellationRegion/regionSimple/regionSimple.component.ts @@ -1,21 +1,21 @@ import { Component } from '@angular/core' -import { RegionBase } from '../region.base' import { Store } from '@ngrx/store' import { IavRootStoreInterface } from 'src/services/stateStore.service' +import { RegionBase } from '../region.base' @Component({ selector: 'simple-region', templateUrl: './regionSimple.template.html', styleUrls: [ - './regionSimple.style.css' - ] + './regionSimple.style.css', + ], }) -export class SimpleRegionComponent extends RegionBase{ +export class SimpleRegionComponent extends RegionBase { constructor( store$: Store<IavRootStoreInterface>, - ){ + ) { super(store$) } -} \ No newline at end of file +} diff --git a/src/ui/pluginBanner/pluginBanner.component.ts b/src/ui/pluginBanner/pluginBanner.component.ts index 299f345f00f196f170ad8b1bf985be20ee5da206..930cec72eb9055a802842143dff7aa4fe2de1857 100644 --- a/src/ui/pluginBanner/pluginBanner.component.ts +++ b/src/ui/pluginBanner/pluginBanner.component.ts @@ -1,21 +1,20 @@ import { Component } from "@angular/core"; -import { PluginServices, PluginManifest } from "src/atlasViewer/atlasViewer.pluginService.service"; - +import { IPluginManifest, PluginServices } from "src/atlasViewer/atlasViewer.pluginService.service"; @Component({ selector : 'plugin-banner', templateUrl : './pluginBanner.template.html', styleUrls : [ - `./pluginBanner.style.css` - ] + `./pluginBanner.style.css`, + ], }) -export class PluginBannerUI{ - - constructor(public pluginServices:PluginServices){ +export class PluginBannerUI { + + constructor(public pluginServices: PluginServices) { } - clickPlugin(plugin:PluginManifest){ + public clickPlugin(plugin: IPluginManifest) { this.pluginServices.launchPlugin(plugin) } -} \ No newline at end of file +} diff --git a/src/ui/searchSideNav/searchSideNav.component.ts b/src/ui/searchSideNav/searchSideNav.component.ts index 949da2a546f735ac2951d4ad26d1965bb57c710d..5e00154b84eee8e60ad1864998131dac94ddfa44 100644 --- a/src/ui/searchSideNav/searchSideNav.component.ts +++ b/src/ui/searchSideNav/searchSideNav.component.ts @@ -1,25 +1,25 @@ -import { Component, Output, EventEmitter, OnDestroy, ViewChild, TemplateRef } from "@angular/core"; -import { MatDialogRef, MatDialog, MatSnackBar } from "@angular/material"; -import { NgLayerInterface } from "src/atlasViewer/atlasViewer.component"; -import { LayerBrowser } from "../layerbrowser/layerbrowser.component"; +import { Component, EventEmitter, OnDestroy, Output, TemplateRef, ViewChild } from "@angular/core"; +import { MatDialog, MatDialogRef, MatSnackBar } from "@angular/material"; +import { select, Store } from "@ngrx/store"; import {Observable, Subscription} from "rxjs"; -import { Store, select } from "@ngrx/store"; -import { map, startWith, scan, filter, mapTo } from "rxjs/operators"; -import { trackRegionBy } from '../viewerStateController/regionHierachy/regionHierarchy.component' +import { filter, map, mapTo, scan, startWith } from "rxjs/operators"; +import { INgLayerInterface } from "src/atlasViewer/atlasViewer.component"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; import { CLOSE_SIDE_PANEL, COLLAPSE_SIDE_PANEL_CURRENT_VIEW, EXPAND_SIDE_PANEL_CURRENT_VIEW, } from "src/services/state/uiState.store"; -import { SELECT_REGIONS, IavRootStoreInterface } from "src/services/stateStore.service"; +import { IavRootStoreInterface, SELECT_REGIONS } from "src/services/stateStore.service"; +import { LayerBrowser } from "../layerbrowser/layerbrowser.component"; +import { trackRegionBy } from '../viewerStateController/regionHierachy/regionHierarchy.component' @Component({ selector: 'search-side-nav', templateUrl: './searchSideNav.template.html', - styleUrls:[ - './searchSideNav.style.css' - ] + styleUrls: [ + './searchSideNav.style.css', + ], }) export class SearchSideNav implements OnDestroy { @@ -28,9 +28,9 @@ export class SearchSideNav implements OnDestroy { private subscriptions: Subscription[] = [] private layerBrowserDialogRef: MatDialogRef<any> - @Output() dismiss: EventEmitter<any> = new EventEmitter() + @Output() public dismiss: EventEmitter<any> = new EventEmitter() - @ViewChild('layerBrowserTmpl', {read: TemplateRef}) layerBrowserTmpl: TemplateRef<any> + @ViewChild('layerBrowserTmpl', {read: TemplateRef}) public layerBrowserTmpl: TemplateRef<any> public autoOpenSideNavDataset$: Observable<any> @@ -42,7 +42,7 @@ export class SearchSideNav implements OnDestroy { private store$: Store<IavRootStoreInterface>, private snackBar: MatSnackBar, private constantService: AtlasViewerConstantsServices, - ){ + ) { this.autoOpenSideNavDataset$ = this.store$.pipe( select('viewerState'), select('regionsSelected'), @@ -50,47 +50,45 @@ export class SearchSideNav implements OnDestroy { startWith(0), scan((acc, curr) => [curr, ...acc], []), filter(([curr, prev]) => prev === 0 && curr > 0), - mapTo(true) + mapTo(true), ) this.sidePanelExploreCurrentViewIsOpen$ = this.store$.pipe( select('uiState'), - select("sidePanelExploreCurrentViewIsOpen") + select("sidePanelExploreCurrentViewIsOpen"), ) this.sidePanelCurrentViewContent = this.store$.pipe( select('uiState'), - select("sidePanelCurrentViewContent") + select("sidePanelCurrentViewContent"), ) } - collapseSidePanelCurrentView() { + public collapseSidePanelCurrentView() { this.store$.dispatch({ type: COLLAPSE_SIDE_PANEL_CURRENT_VIEW, }) } - expandSidePanelCurrentView() { + public expandSidePanelCurrentView() { this.store$.dispatch({ type: EXPAND_SIDE_PANEL_CURRENT_VIEW, }) } - - - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } - handleNonbaseLayerEvent(layers: NgLayerInterface[]){ + public handleNonbaseLayerEvent(layers: INgLayerInterface[]) { if (layers.length === 0) { this.layerBrowserDialogRef && this.layerBrowserDialogRef.close() this.layerBrowserDialogRef = null - return + return } - if (this.layerBrowserDialogRef) return + if (this.layerBrowserDialogRef) { return } this.store$.dispatch({ type: CLOSE_SIDE_PANEL, @@ -101,27 +99,28 @@ export class SearchSideNav implements OnDestroy { hasBackdrop: false, autoFocus: false, panelClass: [ - 'layerBrowserContainer' + 'layerBrowserContainer', ], position: { - top: '0' + top: '0', }, - disableClose: true + disableClose: true, }) this.layerBrowserDialogRef.afterClosed().subscribe(val => { - if (val === 'user action') this.snackBar.open(this.constantService.dissmissUserLayerSnackbarMessage, 'Dismiss', { - duration: 5000 + if (val === 'user action') { this.snackBar.open(this.constantService.dissmissUserLayerSnackbarMessage, 'Dismiss', { + duration: 5000, }) + } }) } - public deselectAllRegions(){ + public deselectAllRegions() { this.store$.dispatch({ type: SELECT_REGIONS, - selectRegions: [] + selectRegions: [], }) } - trackByFn = trackRegionBy -} \ No newline at end of file + public trackByFn = trackRegionBy +} diff --git a/src/ui/sharedModules/angularMaterial.module.ts b/src/ui/sharedModules/angularMaterial.module.ts index 0b02dfe49874d8b0f80575e4b418db810b5fb09c..f780ed86929a9b73958e8dbbe593afe0319f922a 100644 --- a/src/ui/sharedModules/angularMaterial.module.ts +++ b/src/ui/sharedModules/angularMaterial.module.ts @@ -1,35 +1,34 @@ +import { ScrollingModule as ExperimentalScrollingModule } from '@angular/cdk-experimental/scrolling' import { + MAT_DIALOG_DEFAULT_OPTIONS, + MatAutocompleteModule, + MatBadgeModule, + MatBottomSheetModule, MatButtonModule, - MatCheckboxModule, - MatSidenavModule, MatCardModule, - MatTabsModule, - MatTooltipModule, - MatSnackBarModule, - MatBadgeModule, - MatDividerModule, - MatSelectModule, + MatCheckboxModule, MatChipsModule, - MatAutocompleteModule, + MatDialogConfig, MatDialogModule, - MatInputModule, - MatBottomSheetModule, - MatListModule, - MatSlideToggleModule, - MatRippleModule, - MatSliderModule, + MatDividerModule, MatExpansionModule, MatGridListModule, MatIconModule, + MatInputModule, + MatListModule, MatMenuModule, - MAT_DIALOG_DEFAULT_OPTIONS, - MatDialogConfig + MatRippleModule, + MatSelectModule, + MatSidenavModule, + MatSliderModule, + MatSlideToggleModule, + MatSnackBarModule, + MatTabsModule, + MatTooltipModule, } from '@angular/material'; -import { ScrollingModule as ExperimentalScrollingModule } from '@angular/cdk-experimental/scrolling' -import { NgModule } from '@angular/core'; import {DragDropModule} from "@angular/cdk/drag-drop"; - +import { NgModule } from '@angular/core'; const defaultDialogOption: MatDialogConfig = new MatDialogConfig() @@ -59,7 +58,7 @@ const defaultDialogOption: MatDialogConfig = new MatDialogConfig() MatGridListModule, MatIconModule, MatMenuModule, - ExperimentalScrollingModule + ExperimentalScrollingModule, ], exports: [ MatButtonModule, @@ -86,14 +85,14 @@ const defaultDialogOption: MatDialogConfig = new MatDialogConfig() MatGridListModule, MatIconModule, MatMenuModule, - ExperimentalScrollingModule + ExperimentalScrollingModule, ], providers: [{ provide: MAT_DIALOG_DEFAULT_OPTIONS, useValue: { ...defaultDialogOption, - panelClass: 'iav-dialog-class' - } - }] + panelClass: 'iav-dialog-class', + }, + }], }) export class AngularMaterialModule { } diff --git a/src/ui/signinBanner/signinBanner.components.ts b/src/ui/signinBanner/signinBanner.components.ts index c86c86403df836a817068d0ace4c601787584c23..6accb61a3afe43e514079dd23cd33ed8908bf800 100644 --- a/src/ui/signinBanner/signinBanner.components.ts +++ b/src/ui/signinBanner/signinBanner.components.ts @@ -1,30 +1,29 @@ -import {Component, ChangeDetectionStrategy, Input, TemplateRef } from "@angular/core"; -import { AuthService, User } from "src/services/auth.service"; -import { MatDialog, MatDialogRef, MatBottomSheet } from "@angular/material"; +import {ChangeDetectionStrategy, Component, Input, TemplateRef } from "@angular/core"; +import { MatBottomSheet, MatDialog, MatDialogRef } from "@angular/material"; +import { select, Store } from "@ngrx/store"; import { Observable } from "rxjs"; import { map } from "rxjs/operators"; -import { DataEntry, IavRootStoreInterface } from "src/services/stateStore.service"; -import { Store, select } from "@ngrx/store"; - +import { AuthService, IUser } from "src/services/auth.service"; +import { IavRootStoreInterface, IDataEntry } from "src/services/stateStore.service"; @Component({ selector: 'signin-banner', templateUrl: './signinBanner.template.html', styleUrls: [ './signinBanner.style.css', - '../btnShadow.style.css' + '../btnShadow.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class SigninBanner{ +export class SigninBanner { - @Input() darktheme: boolean - @Input() parcellationIsSelected: boolean + @Input() public darktheme: boolean + @Input() public parcellationIsSelected: boolean - public user$: Observable<User> + public user$: Observable<IUser> public userBtnTooltip$: Observable<string> - public favDataEntries$: Observable<DataEntry[]> + public favDataEntries$: Observable<IDataEntry[]> public pluginTooltipText: string = `Plugins and Tools` public screenshotTooltipText: string = 'Take screenshot' @@ -33,48 +32,49 @@ export class SigninBanner{ private store$: Store<IavRootStoreInterface>, private authService: AuthService, private dialog: MatDialog, - public bottomSheet: MatBottomSheet - ){ + public bottomSheet: MatBottomSheet, + ) { this.user$ = this.authService.user$ this.userBtnTooltip$ = this.user$.pipe( map(user => user ? `Logged in as ${(user && user.name) ? user.name : 'Unknown name'}` - : `Not logged in`) + : `Not logged in`), ) this.favDataEntries$ = this.store$.pipe( select('dataStore'), - select('favDataEntries') + select('favDataEntries'), ) } private dialogRef: MatDialogRef<any> - openTmplWithDialog(tmpl: TemplateRef<any>){ + public openTmplWithDialog(tmpl: TemplateRef<any>) { this.dialogRef && this.dialogRef.close() - if (tmpl) this.dialogRef = this.dialog.open(tmpl, { + if (tmpl) { this.dialogRef = this.dialog.open(tmpl, { autoFocus: false, - panelClass: ['col-12','col-sm-12','col-md-8','col-lg-6','col-xl-4'] + panelClass: ['col-12', 'col-sm-12', 'col-md-8', 'col-lg-6', 'col-xl-4'], }) + } } private keyListenerConfigBase = { type: 'keydown', stop: true, prevent: true, - target: 'document' + target: 'document', } public keyListenerConfig = [{ key: 'h', - ...this.keyListenerConfigBase - },{ + ...this.keyListenerConfigBase, + }, { key: 'H', - ...this.keyListenerConfigBase - },{ + ...this.keyListenerConfigBase, + }, { key: '?', - ...this.keyListenerConfigBase + ...this.keyListenerConfigBase, }] -} \ No newline at end of file +} diff --git a/src/ui/signinModal/signinModal.component.ts b/src/ui/signinModal/signinModal.component.ts index ec017cc0a0b8eed5a314a979261c61076d77f0e9..c05a1e81efe659e49d9ee8e74f5dcea77cfbe9e1 100644 --- a/src/ui/signinModal/signinModal.component.ts +++ b/src/ui/signinModal/signinModal.component.ts @@ -1,35 +1,35 @@ import { Component } from "@angular/core"; -import { AuthService, User, AuthMethod } from "src/services/auth.service"; +import { AuthService, IAuthMethod, IUser } from "src/services/auth.service"; @Component({ selector: 'signin-modal', templateUrl: './signinModal.template.html', styleUrls: [ - './signinModal.style.css' - ] + './signinModal.style.css', + ], }) -export class SigninModal{ +export class SigninModal { constructor( - private authService: AuthService - ){ + private authService: AuthService, + ) { } - get user() : User | null { + get user(): IUser | null { return this.authService.user } - - get loginMethods(): AuthMethod[] { + + get loginMethods(): IAuthMethod[] { return this.authService.loginMethods } - - get logoutHref(): String { + + get logoutHref(): string { return this.authService.logoutHref } - loginBtnOnclick() { + public loginBtnOnclick() { this.authService.authSaveState() return true } -} \ No newline at end of file +} diff --git a/src/ui/takeScreenshot/takeScreenshot.component.ts b/src/ui/takeScreenshot/takeScreenshot.component.ts index 1ebccf5871c0151f4527c7bf1561c4f6d4cce883..7645ea1a25b7c93fe9afd12bafb4c335e1ccdf3a 100644 --- a/src/ui/takeScreenshot/takeScreenshot.component.ts +++ b/src/ui/takeScreenshot/takeScreenshot.component.ts @@ -1,94 +1,94 @@ +import {DOCUMENT} from "@angular/common"; import { + ChangeDetectorRef, Component, - ElementRef, + ElementRef, HostListener, Inject, - OnInit, + OnInit, Renderer2, TemplateRef, ViewChild, - ChangeDetectorRef } from "@angular/core"; -import html2canvas from "html2canvas"; -import {DOCUMENT} from "@angular/common"; import {MatDialog, MatDialogRef} from "@angular/material/dialog"; +import html2canvas from "html2canvas"; @Component({ selector: 'take-screenshot', templateUrl: './takeScreenshot.template.html', - styleUrls: ['./takeScreenshot.style.css'] + styleUrls: ['./takeScreenshot.style.css'], }) export class TakeScreenshotComponent implements OnInit { - @ViewChild('screenshotPreviewCard', {read: ElementRef}) screenshotPreviewCard: ElementRef - @ViewChild('previewImageDialog', {read: TemplateRef}) previewImageDialogTemplateRef : TemplateRef<any> + @ViewChild('screenshotPreviewCard', {read: ElementRef}) public screenshotPreviewCard: ElementRef + @ViewChild('previewImageDialog', {read: TemplateRef}) public previewImageDialogTemplateRef: TemplateRef<any> private dialogRef: MatDialogRef<any> - public takingScreenshot:boolean = false - public previewingScreenshot:boolean = false - public loadingScreenshot:boolean = false - - public screenshotName:string = `screenshot.png` + public takingScreenshot: boolean = false + public previewingScreenshot: boolean = false + public loadingScreenshot: boolean = false + + public screenshotName: string = `screenshot.png` private croppedCanvas = null public mouseIsDown = false public isDragging = false - + // Used to calculate where to start showing the dragging area - private startX:number = 0 - private startY:number = 0 - private endX:number = 0 - private endY:number = 0 - - public borderWidth:string = '' + private startX: number = 0 + private startY: number = 0 + private endX: number = 0 + private endY: number = 0 + + public borderWidth: string = '' // The box that contains the border and all required numbers. public boxTop: number = 0 public boxLeft: number = 0 public boxEndWidth: number = 0 public boxEndHeight: number = 0 - + private windowHeight: number = 0 private windowWidth: number = 0 - - private screenshotStartX:number = 0 - private screenshotStartY:number = 0 - - public imageUrl:string + + private screenshotStartX: number = 0 + private screenshotStartY: number = 0 + + public imageUrl: string constructor( private renderer: Renderer2, @Inject(DOCUMENT) private document: any, private matDialog: MatDialog, - private cdr:ChangeDetectorRef + private cdr: ChangeDetectorRef, ) {} - ngOnInit(): void { + public ngOnInit(): void { this.windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth this.windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight } @HostListener('window:resize', ['$event']) - onResize() { + public onResize() { this.windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth this.windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight } @HostListener('window:keyup', ['$event']) - keyEvent(event: KeyboardEvent) { + public keyEvent(event: KeyboardEvent) { if (this.takingScreenshot && event.key === 'Escape') { this.cancelTakingScreenshot() } } - startScreenshot(){ + public startScreenshot() { this.previewingScreenshot = false this.croppedCanvas = null this.loadingScreenshot = false this.takingScreenshot = true } - move(e:MouseEvent){ + public move(e: MouseEvent) { if (this.mouseIsDown) { this.isDragging = true @@ -165,7 +165,7 @@ export class TakeScreenshotComponent implements OnInit { } } - mouseDown(e:MouseEvent){ + public mouseDown(e: MouseEvent) { this.borderWidth = this.windowWidth + 'px ' + this.windowHeight + 'px' this.startX = e.clientX @@ -174,7 +174,7 @@ export class TakeScreenshotComponent implements OnInit { this.mouseIsDown = true } - mouseUp(e:MouseEvent){ + public mouseUp(e: MouseEvent) { this.borderWidth = '0' this.isDragging = false @@ -190,11 +190,11 @@ export class TakeScreenshotComponent implements OnInit { } - loadScreenshot() { + public loadScreenshot() { this.loadingScreenshot = true this.dialogRef = this.matDialog.open(this.previewImageDialogTemplateRef, { - autoFocus: false + autoFocus: false, }) this.dialogRef.afterClosed().toPromise() .then(result => { @@ -211,7 +211,7 @@ export class TakeScreenshotComponent implements OnInit { default: this.cancelTakingScreenshot() } }) - + html2canvas(this.document.querySelector('#neuroglancer-container canvas')).then(canvas => { this.croppedCanvas = null this.croppedCanvas = this.renderer.createElement('canvas') @@ -226,7 +226,7 @@ export class TakeScreenshotComponent implements OnInit { 0, 0, this.boxEndWidth * window.devicePixelRatio, this.boxEndHeight * window.devicePixelRatio) }).then(() => { - + const d = new Date() const n = `${d.getFullYear()}_${d.getMonth() + 1}_${d.getDate()}_${d.getHours()}_${d.getMinutes()}_${d.getSeconds()}` this.screenshotName = `${n}_IAV.png` @@ -235,18 +235,18 @@ export class TakeScreenshotComponent implements OnInit { this.imageUrl = this.croppedCanvas.toDataURL('image/png') this.previewingScreenshot = true this.clearStateAfterScreenshot() - + this.cdr.markForCheck() }) } - cancelTakingScreenshot() { + public cancelTakingScreenshot() { this.takingScreenshot = false this.previewingScreenshot = false this.loadingScreenshot = false this.croppedCanvas = null } - clearStateAfterScreenshot() { + public clearStateAfterScreenshot() { this.mouseIsDown = false this.isDragging = false this.startX = 0 diff --git a/src/ui/templateParcellationCitations/templateParcellationCitations.component.ts b/src/ui/templateParcellationCitations/templateParcellationCitations.component.ts index edffccf8e86f1609e550d06e63b4c702afde635c..81278acc90203bada2057822f7cf957206084f54 100644 --- a/src/ui/templateParcellationCitations/templateParcellationCitations.component.ts +++ b/src/ui/templateParcellationCitations/templateParcellationCitations.component.ts @@ -1,34 +1,34 @@ import { Component } from "@angular/core"; +import { select, Store } from "@ngrx/store"; import { Observable } from "rxjs"; -import { ViewerStateInterface, isDefined, safeFilter } from "../../services/stateStore.service"; -import { Store, select } from "@ngrx/store"; -import { switchMap, map } from "rxjs/operators"; +import { map, switchMap } from "rxjs/operators"; +import { isDefined, safeFilter, ViewerStateInterface } from "../../services/stateStore.service"; @Component({ selector : 'template-parcellation-citation-container', templateUrl : './templateParcellationCitations.template.html', styleUrls : [ - './templateParcellationCitations.style.css' - ] + './templateParcellationCitations.style.css', + ], }) -export class TemplateParcellationCitationsContainer{ - selectedTemplate$: Observable<any> - selectedParcellation$: Observable<any> +export class TemplateParcellationCitationsContainer { + public selectedTemplate$: Observable<any> + public selectedParcellation$: Observable<any> - constructor(private store: Store<ViewerStateInterface>){ + constructor(private store: Store<ViewerStateInterface>) { this.selectedTemplate$ = this.store.pipe( select('viewerState'), safeFilter('templateSelected'), - map(state => state.templateSelected) + map(state => state.templateSelected), ) this.selectedParcellation$ = this.selectedTemplate$.pipe( switchMap(() => this.store.pipe( select('viewerState'), safeFilter('parcellationSelected'), - map(state => state.parcellationSelected) - )) + map(state => state.parcellationSelected), + )), ) } -} \ No newline at end of file +} diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 8c5f780858399753504ef6ebba24b903fb1e28b1..038ed5d6d37dd8541e6a57887e5d9e2ac97ac7e3 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -1,83 +1,83 @@ import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from "@angular/core"; import { ComponentsModule } from "src/components/components.module"; -import { NehubaViewerUnit } from "./nehubaContainer/nehubaViewer/nehubaViewer.component"; -import { NehubaContainer } from "./nehubaContainer/nehubaContainer.component"; -import { SplashScreen, GetTemplateImageSrcPipe, ImgSrcSetPipe } from "./nehubaContainer/splashScreen/splashScreen.component"; -import { LayoutModule } from "src/layouts/layout.module"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { LayoutModule } from "src/layouts/layout.module"; +import { NehubaContainer } from "./nehubaContainer/nehubaContainer.component"; +import { NehubaViewerUnit } from "./nehubaContainer/nehubaViewer/nehubaViewer.component"; +import { GetTemplateImageSrcPipe, ImgSrcSetPipe, SplashScreen } from "./nehubaContainer/splashScreen/splashScreen.component"; -import { GroupDatasetByRegion } from "src/util/pipes/groupDataEntriesByRegion.pipe"; import { filterRegionDataEntries } from "src/util/pipes/filterRegionDataEntries.pipe"; +import { GroupDatasetByRegion } from "src/util/pipes/groupDataEntriesByRegion.pipe"; import { GetUniquePipe } from "src/util/pipes/getUnique.pipe"; -import { LandmarkUnit } from "./nehubaContainer/landmarkUnit/landmarkUnit.component"; +import { GetLayerNameFromDatasets } from "../util/pipes/getLayerNamePipe.pipe"; import { SafeStylePipe } from "../util/pipes/safeStyle.pipe"; -import { PluginBannerUI } from "./pluginBanner/pluginBanner.component"; +import { SortDataEntriesToRegion } from "../util/pipes/sortDataEntriesIntoRegion.pipe"; import { CitationsContainer } from "./citation/citations.component"; -import { LayerBrowser, LockedLayerBtnClsPipe } from "./layerbrowser/layerbrowser.component"; import { KgEntryViewer } from "./kgEntryViewer/kgentry.component"; import { SubjectViewer } from "./kgEntryViewer/subjectViewer/subjectViewer.component"; -import { GetLayerNameFromDatasets } from "../util/pipes/getLayerNamePipe.pipe"; -import { SortDataEntriesToRegion } from "../util/pipes/sortDataEntriesIntoRegion.pipe"; +import { LayerBrowser, LockedLayerBtnClsPipe } from "./layerbrowser/layerbrowser.component"; +import { LandmarkUnit } from "./nehubaContainer/landmarkUnit/landmarkUnit.component"; +import { PluginBannerUI } from "./pluginBanner/pluginBanner.component"; -import { SpatialLandmarksToDataBrowserItemPipe } from "../util/pipes/spatialLandmarksToDatabrowserItem.pipe"; -import { DownloadDirective } from "../util/directives/download.directive"; -import { LogoContainer } from "./logoContainer/logoContainer.component"; -import { TemplateParcellationCitationsContainer } from "./templateParcellationCitations/templateParcellationCitations.component"; -import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.component"; -import { HelpComponent } from "./help/help.component"; -import { ConfigComponent } from './config/config.component' -import { FlatmapArrayPipe } from "src/util/pipes/flatMapArray.pipe"; -import { DatabrowserModule } from "./databrowserModule/databrowser.module"; -import { SigninBanner } from "./signinBanner/signinBanner.components"; -import { SigninModal } from "./signinModal/signinModal.component"; -import { UtilModule } from "src/util/util.module"; -import { FilterNameBySearch } from "./viewerStateController/regionHierachy/filterNameBySearch.pipe"; -import { StatusCardComponent } from "./nehubaContainer/statusCard/statusCard.component"; -import { CookieAgreement } from "./cookieAgreement/cookieAgreement.component"; -import { KGToS } from "./kgtos/kgtos.component"; +import { ScrollingModule } from "@angular/cdk/scrolling" +import { HttpClientModule } from "@angular/common/http"; import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module' import { AppendtooltipTextPipe } from "src/util/pipes/appendTooltipText.pipe"; +import { FlatmapArrayPipe } from "src/util/pipes/flatMapArray.pipe"; +import { GetFileExtension } from "src/util/pipes/getFileExt.pipe"; +import { GetFilenamePipe } from "src/util/pipes/getFilename.pipe"; +import { UtilModule } from "src/util/util.module"; +import { DownloadDirective } from "../util/directives/download.directive"; +import { SpatialLandmarksToDataBrowserItemPipe } from "../util/pipes/spatialLandmarksToDatabrowserItem.pipe"; +import { ConfigComponent } from './config/config.component' +import { CurrentLayout } from "./config/currentLayout/currentLayout.component"; import { FourPanelLayout } from "./config/layouts/fourPanel/fourPanel.component"; import { HorizontalOneThree } from "./config/layouts/h13/h13.component"; -import { VerticalOneThree } from "./config/layouts/v13/v13.component"; import { SinglePanel } from "./config/layouts/single/single.component"; -import { CurrentLayout } from "./config/currentLayout/currentLayout.component"; +import { VerticalOneThree } from "./config/layouts/v13/v13.component"; +import { CookieAgreement } from "./cookieAgreement/cookieAgreement.component"; +import { DatabrowserModule } from "./databrowserModule/databrowser.module"; +import { HelpComponent } from "./help/help.component"; +import { KGToS } from "./kgtos/kgtos.component"; +import { LogoContainer } from "./logoContainer/logoContainer.component"; +import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.component"; import { MobileControlNubStylePipe } from "./nehubaContainer/pipes/mobileControlNubStyle.pipe"; -import { ScrollingModule } from "@angular/cdk/scrolling" -import { HttpClientModule } from "@angular/common/http"; -import { GetFilenamePipe } from "src/util/pipes/getFilename.pipe"; -import { GetFileExtension } from "src/util/pipes/getFileExt.pipe"; +import { StatusCardComponent } from "./nehubaContainer/statusCard/statusCard.component"; +import { SigninBanner } from "./signinBanner/signinBanner.components"; +import { SigninModal } from "./signinModal/signinModal.component"; +import { TemplateParcellationCitationsContainer } from "./templateParcellationCitations/templateParcellationCitations.component"; +import { FilterNameBySearch } from "./viewerStateController/regionHierachy/filterNameBySearch.pipe"; import { ViewerStateController } from 'src/ui/viewerStateController/viewerStateCFull/viewerState.component' import { ViewerStateMini } from 'src/ui/viewerStateController/viewerStateCMini/viewerStateMini.component' -import { BinSavedRegionsSelectionPipe, SavedRegionsSelectionBtnDisabledPipe } from "./viewerStateController/viewerState.pipes"; -import { PluginBtnFabColorPipe } from "src/util/pipes/pluginBtnFabColor.pipe"; +import { HumanReadableFileSizePipe } from "src/util/pipes/humanReadableFileSize.pipe"; import { KgSearchBtnColorPipe } from "src/util/pipes/kgSearchBtnColor.pipe"; +import { PluginBtnFabColorPipe } from "src/util/pipes/pluginBtnFabColor.pipe"; import { TemplateParcellationHasMoreInfo } from "src/util/pipes/templateParcellationHasMoreInfo.pipe"; -import { HumanReadableFileSizePipe } from "src/util/pipes/humanReadableFileSize.pipe"; import { MaximmisePanelButton } from "./nehubaContainer/maximisePanelButton/maximisePanelButton.component"; -import { TouchSideClass } from "./nehubaContainer/touchSideClass.directive"; import { ReorderPanelIndexPipe } from "./nehubaContainer/reorderPanelIndex.pipe"; +import { TouchSideClass } from "./nehubaContainer/touchSideClass.directive"; +import { BinSavedRegionsSelectionPipe, SavedRegionsSelectionBtnDisabledPipe } from "./viewerStateController/viewerState.pipes"; import {ElementOutClickDirective} from "src/util/directives/elementOutClick.directive"; import {FilterWithStringPipe} from "src/util/pipes/filterWithString.pipe"; import { SearchSideNav } from "./searchSideNav/searchSideNav.component"; +import {TakeScreenshotComponent} from "src/ui/takeScreenshot/takeScreenshot.component"; +import {FixedMouseContextualContainerDirective} from "src/util/directives/FixedMouseContextualContainerDirective.directive"; import { RegionHierarchy } from './viewerStateController/regionHierachy/regionHierarchy.component' -import { CurrentlySelectedRegions } from './viewerStateController/regionsListView/currentlySelectedRegions/currentlySelectedRegions.component' import { RegionTextSearchAutocomplete } from "./viewerStateController/regionSearch/regionSearch.component"; +import { CurrentlySelectedRegions } from './viewerStateController/regionsListView/currentlySelectedRegions/currentlySelectedRegions.component' import { RegionsListView } from "./viewerStateController/regionsListView/simpleRegionsListView/regionListView.component"; -import {TakeScreenshotComponent} from "src/ui/takeScreenshot/takeScreenshot.component"; -import {FixedMouseContextualContainerDirective} from "src/util/directives/FixedMouseContextualContainerDirective.directive"; +import {ConnectivityBrowserComponent} from "src/ui/connectivityBrowser/connectivityBrowser.component"; import { RegionMenuComponent } from 'src/ui/parcellationRegion/regionMenu/regionMenu.component' -import { SimpleRegionComponent } from "./parcellationRegion/regionSimple/regionSimple.component"; import { RegionListSimpleViewComponent } from "./parcellationRegion/regionListSimpleView/regionListSimpleView.component"; -import {ConnectivityBrowserComponent} from "src/ui/connectivityBrowser/connectivityBrowser.component"; +import { SimpleRegionComponent } from "./parcellationRegion/regionSimple/regionSimple.component"; @NgModule({ imports : [ @@ -156,12 +156,11 @@ import {ConnectivityBrowserComponent} from "src/ui/connectivityBrowser/connectiv HumanReadableFileSizePipe, ReorderPanelIndexPipe, - /* directive */ DownloadDirective, TouchSideClass, ElementOutClickDirective, - FixedMouseContextualContainerDirective + FixedMouseContextualContainerDirective, ], entryComponents : [ @@ -192,12 +191,12 @@ import {ConnectivityBrowserComponent} from "src/ui/connectivityBrowser/connectiv SearchSideNav, ViewerStateMini, RegionMenuComponent, - FixedMouseContextualContainerDirective + FixedMouseContextualContainerDirective, ], schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ] + CUSTOM_ELEMENTS_SCHEMA, + ], }) -export class UIModule{ +export class UIModule { } diff --git a/src/ui/viewerStateController/regionHierachy/filterNameBySearch.pipe.ts b/src/ui/viewerStateController/regionHierachy/filterNameBySearch.pipe.ts index 274644c8d76dcf1b465eaac37ba8cdc698fdebcc..29f7a0a74a5fc28f707b25045e887404c17f8e3e 100644 --- a/src/ui/viewerStateController/regionHierachy/filterNameBySearch.pipe.ts +++ b/src/ui/viewerStateController/regionHierachy/filterNameBySearch.pipe.ts @@ -1,17 +1,16 @@ import { Pipe, PipeTransform } from "@angular/core"; - @Pipe({ - name : 'filterNameBySearch' + name : 'filterNameBySearch', }) -export class FilterNameBySearch implements PipeTransform{ - public transform(searchFields:string[],searchTerm:string){ - try{ - return searchFields.some(searchField=> new RegExp(searchTerm,'i').test(searchField)) - }catch(e){ +export class FilterNameBySearch implements PipeTransform { + public transform(searchFields: string[], searchTerm: string) { + try { + return searchFields.some(searchField => new RegExp(searchTerm, 'i').test(searchField)) + } catch (e) { /* https://stackoverflow.com/a/9310752/6059235 */ return searchFields.some(searchField => new RegExp(searchTerm.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')).test(searchField)) } } -} \ No newline at end of file +} diff --git a/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts b/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts index 6a059daa3e7790bd06e5b6796545813187b7cd3a..6dcf1f69bce6d59bd2d75c35242c0d751c18da3c 100644 --- a/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts +++ b/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts @@ -1,38 +1,38 @@ -import { EventEmitter, Component, ElementRef, ViewChild, HostListener, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, Input, Output, AfterViewInit } from "@angular/core"; -import { Subscription, Subject, fromEvent } from "rxjs"; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, ViewChild } from "@angular/core"; +import { fromEvent, Subject, Subscription } from "rxjs"; import { buffer, debounceTime, tap } from "rxjs/operators"; -import { FilterNameBySearch } from "./filterNameBySearch.pipe"; import { generateLabelIndexId } from "src/services/stateStore.service"; +import { FilterNameBySearch } from "./filterNameBySearch.pipe"; -const insertHighlight :(name:string, searchTerm:string) => string = (name:string, searchTerm:string = '') => { +const insertHighlight: (name: string, searchTerm: string) => string = (name: string, searchTerm: string = '') => { const regex = new RegExp(searchTerm, 'gi') return searchTerm === '' ? name : name.replace(regex, (s) => `<span class = "highlight">${s}</span>`) } -const getDisplayTreeNode : (searchTerm:string, selectedRegions:any[]) => (item:any) => string = (searchTerm:string = '', selectedRegions:any[] = []) => ({ ngId, name, status, labelIndex }) => { +const getDisplayTreeNode: (searchTerm: string, selectedRegions: any[]) => (item: any) => string = (searchTerm: string = '', selectedRegions: any[] = []) => ({ ngId, name, status, labelIndex }) => { return !!labelIndex && !!ngId && selectedRegions.findIndex(re => - generateLabelIndexId({ labelIndex: re.labelIndex, ngId: re.ngId }) === generateLabelIndexId({ ngId, labelIndex }) + generateLabelIndexId({ labelIndex: re.labelIndex, ngId: re.ngId }) === generateLabelIndexId({ ngId, labelIndex }), ) >= 0 ? `<span class="cursor-default regionSelected">${insertHighlight(name, searchTerm)}</span>` + (status ? ` <span class="text-muted">(${insertHighlight(status, searchTerm)})</span>` : ``) : `<span class="cursor-default regionNotSelected">${insertHighlight(name, searchTerm)}</span>` + (status ? ` <span class="text-muted">(${insertHighlight(status, searchTerm)})</span>` : ``) } -const getFilterTreeBySearch = (pipe:FilterNameBySearch, searchTerm:string) => (node:any) => pipe.transform([node.name, node.status], searchTerm) +const getFilterTreeBySearch = (pipe: FilterNameBySearch, searchTerm: string) => (node: any) => pipe.transform([node.name, node.status], searchTerm) @Component({ selector: 'region-hierarchy', templateUrl: './regionHierarchy.template.html', styleUrls: [ - './regionHierarchy.style.css' + './regionHierarchy.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class RegionHierarchy implements OnInit, AfterViewInit{ +export class RegionHierarchy implements OnInit, AfterViewInit { @Input() public useMobileUI: boolean = false @@ -68,21 +68,21 @@ export class RegionHierarchy implements OnInit, AfterViewInit{ /** * set the height to max, bound by max-height */ - numTotalRenderedRegions: number = 999 - windowHeight: number + public numTotalRenderedRegions: number = 999 + public windowHeight: number @HostListener('document:click', ['$event']) - closeRegion(event: MouseEvent) { + public closeRegion(event: MouseEvent) { const contains = this.el.nativeElement.contains(event.target) this.showRegionTree = contains - if (!this.showRegionTree){ + if (!this.showRegionTree) { this.searchTerm = '' this.numTotalRenderedRegions = 999 } } @HostListener('window:resize', ['$event']) - onResize(event) { + public onResize(event) { this.windowHeight = event.target.innerHeight; } @@ -91,38 +91,38 @@ export class RegionHierarchy implements OnInit, AfterViewInit{ } constructor( - private cdr:ChangeDetectorRef, - private el:ElementRef - ){ + private cdr: ChangeDetectorRef, + private el: ElementRef, + ) { this.windowHeight = window.innerHeight; } - ngOnChanges(){ + public ngOnChanges() { if (this.parcellationSelected) { this.placeHolderText = `Search region in ${this.parcellationSelected.name}` this.aggregatedRegionTree = { name: this.parcellationSelected.name, - children: this.parcellationSelected.regions + children: this.parcellationSelected.regions, } } this.displayTreeNode = getDisplayTreeNode(this.searchTerm, this.selectedRegions) this.filterTreeBySearch = getFilterTreeBySearch(this.filterNameBySearchPipe, this.searchTerm) } - clearRegions(event:MouseEvent){ + public clearRegions(event: MouseEvent) { this.clearAllRegions.emit(event) } - get showRegionTree(){ + get showRegionTree() { return this._showRegionTree } - set showRegionTree(flag: boolean){ + set showRegionTree(flag: boolean) { this._showRegionTree = flag this.showRegionFlagChanged.emit(this._showRegionTree) } - ngOnInit(){ + public ngOnInit() { this.displayTreeNode = getDisplayTreeNode(this.searchTerm, this.selectedRegions) this.filterTreeBySearch = getFilterTreeBySearch(this.filterNameBySearchPipe, this.searchTerm) @@ -130,47 +130,47 @@ export class RegionHierarchy implements OnInit, AfterViewInit{ this.handleRegionTreeClickSubject.pipe( buffer( this.handleRegionTreeClickSubject.pipe( - debounceTime(200) - ) - ) - ).subscribe(arr => arr.length > 1 ? this.doubleClick(arr[0]) : this.singleClick(arr[0])) + debounceTime(200), + ), + ), + ).subscribe(arr => arr.length > 1 ? this.doubleClick(arr[0]) : this.singleClick(arr[0])), ) } - ngAfterViewInit(){ + public ngAfterViewInit() { this.subscriptions.push( fromEvent(this.searchTermInput.nativeElement, 'input').pipe( - debounceTime(200) + debounceTime(200), ).subscribe(ev => { this.changeSearchTerm(ev) - }) + }), ) } - escape(event:KeyboardEvent){ + public escape(event: KeyboardEvent) { this.showRegionTree = false this.searchTerm = ''; (event.target as HTMLInputElement).blur() } - handleTotalRenderedListChanged(changeEvent: {previous: number, current: number}){ + public handleTotalRenderedListChanged(changeEvent: {previous: number, current: number}) { const { current } = changeEvent this.numTotalRenderedRegions = current } - regionHierarchyHeight(){ + public regionHierarchyHeight() { return({ 'height' : (this.numTotalRenderedRegions * 15 + 60).toString() + 'px', - 'max-height': (this.windowHeight - 100) + 'px' + 'max-height': (this.windowHeight - 100) + 'px', }) } /* NB need to bind two way data binding like this. Or else, on searchInput blur, the flat tree will be rebuilt, resulting in first click to be ignored */ - changeSearchTerm(event: any) { - if (event.target.value === this.searchTerm) return + public changeSearchTerm(event: any) { + if (event.target.value === this.searchTerm) { return } this.searchTerm = event.target.value this.ngOnChanges() this.cdr.markForCheck() @@ -178,7 +178,7 @@ export class RegionHierarchy implements OnInit, AfterViewInit{ private handleRegionTreeClickSubject: Subject<any> = new Subject() - handleClickRegion(obj: any) { + public handleClickRegion(obj: any) { const {event} = obj /** * TODO figure out why @closeRegion gets triggered, but also, contains returns false @@ -191,26 +191,28 @@ export class RegionHierarchy implements OnInit, AfterViewInit{ /* single click selects/deselects region(s) */ private singleClick(obj: any) { - if (!obj) return + if (!obj) { return } const { inputItem : region } = obj - if (!region) return + if (!region) { return } this.singleClickRegion.emit(region) } /* double click navigate to the interested area */ private doubleClick(obj: any) { - if (!obj) + if (!obj) { return + } const { inputItem : region } = obj - if (!region) + if (!region) { return + } this.doubleClickRegion.emit(region) } - public displayTreeNode: (item:any) => string + public displayTreeNode: (item: any) => string private filterNameBySearchPipe = new FilterNameBySearch() - public filterTreeBySearch: (node:any) => boolean + public filterTreeBySearch: (node: any) => boolean public aggregatedRegionTree: any @@ -223,6 +225,6 @@ export class RegionHierarchy implements OnInit, AfterViewInit{ } } -export function trackRegionBy(index: number, region: any){ +export function trackRegionBy(index: number, region: any) { return region.labelIndex || region.id -} \ No newline at end of file +} diff --git a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts b/src/ui/viewerStateController/regionSearch/regionSearch.component.ts index 80a4cdb8b596d52f8daef7b20b8cdf06aefc5972..2c452ad7940a14044f5a9ac8b789c1de17769f93 100644 --- a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts +++ b/src/ui/viewerStateController/regionSearch/regionSearch.component.ts @@ -1,14 +1,14 @@ -import { Component, EventEmitter, Output, ViewChild, ElementRef, TemplateRef, Input, ChangeDetectionStrategy } from "@angular/core"; -import { Store, select } from "@ngrx/store"; -import { Observable, combineLatest } from "rxjs"; -import { map, distinctUntilChanged, startWith, debounceTime, shareReplay, take, tap, filter } from "rxjs/operators"; -import { getMultiNgIdsRegionsLabelIndexMap, generateLabelIndexId, IavRootStoreInterface } from "src/services/stateStore.service"; +import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, Output, TemplateRef, ViewChild } from "@angular/core"; import { FormControl } from "@angular/forms"; import { MatAutocompleteSelectedEvent, MatDialog } from "@angular/material"; -import { ADD_TO_REGIONS_SELECTION_WITH_IDS, SELECT_REGIONS, CHANGE_NAVIGATION } from "src/services/state/viewerState.store"; -import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "../viewerState.base"; +import { select, Store } from "@ngrx/store"; +import { combineLatest, Observable } from "rxjs"; +import { debounceTime, distinctUntilChanged, filter, map, shareReplay, startWith, take, tap } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; import { VIEWER_STATE_ACTION_TYPES } from "src/services/effect/effect"; +import { ADD_TO_REGIONS_SELECTION_WITH_IDS, CHANGE_NAVIGATION, SELECT_REGIONS } from "src/services/state/viewerState.store"; +import { generateLabelIndexId, getMultiNgIdsRegionsLabelIndexMap, IavRootStoreInterface } from "src/services/stateStore.service"; +import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "../viewerState.base"; const filterRegionBasedOnText = searchTerm => region => region.name.toLowerCase().includes(searchTerm.toLowerCase()) const compareFn = (it, item) => it.name === item.name @@ -17,20 +17,20 @@ const compareFn = (it, item) => it.name === item.name selector: 'region-text-search-autocomplete', templateUrl: './regionSearch.template.html', styleUrls: [ - './regionSearch.style.css' + './regionSearch.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class RegionTextSearchAutocomplete{ +export class RegionTextSearchAutocomplete { public compareFn = compareFn @Input() public showBadge: boolean = false @Input() public showAutoComplete: boolean = true - @ViewChild('autoTrigger', {read: ElementRef}) autoTrigger: ElementRef - @ViewChild('regionHierarchyDialog', {read:TemplateRef}) regionHierarchyDialogTemplate: TemplateRef<any> + @ViewChild('autoTrigger', {read: ElementRef}) public autoTrigger: ElementRef + @ViewChild('regionHierarchyDialog', {read: TemplateRef}) public regionHierarchyDialogTemplate: TemplateRef<any> public useMobileUI$: Observable<boolean> @@ -39,14 +39,14 @@ export class RegionTextSearchAutocomplete{ constructor( private store$: Store<IavRootStoreInterface>, private dialog: MatDialog, - private constantService: AtlasViewerConstantsServices - ){ + private constantService: AtlasViewerConstantsServices, + ) { this.useMobileUI$ = this.constantService.useMobileUI$ const viewerState$ = this.store$.pipe( select('viewerState'), - shareReplay(1) + shareReplay(1), ) this.regionsWithLabelIndex$ = viewerState$.pipe( @@ -57,18 +57,18 @@ export class RegionTextSearchAutocomplete{ const returnArray = [] const ngIdMap = getMultiNgIdsRegionsLabelIndexMap(parcellationSelected) for (const [ngId, labelIndexMap] of ngIdMap) { - for (const [labelIndex, region] of labelIndexMap){ + for (const [labelIndex, region] of labelIndexMap) { returnArray.push({ ...region, ngId, labelIndex, - labelIndexId: generateLabelIndexId({ ngId, labelIndex }) + labelIndexId: generateLabelIndexId({ ngId, labelIndex }), }) } } return returnArray }), - shareReplay(1) + shareReplay(1), ) this.autocompleteList$ = combineLatest( @@ -78,11 +78,11 @@ export class RegionTextSearchAutocomplete{ debounceTime(200), ), this.regionsWithLabelIndex$.pipe( - startWith([]) - ) + startWith([]), + ), ).pipe( map(([searchTerm, regionsWithLabelIndex]) => regionsWithLabelIndex.filter(filterRegionBasedOnText(searchTerm))), - map(arr => arr.slice(0, 5)) + map(arr => arr.slice(0, 5)), ) this.regionsSelected$ = viewerState$.pipe( @@ -92,41 +92,41 @@ export class RegionTextSearchAutocomplete{ const arrLabelIndexId = regions.map(({ ngId, labelIndex }) => generateLabelIndexId({ ngId, labelIndex })) this.selectedRegionLabelIndexSet = new Set(arrLabelIndexId) }), - shareReplay(1) + shareReplay(1), ) this.parcellationSelected$ = viewerState$.pipe( select('parcellationSelected'), distinctUntilChanged(), - shareReplay(1) + shareReplay(1), ) } - public toggleRegionWithId(id: string, removeFlag=false){ + public toggleRegionWithId(id: string, removeFlag= false) { if (removeFlag) { this.store$.dispatch({ type: VIEWER_STATE_ACTION_TYPES.DESELECT_REGIONS_WITH_ID, - deselecRegionIds: [id] + deselecRegionIds: [id], }) } else { this.store$.dispatch({ type: ADD_TO_REGIONS_SELECTION_WITH_IDS, - selectRegionIds : [id] + selectRegionIds : [id], }) } } - public navigateTo(position){ + public navigateTo(position) { this.store$.dispatch({ type: CHANGE_NAVIGATION, navigation: { position, - animation: {} - } + animation: {}, + }, }) } - public optionSelected(ev: MatAutocompleteSelectedEvent){ + public optionSelected(ev: MatAutocompleteSelectedEvent) { const id = ev.option.value this.autoTrigger.nativeElement.value = '' } @@ -138,28 +138,27 @@ export class RegionTextSearchAutocomplete{ public regionsSelected$: Observable<any> public parcellationSelected$: Observable<any> - @Output() public focusedStateChanged: EventEmitter<boolean> = new EventEmitter() private _focused: boolean = false - set focused(val: boolean){ + set focused(val: boolean) { this._focused = val this.focusedStateChanged.emit(val) } - get focused(){ + get focused() { return this._focused } - public deselectAllRegions(event: MouseEvent){ + public deselectAllRegions(event: MouseEvent) { this.store$.dispatch({ type: SELECT_REGIONS, - selectRegions: [] + selectRegions: [], }) } // TODO handle mobile - handleRegionClick({ mode = null, region = null } = {}){ + public handleRegionClick({ mode = null, region = null } = {}) { const type = mode === 'single' ? VIEWERSTATE_CONTROLLER_ACTION_TYPES.SINGLE_CLICK_ON_REGIONHIERARCHY : mode === 'double' @@ -167,11 +166,11 @@ export class RegionTextSearchAutocomplete{ : '' this.store$.dispatch({ type, - payload: { region } + payload: { region }, }) } - showHierarchy(event:MouseEvent){ + public showHierarchy(event: MouseEvent) { // mat-card-content has a max height of 65vh const dialog = this.dialog.open(this.regionHierarchyDialogTemplate, { height: '65vh', @@ -180,22 +179,22 @@ export class RegionTextSearchAutocomplete{ 'col-sm-10', 'col-md-8', 'col-lg-8', - 'col-xl-6' - ] + 'col-xl-6', + ], }) /** * keep sleight of hand shown while modal is shown - * + * */ this.focused = true - + /** * take 1 to avoid memory leak */ dialog.afterClosed().pipe( - take(1) + take(1), ).subscribe(() => this.focused = false) } -} \ No newline at end of file +} diff --git a/src/ui/viewerStateController/regionsListView/currentlySelectedRegions/currentlySelectedRegions.component.ts b/src/ui/viewerStateController/regionsListView/currentlySelectedRegions/currentlySelectedRegions.component.ts index dfe221228634827feb3b300043caa3a05e667f3c..9d0e71b338e5f4782a14c92cc2dbc463b69b84f7 100644 --- a/src/ui/viewerStateController/regionsListView/currentlySelectedRegions/currentlySelectedRegions.component.ts +++ b/src/ui/viewerStateController/regionsListView/currentlySelectedRegions/currentlySelectedRegions.component.ts @@ -1,47 +1,46 @@ import { Component } from "@angular/core"; -import { Store, select } from "@ngrx/store"; +import { select, Store } from "@ngrx/store"; import { Observable } from "rxjs"; import { distinctUntilChanged, startWith } from "rxjs/operators"; import { DESELECT_REGIONS } from "src/services/state/viewerState.store"; -import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "src/ui/viewerStateController/viewerState.base"; import { IavRootStoreInterface } from "src/services/stateStore.service"; +import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "src/ui/viewerStateController/viewerState.base"; @Component({ selector: 'currently-selected-regions', templateUrl: './currentlySelectedRegions.template.html', styleUrls: [ - './currentlySelectedRegions.style.css' - ] + './currentlySelectedRegions.style.css', + ], }) export class CurrentlySelectedRegions { - public regionSelected$: Observable<any[]> - + constructor( - private store$: Store<IavRootStoreInterface> - ){ + private store$: Store<IavRootStoreInterface>, + ) { this.regionSelected$ = this.store$.pipe( select('viewerState'), select('regionsSelected'), startWith([]), - distinctUntilChanged() + distinctUntilChanged(), ) } - public deselectRegion(event: MouseEvent, region: any){ + public deselectRegion(event: MouseEvent, region: any) { this.store$.dispatch({ type: DESELECT_REGIONS, - deselectRegions: [region] + deselectRegions: [region], }) } - public gotoRegion(event: MouseEvent, region:any){ + public gotoRegion(event: MouseEvent, region: any) { this.store$.dispatch({ type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.DOUBLE_CLICK_ON_REGIONHIERARCHY, - payload: { region } + payload: { region }, }) } -} \ No newline at end of file +} diff --git a/src/ui/viewerStateController/regionsListView/simpleRegionsListView/regionListView.component.ts b/src/ui/viewerStateController/regionsListView/simpleRegionsListView/regionListView.component.ts index 8a964af89956e988fff8fc2b7230c784e002ab3a..f2f52d328448c9610a3d4846ac1c9a0e28bb49b3 100644 --- a/src/ui/viewerStateController/regionsListView/simpleRegionsListView/regionListView.component.ts +++ b/src/ui/viewerStateController/regionsListView/simpleRegionsListView/regionListView.component.ts @@ -1,18 +1,18 @@ -import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter } from "@angular/core"; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core"; @Component({ selector: 'regions-list-view', templateUrl: './regionListView.template.html', styleUrls: [ - './regionListView.style.css' + './regionListView.style.css', ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class RegionsListView{ - @Input() horizontal: boolean = false +export class RegionsListView { + @Input() public horizontal: boolean = false - @Input() regionsSelected: any[] = [] - @Output() deselectRegion: EventEmitter<any> = new EventEmitter() - @Output() gotoRegion: EventEmitter<any> = new EventEmitter() -} \ No newline at end of file + @Input() public regionsSelected: any[] = [] + @Output() public deselectRegion: EventEmitter<any> = new EventEmitter() + @Output() public gotoRegion: EventEmitter<any> = new EventEmitter() +} diff --git a/src/ui/viewerStateController/viewerState.base.ts b/src/ui/viewerStateController/viewerState.base.ts index 49d5eb5127b2f5ab77565b84af2a9e347b6c69fa..61cf9ea8706e89046a44516774d1b2479f452a26 100644 --- a/src/ui/viewerStateController/viewerState.base.ts +++ b/src/ui/viewerStateController/viewerState.base.ts @@ -1,20 +1,19 @@ -import { ViewChild, TemplateRef, OnInit } from "@angular/core"; -import { Store, select } from "@ngrx/store"; +import { OnInit, TemplateRef, ViewChild } from "@angular/core"; +import { MatBottomSheet, MatBottomSheetRef, MatSelectChange } from "@angular/material"; +import { select, Store } from "@ngrx/store"; import { Observable, Subscription } from "rxjs"; -import { distinctUntilChanged, shareReplay, filter } from "rxjs/operators"; -import { SELECT_REGIONS, USER_CONFIG_ACTION_TYPES, IavRootStoreInterface } from "src/services/stateStore.service"; -import { MatSelectChange, MatBottomSheet, MatBottomSheetRef } from "@angular/material"; +import { distinctUntilChanged, filter, shareReplay } from "rxjs/operators"; import { DialogService } from "src/services/dialogService.service"; import { RegionSelection } from "src/services/state/userConfigState.store"; - +import { IavRootStoreInterface, SELECT_REGIONS, USER_CONFIG_ACTION_TYPES } from "src/services/stateStore.service"; const compareWith = (o, n) => !o || !n ? false : o.name === n.name -export class ViewerStateBase implements OnInit{ +export class ViewerStateBase implements OnInit { - @ViewChild('savedRegionBottomSheetTemplate', {read:TemplateRef}) savedRegionBottomSheetTemplate: TemplateRef<any> + @ViewChild('savedRegionBottomSheetTemplate', {read: TemplateRef}) public savedRegionBottomSheetTemplate: TemplateRef<any> public focused: boolean = false @@ -38,153 +37,148 @@ export class ViewerStateBase implements OnInit{ constructor( private store$: Store<IavRootStoreInterface>, private dialogService: DialogService, - private bottomSheet: MatBottomSheet - ){ + private bottomSheet: MatBottomSheet, + ) { const viewerState$ = this.store$.pipe( select('viewerState'), - shareReplay(1) + shareReplay(1), ) this.savedRegionsSelections$ = this.store$.pipe( select('userConfigState'), select('savedRegionsSelection'), - shareReplay(1) + shareReplay(1), ) this.templateSelected$ = viewerState$.pipe( select('templateSelected'), - distinctUntilChanged() + distinctUntilChanged(), ) this.parcellationSelected$ = viewerState$.pipe( select('parcellationSelected'), distinctUntilChanged(), - shareReplay(1) + shareReplay(1), ) this.regionsSelected$ = viewerState$.pipe( select('regionsSelected'), distinctUntilChanged(), - shareReplay(1) + shareReplay(1), ) this.availableTemplates$ = viewerState$.pipe( select('fetchedTemplates'), - distinctUntilChanged() + distinctUntilChanged(), ) this.availableParcellations$ = this.templateSelected$.pipe( - select('parcellations') + select('parcellations'), ) - + } - ngOnInit(){ + public ngOnInit() { this.subscriptions.push( this.savedRegionsSelections$.pipe( - filter(srs => srs.length === 0) - ).subscribe(() => this.savedRegionBottomSheetRef && this.savedRegionBottomSheetRef.dismiss()) + filter(srs => srs.length === 0), + ).subscribe(() => this.savedRegionBottomSheetRef && this.savedRegionBottomSheetRef.dismiss()), ) } - handleTemplateChange(event:MatSelectChange){ - + public handleTemplateChange(event: MatSelectChange) { + this.store$.dispatch({ type: ACTION_TYPES.SELECT_TEMPLATE_WITH_NAME, payload: { - name: event.value - } + name: event.value, + }, }) } - handleParcellationChange(event:MatSelectChange){ - if (!event.value) return + public handleParcellationChange(event: MatSelectChange) { + if (!event.value) { return } this.store$.dispatch({ type: ACTION_TYPES.SELECT_PARCELLATION_WITH_NAME, payload: { - name: event.value - } + name: event.value, + }, }) } - loadSavedRegion(event:MouseEvent, savedRegionsSelection:RegionSelection){ + public loadSavedRegion(event: MouseEvent, savedRegionsSelection: RegionSelection) { this.store$.dispatch({ type: USER_CONFIG_ACTION_TYPES.LOAD_REGIONS_SELECTION, payload: { - savedRegionsSelection - } + savedRegionsSelection, + }, }) } - public editSavedRegion(event: MouseEvent, savedRegionsSelection: RegionSelection){ + public editSavedRegion(event: MouseEvent, savedRegionsSelection: RegionSelection) { event.preventDefault() event.stopPropagation() this.dialogService.getUserInput({ defaultValue: savedRegionsSelection.name, placeholder: `Enter new name`, title: 'Edit name', - iconClass: null + iconClass: null, }).then(name => { - if (!name) throw new Error('user cancelled') + if (!name) { throw new Error('user cancelled') } this.store$.dispatch({ type: USER_CONFIG_ACTION_TYPES.UPDATE_REGIONS_SELECTION, payload: { ...savedRegionsSelection, - name - } + name, + }, }) }).catch(e => { // TODO catch user cancel }) } - public removeSavedRegion(event: MouseEvent, savedRegionsSelection: RegionSelection){ + public removeSavedRegion(event: MouseEvent, savedRegionsSelection: RegionSelection) { event.preventDefault() event.stopPropagation() this.store$.dispatch({ type: USER_CONFIG_ACTION_TYPES.DELETE_REGIONS_SELECTION, payload: { - ...savedRegionsSelection - } + ...savedRegionsSelection, + }, }) } - - displayActiveParcellation(parcellation:any){ + public displayActiveParcellation(parcellation: any) { return `<div class="d-flex"><small>Parcellation</small> <small class = "flex-grow-1 mute-text">${parcellation ? '(' + parcellation.name + ')' : ''}</small> <span class = "fas fa-caret-down"></span></div>` } - displayActiveTemplate(template: any) { + public displayActiveTemplate(template: any) { return `<div class="d-flex"><small>Template</small> <small class = "flex-grow-1 mute-text">${template ? '(' + template.name + ')' : ''}</small> <span class = "fas fa-caret-down"></span></div>` } - public loadSelection(event: MouseEvent){ + public loadSelection(event: MouseEvent) { this.focused = true - + this.savedRegionBottomSheetRef = this.bottomSheet.open(this.savedRegionBottomSheetTemplate) this.savedRegionBottomSheetRef.afterDismissed() - .subscribe(val => { - - }, error => { - - }, () => { + .subscribe(null, null, () => { this.focused = false this.savedRegionBottomSheetRef = null }) } - public saveSelection(event: MouseEvent){ + public saveSelection(event: MouseEvent) { this.focused = true this.dialogService.getUserInput({ defaultValue: `Saved Region`, placeholder: `Name the selection`, title: 'Save region selection', - iconClass: 'far fa-bookmark' + iconClass: 'far fa-bookmark', }) .then(name => { - if (!name) throw new Error('User cancelled') + if (!name) { throw new Error('User cancelled') } this.store$.dispatch({ type: USER_CONFIG_ACTION_TYPES.SAVE_REGIONS_SELECTION, - payload: { name } + payload: { name }, }) }) .catch(e => { @@ -195,10 +189,10 @@ export class ViewerStateBase implements OnInit{ .finally(() => this.focused = false) } - public deselectAllRegions(event: MouseEvent){ + public deselectAllRegions(event: MouseEvent) { this.store$.dispatch({ type: SELECT_REGIONS, - selectRegions: [] + selectRegions: [], }) } @@ -211,7 +205,7 @@ const ACTION_TYPES = { SELECT_PARCELLATION_WITH_NAME: 'SELECT_PARCELLATION_WITH_NAME', TOGGLE_REGION_SELECT: 'TOGGLE_REGION_SELECT', - NAVIGATETO_REGION: 'NAVIGATETO_REGION' + NAVIGATETO_REGION: 'NAVIGATETO_REGION', } export const VIEWERSTATE_CONTROLLER_ACTION_TYPES = ACTION_TYPES diff --git a/src/ui/viewerStateController/viewerState.pipes.ts b/src/ui/viewerStateController/viewerState.pipes.ts index 659d35778f3378966cb58f82d47f789e9ca95d89..331878e551baaa54856911c59876149dddb88d54 100644 --- a/src/ui/viewerStateController/viewerState.pipes.ts +++ b/src/ui/viewerStateController/viewerState.pipes.ts @@ -2,17 +2,16 @@ import { Pipe, PipeTransform } from "@angular/core"; import { RegionSelection } from "src/services/state/userConfigState.store"; @Pipe({ - name: 'binSavedRegionsSelectionPipe' + name: 'binSavedRegionsSelectionPipe', }) -export class BinSavedRegionsSelectionPipe implements PipeTransform{ - public transform(regionSelections:RegionSelection[]):{parcellationSelected:any, templateSelected:any, regionSelections: RegionSelection[]}[]{ +export class BinSavedRegionsSelectionPipe implements PipeTransform { + public transform(regionSelections: RegionSelection[]): Array<{parcellationSelected: any, templateSelected: any, regionSelections: RegionSelection[]}> { const returnMap = new Map() - for (let regionSelection of regionSelections){ + for (const regionSelection of regionSelections) { const key = `${regionSelection.templateSelected.name}\n${regionSelection.parcellationSelected.name}` const existing = returnMap.get(key) - if (existing) existing.push(regionSelection) - else returnMap.set(key, [regionSelection]) + if (existing) { existing.push(regionSelection) } else { returnMap.set(key, [regionSelection]) } } return Array.from(returnMap) .map(([_, regionSelections]) => { @@ -20,19 +19,19 @@ export class BinSavedRegionsSelectionPipe implements PipeTransform{ return { regionSelections, parcellationSelected, - templateSelected + templateSelected, } }) } } @Pipe({ - name: 'savedRegionsSelectionBtnDisabledPipe' + name: 'savedRegionsSelectionBtnDisabledPipe', }) -export class SavedRegionsSelectionBtnDisabledPipe implements PipeTransform{ - public transform(regionSelection: RegionSelection, templateSelected: any, parcellationSelected: any): boolean{ +export class SavedRegionsSelectionBtnDisabledPipe implements PipeTransform { + public transform(regionSelection: RegionSelection, templateSelected: any, parcellationSelected: any): boolean { return regionSelection.parcellationSelected.name !== parcellationSelected.name || regionSelection.templateSelected.name !== templateSelected.name } -} \ No newline at end of file +} diff --git a/src/ui/viewerStateController/viewerState.useEffect.ts b/src/ui/viewerStateController/viewerState.useEffect.ts index 5e7a3fc7fd6698ef1e517f3c74d7525570c749c0..7b734139ab2bc28b01e12c240c9016f12b5d4be1 100644 --- a/src/ui/viewerStateController/viewerState.useEffect.ts +++ b/src/ui/viewerStateController/viewerState.useEffect.ts @@ -1,73 +1,72 @@ -import { Subscription, Observable } from "rxjs"; -import { Injectable, OnInit, OnDestroy } from "@angular/core"; -import { Actions, ofType, Effect } from "@ngrx/effects"; -import { Store, select, Action } from "@ngrx/store"; -import { shareReplay, distinctUntilChanged, map, withLatestFrom, filter } from "rxjs/operators"; -import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "./viewerState.base"; -import { CHANGE_NAVIGATION, SELECT_REGIONS, NEWVIEWER, GENERAL_ACTION_TYPES, SELECT_PARCELLATION, isDefined, IavRootStoreInterface, FETCHED_TEMPLATE } from "src/services/stateStore.service"; -import { regionFlattener } from "src/util/regionFlattener"; -import { UIService } from "src/services/uiService.service"; +import { Injectable, OnDestroy, OnInit } from "@angular/core"; +import { Actions, Effect, ofType } from "@ngrx/effects"; +import { Action, select, Store } from "@ngrx/store"; +import { Observable, Subscription } from "rxjs"; +import { distinctUntilChanged, filter, map, shareReplay, withLatestFrom } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; +import { CHANGE_NAVIGATION, FETCHED_TEMPLATE, GENERAL_ACTION_TYPES, IavRootStoreInterface, isDefined, NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS } from "src/services/stateStore.service"; +import { UIService } from "src/services/uiService.service"; +import { regionFlattener } from "src/util/regionFlattener"; +import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "./viewerState.base"; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ +export class ViewerStateControllerUseEffect implements OnInit, OnDestroy { private subscriptions: Subscription[] = [] private selectedRegions$: Observable<any[]> - @Effect() - init$ = this.constantSerivce.initFetchTemplate$.pipe( + public init$ = this.constantSerivce.initFetchTemplate$.pipe( map(fetchedTemplate => { return { type: FETCHED_TEMPLATE, - fetchedTemplate + fetchedTemplate, } - }) + }), ) @Effect() - selectTemplateWithName$: Observable<any> - + public selectTemplateWithName$: Observable<any> + @Effect() - selectParcellationWithName$: Observable<any> + public selectParcellationWithName$: Observable<any> /** * Determines how single click on region hierarchy will affect view */ @Effect() - singleClickOnHierarchy$: Observable<any> + public singleClickOnHierarchy$: Observable<any> /** * Determines how double click on region hierarchy will effect view */ @Effect() - doubleClickOnHierarchy$: Observable<any> + public doubleClickOnHierarchy$: Observable<any> @Effect() - toggleRegionSelection$: Observable<any> + public toggleRegionSelection$: Observable<any> @Effect() - navigateToRegion$: Observable<any> + public navigateToRegion$: Observable<any> constructor( private actions$: Actions, private store$: Store<IavRootStoreInterface>, private uiService: UIService, - private constantSerivce: AtlasViewerConstantsServices - ){ + private constantSerivce: AtlasViewerConstantsServices, + ) { const viewerState$ = this.store$.pipe( select('viewerState'), - shareReplay(1) + shareReplay(1), ) this.selectedRegions$ = viewerState$.pipe( select('regionsSelected'), - distinctUntilChanged() + distinctUntilChanged(), ) this.selectParcellationWithName$ = this.actions$.pipe( @@ -79,15 +78,15 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ }), filter(name => !!name), withLatestFrom(viewerState$.pipe( - select('parcellationSelected') + select('parcellationSelected'), )), filter(([name, parcellationSelected]) => { - if (parcellationSelected && parcellationSelected.name === name) return false + if (parcellationSelected && parcellationSelected.name === name) { return false } return true }), map(([name, _]) => name), withLatestFrom(viewerState$.pipe( - select('templateSelected') + select('templateSelected'), )), map(([name, templateSelected]) => { @@ -97,17 +96,17 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ return { type: GENERAL_ACTION_TYPES.ERROR, payload: { - message: 'Selected parcellation not found.' - } + message: 'Selected parcellation not found.', + }, } } return { type: SELECT_PARCELLATION, - selectParcellation: newParcellation + selectParcellation: newParcellation, } - }) + }), ) - + this.selectTemplateWithName$ = this.actions$.pipe( ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.SELECT_TEMPLATE_WITH_NAME), map(action => { @@ -117,15 +116,15 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ }), filter(name => !!name), withLatestFrom(viewerState$.pipe( - select('templateSelected') + select('templateSelected'), )), filter(([name, templateSelected]) => { - if (templateSelected && templateSelected.name === name) return false + if (templateSelected && templateSelected.name === name) { return false } return true }), map(([name, templateSelected]) => name), withLatestFrom(viewerState$.pipe( - select('fetchedTemplates') + select('fetchedTemplates'), )), map(([name, availableTemplates]) => { const newTemplateTobeSelected = availableTemplates.find(t => t.name === name) @@ -133,16 +132,16 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ return { type: GENERAL_ACTION_TYPES.ERROR, payload: { - message: 'Selected template not found.' - } + message: 'Selected template not found.', + }, } } return { type: NEWVIEWER, selectTemplate: newTemplateTobeSelected, - selectParcellation: newTemplateTobeSelected.parcellations[0] + selectParcellation: newTemplateTobeSelected.parcellations[0], } - }) + }), ) this.doubleClickOnHierarchy$ = this.actions$.pipe( @@ -150,9 +149,9 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ map(action => { return { ...action, - type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.NAVIGATETO_REGION + type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.NAVIGATETO_REGION, } - }) + }), ) this.navigateToRegion$ = this.actions$.pipe( @@ -164,8 +163,8 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ return { type: GENERAL_ACTION_TYPES.ERROR, payload: { - message: `Go to region: region not defined` - } + message: `Go to region: region not defined`, + }, } } @@ -174,8 +173,8 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ return { type: GENERAL_ACTION_TYPES.ERROR, payload: { - message: `${region.name} - does not have a position defined` - } + message: `${region.name} - does not have a position defined`, + }, } } @@ -183,10 +182,10 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ type: CHANGE_NAVIGATION, navigation: { position, - animation: {} - } + animation: {}, + }, } - }) + }), ) this.singleClickOnHierarchy$ = this.actions$.pipe( @@ -194,9 +193,9 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ map(action => { return { ...action, - type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.TOGGLE_REGION_SELECT + type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.TOGGLE_REGION_SELECT, } - }) + }), ) this.toggleRegionSelection$ = this.actions$.pipe( @@ -215,13 +214,13 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ type: SELECT_REGIONS, selectRegions: selectAll ? regionsSelected.concat(flattenedRegion) - : regionsSelected.filter(r => !flattenedRegionNames.has(r.name)) + : regionsSelected.filter(r => !flattenedRegionNames.has(r.name)), } - }) + }), ) } - ngOnInit(){ + public ngOnInit() { this.subscriptions.push( this.doubleClickOnHierarchy$.subscribe(({ region } = {}) => { const { position } = region @@ -230,24 +229,24 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ type: CHANGE_NAVIGATION, navigation: { position, - animation: {} - } + animation: {}, + }, }) } else { this.uiService.showMessage(`${region.name} does not have a position defined`) } - }) + }), ) } - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } } -interface ViewerStateAction extends Action{ +interface ViewerStateAction extends Action { payload: any config: any -} \ No newline at end of file +} diff --git a/src/ui/viewerStateController/viewerStateCFull/viewerState.component.ts b/src/ui/viewerStateController/viewerStateCFull/viewerState.component.ts index 74ce39cb2fe0e34dd5881b4aef31bbe5797edc5a..71bb0e8df64effe20ada23ac1e6c23b7b4e13393 100644 --- a/src/ui/viewerStateController/viewerStateCFull/viewerState.component.ts +++ b/src/ui/viewerStateController/viewerStateCFull/viewerState.component.ts @@ -1,10 +1,10 @@ import { Component } from "@angular/core"; -import { Store } from "@ngrx/store"; import { MatBottomSheet } from "@angular/material"; +import { Store } from "@ngrx/store"; import { DialogService } from "src/services/dialogService.service"; -import { ViewerStateBase } from '../viewerState.base' import { IavRootStoreInterface } from "src/services/stateStore.service"; +import { ViewerStateBase } from '../viewerState.base' const compareWith = (o, n) => !o || !n ? false @@ -14,18 +14,18 @@ const compareWith = (o, n) => !o || !n selector: 'viewer-state-controller', templateUrl: './viewerState.template.html', styleUrls: [ - './viewerState.style.css' - ] + './viewerState.style.css', + ], }) -export class ViewerStateController extends ViewerStateBase{ +export class ViewerStateController extends ViewerStateBase { constructor( store$: Store<IavRootStoreInterface>, dialogService: DialogService, - bottomSheet: MatBottomSheet - ){ - super(store$,dialogService,bottomSheet) + bottomSheet: MatBottomSheet, + ) { + super(store$, dialogService, bottomSheet) } } diff --git a/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.component.ts b/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.component.ts index 4b49aa2a03dbb585a9491ae06b8bddfd320efe57..c59b21bab288ae507a0e268a2a901d33e540c1c2 100644 --- a/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.component.ts +++ b/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.component.ts @@ -1,26 +1,26 @@ import { Component } from "@angular/core"; -import { Store } from "@ngrx/store"; import { MatBottomSheet } from "@angular/material"; +import { Store } from "@ngrx/store"; import { DialogService } from "src/services/dialogService.service"; -import { ViewerStateBase } from '../viewerState.base' import { IavRootStoreInterface } from "src/services/stateStore.service"; +import { ViewerStateBase } from '../viewerState.base' @Component({ selector: 'viewer-state-mini', templateUrl: './viewerStateMini.template.html', styleUrls: [ - './viewerStateMini.style.css' - ] + './viewerStateMini.style.css', + ], }) -export class ViewerStateMini extends ViewerStateBase{ +export class ViewerStateMini extends ViewerStateBase { constructor( store$: Store<IavRootStoreInterface>, dialogService: DialogService, - bottomSheet: MatBottomSheet - ){ - super(store$,dialogService,bottomSheet) + bottomSheet: MatBottomSheet, + ) { + super(store$, dialogService, bottomSheet) } } diff --git a/src/util/constants.ts b/src/util/constants.ts index f1612bcb4b9294ee210508e448e5859952ed789a..d43935864e7a384045bc4163bd4518ffc10d6ec0 100644 --- a/src/util/constants.ts +++ b/src/util/constants.ts @@ -6,7 +6,7 @@ export const LOCAL_STORAGE_CONST = { AGREE_COOKIE: 'fzj.xg.iv.AGREE_COOKIE', AGREE_KG_TOS: 'fzj.xg.iv.AGREE_KG_TOS', - FAV_DATASET: 'fzj.xg.iv.FAV_DATASET' + FAV_DATASET: 'fzj.xg.iv.FAV_DATASET', } export const COOKIE_VERSION = '0.3.0' diff --git a/src/util/directives/FixedMouseContextualContainerDirective.directive.ts b/src/util/directives/FixedMouseContextualContainerDirective.directive.ts index 458d01069897e2654e32b6b51d966711380e6ff1..d599da891736a16ab8ae78e56cf9a570d61089aa 100644 --- a/src/util/directives/FixedMouseContextualContainerDirective.directive.ts +++ b/src/util/directives/FixedMouseContextualContainerDirective.directive.ts @@ -1,7 +1,7 @@ -import { Directive, Input, HostBinding, HostListener, ElementRef, OnChanges, Output, EventEmitter } from "@angular/core"; +import { Directive, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnChanges, Output } from "@angular/core"; @Directive({ - selector: '[fixedMouseContextualContainerDirective]' + selector: '[fixedMouseContextualContainerDirective]', }) export class FixedMouseContextualContainerDirective { @@ -19,19 +19,19 @@ export class FixedMouseContextualContainerDirective { public onHide: EventEmitter<null> = new EventEmitter() constructor( - private el: ElementRef - ){ - + private el: ElementRef, + ) { + } - public show(){ + public show() { setTimeout(() => { if (window.innerHeight - this.mousePos[1] < this.el.nativeElement.clientHeight) { this.mousePos[1] = window.innerHeight - this.el.nativeElement.clientHeight } if ((window.innerWidth - this.mousePos[0]) < this.el.nativeElement.clientWidth) { - this.mousePos[0] = window.innerWidth-this.el.nativeElement.clientWidth + this.mousePos[0] = window.innerWidth - this.el.nativeElement.clientWidth } this.transform = `translate(${this.mousePos.map(v => v.toString() + 'px').join(', ')})` @@ -41,7 +41,7 @@ export class FixedMouseContextualContainerDirective { this.onShow.emit() } - public hide(){ + public hide() { this.transform = `translate(${this.defaultPos.map(v => v.toString() + 'px').join(', ')})` this.styleDisplay = 'none' this.isShown = false @@ -54,4 +54,4 @@ export class FixedMouseContextualContainerDirective { @HostBinding('style.transform') public transform = `translate(${this.mousePos.map(v => v.toString() + 'px').join(', ')})` -} \ No newline at end of file +} diff --git a/src/util/directives/captureClickListener.directive.ts b/src/util/directives/captureClickListener.directive.ts index 3931bc88187bef63764d5754d688d60a1bdcff4a..655fb8b72f6b76f2cf954b95c09a797f9cebad66 100644 --- a/src/util/directives/captureClickListener.directive.ts +++ b/src/util/directives/captureClickListener.directive.ts @@ -1,20 +1,20 @@ import { Directive, ElementRef, EventEmitter, OnDestroy, OnInit, Output } from "@angular/core"; -import { Subscription, fromEvent } from "rxjs"; +import { fromEvent, Subscription } from "rxjs"; import { switchMapTo, takeUntil } from "rxjs/operators"; @Directive({ - selector: '[iav-captureClickListenerDirective]' + selector: '[iav-captureClickListenerDirective]', }) export class CaptureClickListenerDirective implements OnInit, OnDestroy { private subscriptions: Subscription[] = [] - @Output('iav-captureClickListenerDirective-onClick') mapClicked: EventEmitter<any> = new EventEmitter() - @Output('iav-captureClickListenerDirective-onMousedown') mouseDownEmitter: EventEmitter<any> = new EventEmitter() + @Output('iav-captureClickListenerDirective-onClick') public mapClicked: EventEmitter<any> = new EventEmitter() + @Output('iav-captureClickListenerDirective-onMousedown') public mouseDownEmitter: EventEmitter<any> = new EventEmitter() constructor(private el: ElementRef) { } - ngOnInit() { + public ngOnInit() { const mouseDownObs$ = fromEvent(this.el.nativeElement, 'mousedown', { capture: true }) const mouseMoveObs$ = fromEvent(this.el.nativeElement, 'mousemove', { capture: true }) const mouseUpObs$ = fromEvent(this.el.nativeElement, 'mouseup', { capture: true }) @@ -26,17 +26,17 @@ export class CaptureClickListenerDirective implements OnInit, OnDestroy { mouseDownObs$.pipe( switchMapTo( mouseUpObs$.pipe( - takeUntil(mouseMoveObs$) - ) - ) + takeUntil(mouseMoveObs$), + ), + ), ).subscribe(event => { this.mapClicked.emit(event) - }) + }), ) } - ngOnDestroy() { + public ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()) } -} \ No newline at end of file +} diff --git a/src/util/directives/delayEvent.directive.ts b/src/util/directives/delayEvent.directive.ts index 0194e0dab10d9594a4e8ecefe279afc9ac73b063..e0cd2e4f8b232075746c5d6d1df6583f5eac52ba 100644 --- a/src/util/directives/delayEvent.directive.ts +++ b/src/util/directives/delayEvent.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Input, OnChanges, OnDestroy, ElementRef, Output, EventEmitter } from "@angular/core"; +import { Directive, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output } from "@angular/core"; const VALID_EVENTNAMES = new Set([ 'mousedown', @@ -8,44 +8,47 @@ const VALID_EVENTNAMES = new Set([ 'mouseleave', 'touchstart', 'touchmove', - 'touchend' + 'touchend', ]) @Directive({ - selector: '[iav-delay-event]' + selector: '[iav-delay-event]', }) export class DelayEventDirective implements OnChanges, OnDestroy { - private evListener = (ev:Event) => setTimeout(() => this.delayedEmit.emit(ev)) + private evListener = (ev: Event) => setTimeout(() => this.delayedEmit.emit(ev)) @Input('iav-delay-event') - delayEvent: string = '' + public delayEvent: string = '' @Output() - delayedEmit: EventEmitter<any> = new EventEmitter() + public delayedEmit: EventEmitter<any> = new EventEmitter() - constructor(private el: ElementRef){ + constructor( + private el: ElementRef, + ) { } - private destroyCb: (() => void)[] = [] - ngOnChanges(){ + private destroyCb: Array<() => void> = [] + public ngOnChanges() { this.ngOnDestroy() - if (!this.delayEvent || this.delayEvent === '') return + if (!this.delayEvent || this.delayEvent === '') { return } const el = this.el.nativeElement as HTMLElement - for (const evName of this.delayEvent.split(' ')){ + for (const evName of this.delayEvent.split(' ')) { if (VALID_EVENTNAMES.has(evName)) { el.addEventListener(evName, this.evListener) this.destroyCb.push(() => el.removeEventListener(evName, this.evListener)) } else { + // tslint:disable-next-line console.warn(`${evName} is not a valid event name in the supported set`, VALID_EVENTNAMES) } } } - ngOnDestroy(){ - while(this.destroyCb.length > 0) this.destroyCb.pop()() + public ngOnDestroy() { + while (this.destroyCb.length > 0) { this.destroyCb.pop()() } } -} \ No newline at end of file +} diff --git a/src/util/directives/dockedContainer.directive.ts b/src/util/directives/dockedContainer.directive.ts index 741fa61fd3d190d1320292b4ed955919938a1749..db9c8b065c9867a486f6ee2dc6377bb3135c9094 100644 --- a/src/util/directives/dockedContainer.directive.ts +++ b/src/util/directives/dockedContainer.directive.ts @@ -1,16 +1,15 @@ import { Directive, ViewContainerRef } from "@angular/core"; import { WidgetServices } from "src/atlasViewer/widgetUnit/widgetService.service"; - @Directive({ - selector: '[dockedContainerDirective]' + selector: '[dockedContainerDirective]', }) -export class DockedContainerDirective{ +export class DockedContainerDirective { constructor( widgetService: WidgetServices, - viewContainerRef: ViewContainerRef - ){ + viewContainerRef: ViewContainerRef, + ) { widgetService.dockedContainer = viewContainerRef } -} \ No newline at end of file +} diff --git a/src/util/directives/download.directive.ts b/src/util/directives/download.directive.ts index 650c8108b4f32fa657c01cb9a445ebf212129fb0..c0851002d0977cd7e534bc0bbf0b99cd446a7921 100644 --- a/src/util/directives/download.directive.ts +++ b/src/util/directives/download.directive.ts @@ -1,20 +1,20 @@ import { Directive, ElementRef, Renderer2 } from "@angular/core"; @Directive({ - selector : 'a[download]' + selector : 'a[download]', }) -export class DownloadDirective{ +export class DownloadDirective { - public downloadIcon:HTMLElement + public downloadIcon: HTMLElement - constructor(public el:ElementRef, public rd2:Renderer2){ + constructor(public el: ElementRef, public rd2: Renderer2) { this.downloadIcon = rd2.createElement('i') rd2.addClass(this.downloadIcon, 'fas') rd2.addClass(this.downloadIcon, 'fa-download-alt') } - ngAfterViewInit(){ + public ngAfterViewInit() { this.rd2.appendChild(this.el.nativeElement, this.downloadIcon) } -} \ No newline at end of file +} diff --git a/src/util/directives/dragDrop.directive.ts b/src/util/directives/dragDrop.directive.ts index 83895d5f0c4b81e472eb3e6c714697037986e109..d1a08f762edeb01e7a2c084323617ce7ed5e65a6 100644 --- a/src/util/directives/dragDrop.directive.ts +++ b/src/util/directives/dragDrop.directive.ts @@ -1,44 +1,44 @@ -import { Directive, Input, Output, EventEmitter, HostListener, ElementRef, OnInit, OnDestroy, HostBinding } from "@angular/core"; +import { Directive, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnDestroy, OnInit, Output } from "@angular/core"; import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from "@angular/material"; -import { Observable, fromEvent, merge, Subscription, of, from } from "rxjs"; -import { map, scan, distinctUntilChanged, debounceTime, tap, switchMap, takeUntil } from "rxjs/operators"; +import { from, fromEvent, merge, Observable, of, Subscription } from "rxjs"; +import { debounceTime, distinctUntilChanged, map, scan, switchMap, takeUntil, tap } from "rxjs/operators"; @Directive({ - selector: '[drag-drop]' + selector: '[drag-drop]', }) -export class DragDropDirective implements OnInit, OnDestroy{ +export class DragDropDirective implements OnInit, OnDestroy { @Input() - snackText: string + public snackText: string @Output('drag-drop') - dragDropOnDrop: EventEmitter<File[]> = new EventEmitter() + public dragDropOnDrop: EventEmitter<File[]> = new EventEmitter() @HostBinding('style.transition') - transition = `opacity 300ms ease-in` + public transition = `opacity 300ms ease-in` @HostBinding('style.opacity') - opacity = null + public opacity = null public snackbarRef: MatSnackBarRef<SimpleSnackBar> private dragover$: Observable<boolean> @HostListener('dragover', ['$event']) - ondragover(ev:DragEvent){ + public ondragover(ev: DragEvent) { ev.preventDefault() } @HostListener('drop', ['$event']) - ondrop(ev:DragEvent) { + public ondrop(ev: DragEvent) { ev.preventDefault() this.reset() this.dragDropOnDrop.emit(Array.from(ev.dataTransfer.files)) } - reset(){ + public reset() { if (this.snackbarRef) { this.snackbarRef.dismiss() } @@ -47,10 +47,10 @@ export class DragDropDirective implements OnInit, OnDestroy{ private subscriptions: Subscription[] = [] - ngOnInit(){ + public ngOnInit() { this.subscriptions.push( this.dragover$.pipe( - debounceTime(16) + debounceTime(16), ).subscribe(flag => { if (flag) { this.snackbarRef = this.snackBar.open(this.snackText || `Drop file(s) here.`) @@ -58,32 +58,32 @@ export class DragDropDirective implements OnInit, OnDestroy{ } else { this.reset() } - }) + }), ) } - ngOnDestroy(){ - while(this.subscriptions.length > 0) { + public ngOnDestroy() { + while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() } } - constructor(private snackBar: MatSnackBar, private el:ElementRef){ + constructor(private snackBar: MatSnackBar, private el: ElementRef) { this.dragover$ = merge( of(null), - fromEvent(this.el.nativeElement, 'drop') + fromEvent(this.el.nativeElement, 'drop'), ).pipe( switchMap(() => merge( fromEvent(this.el.nativeElement, 'dragenter').pipe( - map(() => 1) + map(() => 1), ), fromEvent(this.el.nativeElement, 'dragleave').pipe( - map(() => -1) - ) + map(() => -1), + ), ).pipe( scan((acc, curr) => acc + curr, 0), - map(val => val > 0) - )) + map(val => val > 0), + )), ) } -} \ No newline at end of file +} diff --git a/src/util/directives/elementOutClick.directive.ts b/src/util/directives/elementOutClick.directive.ts index b46f2fa97e4e71c103ac75dfdffc79ebd1db4843..1734eeaed04305233a535645535b729ae2256781 100644 --- a/src/util/directives/elementOutClick.directive.ts +++ b/src/util/directives/elementOutClick.directive.ts @@ -1,15 +1,15 @@ import {Directive, ElementRef, EventEmitter, HostListener, Output} from "@angular/core"; @Directive({ - selector: '[elementOutClick]' + selector: '[elementOutClick]', }) export class ElementOutClickDirective { constructor(private elRef: ElementRef) { } - @Output() outsideClick = new EventEmitter() + @Output() public outsideClick = new EventEmitter() @HostListener('document:click', ['$event', '$event.target']) - public onclick(event:MouseEvent, targetElement: HTMLElement): void{ + public onclick(event: MouseEvent, targetElement: HTMLElement): void { if (!targetElement) { return } diff --git a/src/util/directives/floatingContainer.directive.ts b/src/util/directives/floatingContainer.directive.ts index 740ab815a6ebeb6a7ab1c365467136e5288daacd..ecff9cb357f6869d49e0f4a5aee5f03127c01f49 100644 --- a/src/util/directives/floatingContainer.directive.ts +++ b/src/util/directives/floatingContainer.directive.ts @@ -2,14 +2,14 @@ import { Directive, ViewContainerRef } from "@angular/core"; import { WidgetServices } from "src/atlasViewer/widgetUnit/widgetService.service"; @Directive({ - selector: '[floatingContainerDirective]' + selector: '[floatingContainerDirective]', }) -export class FloatingContainerDirective{ +export class FloatingContainerDirective { constructor( widgetService: WidgetServices, - viewContainerRef: ViewContainerRef - ){ + viewContainerRef: ViewContainerRef, + ) { widgetService.floatingContainer = viewContainerRef } -} \ No newline at end of file +} diff --git a/src/util/directives/floatingMouseContextualContainer.directive.ts b/src/util/directives/floatingMouseContextualContainer.directive.ts index 556052c751cc5953c797ed7bdbf3883dcdd744ae..d924d7133e90299e137469a9b49b512695424271 100644 --- a/src/util/directives/floatingMouseContextualContainer.directive.ts +++ b/src/util/directives/floatingMouseContextualContainer.directive.ts @@ -1,29 +1,28 @@ -import { Directive, HostListener, HostBinding } from "@angular/core"; +import { Directive, HostBinding, HostListener } from "@angular/core"; import { DomSanitizer, SafeUrl } from "@angular/platform-browser"; @Directive({ - selector: '[floatingMouseContextualContainerDirective]' + selector: '[floatingMouseContextualContainerDirective]', }) -export class FloatingMouseContextualContainerDirective{ - +export class FloatingMouseContextualContainerDirective { + private mousePos: [number, number] = [0, 0] - constructor(private sanitizer: DomSanitizer){ + constructor(private sanitizer: DomSanitizer) { } @HostListener('document:mousemove', ['$event']) - mousemove(event:MouseEvent){ + public mousemove(event: MouseEvent) { this.mousePos = [event.clientX, event.clientY] this.transform = `translate(${this.mousePos[0]}px,${this.mousePos[1]}px)` } @HostBinding('style') - style: SafeUrl = this.sanitizer.bypassSecurityTrustStyle('position: absolute; width: 0; height: 0; top: 0; left: 0;') - + public style: SafeUrl = this.sanitizer.bypassSecurityTrustStyle('position: absolute; width: 0; height: 0; top: 0; left: 0;') @HostBinding('style.transform') - transform: string = `translate(${this.mousePos[0]}px,${this.mousePos[1]}px)` -} \ No newline at end of file + public transform: string = `translate(${this.mousePos[0]}px,${this.mousePos[1]}px)` +} diff --git a/src/util/directives/glyphiconTooltip.directive.ts b/src/util/directives/glyphiconTooltip.directive.ts deleted file mode 100644 index 38544204a3295fc19ca075266350b53482ee6309..0000000000000000000000000000000000000000 --- a/src/util/directives/glyphiconTooltip.directive.ts +++ /dev/null @@ -1,158 +0,0 @@ -import {Directive, - ViewContainerRef, - ElementRef, - Renderer2} from '@angular/core' -import { TooltipDirective } from 'ngx-bootstrap/tooltip' -import { ComponentLoaderFactory } from 'ngx-bootstrap/component-loader'; - -@Directive({ - selector : '.fas.fa-screenshot' -}) - -export class fasTooltipScreenshotDirective extends TooltipDirective{ - constructor( - public viewContainerRef:ViewContainerRef, - public rd : Renderer2, - public elementRef:ElementRef, - public clf:ComponentLoaderFactory, - ){ - super(viewContainerRef,rd,elementRef,clf,{ - placement : 'bottom', - triggers : 'mouseenter:mouseleave', - container : 'body' - }) - - this.tooltip = `navigate` - this.ngOnInit() - } -} - -@Directive({ - selector : '.fas.fa-remove-sign' -}) - -export class fasTooltipRemoveSignDirective extends TooltipDirective{ - constructor( - public viewContainerRef:ViewContainerRef, - public rd : Renderer2, - public elementRef:ElementRef, - public clf:ComponentLoaderFactory, - ){ - super(viewContainerRef,rd,elementRef,clf,{ - placement : 'bottom', - triggers : 'mouseenter:mouseleave', - container : 'body' - }) - - this.tooltip = `remove area` - this.ngOnInit() - } -} - -@Directive({ - selector : '.fas.fa-remove' -}) - -export class fasTooltipRemoveDirective extends TooltipDirective{ - constructor( - public viewContainerRef:ViewContainerRef, - public rd : Renderer2, - public elementRef:ElementRef, - public clf:ComponentLoaderFactory, - ){ - super(viewContainerRef,rd,elementRef,clf,{ - placement : 'bottom', - triggers : 'mouseenter:mouseleave', - container : 'body' - }) - - this.tooltip = `close` - this.ngOnInit() - } -} - -@Directive({ - selector : '.fas.fa-new-window' -}) - -export class fasTooltipNewWindowDirective extends TooltipDirective{ - constructor( - public viewContainerRef:ViewContainerRef, - public rd : Renderer2, - public elementRef:ElementRef, - public clf:ComponentLoaderFactory, - ){ - super(viewContainerRef,rd,elementRef,clf,{ - placement : 'bottom', - triggers : 'mouseenter:mouseleave', - container : 'body' - }) - - this.tooltip = `undock` - this.ngOnInit() - } -} - -@Directive({ - selector : '.fas.fa-log-in' -}) - -export class fasTooltipLogInDirective extends TooltipDirective{ - constructor( - public viewContainerRef:ViewContainerRef, - public rd : Renderer2, - public elementRef:ElementRef, - public clf:ComponentLoaderFactory, - ){ - super(viewContainerRef,rd,elementRef,clf,{ - placement : 'bottom', - triggers : 'mouseenter:mouseleave', - container : 'body' - }) - - this.tooltip = `dock` - this.ngOnInit() - } -} -@Directive({ - selector : '.fas.fa-question-circle' -}) - -export class fasTooltipQuestionSignDirective extends TooltipDirective{ - constructor( - public viewContainerRef:ViewContainerRef, - public rd : Renderer2, - public elementRef:ElementRef, - public clf:ComponentLoaderFactory, - ){ - super(viewContainerRef,rd,elementRef,clf,{ - placement : 'bottom', - triggers : 'mouseenter:mouseleave', - container : 'body' - }) - - this.tooltip = `help` - this.ngOnInit() - } -} -@Directive({ - selector : '.fas.fa-info-sign' -}) - -export class fasTooltipInfoSignDirective extends TooltipDirective{ - constructor( - public viewContainerRef:ViewContainerRef, - public rd : Renderer2, - public elementRef:ElementRef, - public clf:ComponentLoaderFactory, - ){ - super(viewContainerRef,rd,elementRef,clf,{ - placement : 'bottom', - triggers : 'mouseenter:mouseleave', - container : 'body' - }) - - this.tooltip = `more information` - this.ngOnInit() - } -} \ No newline at end of file diff --git a/src/util/directives/keyDownListener.directive.ts b/src/util/directives/keyDownListener.directive.ts index b6423f210c9e4a754b3827e11e255482c294a802..9cae5845bcbb408312d9bed42b70cfd0dcf1f571 100644 --- a/src/util/directives/keyDownListener.directive.ts +++ b/src/util/directives/keyDownListener.directive.ts @@ -1,93 +1,93 @@ -import { Directive, Input, HostListener, Output, EventEmitter } from "@angular/core"; +import { Directive, EventEmitter, HostListener, Input, Output } from "@angular/core"; const getFilterFn = (ev: KeyboardEvent, isDocument: boolean) => ({ type, key, target }: KeyListenerConfig): boolean => type === ev.type && ev.key === key && (target === 'document') === isDocument @Directive({ - selector: '[iav-key-listener]' + selector: '[iav-key-listener]', }) -export class KeyListner{ +export class KeyListner { @Input('iav-key-listener') - keydownConfig: KeyListenerConfig[] = [] + public keydownConfig: KeyListenerConfig[] = [] - private isTextField(ev: KeyboardEvent):boolean{ + private isTextField(ev: KeyboardEvent): boolean { - const target = <HTMLElement> ev.target + const target = ev.target as HTMLElement const tagName = target.tagName - return (tagName === 'SELECT' || tagName === 'INPUT' || tagName === 'TEXTAREA') + return (tagName === 'SELECT' || tagName === 'INPUT' || tagName === 'TEXTAREA') } @HostListener('keydown', ['$event']) - keydown(ev: KeyboardEvent){ + public keydown(ev: KeyboardEvent) { this.handleSelfListener(ev) } @HostListener('document:keydown', ['$event']) - documentKeydown(ev: KeyboardEvent){ + public documentKeydown(ev: KeyboardEvent) { this.handleDocumentListener(ev) } @HostListener('keyup', ['$event']) - keyup(ev: KeyboardEvent){ + public keyup(ev: KeyboardEvent) { this.handleSelfListener(ev) } @HostListener('document:keyup', ['$event']) - documentKeyup(ev: KeyboardEvent){ + public documentKeyup(ev: KeyboardEvent) { this.handleDocumentListener(ev) } private handleSelfListener(ev: KeyboardEvent) { - if (!this.keydownConfig) return - if (this.isTextField(ev)) return + if (!this.keydownConfig) { return } + if (this.isTextField(ev)) { return } const filteredConfig = this.keydownConfig .filter(getFilterFn(ev, false)) .map(config => { return { config, - ev + ev, } }) this.emitEv(filteredConfig) } - private handleDocumentListener(ev:KeyboardEvent) { - if (!this.keydownConfig) return - if (this.isTextField(ev)) return + private handleDocumentListener(ev: KeyboardEvent) { + if (!this.keydownConfig) { return } + if (this.isTextField(ev)) { return } const filteredConfig = this.keydownConfig .filter(getFilterFn(ev, true)) .map(config => { return { config, - ev + ev, } }) this.emitEv(filteredConfig) } - private emitEv(items: {config:KeyListenerConfig, ev: KeyboardEvent}[]){ - for (const item of items){ - const { config, ev } = item as {config:KeyListenerConfig, ev: KeyboardEvent} + private emitEv(items: Array<{config: KeyListenerConfig, ev: KeyboardEvent}>) { + for (const item of items) { + const { config, ev } = item as {config: KeyListenerConfig, ev: KeyboardEvent} const { stop, prevent } = config - if (stop) ev.stopPropagation() - if (prevent) ev.preventDefault() + if (stop) { ev.stopPropagation() } + if (prevent) { ev.preventDefault() } this.keyEvent.emit({ - config, ev + config, ev, }) } } - @Output('iav-key-event') keyEvent = new EventEmitter<{ config: KeyListenerConfig, ev: KeyboardEvent }>() + @Output('iav-key-event') public keyEvent = new EventEmitter<{ config: KeyListenerConfig, ev: KeyboardEvent }>() } -export interface KeyListenerConfig{ +export interface KeyListenerConfig { type: 'keydown' | 'keyup' key: string target?: 'document' diff --git a/src/util/directives/mouseOver.directive.spec.ts b/src/util/directives/mouseOver.directive.spec.ts index 418befede9892d351ef4f52a3edc0e74cce30e4c..3a7a26b9d20ee44cedc4fc80f1db579070308456 100644 --- a/src/util/directives/mouseOver.directive.spec.ts +++ b/src/util/directives/mouseOver.directive.spec.ts @@ -1,9 +1,9 @@ -import { temporalPositveScanFn } from './mouseOver.directive' -import { Subject, forkJoin } from 'rxjs'; import {} from 'jasmine' -import { scan, take, skip } from 'rxjs/operators'; +import { forkJoin, Subject } from 'rxjs'; +import { scan, skip, take } from 'rxjs/operators'; +import { temporalPositveScanFn } from './mouseOver.directive' -const segmentsPositive = { segments: [{ hello: 'world' }] } as {segments:any} +const segmentsPositive = { segments: [{ hello: 'world' }] } as {segments: any} const segmentsNegative = { segments: null } const userLandmarkPostive = { userLandmark: true } @@ -12,7 +12,7 @@ const userLandmarkNegative = { userLandmark: null } describe('temporalPositveScanFn', () => { const subscriptions = [] afterAll(() => { - while(subscriptions.length > 0) subscriptions.pop().unsubscribe() + while (subscriptions.length > 0) { subscriptions.pop().unsubscribe() } }) it('should scan obs as expected', (done) => { @@ -21,34 +21,34 @@ describe('temporalPositveScanFn', () => { const testFirstEv = source.pipe( scan(temporalPositveScanFn, []), - take(1) + take(1), ) const testSecondEv = source.pipe( scan(temporalPositveScanFn, []), skip(1), - take(1) + take(1), ) const testThirdEv = source.pipe( scan(temporalPositveScanFn, []), skip(2), - take(1) + take(1), ) const testFourthEv = source.pipe( scan(temporalPositveScanFn, []), skip(3), - take(1) + take(1), ) forkJoin( testFirstEv, testSecondEv, testThirdEv, - testFourthEv + testFourthEv, ).pipe( - take(1) + take(1), ).subscribe(([ arr1, arr2, arr3, arr4 ]) => { expect(arr1).toEqual([ segmentsPositive ]) expect(arr2).toEqual([ userLandmarkPostive, segmentsPositive ]) diff --git a/src/util/directives/mouseOver.directive.ts b/src/util/directives/mouseOver.directive.ts index abdb285759bf9c4c69a7be272a8d86f0e4036a96..c0825cf0dadd3fc7f7c725addcb4a9c100098f4e 100644 --- a/src/util/directives/mouseOver.directive.ts +++ b/src/util/directives/mouseOver.directive.ts @@ -1,76 +1,79 @@ import { Directive, Pipe, PipeTransform, SecurityContext } from "@angular/core"; -import { Store, select } from "@ngrx/store"; -import { filter, distinctUntilChanged, map, shareReplay, scan, startWith, withLatestFrom, tap } from "rxjs/operators"; -import { merge, Observable, combineLatest } from "rxjs"; +import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; +import { select, Store } from "@ngrx/store"; +import { combineLatest, merge, Observable } from "rxjs"; +import { distinctUntilChanged, filter, map, scan, shareReplay, startWith, withLatestFrom } from "rxjs/operators"; import { TransformOnhoverSegmentPipe } from "src/atlasViewer/onhoverSegment.pipe"; -import { SafeHtml, DomSanitizer } from "@angular/platform-browser"; +import { LoggingService } from "src/services/logging.service"; import { getNgIdLabelIndexFromId, IavRootStoreInterface } from "src/services/stateStore.service"; - /** * Scan function which prepends newest positive (i.e. defined) value - * + * * e.g. const source = new Subject() * source.pipe( * scan(temporalPositveScanFn, []) - * ).subscribe(console.log) // outputs - * - * - * + * ).subscribe(this.log.log) // outputs + * + * + * */ -export const temporalPositveScanFn = (acc: {segments:any, landmark:any, userLandmark: any}[], curr: {segments:any, landmark:any, userLandmark: any}) => { +export const temporalPositveScanFn = (acc: Array<{segments: any, landmark: any, userLandmark: any}>, curr: {segments: any, landmark: any, userLandmark: any}) => { const keys = Object.keys(curr) const isPositive = keys.some(key => !!curr[key]) - + return isPositive - ? [curr, ...(acc.filter(item => !keys.some(key => !!item[key])))] as {segments?:any, landmark?:any, userLandmark?: any}[] + ? [curr, ...(acc.filter(item => !keys.some(key => !!item[key])))] as Array<{segments?: any, landmark?: any, userLandmark?: any}> : acc.filter(item => !keys.some(key => !!item[key])) } @Directive({ selector: '[iav-mouse-hover]', - exportAs: 'iavMouseHover' + exportAs: 'iavMouseHover', }) -export class MouseHoverDirective{ +export class MouseHoverDirective { - public onHoverObs$: Observable<{segments:any, landmark:any, userLandmark: any}> - public currentOnHoverObs$: Observable<{segments:any, landmark:any, userLandmark: any}> + public onHoverObs$: Observable<{segments: any, landmark: any, userLandmark: any}> + public currentOnHoverObs$: Observable<{segments: any, landmark: any, userLandmark: any}> - constructor(private store$: Store<IavRootStoreInterface>){ + constructor( + private store$: Store<IavRootStoreInterface>, + private log: LoggingService, + ) { const onHoverUserLandmark$ = this.store$.pipe( select('uiState'), - map(state => state.mouseOverUserLandmark) + map(state => state.mouseOverUserLandmark), ) const onHoverLandmark$ = combineLatest( this.store$.pipe( select('uiState'), - map(state => state.mouseOverLandmark) + map(state => state.mouseOverLandmark), ), this.store$.pipe( select('dataStore'), select('fetchedSpatialData'), - startWith([]) - ) + startWith([]), + ), ).pipe( map(([landmark, spatialDatas]) => { - if(landmark === null) return landmark - const idx = Number(landmark.replace('label=','')) - if(isNaN(idx)) { - console.warn(`Landmark index could not be parsed as a number: ${landmark}`) + if (landmark === null) { return landmark } + const idx = Number(landmark.replace('label=', '')) + if (isNaN(idx)) { + this.log.warn(`Landmark index could not be parsed as a number: ${landmark}`) return { - landmarkName: idx + landmarkName: idx, } } else { return { ...spatialDatas[idx], - landmarkName: spatialDatas[idx].name + landmarkName: spatialDatas[idx].name, } } - }) + }), ) const onHoverSegments$ = this.store$.pipe( @@ -81,13 +84,13 @@ export class MouseHoverDirective{ this.store$.pipe( select('viewerState'), select('parcellationSelected'), - startWith(null) - ) + startWith(null), + ), ), map(([ arr, parcellationSelected ]) => parcellationSelected && parcellationSelected.auxillaryMeshIndices ? arr.filter(({ segment }) => { // if segment is not a string (i.e., not labelIndexId) return true - if (typeof segment !== 'string') return true + if (typeof segment !== 'string') { return true } const { labelIndex } = getNgIdLabelIndexFromId({ labelIndexId: segment }) return parcellationSelected.auxillaryMeshIndices.indexOf(labelIndex) < 0 }) @@ -95,7 +98,7 @@ export class MouseHoverDirective{ distinctUntilChanged((o, n) => o.length === n.length && n.every(segment => o.find(oSegment => oSegment.layer.name === segment.layer.name - && oSegment.segment === segment.segment))) + && oSegment.segment === segment.segment))), ) const mergeObs = merge( @@ -103,32 +106,32 @@ export class MouseHoverDirective{ distinctUntilChanged(), map(segments => { return { segments } - }) + }), ), onHoverLandmark$.pipe( distinctUntilChanged(), map(landmark => { return { landmark } - }) + }), ), onHoverUserLandmark$.pipe( distinctUntilChanged(), map(userLandmark => { return { userLandmark } - }) - ) + }), + ), ).pipe( - shareReplay(1) + shareReplay(1), ) this.onHoverObs$ = mergeObs.pipe( scan((acc, curr) => { return { ...acc, - ...curr + ...curr, } }, { segments: null, landmark: null, userLandmark: null }), - shareReplay(1) + shareReplay(1), ) this.currentOnHoverObs$ = mergeObs.pipe( @@ -139,28 +142,27 @@ export class MouseHoverDirective{ segments: null, landmark: null, userLandmark: null, - ...val + ...val, } }), - shareReplay(1) + shareReplay(1), ) } } - @Pipe({ - name: 'mouseOverTextPipe' + name: 'mouseOverTextPipe', }) -export class MouseOverTextPipe implements PipeTransform{ +export class MouseOverTextPipe implements PipeTransform { private transformOnHoverSegmentPipe: TransformOnhoverSegmentPipe - constructor(private sanitizer: DomSanitizer){ + constructor(private sanitizer: DomSanitizer) { this.transformOnHoverSegmentPipe = new TransformOnhoverSegmentPipe(this.sanitizer) } private renderText = ({ label, obj }): SafeHtml[] => { - switch(label) { + switch (label) { case 'landmark': return [this.sanitizer.sanitize(SecurityContext.HTML, obj.landmarkName)] case 'segments': @@ -168,12 +170,13 @@ export class MouseOverTextPipe implements PipeTransform{ case 'userLandmark': return [this.sanitizer.sanitize(SecurityContext.HTML, obj.id)] default: - console.log(obj) + // ts-lint:disable-next-line + console.warn(`mouseOver.directive.ts#mouseOverTextPipe: Cannot be displayed: label: ${label}`) return [this.sanitizer.bypassSecurityTrustHtml(`Cannot be displayed: label: ${label}`)] } } - public transform(inc: {segments:any, landmark:any, userLandmark: any}): {label: string, text: SafeHtml[]} [] { + public transform(inc: {segments: any, landmark: any, userLandmark: any}): Array<{label: string, text: SafeHtml[]}> { const keys = Object.keys(inc) return keys // if is segments, filter out if lengtth === 0 @@ -183,40 +186,40 @@ export class MouseOverTextPipe implements PipeTransform{ .map(key => { return { label: key, - text: this.renderText({ label: key, obj: inc[key] }) + text: this.renderText({ label: key, obj: inc[key] }), } }) } } @Pipe({ - name: 'mouseOverIconPipe' + name: 'mouseOverIconPipe', }) -export class MouseOverIconPipe implements PipeTransform{ +export class MouseOverIconPipe implements PipeTransform { - public transform(type: string): {fontSet:string, fontIcon:string}{ + public transform(type: string): {fontSet: string, fontIcon: string} { - switch(type) { + switch (type) { case 'landmark': return { - fontSet:'fas', - fontIcon: 'fa-map-marker-alt' + fontSet: 'fas', + fontIcon: 'fa-map-marker-alt', } case 'segments': return { fontSet: 'fas', - fontIcon: 'fa-brain' + fontIcon: 'fa-brain', } case 'userLandmark': return { - fontSet:'fas', - fontIcon: 'fa-map-marker-alt' + fontSet: 'fas', + fontIcon: 'fa-map-marker-alt', } default: return { fontSet: 'fas', - fontIcon: 'fa-file' + fontIcon: 'fa-file', } } } diff --git a/src/util/directives/pluginFactory.directive.ts b/src/util/directives/pluginFactory.directive.ts index 5bcd04c54fda4798f82c1784408a709b21895f67..976d1bb52de69d77041a24fa12aa93fe3249a5aa 100644 --- a/src/util/directives/pluginFactory.directive.ts +++ b/src/util/directives/pluginFactory.directive.ts @@ -1,19 +1,21 @@ -import { Directive, ViewContainerRef, Renderer2 } from "@angular/core"; -import { PluginServices } from "src/atlasViewer/atlasViewer.pluginService.service"; +import { Directive, Renderer2, ViewContainerRef } from "@angular/core"; import { AtlasViewerAPIServices } from "src/atlasViewer/atlasViewer.apiService.service"; import { SUPPORT_LIBRARY_MAP } from "src/atlasViewer/atlasViewer.constantService.service"; +import { PluginServices } from "src/atlasViewer/atlasViewer.pluginService.service"; +import { LoggingService } from "src/services/logging.service"; @Directive({ - selector: '[pluginFactoryDirective]' + selector: '[pluginFactoryDirective]', }) -export class PluginFactoryDirective{ +export class PluginFactoryDirective { constructor( pluginService: PluginServices, viewContainerRef: ViewContainerRef, rd2: Renderer2, - apiService:AtlasViewerAPIServices - ){ + apiService: AtlasViewerAPIServices, + private log: LoggingService, + ) { pluginService.pluginViewContainerRef = viewContainerRef pluginService.appendSrc = (src: HTMLElement) => rd2.appendChild(document.head, src) pluginService.removeSrc = (src: HTMLElement) => rd2.removeChild(document.head, src) @@ -21,18 +23,19 @@ export class PluginFactoryDirective{ apiService.interactiveViewer.pluginControl.loadExternalLibraries = (libraries: string[]) => new Promise((resolve, reject) => { const srcHTMLElement = libraries.map(libraryName => ({ name: libraryName, - srcEl: SUPPORT_LIBRARY_MAP.get(libraryName) + srcEl: SUPPORT_LIBRARY_MAP.get(libraryName), })) const rejected = srcHTMLElement.filter(scriptObj => scriptObj.srcEl === null) - if (rejected.length > 0) + if (rejected.length > 0) { return reject(`Some library names cannot be recognised. No libraries were loaded: ${rejected.map(srcObj => srcObj.name).join(', ')}`) + } Promise.all(srcHTMLElement.map(scriptObj => new Promise((rs, rj) => { /** * if browser already support customElements, do not append polyfill */ - if('customElements' in window && scriptObj.name === 'webcomponentsLite'){ + if ('customElements' in window && scriptObj.name === 'webcomponentsLite') { return rs() } const existingEntry = apiService.loadedLibraries.get(scriptObj.name) @@ -48,7 +51,7 @@ export class PluginFactoryDirective{ } }))) .then(() => resolve()) - .catch(e => (console.warn(e), reject(e))) + .catch(e => (this.log.warn(e), reject(e))) }) apiService.interactiveViewer.pluginControl.unloadExternalLibraries = (libraries: string[]) => @@ -57,11 +60,11 @@ export class PluginFactoryDirective{ .forEach(libname => { const ledger = apiService.loadedLibraries.get(libname!) if (!ledger) { - console.warn('unload external libraries error. cannot find ledger entry...', libname, apiService.loadedLibraries) + this.log.warn('unload external libraries error. cannot find ledger entry...', libname, apiService.loadedLibraries) return } if (ledger.src === null) { - console.log('webcomponents is native supported. no library needs to be unloaded') + this.log.log('webcomponents is native supported. no library needs to be unloaded') return } @@ -73,4 +76,4 @@ export class PluginFactoryDirective{ } }) } -} \ No newline at end of file +} diff --git a/src/util/directives/stopPropagation.directive.ts b/src/util/directives/stopPropagation.directive.ts index 6378efd89fa6360b039492af0487869ff3e8ceeb..441d07a806ead5094a78983d8ca00d041d652201 100644 --- a/src/util/directives/stopPropagation.directive.ts +++ b/src/util/directives/stopPropagation.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Input, ElementRef, OnDestroy, OnChanges } from "@angular/core"; +import { Directive, ElementRef, Input, OnChanges, OnDestroy } from "@angular/core"; const VALID_EVENTNAMES = new Set([ 'mousedown', @@ -8,45 +8,46 @@ const VALID_EVENTNAMES = new Set([ 'mouseleave', 'touchstart', 'touchmove', - 'touchend' + 'touchend', ]) const stopPropagation = ev => ev.stopPropagation() @Directive({ - selector: '[iav-stop]' + selector: '[iav-stop]', }) -export class StopPropagationDirective implements OnChanges, OnDestroy{ +export class StopPropagationDirective implements OnChanges, OnDestroy { - @Input('iav-stop') stopString: string = '' + @Input('iav-stop') public stopString: string = '' - private destroyCb: (() => void)[] = [] + private destroyCb: Array<() => void> = [] - constructor(private el: ElementRef){} + constructor(private el: ElementRef) {} + + public ngOnChanges() { - ngOnChanges(){ - this.ngOnDestroy() - if (!this.stopString || this.stopString === '') return + if (!this.stopString || this.stopString === '') { return } const element = (this.el.nativeElement as HTMLElement) - for (const evName of this.stopString.split(' ')){ - if(VALID_EVENTNAMES.has(evName)){ + for (const evName of this.stopString.split(' ')) { + if (VALID_EVENTNAMES.has(evName)) { element.addEventListener(evName, stopPropagation) this.destroyCb.push(() => { element.removeEventListener(evName, stopPropagation) }) } else { + // tslint:disable-next-line console.warn(`${evName} is not a valid event name in the supported set: `, VALID_EVENTNAMES) } } } - ngOnDestroy(){ + public ngOnDestroy() { while (this.destroyCb.length > 0) { this.destroyCb.pop()() } } -} \ No newline at end of file +} diff --git a/src/util/fn.spec.ts b/src/util/fn.spec.ts index 1161a9fbaf2ec88409dd2b506b0977fbfca76410..98f60a54cb0b71f9e5de4c38db0f8b6a8d95f6e2 100644 --- a/src/util/fn.spec.ts +++ b/src/util/fn.spec.ts @@ -18,14 +18,14 @@ describe(`util/fn.ts`, () => { it('should return true with obj with name attribute', () => { const obj = { - name: 'hello' + name: 'hello', } const obj2 = { name: 'hello', - world: 'world' + world: 'world', } expect(isSame(obj, obj2)).toBe(true) expect(obj).not.toEqual(obj2) }) }) -}) \ No newline at end of file +}) diff --git a/src/util/fn.ts b/src/util/fn.ts index efc9974787f07a51989bf59fc7c86d686562f37a..67cfb6436ccaf502e9afeaa9936a1f1da09e6268 100644 --- a/src/util/fn.ts +++ b/src/util/fn.ts @@ -1,4 +1,24 @@ -export function isSame (o, n) { - if (!o) return !n +export function isSame(o, n) { + if (!o) { return !n } return o === n || (o && n && o.name === n.name) -} \ No newline at end of file +} + +export function getViewer() { + return (window as any).viewer +} + +export function setViewer(viewer) { + (window as any).viewer = viewer +} + +export function setNehubaViewer(nehubaViewer) { + (window as any).nehubaViewer = nehubaViewer +} + +export function getDebug() { + return (window as any).__DEBUG__ +} + +export function getExportNehuba() { + return (window as any).export_nehuba +} diff --git a/src/util/generator.ts b/src/util/generator.ts index 5db5d5015a93f467bf1862f4a42dc83db42ce43b..27e4d84ad2a1d047b6ae3163ac2cbe35c71b2ae9 100644 --- a/src/util/generator.ts +++ b/src/util/generator.ts @@ -1,14 +1,14 @@ -export function* timedValues(ms:number = 500,mode:string = 'linear'){ +export function* timedValues(ms: number = 500, mode: string = 'linear') { const startTime = Date.now() - const getValue = (fraction) =>{ - switch (mode){ + const getValue = (fraction) => { + switch (mode) { case 'linear': default: return fraction < 1 ? fraction : 1 } } - while((Date.now() - startTime) < ms){ + while ((Date.now() - startTime) < ms) { yield getValue( (Date.now() - startTime) / ms ) } return 1 @@ -20,8 +20,8 @@ export function clamp(val: number, min: number, max: number) { return Math.min( Math.max( val, - _min + _min, ), - _max + _max, ) -} \ No newline at end of file +} diff --git a/src/util/pipes/appendTooltipText.pipe.ts b/src/util/pipes/appendTooltipText.pipe.ts index 3295876c677f28e34bc92035ffa8824852aebee2..76f8710696a43987f079bb916ccd6e288798353e 100644 --- a/src/util/pipes/appendTooltipText.pipe.ts +++ b/src/util/pipes/appendTooltipText.pipe.ts @@ -1,23 +1,23 @@ import { Pipe, PipeTransform } from "@angular/core"; /** - * TODO + * TODO * merge this pipe into cpProp pipe */ @Pipe({ - name: 'appendTooltipTextPipe' + name: 'appendTooltipTextPipe', }) -export class AppendtooltipTextPipe implements PipeTransform{ - public transform(array: any[]){ +export class AppendtooltipTextPipe implements PipeTransform { + public transform(array: any[]) { return array.map(item => { const { properties = {} } = item const { description: tooltipText } = properties return { ...item, - tooltipText + tooltipText, } }) } -} \ No newline at end of file +} diff --git a/src/util/pipes/doiPipe.pipe.spec.ts b/src/util/pipes/doiPipe.pipe.spec.ts index 300f84ffc07790d02e8ea458ed3ccd03787c04fb..18359ce9bb199fc8e124f52f48e61ab4098968b1 100644 --- a/src/util/pipes/doiPipe.pipe.spec.ts +++ b/src/util/pipes/doiPipe.pipe.spec.ts @@ -14,4 +14,4 @@ describe('doiPipe.pipe.ts', () => { expect(pipe.transform('https://google.com')).toBe('https://google.com') }) }) -}) \ No newline at end of file +}) diff --git a/src/util/pipes/doiPipe.pipe.ts b/src/util/pipes/doiPipe.pipe.ts index 913401ec1a929b9936fdd10c34200439936e2e48..686e914e2984d83532518953dadc603079338d5d 100644 --- a/src/util/pipes/doiPipe.pipe.ts +++ b/src/util/pipes/doiPipe.pipe.ts @@ -1,12 +1,12 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name: 'doiParserPipe' + name: 'doiParserPipe', }) -export class DoiParserPipe implements PipeTransform{ - public transform(s: string, prefix: string = 'https://doi.org/'){ +export class DoiParserPipe implements PipeTransform { + public transform(s: string, prefix: string = 'https://doi.org/') { const hasProtocol = /^https?\:\/\//.test(s) return `${hasProtocol ? '' : prefix}${s}` } -} \ No newline at end of file +} diff --git a/src/util/pipes/filterNull.pipe.ts b/src/util/pipes/filterNull.pipe.ts index c9e28146f52756671270c82402db12a76efe0a89..4ee371f340ff9aa07192fdc7f56d8fb92db19bb0 100644 --- a/src/util/pipes/filterNull.pipe.ts +++ b/src/util/pipes/filterNull.pipe.ts @@ -1,11 +1,11 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name: 'filterNull' + name: 'filterNull', }) -export class FilterNullPipe implements PipeTransform{ - public transform(arr:any[]){ +export class FilterNullPipe implements PipeTransform { + public transform(arr: any[]) { return (arr && arr.filter(obj => obj !== null)) || [] } -} \ No newline at end of file +} diff --git a/src/util/pipes/filterRegionDataEntries.pipe.ts b/src/util/pipes/filterRegionDataEntries.pipe.ts index ef2c4db6736b7d9a483c70f8ec469d339e3edc0e..3feb53a0da9a53b16dca731cbd43e42c1eabb912 100644 --- a/src/util/pipes/filterRegionDataEntries.pipe.ts +++ b/src/util/pipes/filterRegionDataEntries.pipe.ts @@ -1,15 +1,14 @@ import { Pipe, PipeTransform } from "@angular/core"; -import { DataEntry } from "../../services/stateStore.service"; - +import { IDataEntry } from "../../services/stateStore.service"; @Pipe({ - name : 'filterRegionDataEntries' + name : 'filterRegionDataEntries', }) -export class filterRegionDataEntries implements PipeTransform{ - public transform(arr:{region:any|null,searchResults:DataEntry[]}[],selectedRegions:any[]):{region:any|null,searchResults:DataEntry[]}[]{ - return selectedRegions.length > 0 ? - arr.filter(obj=> obj.region !== null && selectedRegions.findIndex(r=>obj.region.name === r.name) >= 0) : +export class filterRegionDataEntries implements PipeTransform { + public transform(arr: Array<{region: any|null, searchResults: IDataEntry[]}>, selectedRegions: any[]): Array<{region: any|null, searchResults: IDataEntry[]}> { + return selectedRegions.length > 0 ? + arr.filter(obj => obj.region !== null && selectedRegions.findIndex(r => obj.region.name === r.name) >= 0) : arr } -} \ No newline at end of file +} diff --git a/src/util/pipes/filterWithString.pipe.ts b/src/util/pipes/filterWithString.pipe.ts index ad492a76bab6e441c0d86535d711805af4f77bca..01d89e5731e190a3d610906df062702565c3ce65 100644 --- a/src/util/pipes/filterWithString.pipe.ts +++ b/src/util/pipes/filterWithString.pipe.ts @@ -1,13 +1,14 @@ import {Pipe, PipeTransform} from "@angular/core"; @Pipe({ - name: 'filterWithString' + name: 'filterWithString', }) export class FilterWithStringPipe implements PipeTransform { public transform(value: any, ...args): any { - if (args[0]) + if (args[0]) { return value.filter(pf => pf.name.toLowerCase().includes(args[0].toLowerCase())) - else + } else { return value + } } } diff --git a/src/util/pipes/flatMapArray.pipe.ts b/src/util/pipes/flatMapArray.pipe.ts index 1b5d62f0dada9b7048d2d866b8a4cca54525b124..695a82dbf3d72f1f16d196657d2fb5b8a4b67f25 100644 --- a/src/util/pipes/flatMapArray.pipe.ts +++ b/src/util/pipes/flatMapArray.pipe.ts @@ -1,11 +1,11 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name: 'flatmapArrayPipe' + name: 'flatmapArrayPipe', }) -export class FlatmapArrayPipe implements PipeTransform{ - public transform(aoa: any[][]){ +export class FlatmapArrayPipe implements PipeTransform { + public transform(aoa: any[][]) { return aoa.reduce((acc, array) => acc.concat(array), []) } -} \ No newline at end of file +} diff --git a/src/util/pipes/getFileExt.pipe.ts b/src/util/pipes/getFileExt.pipe.ts index aea77ceba51c36451836366656529eef661a852c..2d677749525a096e40f94845a967ee1fd450ca02 100644 --- a/src/util/pipes/getFileExt.pipe.ts +++ b/src/util/pipes/getFileExt.pipe.ts @@ -1,4 +1,4 @@ -import { PipeTransform, Pipe } from "@angular/core"; +import { Pipe, PipeTransform } from "@angular/core"; const NIFTI = `NIFTI Volume` const VTK = `VTK Mesh` @@ -6,31 +6,30 @@ const VTK = `VTK Mesh` const extMap = new Map([ ['.nii', NIFTI], ['.nii.gz', NIFTI], - ['.vtk', VTK] + ['.vtk', VTK], ]) @Pipe({ - name: 'getFileExtension' + name: 'getFileExtension', }) -export class GetFileExtension implements PipeTransform{ +export class GetFileExtension implements PipeTransform { private regex: RegExp = new RegExp('(\\.[\\w\\.]*?)$') - private getRegexp(ext){ + private getRegexp(ext) { return new RegExp(`${ext.replace(/\./g, '\\.')}$`, 'i') } - private detFileExt(filename:string):string{ - for (let [key, val] of extMap){ - if(this.getRegexp(key).test(filename)){ + private detFileExt(filename: string): string { + for (const [key, val] of extMap) { + if (this.getRegexp(key).test(filename)) { return val } } return filename } - public transform(filename:string):string{ + public transform(filename: string): string { return this.detFileExt(filename) } } - diff --git a/src/util/pipes/getFilename.pipe.ts b/src/util/pipes/getFilename.pipe.ts index 76afea5627e1ce05a7f527727f99084f30557291..41469ab435b04c222cf95a353ac0680edcbf72e4 100644 --- a/src/util/pipes/getFilename.pipe.ts +++ b/src/util/pipes/getFilename.pipe.ts @@ -1,14 +1,14 @@ -import { PipeTransform, Pipe } from "@angular/core"; +import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name: 'getFilenamePipe' + name: 'getFilenamePipe', }) -export class GetFilenamePipe implements PipeTransform{ +export class GetFilenamePipe implements PipeTransform { private regex: RegExp = new RegExp('[\\/\\\\]([\\w\\.]*?)$') - public transform(fullname: string): string{ + public transform(fullname: string): string { return this.regex.test(fullname) ? this.regex.exec(fullname)[1] : fullname } -} \ No newline at end of file +} diff --git a/src/util/pipes/getLayerNamePipe.pipe.ts b/src/util/pipes/getLayerNamePipe.pipe.ts index 26457608432b4b15d2f66a5db20ce25fb524b545..5acf0167a9708bba9dfe70aef3a2e3bb11bacda5 100644 --- a/src/util/pipes/getLayerNamePipe.pipe.ts +++ b/src/util/pipes/getLayerNamePipe.pipe.ts @@ -1,19 +1,19 @@ import { Pipe, PipeTransform } from "@angular/core"; - @Pipe({ - name : 'getLayerNameFromDatasets' + name : 'getLayerNameFromDatasets', }) -export class GetLayerNameFromDatasets implements PipeTransform{ - public transform(ngLayerName:string, datasets? : any[]):string{ - if(!datasets) +export class GetLayerNameFromDatasets implements PipeTransform { + public transform(ngLayerName: string, datasets?: any[]): string { + if (!datasets) { return ngLayerName - + } + const foundDataset = datasets.find(ds => ds.files.findIndex(file => file.url === ngLayerName) >= 0) - + return foundDataset ? foundDataset.name : ngLayerName } -} \ No newline at end of file +} diff --git a/src/util/pipes/getName.pipe.ts b/src/util/pipes/getName.pipe.ts index 7823c99e5482d662a99efa6f75ef26e03a46ecb3..56e4b4cfadb4ca993539fa93e03f62d7e1856e00 100644 --- a/src/util/pipes/getName.pipe.ts +++ b/src/util/pipes/getName.pipe.ts @@ -1,15 +1,14 @@ import { Pipe, PipeTransform } from "@angular/core"; - @Pipe({ - name : 'getName' + name : 'getName', }) -export class GetNamePipe implements PipeTransform{ +export class GetNamePipe implements PipeTransform { - public transform(object:any):string{ - return object ? - object.name ? object.name : 'Untitled' : + public transform(object: any): string { + return object ? + object.name ? object.name : 'Untitled' : 'Untitled' } -} \ No newline at end of file +} diff --git a/src/util/pipes/getNames.pipe.ts b/src/util/pipes/getNames.pipe.ts index 32cbddbd344fda1e759a6e7ee356025978ce20cd..8e2f0c8331f4cf35b2992b1670eae75b2114ccde 100644 --- a/src/util/pipes/getNames.pipe.ts +++ b/src/util/pipes/getNames.pipe.ts @@ -1,15 +1,14 @@ import { Pipe, PipeTransform } from "@angular/core"; - @Pipe({ - name : 'getNames' + name : 'getNames', }) -export class GetNamesPipe implements PipeTransform{ +export class GetNamesPipe implements PipeTransform { - public transform(array:any[]):string[]{ - return array ? - array.map(item=>item.name ? item.name : 'Untitled') : + public transform(array: any[]): string[] { + return array ? + array.map(item => item.name ? item.name : 'Untitled') : [] } -} \ No newline at end of file +} diff --git a/src/util/pipes/getUnique.pipe.ts b/src/util/pipes/getUnique.pipe.ts index 1ab3800b61ac9f4d7c12390b575523170fee4850..892e28ef85e5c8b7003de589b579d42bc4cdd7f4 100644 --- a/src/util/pipes/getUnique.pipe.ts +++ b/src/util/pipes/getUnique.pipe.ts @@ -1,12 +1,11 @@ import { Pipe, PipeTransform } from "@angular/core"; - @Pipe({ - name: 'getUniquePipe' + name: 'getUniquePipe', }) -export class GetUniquePipe implements PipeTransform{ +export class GetUniquePipe implements PipeTransform { public transform(arr: any[]) { return Array.from(new Set(arr)) } -} \ No newline at end of file +} diff --git a/src/util/pipes/groupDataEntriesByRegion.pipe.ts b/src/util/pipes/groupDataEntriesByRegion.pipe.ts index f4c326b4c253340fa04c819af0b637b85c974a60..334495670b6db9408dcaf1040c43a84787622cfa 100644 --- a/src/util/pipes/groupDataEntriesByRegion.pipe.ts +++ b/src/util/pipes/groupDataEntriesByRegion.pipe.ts @@ -1,41 +1,41 @@ import { Pipe, PipeTransform } from "@angular/core"; -import { DataEntry } from "../../services/stateStore.service"; +import { IDataEntry } from "../../services/stateStore.service"; @Pipe({ - name : 'groupDatasetByRegion' + name : 'groupDatasetByRegion', }) -export class GroupDatasetByRegion implements PipeTransform{ - public transform(datasets:DataEntry[], regions:any[]): {region:any|null,searchResults:DataEntry[]}[]{ - - return datasets.reduce((acc,curr)=>{ +export class GroupDatasetByRegion implements PipeTransform { + public transform(datasets: IDataEntry[], regions: any[]): Array<{region: any|null, searchResults: IDataEntry[]}> { + + return datasets.reduce((acc, curr) => { return (curr.parcellationRegion && curr.parcellationRegion.length > 0) - ? curr.parcellationRegion.reduce((acc2,reName)=>{ - const idx = acc.findIndex(it => it.region === null - ? reName.name === 'none' + ? curr.parcellationRegion.reduce((acc2, reName) => { + const idx = acc.findIndex(it => it.region === null + ? reName.name === 'none' : it.region.name === reName.name ) return idx >= 0 - ? acc2.map((v,i)=> i === idx - ? Object.assign({},v,{searchResults : v.searchResults.concat(curr)}) - : v ) + ? acc2.map((v, i) => i === idx + ? Object.assign({}, v, {searchResults : v.searchResults.concat(curr)}) + : v ) : acc2.concat({ region : this.getRegionFromRegionName(reName.name, regions), - searchResults : [ curr ] + searchResults : [ curr ], }) - },acc) - : acc.findIndex(it=>it.region==null) >= 0 - ? acc.map(it=>it.region === null + }, acc) + : acc.findIndex(it => it.region == null) >= 0 + ? acc.map(it => it.region === null ? Object.assign({}, it, {searchResults: it.searchResults.concat(curr)}) - : it) + : it) : acc.concat({ region : null, - searchResults : [curr] + searchResults : [curr], }) - }, [] as {region:any|null,searchResults:DataEntry[]}[]) + }, [] as Array<{region: any|null, searchResults: IDataEntry[]}>) } - private getRegionFromRegionName(regionName:string,regions:any[]):any|null{ - const idx = regions.findIndex(re=>re.name == regionName) + private getRegionFromRegionName(regionName: string, regions: any[]): any|null { + const idx = regions.findIndex(re => re.name == regionName) return idx >= 0 ? regions[idx] : null } -} \ No newline at end of file +} diff --git a/src/util/pipes/humanReadableFileSize.pipe.spec.ts b/src/util/pipes/humanReadableFileSize.pipe.spec.ts index 802ce27a7586de3008a5f9031a5792df2b86b167..74ae1113992802d8994023e8724acb4ee30a97ca 100644 --- a/src/util/pipes/humanReadableFileSize.pipe.spec.ts +++ b/src/util/pipes/humanReadableFileSize.pipe.spec.ts @@ -1,13 +1,12 @@ -import { HumanReadableFileSizePipe } from './humanReadableFileSize.pipe' import {} from 'jasmine' - +import { HumanReadableFileSizePipe } from './humanReadableFileSize.pipe' describe('humanReadableFileSize.pipe.ts', () => { describe('HumanReadableFileSizePipe', () => { it('steps properly when nubmers ets large', () => { const pipe = new HumanReadableFileSizePipe() const num = 12 - + expect(pipe.transform(num, 0)).toBe(`12 byte(s)`) expect(pipe.transform(num * 1e3, 0)).toBe(`12 KB`) expect(pipe.transform(num * 1e6, 0)).toBe(`12 MB`) @@ -39,6 +38,5 @@ describe('humanReadableFileSize.pipe.ts', () => { // TODO finish tests }) - }) -}) \ No newline at end of file +}) diff --git a/src/util/pipes/humanReadableFileSize.pipe.ts b/src/util/pipes/humanReadableFileSize.pipe.ts index 77b3579b44ff0cfbce9b781e076fa1bef6b8587f..2a1e6f25966afc9fc2a7eadbadf12109bcdc69e6 100644 --- a/src/util/pipes/humanReadableFileSize.pipe.ts +++ b/src/util/pipes/humanReadableFileSize.pipe.ts @@ -1,23 +1,23 @@ -import { PipeTransform, Pipe } from "@angular/core"; +import { Pipe, PipeTransform } from "@angular/core"; export const steps = [ 'byte(s)', 'KB', 'MB', 'GB', - 'TB' + 'TB', ] @Pipe({ - name: 'humanReadableFileSizePipe' + name: 'humanReadableFileSizePipe', }) -export class HumanReadableFileSizePipe implements PipeTransform{ +export class HumanReadableFileSizePipe implements PipeTransform { public transform(input: string | Number, precision: number = 2) { let _input = Number(input) - if (!_input) throw new Error(`HumanReadableFileSizePipe needs a string or a number that can be parsed to number`) - let _precision = Number(precision) - if (_precision === NaN) throw new Error(`precision must be a number`) + if (!_input) { throw new Error(`HumanReadableFileSizePipe needs a string or a number that can be parsed to number`) } + const _precision = Number(precision) + if (_precision === NaN) { throw new Error(`precision must be a number`) } let counter = 0 while (_input > 1000 && counter < 4) { _input = _input / 1000 @@ -25,4 +25,4 @@ export class HumanReadableFileSizePipe implements PipeTransform{ } return `${_input.toFixed(precision)} ${steps[counter]}` } -} \ No newline at end of file +} diff --git a/src/util/pipes/includes.pipe.ts b/src/util/pipes/includes.pipe.ts index 54e01a5c4bd691ee704eab17895d2f48f0566edb..9d7dc2d1d10081f5be47d59c07f5b20417b8ec2c 100644 --- a/src/util/pipes/includes.pipe.ts +++ b/src/util/pipes/includes.pipe.ts @@ -1,15 +1,15 @@ -import { PipeTransform, Pipe } from "@angular/core"; +import { Pipe, PipeTransform } from "@angular/core"; -const defaultCompareFn = (item: any, comparator:any):boolean => item === comparator +const defaultCompareFn = (item: any, comparator: any): boolean => item === comparator @Pipe({ - name: 'includes' + name: 'includes', }) -export class IncludesPipe implements PipeTransform{ - public transform(array: any[], item: any, compareFn=defaultCompareFn):boolean{ - if (!array) return false - if (!(array instanceof Array)) return false +export class IncludesPipe implements PipeTransform { + public transform(array: any[], item: any, compareFn= defaultCompareFn): boolean { + if (!array) { return false } + if (!(array instanceof Array)) { return false } return array.some(it => compareFn(it, item)) } -} \ No newline at end of file +} diff --git a/src/util/pipes/kgSearchBtnColor.pipe.ts b/src/util/pipes/kgSearchBtnColor.pipe.ts index c9bbb25bf655bbe4d5e90ef08768f058fae915f2..e7d3bb25ffdd2eb0ddd7fa8f74f2a5a5185b8b00 100644 --- a/src/util/pipes/kgSearchBtnColor.pipe.ts +++ b/src/util/pipes/kgSearchBtnColor.pipe.ts @@ -2,13 +2,13 @@ import { Pipe, PipeTransform } from "@angular/core"; import { WidgetUnit } from "src/atlasViewer/widgetUnit/widgetUnit.component"; @Pipe({ - name: 'kgSearchBtnColorPipe' + name: 'kgSearchBtnColorPipe', }) -export class KgSearchBtnColorPipe implements PipeTransform{ - public transform([minimisedWidgetUnit, themedBtnCls]: [Set<WidgetUnit>, string], wu: WidgetUnit ){ +export class KgSearchBtnColorPipe implements PipeTransform { + public transform([minimisedWidgetUnit, themedBtnCls]: [Set<WidgetUnit>, string], wu: WidgetUnit ) { return minimisedWidgetUnit.has(wu) ? 'primary' : 'accent' } -} \ No newline at end of file +} diff --git a/src/util/pipes/newViewerDistinctViewToLayer.pipe.ts b/src/util/pipes/newViewerDistinctViewToLayer.pipe.ts index dc384435b44f602cbda8905db25d5342fa4da6e2..16e1ae681c10b108c130f9e832bd807285afd623 100644 --- a/src/util/pipes/newViewerDistinctViewToLayer.pipe.ts +++ b/src/util/pipes/newViewerDistinctViewToLayer.pipe.ts @@ -1,14 +1,13 @@ import { Pipe, PipeTransform } from "@angular/core"; - @Pipe({ - name : 'newViewerDisctinctViewToLayer' + name : 'newViewerDisctinctViewToLayer', }) -export class NewViewerDisctinctViewToLayer implements PipeTransform{ - public transform(input:[any | null, string | null]):AtlasViewerLayerInterface[]{ - try{ - if(!input){ +export class NewViewerDisctinctViewToLayer implements PipeTransform { + public transform(input: [any | null, string | null]): AtlasViewerLayerInterface[] { + try { + if (!input) { return [] } const newViewer = input[0] @@ -16,7 +15,7 @@ export class NewViewerDisctinctViewToLayer implements PipeTransform{ return [] .concat(newViewer ? Object.keys(newViewer.nehubaConfig.dataset.initialNgState.layers).map(key => ({ - name : key, + name : key, url : newViewer.nehubaConfig.dataset.initialNgState.layers[key].source, type : newViewer.nehubaConfig.dataset.initialNgState.layers[key].type === 'image' ? 'base' @@ -25,28 +24,30 @@ export class NewViewerDisctinctViewToLayer implements PipeTransform{ : 'nonmixable', transform : newViewer.nehubaConfig.dataset.initialNgState.layers[key].transform ? newViewer.nehubaConfig.dataset.initialNgState.layers[key].transform.map(quat => Array.from(quat)) - : null + : null, })) : []) .concat(dedicatedViewer ? { name : 'dedicated view', url : dedicatedViewer, type : 'nonmixable' } : []) - .sort((l1,l2) => l1.type < l2.type + .sort((l1, l2) => l1.type < l2.type ? -1 : l1.type > l2.type ? 1 : 0 ) - }catch(e){ + } catch (e) { + + // tslint:disable-next-line console.error('new viewer distinct view to layer error', e) return [] } } } -export interface AtlasViewerLayerInterface{ - name : string - url : string - type : string // 'base' | 'mixable' | 'nonmixable' - transform? : [[number, number, number, number],[number, number, number, number],[number, number, number, number],[number, number, number, number]] | null +export interface AtlasViewerLayerInterface { + name: string + url: string + type: string // 'base' | 'mixable' | 'nonmixable' + transform?: [[number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number]] | null // colormap : string } diff --git a/src/util/pipes/pagination.pipe.ts b/src/util/pipes/pagination.pipe.ts index 31f177c2e2fa1f99da7564adf08fa1aa145b216b..b574b1f16115714113d365c214dd9fc2c600d953 100644 --- a/src/util/pipes/pagination.pipe.ts +++ b/src/util/pipes/pagination.pipe.ts @@ -1,15 +1,15 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name : 'searchResultPagination' + name : 'searchResultPagination', }) -export class SearchResultPaginationPipe implements PipeTransform{ - private _hitsPerPage:number = 15 - private _pageNumber:number = 0 - public transform(arr:any[],pageNumber?:number,hitsPerPage?:number){ - return arr.filter((_,idx)=> +export class SearchResultPaginationPipe implements PipeTransform { + private _hitsPerPage: number = 15 + private _pageNumber: number = 0 + public transform(arr: any[], pageNumber?: number, hitsPerPage?: number) { + return arr.filter((_, idx) => (idx >= (pageNumber === undefined ? this._pageNumber : pageNumber) * (hitsPerPage === undefined ? this._hitsPerPage : hitsPerPage)) && idx < ((pageNumber === undefined ? this._pageNumber : pageNumber) + 1) * (hitsPerPage === undefined ? this._hitsPerPage : hitsPerPage)) } -} \ No newline at end of file +} diff --git a/src/util/pipes/pluginBtnFabColor.pipe.ts b/src/util/pipes/pluginBtnFabColor.pipe.ts index bfb11d4ae59e41107e8fa258be73a122c1b1de7c..71e9b45ad57934df56b13c96544571f16a21fda4 100644 --- a/src/util/pipes/pluginBtnFabColor.pipe.ts +++ b/src/util/pipes/pluginBtnFabColor.pipe.ts @@ -1,15 +1,15 @@ import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name: 'pluginBtnFabColorPipe' + name: 'pluginBtnFabColorPipe', }) -export class PluginBtnFabColorPipe implements PipeTransform{ - public transform([launchedSet, minimisedSet, themedBtnCls], pluginName){ +export class PluginBtnFabColorPipe implements PipeTransform { + public transform([launchedSet, minimisedSet, themedBtnCls], pluginName) { return minimisedSet.has(pluginName) ? 'primary' : launchedSet.has(pluginName) ? 'accent' : 'basic' } -} \ No newline at end of file +} diff --git a/src/util/pipes/safeHtml.pipe.ts b/src/util/pipes/safeHtml.pipe.ts index c0517149e46047c2bbf314efc90a2789831505e4..f0a074c8a685c965beb7c1babb3c3a5f41f27242 100644 --- a/src/util/pipes/safeHtml.pipe.ts +++ b/src/util/pipes/safeHtml.pipe.ts @@ -1,17 +1,17 @@ -import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { Pipe, PipeTransform } from '@angular/core'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @Pipe({ - name : 'safeHtml' + name : 'safeHtml', }) -export class SafeHtmlPipe implements PipeTransform{ - constructor(){ +export class SafeHtmlPipe implements PipeTransform { + constructor() { } - public transform(html:string):string{ + public transform(html: string): string { return html // return this.ds.bypassSecurityTrustHtml(html) } -} \ No newline at end of file +} diff --git a/src/util/pipes/safeStyle.pipe.ts b/src/util/pipes/safeStyle.pipe.ts index c7797697987eb9b62da4be8141e8abcc14e83ee5..7901ca1cc515310cb40f495d9ffe7f520f81a5e9 100644 --- a/src/util/pipes/safeStyle.pipe.ts +++ b/src/util/pipes/safeStyle.pipe.ts @@ -2,15 +2,15 @@ import { Pipe, PipeTransform } from "@angular/core"; import { DomSanitizer, SafeStyle } from "@angular/platform-browser"; @Pipe({ - name : 'safeStyle' + name : 'safeStyle', }) -export class SafeStylePipe implements PipeTransform{ - constructor(private sanitizer:DomSanitizer){ +export class SafeStylePipe implements PipeTransform { + constructor(private sanitizer: DomSanitizer) { } - transform(style:string):SafeStyle{ + public transform(style: string): SafeStyle { return this.sanitizer.bypassSecurityTrustStyle(style) } -} \ No newline at end of file +} diff --git a/src/util/pipes/sortDataEntriesIntoRegion.pipe.ts b/src/util/pipes/sortDataEntriesIntoRegion.pipe.ts index 7f2393f94269caa3fc760c50fb025f69dbb47ba3..71433de5592ca49593ca727d6e972176529f64fe 100644 --- a/src/util/pipes/sortDataEntriesIntoRegion.pipe.ts +++ b/src/util/pipes/sortDataEntriesIntoRegion.pipe.ts @@ -1,15 +1,15 @@ -import { PipeTransform, Pipe } from "@angular/core"; -import { DataEntry } from "../../services/stateStore.service"; +import { Pipe, PipeTransform } from "@angular/core"; +import { IDataEntry } from "../../services/stateStore.service"; @Pipe({ - name : 'sortDataEntriesToRegion' + name : 'sortDataEntriesToRegion', }) -export class SortDataEntriesToRegion implements PipeTransform{ - public transform(regions: any[], datasets:DataEntry[]):{region:any,searchResults:DataEntry[]}[]{ +export class SortDataEntriesToRegion implements PipeTransform { + public transform(regions: any[], datasets: IDataEntry[]): Array<{region: any, searchResults: IDataEntry[]}> { return regions.map(region => ({ region, - searchResults : datasets.filter(dataset => dataset.parcellationRegion.some(r => r.name === region.name)) + searchResults : datasets.filter(dataset => dataset.parcellationRegion.some(r => r.name === region.name)), })) } -} \ No newline at end of file +} diff --git a/src/util/pipes/spatialLandmarksToDatabrowserItem.pipe.ts b/src/util/pipes/spatialLandmarksToDatabrowserItem.pipe.ts index 4b5316976e9eefe521d571c28999ee09eef8a9d4..6972299d58fde68b710c8992461cc476b33c2386 100644 --- a/src/util/pipes/spatialLandmarksToDatabrowserItem.pipe.ts +++ b/src/util/pipes/spatialLandmarksToDatabrowserItem.pipe.ts @@ -1,30 +1,29 @@ import { Pipe, PipeTransform } from "@angular/core"; -import { DataEntry, Landmark, PointLandmarkGeometry, PlaneLandmarkGeometry } from "../../services/stateStore.service"; - +import { IDataEntry, ILandmark, IPlaneLandmarkGeometry, IPointLandmarkGeometry } from "../../services/stateStore.service"; @Pipe({ - name : 'spatialLandmarksToDataBrowserItemPipe' + name : 'spatialLandmarksToDataBrowserItemPipe', }) -export class SpatialLandmarksToDataBrowserItemPipe implements PipeTransform{ - public transform(landmarks:Landmark[]):{region:any, searchResults:Partial<DataEntry>[]}[]{ +export class SpatialLandmarksToDataBrowserItemPipe implements PipeTransform { + public transform(landmarks: ILandmark[]): Array<{region: any, searchResults: Array<Partial<IDataEntry>>}> { return landmarks.map(landmark => ({ region : Object.assign({}, landmark, { - spatialLandmark : true + spatialLandmark : true, }, landmark.geometry.type === 'point' ? { - position : (landmark.geometry as PointLandmarkGeometry).position.map(v => v*1e6), + position : (landmark.geometry as IPointLandmarkGeometry).position.map(v => v * 1e6), } - : landmark.geometry.type === 'plane' + : landmark.geometry.type === 'plane' ? { - POIs : (landmark.geometry as PlaneLandmarkGeometry).corners.map(corner => corner.map(coord => coord * 1e6)) + POIs : (landmark.geometry as IPlaneLandmarkGeometry).corners.map(corner => corner.map(coord => coord * 1e6)), } : {}), searchResults : [{ name : 'Associated dataset', type : 'Associated dataset', - files : landmark.files - }] + files : landmark.files, + }], })) } -} \ No newline at end of file +} diff --git a/src/util/pipes/templateParcellationHasMoreInfo.pipe.ts b/src/util/pipes/templateParcellationHasMoreInfo.pipe.ts index 6d9facbe7f966ad4f7070fa294832fd05e47cf82..5c2003c6feede070992cf5cf3ec1eeb24998f49f 100644 --- a/src/util/pipes/templateParcellationHasMoreInfo.pipe.ts +++ b/src/util/pipes/templateParcellationHasMoreInfo.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from "@angular/core"; -import { Publication } from "src/services/stateStore.service"; +import { IPublication } from "src/services/stateStore.service"; interface KgSchemaId { kgSchema: string @@ -9,7 +9,7 @@ interface KgSchemaId { interface MoreInfo { name: string description: string - publications: Publication[] + publications: IPublication[] originDatasets: KgSchemaId[] mindsId: KgSchemaId } @@ -18,14 +18,14 @@ const notNullNotEmptyString = (string) => !!string && string !== '' const notEmptyArray = (arr) => !!arr && arr instanceof Array && arr.length > 0 @Pipe({ - name: 'templateParcellationHasMoreInfoPipe' + name: 'templateParcellationHasMoreInfoPipe', }) -export class TemplateParcellationHasMoreInfo implements PipeTransform{ - public transform(obj: any):MoreInfo{ +export class TemplateParcellationHasMoreInfo implements PipeTransform { + public transform(obj: any): MoreInfo { const { description, properties = {}, publications, name, originDatasets, mindsId } = obj - const { description:pDescriptions, publications: pPublications, name: pName, mindsId: pMindsId } = properties + const { description: pDescriptions, publications: pPublications, name: pName, mindsId: pMindsId } = properties const hasMoreInfo = notNullNotEmptyString(description) || notNullNotEmptyString(pDescriptions) @@ -41,8 +41,8 @@ export class TemplateParcellationHasMoreInfo implements PipeTransform{ originDatasets: notEmptyArray(originDatasets) ? originDatasets : [{ kgSchema: null, kgId: null }], - mindsId: pMindsId || mindsId + mindsId: pMindsId || mindsId, } : null } -} \ No newline at end of file +} diff --git a/src/util/pipes/treeSearch.pipe.spec.ts b/src/util/pipes/treeSearch.pipe.spec.ts index fe4ddc223496964cc97491d9c0f6e1dd76e7edb6..1c91e0ae8a37b35fafbed8688331d725478d292d 100644 --- a/src/util/pipes/treeSearch.pipe.spec.ts +++ b/src/util/pipes/treeSearch.pipe.spec.ts @@ -3,4 +3,4 @@ import { TreeSearchPipe } from './treeSearch.pipe' describe('TreeSearchPipe works as intended', () => { const pipe = new TreeSearchPipe() /* TODO add tests for tree search pipe */ -}) \ No newline at end of file +}) diff --git a/src/util/pipes/treeSearch.pipe.ts b/src/util/pipes/treeSearch.pipe.ts index 89ae5a20013cbea485e80d42aee770691618fcfa..63c70a8e023bc4e395bf767e44428f7b9fcba308 100644 --- a/src/util/pipes/treeSearch.pipe.ts +++ b/src/util/pipes/treeSearch.pipe.ts @@ -1,12 +1,12 @@ -import { PipeTransform, Pipe } from "@angular/core"; +import { Pipe, PipeTransform } from "@angular/core"; @Pipe({ - name : 'treeSearch' + name : 'treeSearch', }) -export class TreeSearchPipe implements PipeTransform{ - public transform(array:any[]|null,filterFn:(item:any)=>boolean,getChildren:(item:any)=>any[]):any[]{ - const transformSingle = (item:any):boolean=> +export class TreeSearchPipe implements PipeTransform { + public transform(array: any[]|null, filterFn: (item: any) => boolean, getChildren: (item: any) => any[]): any[] { + const transformSingle = (item: any): boolean => filterFn(item) || (getChildren(item) ? getChildren(item).some(transformSingle) @@ -15,4 +15,4 @@ export class TreeSearchPipe implements PipeTransform{ ? array.filter(transformSingle) : [] } -} \ No newline at end of file +} diff --git a/src/util/pluginHandler.ts b/src/util/pluginHandler.ts new file mode 100644 index 0000000000000000000000000000000000000000..e2767e29412d48300bdf124231ac6f8408069626 --- /dev/null +++ b/src/util/pluginHandler.ts @@ -0,0 +1,12 @@ +export class PluginHandler { + public onShutdown: (callback: () => void) => void + public blink: (sec?: number) => void + public shutdown: () => void + + public initState?: any + public initStateUrl?: string + + public setInitManifestUrl: (url: string|null) => void + + public setProgressIndicator: (progress: number) => void +} diff --git a/src/util/pluginHandlerClasses/modalHandler.ts b/src/util/pluginHandlerClasses/modalHandler.ts index 07cc76cf3f6c031243869333b6459dfb6d115faa..97e0b652c8d0659cde9610dc7391e98dcf0df04d 100644 --- a/src/util/pluginHandlerClasses/modalHandler.ts +++ b/src/util/pluginHandlerClasses/modalHandler.ts @@ -1,14 +1,14 @@ -export class ModalHandler{ +export class ModalHandler { - hide : () => void - show : () => void + public hide: () => void + public show: () => void // onHide : (callback: () => void) => void // onHidden : (callback : () => void) => void // onShow : (callback : () => void) => void // onShown : (callback : () => void) => void - title : string - body : string - footer : String - - dismissable: boolean = true -} \ No newline at end of file + public title: string + public body: string + public footer: string + + public dismissable: boolean = true +} diff --git a/src/util/pluginHandlerClasses/toastHandler.ts b/src/util/pluginHandlerClasses/toastHandler.ts index 5452dc78f492b9b7af2f927335910a18e2f48ec1..0f81cfcfc0a014e61160baadb6a5161a1d54c451 100644 --- a/src/util/pluginHandlerClasses/toastHandler.ts +++ b/src/util/pluginHandlerClasses/toastHandler.ts @@ -1,8 +1,8 @@ -export class ToastHandler{ - message : string = 'Toast message' - timeout : number = 3000 - dismissable : boolean = true - show : () => void - hide : () => void - htmlMessage: string -} \ No newline at end of file +export class ToastHandler { + public message: string = 'Toast message' + public timeout: number = 3000 + public dismissable: boolean = true + public show: () => void + public hide: () => void + public htmlMessage: string +} diff --git a/src/util/regionFlattener.ts b/src/util/regionFlattener.ts index d5ae471e16e5a2fba8d4bf3de59720087a4f5491..2c0b7cc8ed7847bb0c4f69e3cbd428c900b34c90 100644 --- a/src/util/regionFlattener.ts +++ b/src/util/regionFlattener.ts @@ -1,6 +1,6 @@ -export function regionFlattener(region:any){ +export function regionFlattener(region: any) { return[ [ region ], - ...((region.children && region.children.map && region.children.map(regionFlattener)) || []) + ...((region.children && region.children.map && region.children.map(regionFlattener)) || []), ].reduce((acc, item) => acc.concat(item), []) -} \ No newline at end of file +} diff --git a/src/util/util.module.ts b/src/util/util.module.ts index c5cfb55540b81d802a192fb9c799fcc5ebdbf8a3..8507cd6a04c54d25eea4bc80aae81bffa595b75f 100644 --- a/src/util/util.module.ts +++ b/src/util/util.module.ts @@ -1,10 +1,10 @@ import { NgModule } from "@angular/core"; -import { FilterNullPipe } from "./pipes/filterNull.pipe"; import { FilterRowsByVisbilityPipe } from "src/components/flatTree/filterRowsByVisibility.pipe"; -import { StopPropagationDirective } from "./directives/stopPropagation.directive"; import { DelayEventDirective } from "./directives/delayEvent.directive"; -import { MouseHoverDirective, MouseOverTextPipe, MouseOverIconPipe } from "./directives/mouseOver.directive"; import { KeyListner } from "./directives/keyDownListener.directive"; +import { MouseHoverDirective, MouseOverIconPipe, MouseOverTextPipe } from "./directives/mouseOver.directive"; +import { StopPropagationDirective } from "./directives/stopPropagation.directive"; +import { FilterNullPipe } from "./pipes/filterNull.pipe"; import { IncludesPipe } from "./pipes/includes.pipe"; @NgModule({ @@ -17,7 +17,7 @@ import { IncludesPipe } from "./pipes/includes.pipe"; MouseOverTextPipe, MouseOverIconPipe, KeyListner, - IncludesPipe + IncludesPipe, ], exports: [ FilterNullPipe, @@ -28,10 +28,10 @@ import { IncludesPipe } from "./pipes/includes.pipe"; MouseOverTextPipe, MouseOverIconPipe, KeyListner, - IncludesPipe - ] + IncludesPipe, + ], }) -export class UtilModule{ +export class UtilModule { -} \ No newline at end of file +} diff --git a/tslint.json b/tslint.json index 7d5f972dddc6a305cef3f6e370af74b417d1b380..d96430c1d0da68127028648f02108ce49b766912 100644 --- a/tslint.json +++ b/tslint.json @@ -5,10 +5,34 @@ ], "jsRules": {}, "rules": { + "variable-name": { + "options": [ + "allow-leading-underscore" + ] + }, "indent": [true, "spaces", 2], "quotemark":false, "semicolon":false, - "object-literal-sort-keys":false + "object-literal-sort-keys":false, + "arrow-parens": false, + "no-var-requires": false, + "ban-types": false, + "no-require-imports": { + "severity":"off" + }, + "interface-name": { + "severity": "off" + }, + "member-ordering": { + "options": [ + { + "order": "statics-first", + "alphabetize": true + } + ], + "severity": "off" + }, + "max-line-length": false }, "rulesDirectory": [] } \ No newline at end of file diff --git a/typings/index.d.ts b/typings/index.d.ts index 8e85daee2ef8e949504faa2cba66baf1a5861953..8c0461449380840d0bbbcdaf7ee5183a6b08b7bc 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -13,4 +13,4 @@ declare var BUNDLEDPLUGINS : string[] declare var VERSION : string declare var PRODUCTION: boolean declare var BACKEND_URL: string -declare var USE_LOGO: string \ No newline at end of file +declare var USE_LOGO: string