diff --git a/src/app/content-view-tab-component/content-view-tab-component.component.scss b/src/app/content-view-tab-component/content-view-tab-component.component.scss index c04c5d0..09ad229 100644 --- a/src/app/content-view-tab-component/content-view-tab-component.component.scss +++ b/src/app/content-view-tab-component/content-view-tab-component.component.scss @@ -1,39 +1,38 @@ // the navigation drawer on the very left containing the items to choose from .mat-drawer { overflow: hidden; padding: 10px; display: table; } mat-drawer-content { - height: 100vh; display: table; } .button-container { height:100%; vertical-align: top; min-width:2em; display: table-cell; padding: 1em; } - +// the main content of the content view .content-container { - height:100%; + height: 90vh; vertical-align: top; align-content: left; width:100%; display: table-cell; padding: 10px; } .toggle-button { // fills sidenav-button-container completely, so all is one button height: 100px; min-width: unset; // needed because angular sets an own min width!? width: 15px; display: flex; justify-content: center; align-items: center; margin: 0; padding: 0; } diff --git a/src/app/models/models.ts b/src/app/models/models.ts index 22e71b4..f517aea 100644 --- a/src/app/models/models.ts +++ b/src/app/models/models.ts @@ -1,149 +1,153 @@ // 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) + thumb?: SafeUrl; // url of image (Thumbnail) + img?: SafeUrl; // full image url + svg?: SafeUrl; // svg url - constructor(idx: number, tlnEntity: TlnEntity, img?: string ) { + constructor(idx: number, tlnEntity: TlnEntity, thumb?: string, img?, svg?: SafeUrl ) { this.idx = idx; - this.img = img; // iri of image for passing this.tlnEntity = tlnEntity; + this.thumb = thumb; + this.img = img; + this.svg = svg; } } // subclass for TlnManuscript, TlnPage, Word with all common properties export class TlnPhysicalEntity { entity: TlnEntity; description?: string; image?: SafeUrl; // url of image (Thumbnail); - svg?: Svg; + svg?: SafeUrl; - constructor(entity: TlnEntity, description?: string, image?: SafeUrl, svg?: Svg) { + constructor(entity: TlnEntity, description?: string, image?: SafeUrl, svg?: SafeUrl) { 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?: SafeUrl, 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?: SafeUrl, svg?: Svg, pageSpec?: string ) { + constructor(entity: TlnEntity, description?: string, image?: SafeUrl, svg?: SafeUrl, pageSpec?: string ) { super( entity, description, image, svg); this.pageSpec = pageSpec; } } export class TlnRow extends TlnPhysicalEntity { rowSpec?: PositionalEntity; constructor(entity: TlnEntity, description?: string, image?: SafeUrl, 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?: SafeUrl, svg?: Svg, wordSpec?: PositionalEntity ) { + constructor(entity: TlnEntity, description?: string, image?: SafeUrl, svg?: SafeUrl, wordSpec?: PositionalEntity ) { super( entity, description, image, svg); this.wordSpec = wordSpec; } } export class Annotation { id: string; text: string; styles: Array; } export class Svg { id: string; svgUrl: string; - binaryValue?: string; + svg?: SafeUrl; - constructor(id, svgUrl, binaryValue?) { + constructor(id, svgUrl, svg?) { this.id = id; this.svgUrl = svgUrl; - this.binaryValue = this.binaryValue; + this.svg = svg; } } // 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 ... } export class NavTree { idx: number; isActive: boolean; label: string; treeClass: string; constructor( idx: number, isActive: boolean, label: string, treeClass: string ) { this.idx = idx; this.isActive = isActive; this.label = label; this.treeClass = treeClass; } } 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 c2c4850..b520a7e 100644 --- a/src/app/navigation-list-component/navigation-list-component.component.html +++ b/src/app/navigation-list-component/navigation-list-component.component.html @@ -1,31 +1,31 @@ Navigation

Manuskript {{selectedManId}}

