osd.component.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, Output, ViewChild } from '@angular/core';
  2. import { HttpClient } from '@angular/common/http';
  3. import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
  4. import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
  5. import { distinctUntilChanged } from 'rxjs/operators';
  6. import { ViewerDataType } from '../../models/evt-models';
  7. import { OsdTileSource, ViewerDataInput, ViewerSource } from '../../models/evt-polymorphic-models';
  8. import { uuid } from '../../utils/js-utils';
  9. declare var OpenSeadragon;
  10. interface OsdAnnotation {
  11. id: string;
  12. element: HTMLElement;
  13. x: number;
  14. y: number;
  15. width: number;
  16. height: number;
  17. fontSize?: number;
  18. text: string;
  19. modalService?: NgbModal;
  20. }
  21. interface OsdAnnotationAPI {
  22. elements: OsdAnnotation[];
  23. getElements: () => OsdAnnotation[];
  24. getElementById: (id: string) => OsdAnnotation;
  25. addElement: (e: OsdAnnotation) => OsdAnnotation[];
  26. addElements: (es: OsdAnnotation[]) => OsdAnnotation[];
  27. removeElementById: (id: string) => void;
  28. removeElementsById: (ids: string[]) => void;
  29. goToElementLocation: (id: string) => void;
  30. }
  31. interface OsdViewerAPI {
  32. addHandler: (eventName: string, handler: (x: { page: number }) => void) => void;
  33. goToPage: (page: number) => void;
  34. viewport;
  35. gestureSettingsMouse;
  36. raiseEvent: (evtName: string) => void;
  37. }
  38. /*
  39. Observable<OsdTileSource[]>
  40. "@id": "https://www.e-codices.unifr.ch:443/loris/bge/bge-gr0044/bge-gr0044_e001.jp2/full/full/0/default.jpg",
  41. "@type": "dctypes:Image",
  42. "format": "image/jpeg",
  43. "height": 7304,
  44. "width": 5472,
  45. "service": {
  46. "@context": "http://iiif.io/api/image/2/context.json",
  47. "@id": "https://www.e-codices.unifr.ch/loris/bge/bge-gr0044/bge-gr0044_e001.jp2",
  48. "profile": "http://iiif.io/api/image/2/level2.json"
  49. }
  50. }
  51. To:
  52. {
  53. '@context': 'http://iiif.io/api/image/2/context.json',
  54. '@id': 'https://www.e-codices.unifr.ch/loris/bge/bge-gr0044/bge-gr0044_e001.jp2',
  55. 'profile': ['http://iiif.io/api/image/2/level2.json'],
  56. 'protocol': 'http://iiif.io/api/image',
  57. 'height': 7304,
  58. 'width': 5472,
  59. }
  60. */
  61. @Component({
  62. selector: 'evt-osd',
  63. templateUrl: './osd.component.html',
  64. styleUrls: ['./osd.component.scss'],
  65. })
  66. export class OsdComponent implements AfterViewInit, OnDestroy {
  67. @ViewChild('osd', { read: ElementRef, static: true }) div: ElementRef;
  68. // tslint:disable-next-line: variable-name
  69. private _options;
  70. @Input() set options(v) { // TODO: add interface to better type this object
  71. if (v !== this._options) {
  72. this._options = v;
  73. this.optionsChange.next(this._options);
  74. }
  75. }
  76. get options() { return this._options; }
  77. optionsChange = new BehaviorSubject({});
  78. private _viewerDataType: string; // tslint:disable-line: variable-name
  79. public _viewerSource: ViewerDataInput; // tslint:disable-line: variable-name
  80. @Input() set viewerData(v: ViewerDataType) {
  81. this._viewerDataType = v.type;
  82. this._viewerSource = ViewerSource.getSource(v, v.type);
  83. this.sourceChange.next(this._viewerSource);
  84. }
  85. sourceChange = new BehaviorSubject<ViewerDataInput>([]);
  86. // tslint:disable-next-line: variable-name
  87. private _page: number;
  88. @Input() set page(v: number) {
  89. if (v !== this._page) {
  90. this._page = v;
  91. this.pageChange.next(this._page);
  92. }
  93. }
  94. get page() { return this._page; }
  95. @Output() pageChange = new EventEmitter<number>();
  96. @Input() text: string;
  97. viewer: Partial<OsdViewerAPI>;
  98. viewerId: string;
  99. annotationsHandle: OsdAnnotationAPI;
  100. private subscriptions: Subscription[] = [];
  101. tileSources: Observable<OsdTileSource[]>;
  102. constructor(
  103. private http: HttpClient,
  104. ) {
  105. this.subscriptions.push(this.pageChange.pipe(
  106. distinctUntilChanged(),
  107. ).subscribe((x) => {
  108. if (!!this.viewer) {
  109. this.viewer.goToPage(x - 1);
  110. }
  111. }));
  112. }
  113. ngAfterViewInit() {
  114. this.viewerId = uuid('openseadragon');
  115. this.div.nativeElement.id = this.viewerId;
  116. this.tileSources = ViewerSource.getTileSource(this.sourceChange, this._viewerDataType, this.http);
  117. const commonOptions = {
  118. visibilityRatio: 0.1,
  119. minZoomLevel: 0.5,
  120. defaultZoomLevel: 1,
  121. sequenceMode: true,
  122. prefixUrl: 'assets/osd/images/',
  123. id: this.div.nativeElement.id,
  124. navigatorBackground: '#606060',
  125. showNavigator: false,
  126. gestureSettingsMouse: {
  127. clickToZoom: false,
  128. dblClickToZoom: true,
  129. },
  130. };
  131. this.subscriptions.push(combineLatest([this.optionsChange, this.tileSources])
  132. .subscribe(([_, tileSources]) => {
  133. if (!!tileSources) {
  134. this.viewer = OpenSeadragon({
  135. ...commonOptions,
  136. tileSources,
  137. });
  138. } else {
  139. this.viewer = OpenSeadragon({
  140. ...commonOptions,
  141. ...this.options,
  142. });
  143. }
  144. this.viewer.addHandler('page', ({ page }) => {
  145. this.pageChange.next(page + 1);
  146. });
  147. }));
  148. }
  149. ngOnDestroy(): void {
  150. this.subscriptions.forEach((s) => s.unsubscribe());
  151. }
  152. }