UNPKG

@knora/viewer

Version:
1,085 lines (1,054 loc) 92 kB
import { __decorate, __metadata, __param } from 'tslib'; import { Component, EventEmitter, ElementRef, Input, Output, HostListener, Inject, ViewChild, NgModule } from '@angular/core'; import { Constants, Point2D, ReadBooleanValue, ReadColorValue, KnoraPeriod, Precision, ReadDateValue, ReadDecimalValue, ReadGeomValue, ReadIntValue, ReadIntervalValue, ReadLinkValue, ReadListValue, ReadTextValueAsHtml, ReadTextValueAsString, ReadTextValueAsXml, ReadFileValue, ReadUriValue, ResourcePropertyDefinition, KnoraApiConnection } from '@knora/api'; import { OntologyInformation, KnoraApiConnectionToken, SearchParamsService, KuiCoreModule } from '@knora/core'; import { Router, ActivatedRoute } from '@angular/router'; import { PageEvent, MatMenuModule } from '@angular/material'; import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; import { ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatNativeDateModule } from '@angular/material/core'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatListModule } from '@angular/material/list'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatTabsModule } from '@angular/material/tabs'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatTooltipModule } from '@angular/material/tooltip'; import { KuiActionModule } from '@knora/action'; let MovingImageComponent = class MovingImageComponent { constructor() { } ngOnInit() { } }; MovingImageComponent = __decorate([ Component({ selector: 'kui-moving-image', template: "<!-- video container -->\n<div class=\"moving-image-viewer\">\n\n <!-- video source -->\n <video></video>\n\n <!-- timeline incl. preview -->\n <div class=\"kui-mi-timeline\">\n\n </div>\n <div class=\"kui-mi-navigation\">\n\n\n </div>\n\n</div>\n", styles: [""] }), __metadata("design:paramtypes", []) ], MovingImageComponent); var StillImageComponent_1; /** * Represents a region. * Contains a reference to the resource representing the region and its geometries. */ class ImageRegion { /** * * @param regionResource a resource of type Region */ constructor(regionResource) { this.regionResource = regionResource; } /** * Get all geometry information belonging to this region. * * @returns */ getGeometries() { return this.regionResource.properties[Constants.HasGeometry]; } } /** * Represents an image including its regions. */ class StillImageRepresentation { /** * * @param stillImageFileValue a [[ReadStillImageFileValue]] representing an image. * @param regions the regions belonging to the image. */ constructor(stillImageFileValue, regions) { this.stillImageFileValue = stillImageFileValue; this.regions = regions; } } /** * Represents a geometry belonging to a specific region. */ class GeometryForRegion { /** * * @param geometry the geometrical information. * @param region the region the geometry belongs to. */ constructor(geometry, region) { this.geometry = geometry; this.region = region; } } /** * This component creates a OpenSeadragon viewer instance. * Accepts an array of ReadResource containing (among other resources) ReadStillImageFileValues to be rendered. * @member resources - resources containing (among other resources) the StillImageFileValues and incoming regions to be rendered. (Use as angular @Input data binding property.) */ let StillImageComponent = StillImageComponent_1 = class StillImageComponent { constructor(elementRef) { this.elementRef = elementRef; this.currentImageIndex = new EventEmitter(); this.regionHovered = new EventEmitter(); this.regions = {}; } /** * Calculates the surface of a rectangular region. * * @param geom the region's geometry. * @returns the surface. */ static surfaceOfRectangularRegion(geom) { if (geom.type !== 'rectangle') { console.log('expected rectangular region, but ' + geom.type + ' given'); return 0; } const w = Math.max(geom.points[0].x, geom.points[1].x) - Math.min(geom.points[0].x, geom.points[1].x); const h = Math.max(geom.points[0].y, geom.points[1].y) - Math.min(geom.points[0].y, geom.points[1].y); return w * h; } /** * Prepare tile sources from the given sequence of [[ReadStillImageFileValue]]. * * @param imagesToDisplay the given file values to de displayed. * @returns the tile sources to be passed to OSD viewer. */ static prepareTileSourcesFromFileValues(imagesToDisplay) { let imageXOffset = 0; const imageYOffset = 0; const tileSources = []; for (const image of imagesToDisplay) { const sipiBasePath = image.iiifBaseUrl + '/' + image.filename; const width = image.dimX; const height = image.dimY; // construct OpenSeadragon tileSources according to https://openseadragon.github.io/docs/OpenSeadragon.Viewer.html#open tileSources.push({ // construct IIIF tileSource configuration according to // http://iiif.io/api/image/2.1/#technical-properties // see also http://iiif.io/api/image/2.0/#a-implementation-notes 'tileSource': { '@context': 'http://iiif.io/api/image/2/context.json', '@id': sipiBasePath, 'height': height, 'width': width, 'profile': ['http://iiif.io/api/image/2/level2.json'], 'protocol': 'http://iiif.io/api/image', 'tiles': [{ 'scaleFactors': [1, 2, 4, 8, 16, 32], 'width': 1024 }] }, 'x': imageXOffset, 'y': imageYOffset }); imageXOffset++; } return tileSources; } ngOnChanges(changes) { if (changes['images'] && changes['images'].isFirstChange()) { this.setupViewer(); // this.currentImageIri.emit(this.images[this.viewer.currentPage()].stillImageFileValue.id); } if (changes['images']) { this.openImages(); this.renderRegions(); this.unhighlightAllRegions(); if (this.activateRegion !== undefined) { this.highlightRegion(this.activateRegion); } } else if (changes['activateRegion']) { this.unhighlightAllRegions(); if (this.activateRegion !== undefined) { this.highlightRegion(this.activateRegion); } } if (this.viewer) { // console.log(this.viewer); // this.currentImageIndex.emit(this.viewer.currentPage()); } } ngOnInit() { // initialisation is done on first run of ngOnChanges } ngOnDestroy() { if (this.viewer) { this.viewer.destroy(); this.viewer = undefined; } } /** * Renders all ReadStillImageFileValues to be found in [[this.images]]. * (Although this.images is a Angular Input property, the built-in change detection of Angular does not detect changes in complex objects or arrays, only reassignment of objects/arrays. * Use this method if additional ReadStillImageFileValues were added to this.images after creation/assignment of the this.images array.) */ updateImages() { if (!this.viewer) { this.setupViewer(); } this.openImages(); } /** * Renders all regions to be found in [[this.images]]. * (Although this.images is a Angular Input property, the built-in change detection of Angular does not detect changes in complex objects or arrays, only reassignment of objects/arrays. * Use this method if additional regions were added to the resources.images) */ updateRegions() { if (!this.viewer) { this.setupViewer(); } this.renderRegions(); } /** * Highlights the polygon elements associated with the given region. * * @param regionIri the Iri of the region whose polygon elements should be highlighted.. */ highlightRegion(regionIri) { const activeRegion = this.regions[regionIri]; if (activeRegion !== undefined) { for (const pol of activeRegion) { pol.setAttribute('class', 'roi-svgoverlay active'); } } } /** * Unhighlights the polygon elements of all regions. * */ unhighlightAllRegions() { for (const reg in this.regions) { if (this.regions.hasOwnProperty(reg)) { for (const pol of this.regions[reg]) { pol.setAttribute('class', 'roi-svgoverlay'); } } } } /** * Initializes the OpenSeadragon viewer */ setupViewer() { const viewerContainer = this.elementRef.nativeElement.getElementsByClassName('osd-container')[0]; const osdOptions = { element: viewerContainer, sequenceMode: true, showReferenceStrip: true, showNavigator: true, zoomInButton: 'KUI_OSD_ZOOM_IN', zoomOutButton: 'KUI_OSD_ZOOM_OUT', previousButton: 'KUI_OSD_PREV_PAGE', nextButton: 'KUI_OSD_NEXT_PAGE', homeButton: 'KUI_OSD_HOME', fullPageButton: 'KUI_OSD_FULL_PAGE', rotateLeftButton: 'KUI_OSD_ROTATE_LEFT', rotateRightButton: 'KUI_OSD_ROTATE_RIGHT' // doesn't work yet }; this.viewer = new OpenSeadragon.Viewer(osdOptions); this.viewer.addHandler('full-screen', function (args) { if (args.fullScreen) { viewerContainer.classList.add('fullscreen'); } else { viewerContainer.classList.remove('fullscreen'); } }); this.viewer.addHandler('resize', function (args) { args.eventSource.svgOverlay().resize(); }); const fileValues = this.images.map((img) => { return img; }); this.viewer.addHandler('page', function (event) { console.log('event on page', event); console.log('Now on page', event.page); const index = event.page; console.log('= id', fileValues[index].id); const id = fileValues[index].id; // return id; }); // // this.currentImageIri.emit(this.viewer.getCurrentImage()); } /** * Adds all images in this.images to the viewer. * Images are positioned in a horizontal row next to each other. */ openImages() { // imageXOffset controls the x coordinate of the left side of each image in the OpenSeadragon viewport coordinate system. // The first image has its left side at x = 0, and all images are scaled to have a width of 1 in viewport coordinates. // see also: https://openseadragon.github.io/examples/viewport-coordinates/ const fileValues = this.images.map((img) => { return img; }); // display only the defined range of this.images const tileSources = StillImageComponent_1.prepareTileSourcesFromFileValues(fileValues); this.removeOverlays(); this.viewer.open(tileSources); } /** * Removes SVG overlays from the DOM. */ removeOverlays() { for (const reg in this.regions) { if (this.regions.hasOwnProperty(reg)) { for (const pol of this.regions[reg]) { if (pol instanceof SVGPolygonElement) { pol.remove(); } } } } this.regions = {}; // TODO: make this work by using osdviewer's addOverlay method this.viewer.clearOverlays(); } /** * Adds a ROI-overlay to the viewer for every region of every image in this.images */ renderRegions() { this.removeOverlays(); let imageXOffset = 0; // see documentation in this.openImages() for the usage of imageXOffset for (const image of this.images) { const aspectRatio = (image.dimY / image.dimX); // collect all geometries belonging to this page const geometries = []; /* TODO: knora-api-js-lib integration needs another region handling? image.regions.map((reg) => { this.regions[reg.regionResource.id] = []; const geoms = reg.getGeometries(); geoms.map((geom) => { const geomForReg = new GeometryForRegion(geom.geometry, reg.regionResource); geometries.push(geomForReg); }); }); // sort all geometries belonging to this page geometries.sort((geom1, geom2) => { if (geom1.geometry.type === 'rectangle' && geom2.geometry.type === 'rectangle') { const surf1 = StillImageComponent.surfaceOfRectangularRegion(geom1.geometry); const surf2 = StillImageComponent.surfaceOfRectangularRegion(geom2.geometry); // if reg1 is smaller than reg2, return 1 // reg1 then comes after reg2 and thus is rendered later if (surf1 < surf2) { return 1; } else { return -1; } } else { return 0; } }); // render all geometries for this page for (const geom of geometries) { const geometry = geom.geometry; this.createSVGOverlay(geom.region.id, geometry, aspectRatio, imageXOffset, geom.region.label); } */ imageXOffset++; } } /** * Creates and adds a ROI-overlay to the viewer * @param regionIri the Iri of the region. * @param geometry - the geometry describing the ROI * @param aspectRatio - the aspectRatio (h/w) of the image on which the geometry should be placed * @param xOffset - the x-offset in Openseadragon viewport coordinates of the image on which the geometry should be placed * @param toolTip - the tooltip which should be displayed on mousehover of the svg element */ createSVGOverlay(regionIri, geometry, aspectRatio, xOffset, toolTip) { const lineColor = geometry.lineColor; const lineWidth = geometry.lineWidth; let svgElement; switch (geometry.type) { case 'rectangle': svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); // yes, we render rectangles as svg polygon elements this.addSVGAttributesRectangle(svgElement, geometry, aspectRatio, xOffset); break; case 'polygon': svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); this.addSVGAttributesPolygon(svgElement, geometry, aspectRatio, xOffset); break; case 'circle': svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); this.addSVGAttributesCircle(svgElement, geometry, aspectRatio, xOffset); break; default: console.log('ERROR: StillImageOSDViewerComponent.createSVGOverlay: unknown geometryType: ' + geometry.type); return; } svgElement.id = 'roi-svgoverlay-' + Math.random() * 10000; svgElement.setAttribute('class', 'roi-svgoverlay'); svgElement.setAttribute('style', 'stroke: ' + lineColor + '; stroke-width: ' + lineWidth + 'px;'); // event when a region is clicked (output) svgElement.addEventListener('click', () => { this.regionHovered.emit(regionIri); }, false); const svgTitle = document.createElementNS('http://www.w3.org/2000/svg', 'title'); svgTitle.textContent = toolTip; const svgGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g'); svgGroup.appendChild(svgTitle); svgGroup.appendChild(svgElement); const overlay = this.viewer.svgOverlay(); overlay.node().appendChild(svgGroup); // TODO: use method osdviewer's method addOverlay this.regions[regionIri].push(svgElement); } /** * Adds the necessary attributes to create a ROI-overlay of type 'rectangle' to a SVGElement * @param svgElement - an SVGElement (should have type 'polygon' (sic)) * @param geometry - the geometry describing the rectangle * @param aspectRatio - the aspectRatio (h/w) of the image on which the circle should be placed * @param xOffset - the x-offset in Openseadragon viewport coordinates of the image on which the circle should be placed */ addSVGAttributesRectangle(svgElement, geometry, aspectRatio, xOffset) { const pointA = geometry.points[0]; const pointB = geometry.points[1]; // geometry.points contains two diagonally opposed corners of the rectangle, but the order of the corners is arbitrary. // We therefore construct the upperleft (UL), lowerright (LR), upperright (UR) and lowerleft (LL) positions of the corners with min and max operations. const positionUL = new Point2D(Math.min(pointA.x, pointB.x), Math.min(pointA.y, pointB.y)); const positionLR = new Point2D(Math.max(pointA.x, pointB.x), Math.max(pointA.y, pointB.y)); const positionUR = new Point2D(Math.max(pointA.x, pointB.x), Math.min(pointA.y, pointB.y)); const positionLL = new Point2D(Math.min(pointA.x, pointB.x), Math.max(pointA.y, pointB.y)); const points = [positionUL, positionUR, positionLR, positionLL]; const viewCoordPoints = this.image2ViewPortCoords(points, aspectRatio, xOffset); const pointsString = this.createSVGPolygonPointsAttribute(viewCoordPoints); svgElement.setAttribute('points', pointsString); } /** * Adds the necessary attributes to create a ROI-overlay of type 'polygon' to a SVGElement * @param svgElement - an SVGElement (should have type 'polygon') * @param geometry - the geometry describing the polygon * @param aspectRatio - the aspectRatio (h/w) of the image on which the circle should be placed * @param xOffset - the x-offset in Openseadragon viewport coordinates of the image on which the circle should be placed */ addSVGAttributesPolygon(svgElement, geometry, aspectRatio, xOffset) { const viewCoordPoints = this.image2ViewPortCoords(geometry.points, aspectRatio, xOffset); const pointsString = this.createSVGPolygonPointsAttribute(viewCoordPoints); svgElement.setAttribute('points', pointsString); } /** * Adds the necessary attributes to create a ROI-overlay of type 'circle' to a SVGElement * @param svgElement - an SVGElement (should have type 'circle') * @param geometry - the geometry describing the circle * @param aspectRatio - the aspectRatio (h/w) of the image on which the circle should be placed * @param xOffset - the x-offset in Openseadragon viewport coordinates of the image on which the circle should be placed */ addSVGAttributesCircle(svgElement, geometry, aspectRatio, xOffset) { const viewCoordPoints = this.image2ViewPortCoords(geometry.points, aspectRatio, xOffset); const cx = String(viewCoordPoints[0].x); const cy = String(viewCoordPoints[0].y); // geometry.radius contains not the radius itself, but the coordinates of a (arbitrary) point on the circle. // We therefore have to calculate the length of the vector geometry.radius to get the actual radius. -> sqrt(x^2 + y^2) // Since geometry.radius has its y coordinate scaled to the height of the image, // we need to multiply it with the aspectRatio to get to the scale used by Openseadragon, analoguous to this.image2ViewPortCoords() const radius = String(Math.sqrt(geometry.radius.x * geometry.radius.x + aspectRatio * aspectRatio * geometry.radius.y * geometry.radius.y)); svgElement.setAttribute('cx', cx); svgElement.setAttribute('cy', cy); svgElement.setAttribute('r', radius); } /** * Maps a Point2D[] with coordinates relative to an image to a new Point2D[] with coordinates in the viewport coordinate system of Openseadragon * see also: https://openseadragon.github.io/examples/viewport-coordinates/ * @param points - an array of points in coordinate system relative to an image * @param aspectRatio - the aspectRatio (h/w) of the image * @param xOffset - the x-offset in viewport coordinates of the image * @returns - a new Point2D[] with coordinates in the viewport coordinate system of Openseadragon */ image2ViewPortCoords(points, aspectRatio, xOffset) { return points.map((point) => { return new Point2D(point.x + xOffset, point.y * aspectRatio); }); } /** * Returns a string in the format expected by the 'points' attribute of a SVGElement * @param points - an array of points to be serialized to a string * @returns - the points serialized to a string in the format expected by the 'points' attribute of a SVGElement */ createSVGPolygonPointsAttribute(points) { let pointsString = ''; for (const i in points) { if (points.hasOwnProperty(i)) { pointsString += points[i].x; pointsString += ','; pointsString += points[i].y; pointsString += ' '; } } return pointsString; } getCurrentImage() { } }; StillImageComponent.ctorParameters = () => [ { type: ElementRef } ]; __decorate([ Input(), __metadata("design:type", Array) ], StillImageComponent.prototype, "images", void 0); __decorate([ Input(), __metadata("design:type", String) ], StillImageComponent.prototype, "imageCaption", void 0); __decorate([ Input(), __metadata("design:type", String) ], StillImageComponent.prototype, "activateRegion", void 0); __decorate([ Output(), __metadata("design:type", EventEmitter) ], StillImageComponent.prototype, "currentImageIndex", void 0); __decorate([ Output(), __metadata("design:type", Object) ], StillImageComponent.prototype, "regionHovered", void 0); StillImageComponent = StillImageComponent_1 = __decorate([ Component({ selector: 'kui-still-image', template: "<div class=\"still-image-viewer\">\n <div class=\"navigation left\">\n <button mat-button class=\"full-size\" id=\"KUI_OSD_PREV_PAGE\">\n <mat-icon>keyboard_arrow_left</mat-icon>\n </button>\n </div>\n\n <!-- main content with navigation and osd viewer -->\n <div class=\"content\">\n\n <!-- openseadragon (osd) viewer -->\n <div class=\"osd-container\"></div>\n <!-- /openseadragon (osd) -->\n\n <!-- footer with image caption e.g. copyright information -->\n <div class=\"footer\">\n <p class=\"mat-caption\" [innerHtml]=\"imageCaption\"></p>\n </div>\n\n <!-- action panel with tools for image -->\n <mat-toolbar class=\"action\">\n <mat-toolbar-row>\n <!-- home button -->\n <span>\n <button mat-icon-button id=\"KUI_OSD_HOME\">\n <mat-icon>home</mat-icon>\n </button>\n </span>\n <!-- zoom buttons -->\n <span class=\"fill-remaining-space\"></span>\n <span>\n <button mat-icon-button id=\"KUI_OSD_ZOOM_IN\">\n <mat-icon>add</mat-icon>\n </button>\n <button mat-icon-button id=\"KUI_OSD_ZOOM_OUT\">\n <mat-icon>remove</mat-icon>\n </button>\n </span>\n <!-- window button -->\n <span class=\"fill-remaining-space\"></span>\n <span>\n <button mat-icon-button id=\"KUI_OSD_FULL_PAGE\">\n <mat-icon>fullscreen</mat-icon>\n </button>\n </span>\n </mat-toolbar-row>\n </mat-toolbar>\n\n </div>\n\n <div class=\"navigation\">\n <button mat-button class=\"full-size\" id=\"KUI_OSD_NEXT_PAGE\" (click)=\"getCurrentImage()\">\n <mat-icon>keyboard_arrow_right</mat-icon>\n </button>\n </div>\n\n</div>\n\n<!-- simple image viewer e.g. as a preview -->\n<!-- TODO: handle images array -->\n<!--<img *ngIf=\"simple && images?.length === 1; else osdViewer\" [src]=\"simpleImageExample\">-->\n\n\n<!--\n <div>\n <span *ngIf=\"images.length > 1\" (click)=\"gotoLeft()\">&lt;&lt;</span>\n <span *ngIf=\"images.length > 1\" (click)=\"gotoRight()\">&gt;&gt;</span>\n </div>\n-->\n", styles: [".still-image-viewer{display:-webkit-inline-box;display:inline-flex;height:400px;max-width:960px;width:100%}@media (max-height:636px){.still-image-viewer{height:300px}}.still-image-viewer .navigation{height:calc(400px + 64px + 24px);width:36px}.still-image-viewer .navigation .mat-button.full-size{height:100%!important;width:36px!important;padding:0!important;min-width:36px!important}.still-image-viewer .content{height:calc(400px + 64px + 24px);max-width:calc(960px - (2 * 36px));width:calc(100% - (2 * 36px))}.still-image-viewer .content .osd-container{color:#ccc;background-color:#000;height:400px}.still-image-viewer .content .osd-container.fullscreen{max-width:100vw}.still-image-viewer .content .footer p{color:#ccc;background-color:#000;height:24px;margin:0;padding:0 16px}::ng-deep .roi-svgoverlay{opacity:.4;fill:transparent;stroke:#00695c;stroke-width:2px;vector-effect:non-scaling-stroke}.roi-svgoverlay:focus,::ng-deep .roi-svgoverlay:hover{opacity:1}::ng-deep .roi-svgoverlay.active{opacity:1}"] }), __metadata("design:paramtypes", [ElementRef]) ], StillImageComponent); let BooleanValueComponent = class BooleanValueComponent { constructor() { } set valueObject(value) { this._booleanValueObj = value; } get valueObject() { return this._booleanValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadBooleanValue), __metadata("design:paramtypes", [ReadBooleanValue]) ], BooleanValueComponent.prototype, "valueObject", null); BooleanValueComponent = __decorate([ Component({ selector: 'kui-boolean-value', template: "<mat-checkbox [checked]=\"valueObject.bool\" readonly=\"true\"></mat-checkbox>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], BooleanValueComponent); let ColorValueComponent = class ColorValueComponent { constructor() { } set valueObject(value) { this._colorValueObj = value; } get valueObject() { return this._colorValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadColorValue), __metadata("design:paramtypes", [ReadColorValue]) ], ColorValueComponent.prototype, "valueObject", null); ColorValueComponent = __decorate([ Component({ selector: 'kui-color-value', template: "<span [style.background-color]=\"valueObject.color\">{{valueObject.color}}</span>\n", styles: [".fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}.mat-form-field{width:36px;overflow-x:visible}"] }), __metadata("design:paramtypes", []) ], ColorValueComponent); let DateValueComponent = class DateValueComponent { constructor() { } set calendar(value) { this._calendar = value; } get calendar() { return this._calendar; } set era(value) { this._era = value; } get era() { return this._era; } set valueObject(value) { this._dateValueObj = value; const dateOrRange = this.valueObject.date; if (dateOrRange instanceof KnoraPeriod) { // period (start and end dates) this.period = true; this.dates = [this.getJSDate(dateOrRange.start), this.getJSDate(dateOrRange.end)]; } else { // single date this.period = false; this.dates = [this.getJSDate(dateOrRange)]; } } get valueObject() { return this._dateValueObj; } /** * Converts a `KnoraDate` to a JS Date, providing necessary formatting information. * JULIAN and GREGORIAN calendar are the only available for the moment. * * @param date the date to be converted. * @return DateFormatter. */ getJSDate(date) { if (date.precision === Precision.yearPrecision) { return { format: 'yyyy', date: new Date(date.year.toString()), era: date.era, calendar: date.calendar }; } else if (date.precision === Precision.monthPrecision) { return { format: 'MMMM ' + 'yyyy', date: new Date(date.year, date.month - 1, 1), era: date.era, calendar: date.calendar }; } else if (date.precision === Precision.dayPrecision) { return { format: 'longDate', date: new Date(date.year, date.month - 1, date.day), era: date.era, calendar: date.calendar }; } else { console.error('Error: incorrect precision for date'); } } }; __decorate([ Input(), __metadata("design:type", Boolean), __metadata("design:paramtypes", [Boolean]) ], DateValueComponent.prototype, "calendar", null); __decorate([ Input(), __metadata("design:type", Boolean), __metadata("design:paramtypes", [Boolean]) ], DateValueComponent.prototype, "era", null); __decorate([ Input(), __metadata("design:type", ReadDateValue), __metadata("design:paramtypes", [ReadDateValue]) ], DateValueComponent.prototype, "valueObject", null); DateValueComponent = __decorate([ Component({ selector: 'kui-date-value', template: "<span *ngIf=\"period; else preciseDate\">\n {{dates[0].date | date: dates[0].format}}\n <span *ngIf=\"era\">\n {{dates[0].era}}\n </span>\n - {{dates[1].date | date: dates[1].format}}\n <span *ngIf=\"era\">\n {{dates[1].era}}\n </span>\n\n <span *ngIf=\"calendar\">\n ({{dates[0].calendar}})\n </span>\n</span>\n\n<ng-template #preciseDate>\n\n <span>\n {{dates[0].date | date: dates[0].format}}\n <span *ngIf=\"era\">\n {{dates[0].era}}\n </span>\n <span *ngIf=\"calendar\">\n ({{dates[0].calendar}})\n </span>\n </span>\n\n</ng-template>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], DateValueComponent); let DecimalValueComponent = class DecimalValueComponent { constructor() { } set valueObject(value) { this._decimalValueObj = value; } get valueObject() { return this._decimalValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadDecimalValue), __metadata("design:paramtypes", [ReadDecimalValue]) ], DecimalValueComponent.prototype, "valueObject", null); DecimalValueComponent = __decorate([ Component({ selector: 'kui-decimal-value', template: "<span>{{valueObject.decimal}}</span>", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], DecimalValueComponent); let GeometryValueComponent = class GeometryValueComponent { constructor() { } set valueObject(value) { this._geomValueObj = value; } get valueObject() { return this._geomValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadGeomValue), __metadata("design:paramtypes", [ReadGeomValue]) ], GeometryValueComponent.prototype, "valueObject", null); GeometryValueComponent = __decorate([ Component({ selector: 'kui-geometry-value', template: "<span>{{valueObject.geometry}}</span>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], GeometryValueComponent); let GeonameValueComponent = class GeonameValueComponent { constructor() { } ngOnInit() { } }; GeonameValueComponent = __decorate([ Component({ selector: 'kui-geoname-value', template: "<p>\n geoname-value works!\n</p>", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], GeonameValueComponent); let IntegerValueComponent = class IntegerValueComponent { constructor() { } set valueObject(value) { this._integerValueObj = value; } get valueObject() { return this._integerValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadIntValue), __metadata("design:paramtypes", [ReadIntValue]) ], IntegerValueComponent.prototype, "valueObject", null); IntegerValueComponent = __decorate([ Component({ selector: 'kui-integer-value', template: "<span>{{valueObject.int}}</span>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], IntegerValueComponent); let IntervalValueComponent = class IntervalValueComponent { constructor() { } set valueObject(value) { this._intervalValueObj = value; } get valueObject() { return this._intervalValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadIntervalValue), __metadata("design:paramtypes", [ReadIntervalValue]) ], IntervalValueComponent.prototype, "valueObject", null); IntervalValueComponent = __decorate([ Component({ selector: 'kui-interval-value', template: "<span>{{valueObject.start}} - {{valueObject.end}}</span>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], IntervalValueComponent); let LinkValueComponent = class LinkValueComponent { constructor() { this.referredResourceClicked = new EventEmitter(); } set valueObject(value) { this._linkValueObj = value; if (this.valueObject.linkedResource !== undefined) { this.referredResource = this.valueObject.linkedResource.label; } else { this.referredResource = this.valueObject.linkedResourceIri; } } get valueObject() { return this._linkValueObj; } refResClicked() { this.referredResourceClicked.emit(this._linkValueObj); } }; __decorate([ Input(), __metadata("design:type", ReadLinkValue), __metadata("design:paramtypes", [ReadLinkValue]) ], LinkValueComponent.prototype, "valueObject", null); __decorate([ Output(), __metadata("design:type", EventEmitter) ], LinkValueComponent.prototype, "referredResourceClicked", void 0); LinkValueComponent = __decorate([ Component({ selector: 'kui-link-value', template: "<a class=\"kui-link\" (click)=\"refResClicked()\">{{referredResource}}</a>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], LinkValueComponent); let ListValueComponent = class ListValueComponent { constructor() { } set valueObject(value) { this._listValueObj = value; } get valueObject() { return this._listValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadListValue), __metadata("design:paramtypes", [ReadListValue]) ], ListValueComponent.prototype, "valueObject", null); ListValueComponent = __decorate([ Component({ selector: 'kui-list-value', template: "<span>{{valueObject.listNodeLabel}}</span>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], ListValueComponent); let TextValueAsHtmlComponent = class TextValueAsHtmlComponent { constructor(el) { this.el = el; this.referredResourceClicked = new EventEmitter(); } set ontologyInfo(value) { this._ontoInfo = value; } get ontologyInfo() { return this._ontoInfo; } set bindEvents(value) { this._bindEvents = value; } get bindEvents() { return this._bindEvents; } set valueObject(value) { this._htmlValueObj = value; if (this.el.nativeElement.innerHTML) { this.el.nativeElement.innerHTML = this.valueObject.html; } } get valueObject() { return this._htmlValueObj; } refResClicked(refResourceIri) { this.referredResourceClicked.emit(refResourceIri); } /** * Binds a click event to standoff links that shows the referred resource. * * @param targetElement */ onClick(targetElement) { if (this._bindEvents && targetElement.nodeName.toLowerCase() === 'a' && targetElement.className.toLowerCase().indexOf('salsah-link') >= 0 && targetElement.href !== undefined) { this.refResClicked(targetElement.href); // prevent propagation return false; } else if (this.bindEvents && targetElement.nodeName.toLowerCase() === 'a' && targetElement.href !== undefined) { // open link in a new window window.open(targetElement.href, '_blank'); // prevent propagation return false; } else { // prevent propagation return false; } } }; TextValueAsHtmlComponent.ctorParameters = () => [ { type: ElementRef } ]; __decorate([ Output(), __metadata("design:type", EventEmitter) ], TextValueAsHtmlComponent.prototype, "referredResourceClicked", void 0); __decorate([ Input(), __metadata("design:type", Object), __metadata("design:paramtypes", [Object]) ], TextValueAsHtmlComponent.prototype, "ontologyInfo", null); __decorate([ Input(), __metadata("design:type", Boolean), __metadata("design:paramtypes", [Boolean]) ], TextValueAsHtmlComponent.prototype, "bindEvents", null); __decorate([ Input(), __metadata("design:type", ReadTextValueAsHtml), __metadata("design:paramtypes", [ReadTextValueAsHtml]) ], TextValueAsHtmlComponent.prototype, "valueObject", null); __decorate([ HostListener('click', ['$event.target']), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], TextValueAsHtmlComponent.prototype, "onClick", null); TextValueAsHtmlComponent = __decorate([ Component({ selector: 'kui-text-value-as-html', template: "<div>{{valueObject.html}}</div>", styles: [""] }), __metadata("design:paramtypes", [ElementRef]) ], TextValueAsHtmlComponent); let TextValueAsStringComponent = class TextValueAsStringComponent { constructor() { this.regexUrl = /(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/; } set valueObject(value) { // console.log(value); const str = value.text; if (this.regexUrl.exec(str)) { const url = this.regexUrl.exec(str)[0]; const newStr = str.replace(this.regexUrl, '<a class="kui-link" href="' + url + '">' + url + '</a>'); value.text = newStr; this._textStringValueObj = value; } else { this._textStringValueObj = value; } } get valueObject() { return this._textStringValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadTextValueAsString), __metadata("design:paramtypes", [ReadTextValueAsString]) ], TextValueAsStringComponent.prototype, "valueObject", null); TextValueAsStringComponent = __decorate([ Component({ selector: 'kui-text-value-as-string', template: "<span [innerHTML]=\"valueObject.text\"></span>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.kui-link:hover{box-shadow:0 -10px 0 rgba(0,105,92,.25) inset}"] }), __metadata("design:paramtypes", []) ], TextValueAsStringComponent); let TextValueAsXmlComponent = class TextValueAsXmlComponent { constructor() { } set valueObject(value) { this._xmlValueObj = value; } get valueObject() { return this._xmlValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadTextValueAsXml), __metadata("design:paramtypes", [ReadTextValueAsXml]) ], TextValueAsXmlComponent.prototype, "valueObject", null); TextValueAsXmlComponent = __decorate([ Component({ selector: 'kui-text-value-as-xml', template: "<span>{{valueObject.xml}}</span>", styles: [""] }), __metadata("design:paramtypes", []) ], TextValueAsXmlComponent); // TEMP CLASS DEFINITION BECAUSE MISSING IN KNORA/API LIB // TODO: this class must be replaced with the new definition from the lib class ReadTextFileValue extends ReadFileValue { } let TextfileValueComponent = class TextfileValueComponent { constructor() { } set valueObject(value) { this._textfileValueObj = value; } get valueObject() { return this._textfileValueObj; } }; __decorate([ Input(), __metadata("design:type", ReadTextFileValue), __metadata("design:paramtypes", [ReadTextFileValue]) ], TextfileValueComponent.prototype, "valueObject", null); TextfileValueComponent = __decorate([ Component({ selector: 'kui-textfile-value', template: "<a target=\"_blank\" href=\"{{valueObject.fileUrl}}\">{{valueObject.filename}}</a>\n", styles: [""] }), __metadata("design:paramtypes", []) ], TextfileValueComponent); let UriValueComponent = class UriValueComponent { constructor() { } set valueObject(value) { this.__uriValueObj = value; } get valueObject() { return this.__uriValueObj; } ngOnChanges() { if (this.label === undefined) { this.displayString = this.__uriValueObj.uri; } else { this.displayString = this.label; } } }; __decorate([ Input(), __metadata("design:type", ReadUriValue), __metadata("design:paramtypes", [ReadUriValue]) ], UriValueComponent.prototype, "valueObject", null); __decorate([ Input(), __metadata("design:type", String) ], UriValueComponent.prototype, "label", void 0); UriValueComponent = __decorate([ Component({ selector: ' kui-uri-value', template: "<a href=\"{{valueObject.uri}}\" target=\"_blank\" class=\"kui-link\">{{displayString}}</a>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}"] }), __metadata("design:paramtypes", []) ], UriValueComponent); let GridViewComponent = class GridViewComponent { constructor(_router) { this._router = _router; } ngOnInit() { } /** * Navigate to the resource viewer when clicking on one resource of the search result grid * @param {string} id */ openResource(id) { const url = '/resource/' + encodeURIComponent(id); this._router.navigate([url]); } }; GridViewComponent.ctorParameters = () => [ { type: Router } ]; __decorate([ Input(), __metadata("design:type", Object) ], GridViewComponent.prototype, "result", void 0); __decorate([ Input(), __metadata("design:type", OntologyInformation) ], GridViewComponent.prototype, "ontologyInfo", void 0); GridViewComponent = __decorate([ Component({ selector: 'kui-grid-view', template: "<div>\n <!-- <kui-progress-indicator *ngIf=\"isLoading\" [color]=\"'#D88958'\"></kui-progress-indicator> -->\n\n <div fxLayout=\"row wrap\" fxLayout.xs=\"column\" fxLayoutGap=\"grid\">\n\n <div fxFlex.xs=\"100\" fxFlex.sm=\"50\" fxFlex.md=\"33.3\" fxFlex.lg=\"25\" fxFlex.xl=\"20\" *ngFor=\"let res of result\"\n class=\"gv-preview\">\n <mat-card class=\"link\" (click)=\"openResource(res.id)\">\n\n <mat-card-subtitle>{{res.entityInfo.classes[res.type].label}}</mat-card-subtitle>\n <mat-card-title>{{res.label}}</mat-card-title>\n\n\n <mat-card-content *ngFor=\"let prop of res.properties | kuiKey\">\n <div *ngFor=\"let val of prop.value | kuiKey\">\n <span class=\"lv-prop-label\">\n {{res.entityInfo.properties[val.value.property].label}}:&nbsp;\n </span>\n\n <div class=\"lv-html-text\">\n {{val.value.strval | kuiTruncate:['200', '...']}}\n </div>\n </div>\n </mat-card-content>\n\n </mat-card>\n </div>\n </div>\n\n\n</div>\n", styles: [".mat-form-field{width:320px}.fill-remaining-space{-webkit-box-flex:1;flex:1 1 auto}.center{text-align:center}a{text-decoration:none;color:inherit}.kui-link{cursor:pointer;border-bottom:2px solid rgba(0,105,92,.25)}.lv-prop-label{color:rgba(0,0,0,.54)}.lv-html-text{position:relative;overflow:hidden}.gv-preview{margin:6px 0;padding:24px;word-wrap:break-word;border-radius:5px}.gv-preview .mat-card{height:180px;color:rgba(0,0,0,.81);overflow:hidden;padding-bottom:25px}.gv-preview .mat-card:hover{background:rgba(0,105,92,.39);color:#000}.gv-preview .mat-card:active{background:rgba(0,105,92,.61)}.gv-preview .mat-card .mat-card-title{font-siz