diff --git a/src/app/navigation-list-component/navigation-list-component.component.scss b/src/app/navigation-list-component/navigation-list-component.component.scss index d27eb37..7764ebe 100644 --- a/src/app/navigation-list-component/navigation-list-component.component.scss +++ b/src/app/navigation-list-component/navigation-list-component.component.scss @@ -1,28 +1,27 @@ .fixed-nav-header { position: sticky; } .navlist-container { - min-height: 70vh; - max-height: 80vh; + height: 70vh; overflow: auto; overflow-x: hidden; } .mat-divider { padding: 2px; } .active-item { background-color: #dadada !important; } .mat-nav-list .mat-list-item { // flex: none; min-height: 72px; height: 100%; /* default is 72px */ } .thumbnail { max-width: 120px; } 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 5cece05..4d512d1 100644 --- a/src/app/navigation-list-component/navigation-list-component.component.ts +++ b/src/app/navigation-list-component/navigation-list-component.component.ts @@ -1,109 +1,97 @@ import {AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, QueryList, ViewChild, ViewChildren} from '@angular/core'; -import {NavigationServiceService} from "../services/navigation-service.service"; +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'; +import {Subscription} from 'rxjs/index'; @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, AfterViewInit { @ViewChildren('tabFocus') focussedElements: QueryList; 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 lastSelectedItem: NavigationEntity; constructor( private naviService: NavigationServiceService ) { // if the displayed navTree changes through navigation. this.navTreeSubscription = this.naviService.navTreeIndexChange.subscribe(trees => { this.navTrees = trees; this.ngAfterViewInit(); } ); // Listening to changes and set the selected things accordingly. this.selectedManuscriptSub = this.naviService.selectedManuscriptChange.subscribe(m => { this.selectedManId = m.entity.id; + console.log('new man'); // Only if the active tree is the manuscrip tree, scroll into view of the selected manuscript if (this.navTrees[this.navTrees.findIndex((tree) => tree.isActive )].idx === 0 && this.navTreeData ) { const itemToScrollTo = document.getElementById(m.entity.id); this.scrollOnToSelectedItem(itemToScrollTo); } }); } ngOnInit() { this.navTreeDataSub = this.naviService.activeTreeDataChange.subscribe(treeData => this.navTreeData = treeData ); this.selectedPageSub = this.naviService.selectedPageChange.subscribe(p => { this.selectedPageId = p.entity.id; const itemToScrollTo = document.getElementById(p.entity.id); this.scrollOnToSelectedItem(itemToScrollTo); }); } ngAfterViewInit() { const activeTab = (element => element.isActive === true); const activeTabIndex = this.navTrees.findIndex(activeTab); if ( this.focussedElements ) { this.focussedElements.forEach((elem, index) => { if ( index === activeTabIndex ) { - console.log('fuuu') elem.nativeElement.focus(); } }); } } onSelectNavItem(item: NavigationEntity) { this.naviService.onSelectedItem(item); } // For scrolling to the selected item (manuscript, page) into view scrollOnToSelectedItem(item: any) { - if ( item ) { - item.scrollIntoView({ behavior: 'smooth' }); + if ( item ) { 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/page-view-component/page-view-component.component.html b/src/app/page-view-component/page-view-component.component.html index bbc4237..3d53440 100644 --- a/src/app/page-view-component/page-view-component.component.html +++ b/src/app/page-view-component/page-view-component.component.html @@ -1,94 +1,95 @@ -
{{activePage | json}}
+ # ## vertical_split horizontal_split
+ diff --git a/src/app/page-view-component/page-view-component.component.scss b/src/app/page-view-component/page-view-component.component.scss index 325518c..2256e71 100644 --- a/src/app/page-view-component/page-view-component.component.scss +++ b/src/app/page-view-component/page-view-component.component.scss @@ -1,115 +1,117 @@ -.content-area { - height: 80%; - display:table; +// the overall mat-card + +mat-card { + height: 85vh; + overflow: hidden; } -// the navigation drawer on the very right +// the details drawer on the very right .drawer { padding: 10px; width: 25%; } // The content area of the drawer as a table containing button & content .in-drawer-content-area { min-height: 300px; - height: 100%; + height: 70vh; min-width: 95%; display:table; } .in-drawer-toggle-button-container { height:100%; vertical-align: top; min-width: 10%; width:10%; display: table-cell; } .in-drawer-content-container { height:100%; // vertical-align: top; align-content: left; width:90%; display: table-cell; padding: 10px; } // the content container containing the content area .main-content-container { min-height: 300px; width: 100%; height: 100%; margin: 0; padding: 0; } // The content area as a table containing the main content and the outer toggle button container .outer-content-area { min-height: 300px; height: 100%; display:table; } .content-container { height:100%; vertical-align: top; align-content: left; width:80%; display: table-cell; padding: 10px; } // nav buttons .page-turn-button-container { height:100%; vertical-align: middle; min-width: unset; // needed because angular sets an own min width!? width:20px; display: table-cell; } .page-turn-button { // fills sidenav-button-container completely, so all is one button height: 100px; min-width: unset; // needed because angular sets an own min width!? width: 15px; display: flex; justify-content: center; align-items: center; margin: 0; padding: 0; } // the cell where the button will be in the main content area .toggle-button-container { height:100%; display: table-cell; vertical-align: top; align-content: right; align-items: right; min-width: 5%; width: 100%; padding: 1em; } .toggle-button { // fills sidenav-button-container completely, so all is one button height: 100px; min-width: unset; // needed because angular sets an own min width!? width: 15px; display: flex; justify-content: center; margin: 0; padding: 0; } // for options .right-outer-element { margin-right: 3em; } .right-inner-element { margin-left: 1em; } .mat-button-toggle-group.mat-button-toggle-group { display: flex; align-items: right; } diff --git a/src/app/page-view-component/page-view-component.component.ts b/src/app/page-view-component/page-view-component.component.ts index 6858c38..5828eb7 100644 --- a/src/app/page-view-component/page-view-component.component.ts +++ b/src/app/page-view-component/page-view-component.component.ts @@ -1,139 +1,110 @@ import {Component, OnInit} from '@angular/core'; import {PositionalEntity, Svg, TlnEntity, NavigationEntity, TlnPage, TlnWord} from "../models/models"; import {SvgService} from "../services/svg.service"; import {PageViewService} from "../services/field-interaction.service"; import {Subscription} from "rxjs/index"; import {ActivatedRoute, Params, Router} from "@angular/router"; import {NavigationServiceService} from "../services/navigation-service.service"; -import {SafeUrl} from '@angular/platform-browser'; +import {DomSanitizer, SafeUrl} from '@angular/platform-browser'; @Component({ selector: 'app-page-view-component', templateUrl: './page-view-component.component.html', styleUrls: ['./page-view-component.component.scss'] }) export class PageViewComponentComponent implements OnInit { activePage: TlnPage; activePageChange: Subscription; - // TODO: implement images over http - imageToShow: any; // the returned image from http to display + // images selectedImage: SafeUrl; // the image selected in gallery, menue or so - isImageLoading: boolean; // status of the image: ready and available for the component to show? - // svgs - svgToShow: any; // the returned svg from http to display - selectedSvg: Svg; // the svg selected in gallery, menue or so - isSvgLoading: boolean; // status of the svg: ready and available for the component to show? + // svg + selectedSvg: SafeUrl; // the svg to show // settings for user to choose paging: string; alignment: string; fixSidebar: boolean; highlight: boolean; manQueryParam: string; pageQueryParam: string; rowQueryParam: string; wordSubscription: Subscription; wordQueryParam: string; // NavBar/Infobox selectedWord: TlnWord; openState: boolean; defaultOpenState = false; constructor(private svgService: SvgService, private pageViewService: PageViewService, private router: Router, private activatedRoute: ActivatedRoute, - private naviService: NavigationServiceService) { } + private naviService: NavigationServiceService, + private sanitizer: DomSanitizer ) { } ngOnInit() { this.activePageChange = this.naviService.selectedPageChange.subscribe( page => { this.activePage = page; - this.selectedSvg = page.svg; } + console.log(page); + this.selectedSvg = this.sanitizer.bypassSecurityTrustResourceUrl(page.svg); } ); // TODO: make the selected scg dynamic // this.selectedSvg = {id: '131', // svgUrl: 'https://drive.switch.ch/remote.php/webdav/Der_spaete_Nietzsche/DATA/svg/W_II_1_page131_web.svg'}; // this.getSvgFromService(); // infobox this.openState = this.defaultOpenState; // default settings oninit this.paging = 'double'; this.alignment = 'horizontal'; this.fixSidebar = false; this.highlight = true; this.svgService.onClickedSvg.subscribe( // subscribe to changes if another svg is selected (changedSVG: Svg ) => this.selectedSvg = changedSVG ); this.wordSubscription = this.pageViewService.wordChange$.subscribe( word => { if (this.selectedWord) { if (this.selectedWord.wordSpec === word) { this.toggleDetailsDrawer(); } else { this.selectedWord.wordSpec = word; this.selectedWord.entity = new TlnEntity(word.id, word.iri, 'TlnWord', 0, word.text); const wordNav = new NavigationEntity(0, this.selectedWord.entity); this.naviService.onSelectedItem(wordNav); this.naviService.setResourceOfInterest(word.id); if (!this.openState) { this.toggleDetailsDrawer(); } } } else { const e = new TlnEntity(word.id, word.iri, 'TlnWord', 0, word.id); this.selectedWord = new TlnWord(e, word.text, this.selectedImage, this.selectedSvg, word); } }); } private turnPage(direction: number) { this.naviService.turnPage(direction); // change chosen page } toggleDetailsDrawer() { this.openState = !this.openState; } toggleHighlight() { this.highlight = !this.highlight; } toggleSidebarFixation() { this.fixSidebar = !this.fixSidebar; } - private getSvgFromService() { - this.svgService.getSvg(this.selectedSvg.svgUrl).subscribe(data => { - this.createThingFromBlob(data, 1); - this.isSvgLoading = false; - }, error => { - this.isSvgLoading = false; - console.log(error); - }); - } - - // TODO: handle correctly && display - private createThingFromBlob(thing: Blob, type) { - const reader = new FileReader(); - reader.addEventListener('load', () => { - if (type === 0 ) { - this.imageToShow = reader.result; - } - if (type === 1 ) { - this.svgToShow = reader.result; - } - }, false); - - if (thing) { - reader.readAsDataURL(thing); - } - } - } diff --git a/src/app/page-view-component/transcription-component/transcription-component.component.html b/src/app/page-view-component/transcription-component/transcription-component.component.html index 1d77801..9e6fc4e 100644 --- a/src/app/page-view-component/transcription-component/transcription-component.component.html +++ b/src/app/page-view-component/transcription-component/transcription-component.component.html @@ -1,7 +1,4 @@ -
{{activePage.svg.svgUrl | json}}
- diff --git a/src/app/services/navigation-service.service.ts b/src/app/services/navigation-service.service.ts index 957e2cd..90745d8 100644 --- a/src/app/services/navigation-service.service.ts +++ b/src/app/services/navigation-service.service.ts @@ -1,363 +1,418 @@ import {EventEmitter, Injectable, OnDestroy} from '@angular/core'; import { TlnManuscript, NavigationEntity, TlnPage, TlnEntity, TlnWord, 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 {ActivatedRoute, Params, Router, NavigationEnd, ChildActivationEnd} 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; + viewTabSubscription: Subscription; + activeViewTab = ''; 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 ) => { + // set new query params this.queryParams = new TlnQueryParams(queryparams.man, queryparams.page, queryparams.row, queryparams.word, queryparams.navBarOpenState, queryparams.navTreeIndex ); } ); + // also listening to changes in viewtab and impose navTreeChange + this.viewTabSubscription = this.router.events.subscribe(event => { + if (event instanceof ChildActivationEnd) { + const regex = new RegExp('\\W'); // for splitting at every non alphanumeric char + const newTab = this.router.url.split(regex)[2]; + if (this.activeViewTab !== newTab) { + this.changeNavTreeOnViewChange(newTab); + } + } + }); + 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 + * Gets the active url without query parameters and routes to the updated query params of this class. + */ updateRoute() { - const activeUrl = this.router.url.split('?')[0]; // seems workaroundish, but easy + const activeUrl = this.router.url.split('?')[0]; // seems workaroundish, but actually safe & secure; no other suitable solution at hand 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(); } + /** + * changeNavTreeOnViewChange: Changes the active Navigation tree + * e.g. if a user changes to page view, the navigation bar should also switch to pages. If Manuscript view is clicked, the navigation bar + * should show manuscript items + * param: oldView: The view before view changes + * param: chengedView: The newly active view + * + */ + changeNavTreeOnViewChange(changedView) { + let treeIndex; + if ( changedView === 'manuscript' ) { + treeIndex = 0; } else { treeIndex = 1; } + this.changeActiveTreeIndex(treeIndex); + this.setActiveTreeData(treeIndex); + this.activeViewTab = changedView; + } + /** * 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(); + this.getManuscriptsToNavTree(); // 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.getPagesToNavTree(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(); + if (!this.selectedManuscript) { // if no manuscript is selected the first entry will be set as selected manuscript; + this.manuscriptNavTreeChange.subscribe(manTree => this.setSelectedManuscript(manTree[0].tlnEntity)); } } 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() { + /** + * getManuscriptsToNavTree + * Gets the convolutes/manuscripts from nietzscheSource and pushes them as NavigationEntitiy objects to the manuscriptNavTree array + */ + getManuscriptsToNavTree() { 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); - if (index + 1 === convolutes.length) { + const manuscripts = res.result.children; + manuscripts.forEach(( man, index ) => { + const entity = new TlnEntity(man.id, man.api_retrieve_content, 'man', index, man.id); + const manEntity = new NavigationEntity(index, entity, ''); + this.manuscriptNavTree.push(manEntity); + if (index + 1 === manuscripts.length) { // if everything is pushed to the manuscriptNavTree we emit the new tree to the navigation-list-component this.manuscriptNavTreeChange.emit(this.manuscriptNavTree); } }); } ); } + /** + * createManuscriptNavTreeFromUrl + * Gets the convolutes/manuscripts from nietzscheSource and pushes them as NavigationEntitiy objects to the manuscriptNavTree array + * param: params: the query parameters + */ createManuscriptNavTreeFromUrl(params) { - if (params.man) { - this.createManuscriptNavTree(); - this.manuscriptNavTreeChange.subscribe(tree => { - tree.forEach(navEntity => { - if (navEntity.tlnEntity.id === params.man) { - this.setSelectedManuscript(navEntity.tlnEntity); - // resetting the a - if (params.navTreeIndex === '0') { - this.setActiveTreeData(Number(params.navTreeIndex)); - } + this.getManuscriptsToNavTree(); + this.manuscriptNavTreeChange.subscribe(tree => { + tree.forEach(navEntity => { + if (navEntity.tlnEntity.id === params.man) { + this.setSelectedManuscript(navEntity.tlnEntity); + // setting the active tree to the manuscriptNavTree + if (params.navTreeIndex === '0') { + this.setActiveTreeData(Number(params.navTreeIndex)); } - }); + } }); - } + }); } - // If no bookId passed, pages of the first book entry will be loaded - createPageNavTree(bookId: string, pageId?: string) { + /** + * getPagesToNavTree + * Gets the pages from a given manuscript to the pageNavTree. Sets the selectedPage to the passed pageId. If no pageId is passed, + * the selectedPage will be set to the first page of the given manuscript. + * param: manId: the manuscript id for ehich the pages are loaded + * param: pageId: the page id which should be set as the selected page + */ + getPagesToNavTree(manId: string, pageId?: string) { this.pageNavTree = []; - this.nietzscheSourceService.getPages(bookId).subscribe( res => { + this.nietzscheSourceService.getPages(manId).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); - // if there is no page param passed set the first entry as selected page - if (!pageId && index === 0) { - this.setSelectedPage(this.pageNavTree[0].tlnEntity); - } - if (pageId && x.id === pageId ) { - this.setSelectedPage(entity); - } + let intIndex = 0; // internal index + pages.forEach((p, index) => { + // only if the last id bart of the id can be converted to a number, it is a single page for which we have use. + // (we only transcribe single pages + if (isNaN( p.id.split(',').pop() ) === false) { + const entity = new TlnEntity(p.id, p.api_retrieve_content, 'page', intIndex, p.id ); + const pageEntity = new NavigationEntity(intIndex, entity, ''); + this.pageNavTree.push(pageEntity); + if (!pageId && intIndex === 0) { // if there is no page param passed set the first entry as selected page + this.setSelectedPage(this.pageNavTree[0]); + } + if (pageId && p.id === pageId ) { // if the p.id matches the pageId we set that page as selectedPage + this.setSelectedPage(pageEntity); + } + } else {console.log('skipped double page', p.id); } + // if last page has been loaded to pageNavTree we emit to navlist for display and add the additional data from other sources if (index + 1 === pages.length) { this.pageNavTreeChange.emit(this.pageNavTree); - this.addImagesToPageTree(); - }}); + this.addDataToPageTree(); + } + intIndex += 1; + }); }); } - + /** + * createNavTreesFromUrlParams + * Loads the navigation view & trees accoring to the given query parameters. + * param: params: the query parameters + */ createNavTreesFromUrlParams(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); + setSelectedPage(pageData: NavigationEntity ) { + this.selectedPage = new TlnPage(pageData.tlnEntity, pageData.tlnEntity.label, pageData.img, pageData.svg); + // 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)); + this.pageNavTreeChange.subscribe(pageTree => this.setSelectedPage(pageTree[0])); + this.pageNavTreeChange.unsubscribe(); } } turnPage(modifier: number) { const newPageIndex = this.selectedPage.entity.navIndex + modifier; - this.setSelectedPage(this.pageNavTree[newPageIndex].tlnEntity); + this.setSelectedPage(this.pageNavTree[newPageIndex]); } /** * createPageNavTreeFromUrl: Loads all navigation navigation trees according to the given queryParams passed - * param: manId:number: the index of the chosen manuscript to be displayed. + * param: params: the query parameters * */ createPageNavTreeFromUrl(params) { - if (params.man) { - this.createPageNavTree(params.man, params.page); - // if the active tree === '1' we have to set pageNavTree as active tree - if (params.navTreeIndex === '1' ) { - // update active tree data each time the pageNavTree changes - this.pageNavTreeChange.subscribe(tree => this.setActiveTreeData(Number(params.navTreeIndex))); - } + this.getPagesToNavTree(params.man, params.page); + // if the active tree === '1' we have to set pageNavTree as active tree + if (params.navTreeIndex === '1' ) { + // update active tree data each time the pageNavTree changes + this.pageNavTreeChange.subscribe(tree => this.setActiveTreeData(Number(params.navTreeIndex))); } } switchTreeIfPageView(activeTab: string) { if ( activeTab === 'page') { + // console.log('switching tree hence acivetab ===', activeTab); this.setActiveTreeData(1); this.changeActiveTreeIndex(1); this.setSelectedPageIfNone(); } } onSelectedItem(item: NavigationEntity) { + console.log('clicked item: ', item); 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); + this.getPagesToNavTree(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.setSelectedPage(item); 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.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() { + // adds all file data such as thumbnails as well as imageUrls or svgUrls to the pageNavTree + addDataToPageTree() { for (let i = 0; i < this.pageNavTree.length; i++) { - this.getThumb(this.pageNavTree[i].tlnEntity.iri, i); + this.addFileDataToPageTree(this.pageNavTree[i].tlnEntity.iri, i); } } - // gets the thumbnail and updates pageNavTree - 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', () => { - if ( this.pageNavTree[index] ) { - this.pageNavTree[index].img = reader.result; - } - }, false); - if (img) { reader.readAsDataURL(img); } - this.pageNavTreeChange.emit(this.pageNavTree); - }); + addFileDataToPageTree(pageIri, index) { + this.nietzscheSourceService.getFileUrl(pageIri).subscribe(data => { + this.getFileToEntity(data.result.metadata.download_version.thumb, index, 'thumb'); // get thumbnail + this.pageNavTree[index].img = data.result.metadata.download_version.medium; // setting the image url + // console.log(data.result.metadata); + this.setSvgUrl(data.result.metadata.siglum, index); // setting the svg url + }); + } + + private setSvgUrl(pageName: string, index) { + const baseUrl = 'http://130.60.24.65:8081/exist/rest/db/ProjectData/Nietzsche/svg/'; + const pNamepre = pageName.split('-').join('_'); // exchanging '-' with underscore + const pName = pNamepre.split(',')[0]; + const pNumber = pNamepre.split(',')[1]; + const pageZerofilled = ('000' + pNumber).slice(-3); + this.pageNavTree[index].svg = baseUrl + pName + '_page' + pageZerofilled + '_web.svg'; + } + + /** + * getFileToEntity: Gets a file from a url and adds it to the desired property of a navigation entity + * param: url: the query parameters + * param: index: the index of the navTree entity to update + * param: prop: The property which will be set, e.g. 'thumb' for navEntity.thumb + */ + getFileToEntity(url: string, index: number, prop: string) { + this.nietzscheSourceService.getFileFromUrl(url).subscribe(file => { + const reader = new FileReader(); + reader.addEventListener('load', () => { + if ( this.pageNavTree[index] ) { + this.pageNavTree[index][prop] = reader.result; + } + }, false); + if (file) { reader.readAsDataURL(file); } + this.pageNavTreeChange.emit(this.pageNavTree); }); } getFullImage(pageIri: string) { - this.nietzscheSourceService.getPageData(pageIri).subscribe(data => { - this.nietzscheSourceService.getImageFromUrl(data.result.metadata.download_version.medium).subscribe(img => { + this.nietzscheSourceService.getFileUrl(pageIri).subscribe(data => { + this.nietzscheSourceService.getFileFromUrl(data.result.metadata.download_version.medium).subscribe(img => { const reader = new FileReader(); reader.addEventListener('load', () => { if ( this.selectedPage ) { this.selectedPage.image = reader.result; } }, false); if (img) { reader.readAsDataURL(img); } this.selectedPageChange.emit(this.selectedPage); }); }); } - 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.getFullImage(pageIri); - 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(); } } diff --git a/src/app/services/nietzsche-source-sevice.service.ts b/src/app/services/nietzsche-source-sevice.service.ts index 64a0b26..3a39278 100644 --- a/src/app/services/nietzsche-source-sevice.service.ts +++ b/src/app/services/nietzsche-source-sevice.service.ts @@ -1,36 +1,36 @@ import { Injectable } from '@angular/core'; import {HttpClient} from "@angular/common/http"; import {map} from "rxjs/internal/operators"; import {Observable} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class NietzscheSourceSeviceService { baseApi = 'http://www.nietzschesource.org//DFGAapi'; bookBaseUrl = 'http://www.nietzschesource.org//DFGAapi//api//book//'; pageBaseUrl = 'http://www.nietzschesource.org/DFGAapi/api/page/'; // thumb = this.pageUrl.download_version.thumb; constructor( private http: HttpClient ) { } public getConvolutes() { return this.http.get(this.baseApi); } public getPages(bookId) { const bookUrl = `${this.bookBaseUrl}${bookId}`; // 'http://www.nietzschesource.org//DFGAapi//api//book//N-VI-1'; return this.http.get(bookUrl); } - public getPageData(pageIri) { + public getFileUrl(pageIri) { return this.http.get(pageIri); // 'http://www.nietzschesource.org/DFGAapi/api/page/N-VI-1,d1'; } - getImageFromUrl(imageUrl: string): Observable { - return this.http.get(imageUrl, { responseType: 'blob' }); + getFileFromUrl(url: string): Observable { + return this.http.get(url, { responseType: 'blob' }); } } diff --git a/src/app/services/svg.service.ts b/src/app/services/svg.service.ts index 3edc18f..4872462 100644 --- a/src/app/services/svg.service.ts +++ b/src/app/services/svg.service.ts @@ -1,58 +1,56 @@ import {EventEmitter, Injectable} from '@angular/core'; import {Observable} from 'rxjs'; import {map} from 'rxjs/operators'; import {HttpClient} from '@angular/common/http'; import {Svg} from '../models/models'; @Injectable({ providedIn: 'root' }) export class SvgService { constructor( private http: HttpClient ) { } onClickedSvg = new EventEmitter(); // TODO refactoring so all files use the same get: images, svg public getSvg(svgUrl: string): Observable { return this.http .get(svgUrl, { responseType: 'blob', withCredentials: true }); } } @Injectable({ providedIn: 'root' }) export class ExistDbSvgService { baseApi = 'http://130.60.24.65:8081/exist/rest/db/ProjectData/Nietzsche/svg/'; constructor( private http: HttpClient ) { } // http://130.60.24.65:8081/exist/rest/db/ProjectData/Nietzsche/svg/W_II_1_page138_web.svg // http://130.60.24.65:8081/exist/rest/db/ProjectData/Nietzsche/svg/N_VIIpage_1_web.svg // "DFGA/N-VII-1" - // W_II_1_page138_web.svg + // W_II_1_pag + // e138_web.svg - public getSvgUrl(pageName: string) { - const p = pageName.split('/')[1]; - const pName = p.split('-').join('_'); - const pos = pName.lastIndexOf('_'); - - const pageZerofilled = ('0000' + pName.slice(pos)).slice(-4); - // console.log('pageZerofilled', pageZerofilled); - // console.log(this.baseApi + pName.slice(0, pos) + '_page' + pName.slice(pos) + '_web.svg'); - return this.baseApi + pName.slice(0, pos) + '_page' + pageZerofilled + '_web.svg'; + public getSvgFromUrl(url: string): Observable { + return this.http + .get(url, { responseType: 'blob', + withCredentials: true + }); } - public getSvg(pageId: string): Observable { + public getSvgFromRdf(pageId: string): Observable { const svgUrl = `${this.baseApi}, ${pageId}`; return this.http .get(svgUrl, { responseType: 'blob', withCredentials: true - }); } + }); + } } diff --git a/src/app/textfield-component/textfield.component.ts b/src/app/textfield-component/textfield.component.ts index f70f5ca..9b41dd7 100644 --- a/src/app/textfield-component/textfield.component.ts +++ b/src/app/textfield-component/textfield.component.ts @@ -1,107 +1,118 @@ import { Component, Input, OnInit } from '@angular/core'; -import { PositionalEntity, Svg} from '../models/models'; +import {PositionalEntity, Svg, TlnPage} from '../models/models'; import wordData from '../../assets/W_II_1_page131.json'; import { PageViewService } from '../services/field-interaction.service'; import { OptionService } from '../services/options.service'; +import {NavigationServiceService} from '../services/navigation-service.service'; +import {Subscription} from 'rxjs'; @Component({ selector: 'text-field', templateUrl: './textfield.component.html', styleUrls: ['./textfield.component.css'] }) /** * Textfield component */ export class TextFieldComponent implements OnInit { + activePage: TlnPage; + activePageChange: Subscription; + selectedSvg: Svg; + @Input() image; @Input() svg; @Input() text_field; svg_width: number = 600; svg_height: number = 800; svg_left: number = 0; svg_top: number = 0; viewBox: string = ''; words: PositionalEntity[]; clickedWord?: PositionalEntity; hoveredWord?: PositionalEntity; offHoveredWord?: PositionalEntity; // Options fadeOut = false; markupAll = false; constructor( private wordservice: PageViewService, - private optionservice: OptionService ) { + private optionservice: OptionService, + private naviService: NavigationServiceService) { this.words = wordData; } ngOnInit() { + this.activePageChange = this.naviService.selectedPageChange.subscribe( page => { + this.activePage = page; + this.selectedSvg = page.svg; } + ); if (this.text_field != null) { this.svg_left = this.text_field.left; this.svg_top = this.text_field.top; this.svg_width = this.text_field.width; this.svg_height = this.text_field.height; } else if (this.image != null) { this.svg_width = this.image.width; this.svg_height = this.image.height; } this.viewBox = this.svg_left + ' ' + this.svg_top + ' ' + this.svg_width + ' ' + this.svg_height; // console.log(this.viewBox) this.wordservice.onClickedWord.subscribe( (changedWord: PositionalEntity ) => this.clickedWord = changedWord ); this.wordservice.onHoveredWord.subscribe( (changedWord: PositionalEntity ) => this.hoveredWord = changedWord ); this.wordservice.offHoveredWord.subscribe( (changedWord: PositionalEntity ) => { this.offHoveredWord = changedWord; } ); this.optionservice.fadeOutchange.subscribe( fadeout => { this.fadeOut = fadeout; console.log('fadeout now: ' + this.fadeOut); } ); this.optionservice.markupAll.subscribe( markup => { this.markupAll = markup; console.log('marking up all words in transcription: ' + this.markupAll); } ); } /** * This method checks all Words which classes should applied to a word or not. If a class is applicable, the method returns true. * * @param mode The mode of styling: 0 === howered word itself; 1 === words with the same row as the howered word. * @param word The word * @returns true or false for each of the words. */ private assignClass(mode: number, word: PositionalEntity) { if (typeof this.hoveredWord !== 'undefined' && this.hoveredWord !== null && this.hoveredWord !== this.offHoveredWord ) { switch (mode) { case 0: if (word === this.hoveredWord && word !== this.offHoveredWord) { return true; } break; case 1: if (word.hasOwnProperty('row')) { if (word.row === this.hoveredWord.row && word.id !== this.hoveredWord.id) { // console.log(word.text + ' has also row == ' + word.row); return true; } } break; } } } private assignTextClass(word: PositionalEntity) { if (this.fadeOut) { if ( word === this.hoveredWord) { return false; } else { return true; } } } }