-
Notifications
You must be signed in to change notification settings - Fork 423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Angular support for IIIF and Mirador #1079
Changes from 54 commits
156e9a0
a56ebad
7e1c441
c6bc21f
27afd41
6081dab
30ff10e
2ec1096
f377347
aa27dda
3b5df5d
c02983c
42802bd
23e7675
71851fe
2d694a8
187ae50
45c6f4e
5a8f38a
0330b3e
f52ed2a
c0aac8a
e4a51af
0250b18
97e8c9b
3744abb
ce11ae2
3bc7739
247ae73
6f55225
c37b315
2561d54
4791cce
98571a4
3dacbe5
3bd8e35
da2bbce
9c41cd7
de352a8
20477f0
9cc3351
6019a21
fb0d51c
668a08b
ded5e29
3bf9c5f
abb733b
ec0e8c7
670a0b8
d4a6ed6
baa94ca
681b10e
2d11136
538e3cb
a875f68
b738065
8c05c6d
3a1abe8
24a6a4c
80a9770
073296e
6a2d856
a3511ab
8ac122b
b0fcdf6
2b19b16
2c60cc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<p class="full-text-op">{{'iiifviewer.fullscreen.notice' | translate}}</p> | ||
<iframe title="Mirador Viewer" allowtransparency="true" [src]="iframeViewerUrl | async" id="mirador-viewer"></iframe> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#mirador-viewer { | ||
border: 1px solid #cccccc; | ||
height: 660px; | ||
width: 100% | ||
} | ||
.full-text-op { | ||
text-align: right; | ||
color: #333333; | ||
font-size: 0.8em; | ||
} | ||
p.full-text-op { | ||
margin-bottom: 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
// tests needed. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit, PLATFORM_ID } from '@angular/core'; | ||
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; | ||
import { Item } from '../../core/shared/item.model'; | ||
import { environment } from '../../../environments/environment'; | ||
import { BitstreamDataService } from '../../core/data/bitstream-data.service'; | ||
import { | ||
getFirstCompletedRemoteData, | ||
getFirstSucceededRemoteDataPayload | ||
} from '../../core/shared/operators'; | ||
import { RemoteData } from '../../core/data/remote-data'; | ||
import { PaginatedList } from '../../core/data/paginated-list.model'; | ||
import { Bitstream } from '../../core/shared/bitstream.model'; | ||
import { Observable } from 'rxjs/internal/Observable'; | ||
import { last, map, switchMap} from 'rxjs/operators'; | ||
import { of } from 'rxjs'; | ||
import { isPlatformBrowser } from '@angular/common'; | ||
import { BitstreamFormat } from '../../core/shared/bitstream-format.model'; | ||
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; | ||
|
||
@Component({ | ||
selector: 'ds-mirador-viewer', | ||
styleUrls: ['./mirador-viewer.component.scss'], | ||
templateUrl: './mirador-viewer.component.html', | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class MiradorViewerComponent implements OnInit { | ||
|
||
@Input() item: Item; | ||
|
||
/** | ||
* A previous dspace search query. | ||
*/ | ||
@Input() query: string; | ||
|
||
/** | ||
* True if searchable. | ||
*/ | ||
@Input() searchable: boolean; | ||
|
||
/** | ||
* The url for the iframe. | ||
*/ | ||
iframeViewerUrl: Observable<SafeResourceUrl>; | ||
|
||
/** | ||
* Sets the viewer to show or hide thumbnail side navigation menu. | ||
*/ | ||
multi = false; | ||
|
||
/** | ||
* Hides the thumbnail navigation menu on smaller viewports. | ||
*/ | ||
notMobile = false; | ||
|
||
LINKS_TO_FOLLOW: FollowLinkConfig<Bitstream>[] = [ | ||
followLink('format'), | ||
]; | ||
|
||
constructor(private sanitizer: DomSanitizer, | ||
private bitstreamDataService: BitstreamDataService, | ||
@Inject(PLATFORM_ID) private platformId: any) { | ||
} | ||
|
||
/** | ||
* Creates the url for the Mirador iframe. Adds parameters for the displaying the search panel, query results, | ||
* or multi-page thumbnail navigation. | ||
*/ | ||
setURL() { | ||
// The path to the REST manifest endpoint. | ||
const manifestApiEndpoint = encodeURIComponent(environment.rest.baseUrl + '/iiif/' | ||
+ this.item.id + '/manifest'); | ||
// The Express path to Mirador viewer. | ||
let viewerPath = '/iiif/mirador/index.html?manifest=' + manifestApiEndpoint; | ||
if (this.searchable) { | ||
// Tell the viewer add search to menu. | ||
viewerPath += '&searchable=' + this.searchable; | ||
} | ||
if (this.query) { | ||
// Tell the viewer to execute a search for the query term. | ||
viewerPath += '&query=' + this.query; | ||
} | ||
if (this.multi) { | ||
// Tell the viewer to add thumbnail navigation. If searchable, thumbnail navigation is added by default. | ||
viewerPath += '&multi=' + this.multi; | ||
} | ||
if (this.notMobile) { | ||
viewerPath += '¬Mobile=true'; | ||
} | ||
// TODO: review whether the item.id should be sanitized. The query term probably should be. | ||
return this.sanitizer.bypassSecurityTrustResourceUrl(viewerPath); | ||
} | ||
|
||
ngOnInit(): void { | ||
/** | ||
* Initializes the iframe url observable. | ||
*/ | ||
if (isPlatformBrowser(this.platformId)) { | ||
// The notMobile property affects only the thumbnail navigation | ||
// menu by hiding it for smaller viewports. This will not be | ||
// responsive to resizing. | ||
if (window.innerWidth > 768) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please use the However it'd be nice if the player is responsive to resizing in order to have the mobile version on small windows size when I start from a large window view and viceversa. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @atarix83 , I agree with you on resizing as a general rule but this addresses a slightly different problem. It configures the initial state of the Mirador viewer inside the iframe and has no effect after the viewer is loaded. It's here because I noticed that the viewer requires the user to hide panels in the UI when there's a configuration change. The mobile check exists to get them to a good place at the initial load. Otherwise mobile devices always start with panels that need to be manually closed. I can switch to HostWIndowService if you recommend it but checking with you first since this seems like is a situation that may not require the observable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I concur with @atarix83 here: it would be better to use hostWindowService, if only to not to have to hardcode that screen width in multiple places. The observable shouldn't be a problem. It's an ngrx store observable, so it'll emit right away (synchronously). You can replace the if you have with something like: this.hostWindowService.widthCategory
.pipe(take(1))
.subscribe((category: WidthCategory) => {
this.notMobile = !(category === WidthCategory.XS || category === WidthCategory.SM);
}); And in the line below that subscribe, you can be sure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to use hostWindowService. |
||
this.notMobile = true; | ||
} | ||
// We need to set the multi property to true if the | ||
// item is searchable or the ORIGINAL bundle contains more | ||
// than 1 image bitstream. The multi property determine whether the | ||
// Mirador side navigation panel is shown. | ||
if (this.searchable) { | ||
// If it's searchable set multi to true. | ||
const observable = of({multi: true}); | ||
this.iframeViewerUrl = observable.pipe( | ||
map((val) => { | ||
this.multi = val.multi; | ||
return this.setURL(); | ||
}) | ||
); | ||
} else { | ||
// Gets the first 10 items in the bundle and counts the number of images. Emits the final count. | ||
let count = 0; | ||
const imageCount$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL', { | ||
currentPage: 1, | ||
elementsPerPage: 10 | ||
}, true, true, ...this.LINKS_TO_FOLLOW) | ||
.pipe( | ||
getFirstCompletedRemoteData(), | ||
map((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => bitstreamsRD.payload), | ||
map((paginatedList: PaginatedList<Bitstream>) => paginatedList.page), | ||
switchMap((bitstreams: Bitstream[]) => bitstreams), | ||
switchMap((bitstream: Bitstream) => bitstream.format.pipe( | ||
getFirstSucceededRemoteDataPayload(), | ||
map((format: BitstreamFormat) => format) | ||
)), | ||
map((format: BitstreamFormat) => { | ||
if (format.mimetype.includes('image')) { | ||
count++; | ||
} | ||
return count; | ||
}), | ||
last() | ||
); | ||
|
||
// Sets the multi value based on the image count and then sets the iframe url. | ||
this.iframeViewerUrl = imageCount$.pipe( | ||
map(c => { | ||
if (count > 1) { | ||
this.multi = true; | ||
} | ||
return this.setURL(); | ||
}) | ||
); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add typedoc here and for the following properties