diff --git a/src/app/models/models.ts b/src/app/models/models.ts index ff7e6b7..641996f 100644 --- a/src/app/models/models.ts +++ b/src/app/models/models.ts @@ -1,174 +1,155 @@ // class for Everything: extended by NavigationEntity, Manuscript, Page, Word with all common properties import {SafeUrl, ɵDomSanitizerImpl} from '@angular/platform-browser'; export class TlnEntity { id: string; // Short id, iri in most cases iri: string; // iri type: string; // rdfs:type navIndex: number; label?: string; constructor(id: string, iri: string, type: string, navIndex: number, label?: string) { this.id = id; this.iri = iri; this.type = type; this.navIndex = navIndex; this.label = label; } } export class TlnQueryParams { man: string; page: string; row: string; word: string; navBarOpenState: boolean; navTreeIndex: number; constructor(man: string, page: string, row: string, word: string, navBarOpenState: boolean, navTreeIndex: number ) { this.man = man; this.page = page; this.word = word; this.navBarOpenState = navBarOpenState; this.navTreeIndex = navTreeIndex; } } // The navigation entries in each tree for each viewtab (TlnManuscript, TlnPage, PositionalEntity) used by navigation // TODO: add several idxes? Or choosing another order will reload all, hence there is only one idx // export class NavigationEntity { idx: number; tlnEntity: TlnEntity; img?: SafeUrl; // url of image (Thumbnail) constructor(idx: number, tlnEntity: TlnEntity, img?: string ) { this.idx = idx; this.img = img; // iri of image for passing this.tlnEntity = tlnEntity; } } // subclass for TlnManuscript, TlnPage, Word with all common properties export class TlnPhysicalEntity { entity: TlnEntity; description?: string; image?: Image; svg?: Svg; constructor(entity: TlnEntity, description?: string, image?: Image, svg?: Svg) { this.entity = entity; this.description = description; this.image = image; this.svg = svg; } } // the manuscript class export class TlnManuscript extends TlnPhysicalEntity { manuscriptSpec?: string; constructor(entity: TlnEntity, description?: string, image?: Image, svg?: Svg, manuscriptSpec?: string ) { super( entity, description, image, svg); this.manuscriptSpec = manuscriptSpec; } } // the TlnPage class export class TlnPage extends TlnPhysicalEntity { pageSpec?: string; constructor(entity: TlnEntity, description?: string, image?: Image, svg?: Svg, pageSpec?: string ) { super( entity, description, image, svg); this.pageSpec = pageSpec; } } export class TlnRow extends TlnPhysicalEntity { rowSpec?: PositionalEntity; constructor(entity: TlnEntity, description?: string, image?: Image, svg?: Svg, rowSpec?: PositionalEntity ) { super( entity, description, image, svg); this.rowSpec = rowSpec; } } // the TlnWord class export class TlnWord extends TlnPhysicalEntity { wordSpec?: PositionalEntity; constructor(entity: TlnEntity, description?: string, image?: Image, svg?: Svg, wordSpec?: PositionalEntity ) { super( entity, description, image, svg); this.wordSpec = wordSpec; } } export class Annotation { id: string; text: string; styles: Array; } export class Image { id: string; imageUrl: string; thumbnailUrl: string; } export class Svg { id: string; svgUrl: string; binaryValue?: string; - constructor (id, svgUrl, binaryValue?) { + constructor(id, svgUrl, binaryValue?) { this.id = id; this.svgUrl = svgUrl; this.binaryValue = this.binaryValue; } } // Only relevant if we create an svg ourselves? export interface PositionalEntity { // used for word rectangles as well as for line numbering id: string; text: string; left: number; top: number; width: number; height: number; row?: number; iri?: string; // TODO, change this. will be the id later when change data source to rdf ... } - - -// TODO: check usage, maybe not needed at all -export class SelectedEntityGroup { - manuscript: TlnManuscript; - page: TlnPage; - line?: NavigationEntity; - word?: NavigationEntity; - - constructor(manuscript: TlnManuscript, page: TlnPage, line: NavigationEntity, word: NavigationEntity ) { - this.manuscript = manuscript; - this.page = page; - this.line = line; - this.word = word; - } -} - export class NavTree { idx: number; isActive: boolean; label: string; treeClass: string; - selectedItem?: TlnEntity; - constructor(idx: number, isActive: boolean, label: string, treeClass: string, selectedItem?: TlnEntity) { + constructor( idx: number, isActive: boolean, label: string, treeClass: string ) { this.idx = idx; this.isActive = isActive; this.label = label; this.treeClass = treeClass; - this.selectedItem = selectedItem; } } diff --git a/src/app/navigation-list-component/navigation-list-component.component.html b/src/app/navigation-list-component/navigation-list-component.component.html index 341c4ea..994cbcf 100644 --- a/src/app/navigation-list-component/navigation-list-component.component.html +++ b/src/app/navigation-list-component/navigation-list-component.component.html @@ -1,24 +1,25 @@ Navigation

Manuskript {{selectedManId}}

- +

{{item.tlnEntity.label}}

description
diff --git a/src/app/navigation-list-component/navigation-list-component.component.ts b/src/app/navigation-list-component/navigation-list-component.component.ts index df4b1a4..c68fea8 100644 --- a/src/app/navigation-list-component/navigation-list-component.component.ts +++ b/src/app/navigation-list-component/navigation-list-component.component.ts @@ -1,86 +1,84 @@ -import {Component, Input, OnChanges, OnInit} from '@angular/core'; +import {Component, ElementRef, Input, OnChanges, OnInit, ViewChild} from '@angular/core'; import {NavigationServiceService} from "../services/navigation-service.service"; import {NavigationEntity, NavTree, TlnEntity, TlnManuscript, TlnPage, TlnQueryParams, TlnWord} from '../models/models'; import {Subscription} from "rxjs/index"; import {ActivatedRoute, Params, Router} from '@angular/router'; @Component({ selector: 'app-navigation-list-component', templateUrl: './navigation-list-component.component.html', styleUrls: ['./navigation-list-component.component.scss'] }) /** * NavigationListComponent * Does sinply consume data coming from parent component and naviservice. triggers click event to service. */ -export class NavigationListComponentComponent implements OnInit, OnChanges { +export class NavigationListComponentComponent implements OnInit { navTreeData: Array; // the data displayed in the navigation bar navTreeDataSub: Subscription; // detecting/reloding if the tree data changes navTreeSubscription: Subscription; // subscribing if the displayed tree changes. navTrees = []; // The navTrees available for the nav bar for switchich between navtrees // tln specific ... // selectedManuscript: TlnManuscript; // selectedPage: TlnPage; // selectedWord: TlnWord; // lastSelectedEntity: TlnEntity; // one of the above // Subscribtions selectedManuscriptSub: Subscription; selectedPageSub: Subscription; // selectedWordSub: Subscription; selectedManId = ''; // for highlighting a chosen nav item. Will be set by click in the template as well as by subscription of nav service selectedPageId = ''; // for highlighting a chosen nav item constructor( private naviService: NavigationServiceService ) { // if the displayed navTree changes through navigation. this.navTreeSubscription = this.naviService.navTreeIndexChange.subscribe(trees => this.navTrees = trees ); } ngOnInit() { this.navTreeDataSub = this.naviService.activeTreeDataChange.subscribe(treeData => this.navTreeData = treeData ); // Listening to changes and set the selected things accordingly. this.selectedManuscriptSub = this.naviService.selectedManuscriptChange.subscribe(m => { // this.selectedManuscript = m; // this.lastSelectedEntity = m.entity; this.selectedManId = m.entity.id; }); this.selectedPageSub = this.naviService.selectedPageChange.subscribe(p => { - // this.selectedPage = p; - // this.lastSelectedEntity = p.entity; - this.selectedPageId = p.entity.id; }); - // this.selectedWordSub = this.naviService.selectedWordChange.subscribe(w => { - // this.selectedWord = w; - // this.lastSelectedEntity = w.entity; - // }); - } - - ngOnChanges() { + this.selectedPageId = p.entity.id; + const itemToScrollTo = document.getElementById(p.entity.id); + this.scrollOnToSelectedItem(itemToScrollTo); }); } onSelectNavItem(item: NavigationEntity) { this.naviService.onSelectedItem(item); } + // For scrolling to the selected item (manuscript, page) into view + scrollOnToSelectedItem(item: any) { + item.scrollIntoView({ behavior: 'smooth' }); + } + // switches tabs in navtab ov navigation bar changeToTree(idx) { // set treedata to new tab index this.naviService.setActiveTreeData(idx); // change also the tab itself. this.naviService.changeActiveTreeIndex(idx); } isItemSelected(itemId) { let isSelected = false; if (this.selectedManId === itemId || this.selectedPageId === itemId) { isSelected = true; } return isSelected; } treeHasChanged(idx) { if (this.navTrees[this.navTrees.findIndex((tree) => tree.isActive)].idx === idx) { return false; } else { return true; } } } diff --git a/src/app/services/navigation-service.service.ts b/src/app/services/navigation-service.service.ts index e72e44b..e4e9d60 100644 --- a/src/app/services/navigation-service.service.ts +++ b/src/app/services/navigation-service.service.ts @@ -1,341 +1,340 @@ import {EventEmitter, Injectable, OnDestroy} from '@angular/core'; import { TlnManuscript, NavigationEntity, TlnPage, TlnEntity, TlnWord, Image, TlnQueryParams, NavTree, Svg, TlnRow } from '../models/models'; import {NietzscheSourceSeviceService} from "./nietzsche-source-sevice.service"; import {Subscription} from "rxjs/index"; import {ActivatedRoute, Params, Router} from "@angular/router"; import {QueryService} from "./query.service"; import {ExistDbSvgService, SvgService} from "./svg.service"; import {DomSanitizer} from '@angular/platform-browser'; @Injectable({ providedIn: 'root' }) export class NavigationServiceService implements OnDestroy { selectedManuscript: TlnManuscript; selectedPage: TlnPage; selectedRow: TlnRow; selectedWord: TlnWord; queryParams: TlnQueryParams; // Listening to the queries entered actively (so also if page is refreshed or a url loaded) queryParamSubscription: Subscription; constructor(private nietzscheSourceService: NietzscheSourceSeviceService, private router: Router, private sanitizer: DomSanitizer, private activatedRoute: ActivatedRoute, private queryService: QueryService, private svgService: ExistDbSvgService) { this.queryParams = new TlnQueryParams('', '', '', '', false, 0); // Listening to the queries entered actively (so also if page is refreshed or a url loaded) this.queryParamSubscription = this.activatedRoute.queryParams.subscribe( (queryparams: Params ) => { this.queryParams = new TlnQueryParams(queryparams.man, queryparams.page, queryparams.row, queryparams.word, queryparams.navBarOpenState, queryparams.navTreeIndex ); } ); this.navTreeIndex = [ { idx: 0, isActive: true, label: 'Manuskripte', treeClass: 'Tln:Manuscript' }, { idx: 1, isActive: false, label: 'Seiten', treeClass: 'TlnPage' }/*, { idx: 2, isActive: false, label: 'Worte', treeClass: 'TlnWord' }*/ ]; } resourceOfInterest: string; // simple iri for Databrowser and rdf stuff to come resourceOfInterestChange = new EventEmitter(); selectedManuscriptChange = new EventEmitter(); selectedPageChange = new EventEmitter(); selectedRowChange = new EventEmitter(); selectedWordChange = new EventEmitter(); activeTreeData: Array; activeTreeDataChange = new EventEmitter>(); // if the active tree data changes navTreeIndex: Array; navTreeIndexChange = new EventEmitter>(); // Weather only the index changes manuscriptNavTree: Array = []; manuscriptNavTreeChange = new EventEmitter>(); // internally used pageNavTree: Array = []; pageNavTreeChange = new EventEmitter>(); // internally used wordNavTree: Array = []; /** * setActiveTreeData * Set the active tree data and emits it back to navigation list component, * param: idx:number : the index of the tree data to be displayed. */ setActiveTreeData(idx: number) { switch (idx) { case 0: // Manuscript this.activeTreeData = this.manuscriptNavTree; break; case 1: // Page this.activeTreeData = this.pageNavTree; break; } this.activeTreeDataChange.emit(this.activeTreeData); } updateRoute() { const activeUrl = this.router.url.split('?')[0]; // seems workaroundish, but easy this.router.navigate([activeUrl], { queryParams: this.queryParams }); } /** * changeActiveTreeIndex * Simply changing the active tree index for the navBar in * param: idx:number : the index of the tree which should be displayed. */ changeActiveTreeIndex(idx: number) { // setting all to false first for (const tree of this.navTreeIndex ) { tree.isActive = false; } this.navTreeIndex[idx].isActive = true; this.navTreeIndexChange.emit(this.navTreeIndex); this.setQueryParam('navTreeIndex', idx.toString() ); // also routing according to the chosen thing this.updateRoute(); } /** * createNavTreesOnInit creates the first trees if no query params are available in the url: * It ceates the manuscripNavTree and the pageNavTree of the first manuscript per default. * */ createNavTreesOnInit() { this.createManuscriptNavTree(); // create the pageNavTree with the first item of manuscriptNavTree AFTER the first TlnManuscript is in the manuscriptNavTree this.manuscriptNavTreeChange.subscribe(tree => { this.createPageNavTree(tree[0].tlnEntity.id); this.activeTreeData = tree; this.setActiveTreeData(0); this.changeActiveTreeIndex(0); }); // TODO: Change to RDF for all entry points // this.createManuscriptNavTreeFromRdf(); this.setSelectedManuscriptIfNone(); } createManuscriptNavTreeFromRdf() { // TODO: implement this further for switch to RDF data only this.queryService.getQueryfromFilename('getConvolutes.rq').subscribe(qString => { this.queryService.getData(qString, 'SELECT').subscribe(data => { console.log('convolute data: ', data); }); }); } createManuscriptNavTree() { this.nietzscheSourceService.getConvolutes().subscribe( res => { const convolutes = res.result.children; convolutes.forEach(( x, index ) => { const entity = new TlnEntity(x.id, x.api_retrieve_content, 'man', index, x.id); const man = new NavigationEntity(index, entity, ''); this.manuscriptNavTree.push(man); }); this.manuscriptNavTreeChange.emit(this.manuscriptNavTree); } ); } // If no bookId passed, pages of the first book entry will be loaded createPageNavTree(bookId: string) { this.pageNavTree = []; this.nietzscheSourceService.getPages(bookId).subscribe( res => { const pages = res.result.children; pages.forEach((x, index) => { const entity = new TlnEntity(x.id, x.api_retrieve_content, 'page', index, x.id ); // TODO: change from static 'TlnPage' to rdfs:sth const pageEntity = new NavigationEntity(index, entity, ''); this.pageNavTree.push(pageEntity); }); this.addImagesToPageTree(); this.pageNavTreeChange.emit(this.pageNavTree); this.setSelectedPageIfNone(); }); } createManuscriptNavTreeFromUrl(params) { if (params.man) { this.createManuscriptNavTree(); this.manuscriptNavTreeChange.subscribe(tree => { tree.forEach(navEntity => { if (navEntity.tlnEntity.id === params.man) { - this.selectedManuscript = new TlnManuscript(navEntity.tlnEntity); + this.setSelectedManuscript(navEntity.tlnEntity); // resetting the a if (params.navTreeIndex === '0' ) { this.setActiveTreeData(Number(params.navTreeIndex)); } }}); }); } } createNavTreesFromUrl(params) { // change active this.changeActiveTreeIndex(Number(params.navTreeIndex)); this.createManuscriptNavTreeFromUrl(params); this.createPageNavTreeFromUrl(params); } setSelectedManuscript(man: TlnEntity) { this.selectedManuscript = new TlnManuscript(man); this.selectedManuscriptChange.emit(this.selectedManuscript); } setSelectedManuscriptIfNone() { if (!this.selectedManuscript) { this.manuscriptNavTreeChange.subscribe(manTree => this.setSelectedManuscript(manTree[0].tlnEntity)); } } setSelectedPage(pageData: TlnEntity ) { this.selectedPage = new TlnPage(pageData); this.addPageData(pageData.iri); } // sets the first page as selected if no selectedPage setSelectedPageIfNone() { if (!this.selectedPage) { this.pageNavTreeChange.subscribe(pageTree => this.setSelectedPage(pageTree[0].tlnEntity)); } } turnPage(modifier: number) { const newPageIndex = this.selectedPage.entity.navIndex + modifier; this.setSelectedPage(this.pageNavTree[newPageIndex].tlnEntity); } /** * createPageNavTreeFromUrl: Loads all navigation navigation trees according to the given queryParams passed * param: manId:number: the index of the chosen manuscript to be displayed. * */ createPageNavTreeFromUrl(params) { if (params.man) { this.createPageNavTree(params.man); this.pageNavTreeChange.subscribe(tree => { tree.forEach( navEntity => { if (navEntity.tlnEntity.id === params.page ) { // TODO: outsource creation of selectedPage into own method and get also its arrayIndex in pageNavTree via method. this.setSelectedPage(navEntity.tlnEntity); } // if the active tree === '1' we have to set pageNavTree as active tree if (params.navTreeIndex === '1' ) { // set pageTree as active tree this.setActiveTreeData(Number(params.navTreeIndex)); } }); }); } } switchTreeIfPageView(activeTab: string) { if ( activeTab === 'page') { this.setActiveTreeData(1); this.changeActiveTreeIndex(1); this.setSelectedPageIfNone(); } } onSelectedItem(item: NavigationEntity) { const activeTab = this.activatedRoute.snapshot['_urlSegment'].children.primary.segments[1].path; switch (item.tlnEntity.type) { case 'man': { // set the new url with the chose man parameter this.createPageNavTree(item.tlnEntity.id); // If the active tab is the page view we switch automatically to page tree in navigation this.switchTreeIfPageView(activeTab); this.setSelectedManuscript(item.tlnEntity); this.setQueryParam('man', this.selectedManuscript.entity.id ); // also routing according to the chosen thing break; } case 'page': { this.setSelectedPage(item.tlnEntity); this.selectedPageChange.emit(this.selectedPage); this.setQueryParam('page', this.selectedPage.entity.id ); // also routing according to the chosen thing break; } case 'word': { this.selectedWord = new TlnWord(item.tlnEntity); this.selectedWordChange.emit(this.selectedWord); - this.navTreeIndex[2].selectedItem = item.tlnEntity; // this.navTreeIndexChange.emit(this.navTreeIndex); this.setQueryParam('word', this.selectedWord.entity.id ); // also routing according to the chosen thing break; } default: { console.log('unknown item.tlnEntity.type: ', item.tlnEntity.type); } } this.updateRoute(); } setResourceOfInterest(res: string) { this.resourceOfInterest = res; this.resourceOfInterestChange.emit(res); } addImagesToPageTree() { for (let i = 0; i < this.pageNavTree.length; i++) { this.getThumb(this.pageNavTree[i].tlnEntity.iri, i); } } getThumb(pageIri: string, index) { this.nietzscheSourceService.getPageData(pageIri).subscribe(data => { this.nietzscheSourceService.getImageFromUrl(data.result.metadata.download_version.thumb).subscribe(img => { const reader = new FileReader(); reader.addEventListener('load', () => { this.pageNavTree[index].img = reader.result; }, false); if (img) { reader.readAsDataURL(img); } this.pageNavTreeChange.emit(this.pageNavTree); }); }); } addPageData(pageIri: string) { this.nietzscheSourceService.getPageData(pageIri).subscribe(data => { const url = this.svgService.getSvgUrl(data.result.metadata.related_resource_identifier); // TODO: Ŝetting the selected page only only onSelectItem? this.selectedPage.svg = new Svg(data.result.metadata.related_resource_identifier, url); this.selectedPageChange.emit(this.selectedPage); }); } // sets queryParams: to the queryParam object setQueryParam(type: string, value: string) { this.queryParams[type] = value; // this.queryParamsChange.emit(this.queryParams); } ngOnDestroy() { // unsubscribe to subscriptions if component change so there is no memory leak this.queryParamSubscription.unsubscribe(); } }