Merge remote-tracking branch 'origin/iiif-mirador' into iiif-mirador

This commit is contained in:
Michael W Spalti
2021-04-20 14:46:44 -07:00
54 changed files with 2046 additions and 17 deletions

View File

@@ -46,7 +46,12 @@
"clean": "yarn run clean:prod && yarn run clean:env && yarn run clean:node", "clean": "yarn run clean:prod && yarn run clean:env && yarn run clean:node",
"clean:env": "rimraf src/environments/environment.ts", "clean:env": "rimraf src/environments/environment.ts",
"sync-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts", "sync-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts",
"postinstall": "ngcc" "postinstall": "ngcc",
"build:mirador": "webpack --config webpack/webpack.mirador.config.ts",
"build:client-and-server-bundles:mirador": "node --max-old-space-size=4096 ./node_modules/@angular/cli/bin/ng build --prod && ./node_modules/@angular/cli/bin/ng run dspace-angular:server:production --bundleDependencies true",
"build:ssr:mirador": "yarn run build:client-and-server-bundles:mirador && yarn run compile:server",
"start:prod:mirador": "yarn run build:ssr:mirador && yarn run serve:ssr",
"start:mirador:prod": "yarn run build:mirador && yarn run start:prod:mirador"
}, },
"browser": { "browser": {
"fs": false, "fs": false,
@@ -103,6 +108,9 @@
"jsonschema": "1.4.0", "jsonschema": "1.4.0",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"klaro": "^0.7.10", "klaro": "^0.7.10",
"mirador": "^3.0.0",
"mirador-dl-plugin": "^0.13.0",
"mirador-share-plugin": "^0.10.0",
"moment": "^2.29.1", "moment": "^2.29.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"ng-mocks": "10.5.4", "ng-mocks": "10.5.4",
@@ -115,6 +123,8 @@
"nouislider": "^14.6.3", "nouislider": "^14.6.3",
"pem": "1.14.4", "pem": "1.14.4",
"postcss-cli": "^8.3.0", "postcss-cli": "^8.3.0",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rxjs": "^6.6.3", "rxjs": "^6.6.3",
"rxjs-spy": "^7.5.3", "rxjs-spy": "^7.5.3",
@@ -151,6 +161,7 @@
"deep-freeze": "0.0.1", "deep-freeze": "0.0.1",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"fork-ts-checker-webpack-plugin": "^6.0.3", "fork-ts-checker-webpack-plugin": "^6.0.3",
"html-loader": "^1.3.2",
"html-webpack-plugin": "^4.5.0", "html-webpack-plugin": "^4.5.0",
"http-proxy-middleware": "^1.0.5", "http-proxy-middleware": "^1.0.5",
"jasmine-core": "^3.6.0", "jasmine-core": "^3.6.0",

View File

@@ -41,6 +41,8 @@ import { UIServerConfig } from './src/config/ui-server-config.interface';
* Set path for the browser application's dist folder * Set path for the browser application's dist folder
*/ */
const DIST_FOLDER = join(process.cwd(), 'dist/browser'); const DIST_FOLDER = join(process.cwd(), 'dist/browser');
// Set path fir IIIF viewer.
const IIIF_VIEWER = join(process.cwd(), 'dist/iiif');
const indexHtml = existsSync(join(DIST_FOLDER, 'index.html')) ? 'index.html' : 'index'; const indexHtml = existsSync(join(DIST_FOLDER, 'index.html')) ? 'index.html' : 'index';
@@ -135,6 +137,10 @@ export function app() {
* Serve static resources (images, i18n messages, …) * Serve static resources (images, i18n messages, …)
*/ */
server.get('*.*', cacheControl, express.static(DIST_FOLDER, { index: false })); server.get('*.*', cacheControl, express.static(DIST_FOLDER, { index: false }));
/*
* Fallthrough to the IIIF viewer (must be included in the build).
*/
server.use('/iiif', express.static(IIIF_VIEWER, {index:false}));
// Register the ngApp callback function to handle incoming requests // Register the ngApp callback function to handle incoming requests
server.get('*', ngApp); server.get('*', ngApp);

View File

@@ -12,7 +12,6 @@ import { ItemPageAbstractFieldComponent } from './simple/field-components/specif
import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component'; import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component';
import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component'; import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component';
import { ItemPageFieldComponent } from './simple/field-components/specific-field/item-page-field.component'; import { ItemPageFieldComponent } from './simple/field-components/specific-field/item-page-field.component';
import { FileSectionComponent } from './simple/field-components/file-section/file-section.component';
import { CollectionsComponent } from './field-components/collections/collections.component'; import { CollectionsComponent } from './field-components/collections/collections.component';
import { FullItemPageComponent } from './full/full-item-page.component'; import { FullItemPageComponent } from './full/full-item-page.component';
import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component'; import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component';
@@ -27,6 +26,7 @@ import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal
import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module';
import { ThemedItemPageComponent } from './simple/themed-item-page.component'; import { ThemedItemPageComponent } from './simple/themed-item-page.component';
import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component'; import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component';
import { IIIFEntitiesModule } from '../entity-groups/iiif-entities/iiif-entities.module';
import { MediaViewerComponent } from './media-viewer/media-viewer.component'; import { MediaViewerComponent } from './media-viewer/media-viewer.component';
import { MediaViewerVideoComponent } from './media-viewer/media-viewer-video/media-viewer-video.component'; import { MediaViewerVideoComponent } from './media-viewer/media-viewer-video/media-viewer-video.component';
import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/media-viewer-image.component'; import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/media-viewer-image.component';
@@ -50,7 +50,6 @@ const DECLARATIONS = [
ItemPageUriFieldComponent, ItemPageUriFieldComponent,
ItemPageTitleFieldComponent, ItemPageTitleFieldComponent,
ItemPageFieldComponent, ItemPageFieldComponent,
FileSectionComponent,
CollectionsComponent, CollectionsComponent,
FullFileSectionComponent, FullFileSectionComponent,
PublicationComponent, PublicationComponent,
@@ -72,7 +71,8 @@ const DECLARATIONS = [
StatisticsModule.forRoot(), StatisticsModule.forRoot(),
JournalEntitiesModule.withEntryComponents(), JournalEntitiesModule.withEntryComponents(),
ResearchEntitiesModule.withEntryComponents(), ResearchEntitiesModule.withEntryComponents(),
NgxGalleryModule, IIIFEntitiesModule.withEntryComponents(),
NgxGalleryModule,
], ],
declarations: [ declarations: [
...DECLARATIONS ...DECLARATIONS

View File

@@ -0,0 +1,50 @@
import {IIIFSearchableComponent} from './item-pages/iiif-searchable/iiif-searchable.component';
import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {SharedModule} from '../../shared/shared.module';
import {IIIFSearchableGridElementComponent} from './item-grid-elements/iiif-searchable/iiif-searchable-grid-element.component';
import {IIIFSearchableSearchResultListElementComponent} from './item-list-elements/search-result-list-elements/iiif-searchable/iiif-searchable-search-result-list-element.component';
import {IIIFSearchableSearchResultGridElementComponent} from './item-grid-elements/search-result-grid-elements/iiif-searchable/iiif-searchable-search-result-grid-element.component';
import {MiradorViewerComponent} from './mirador-viewer/mirador-viewer.component';
import {IIIFSearchableListElementComponent} from './item-list-elements/iiif-searchable/iiif-searchable-list-element.component';
import {IIIFComponent} from './item-pages/iiif/iiif.component';
import {IIIFListElementComponent} from './item-list-elements/iiif/iiif-list-element.component';
import {IIIFGridElementComponent} from './item-grid-elements/iiif/iiif-grid-element.component';
import {IIIFSearchResultListElementComponent} from './item-list-elements/search-result-list-elements/iiif/iiif-search-result-list-element.component';
import {IIIFSearchResultGridElementComponent} from './item-grid-elements/search-result-grid-elements/iiif/iiif-search-result-grid-element.component';
const ENTRY_COMPONENTS = [
IIIFComponent,
IIIFSearchableComponent,
IIIFListElementComponent,
IIIFSearchableListElementComponent,
IIIFGridElementComponent,
IIIFSearchableGridElementComponent,
IIIFSearchResultListElementComponent,
IIIFSearchableSearchResultListElementComponent,
IIIFSearchResultGridElementComponent,
IIIFSearchableSearchResultGridElementComponent,
MiradorViewerComponent
];
@NgModule({
imports: [
CommonModule,
SharedModule,
],
declarations: [
...ENTRY_COMPONENTS
],
entryComponents: [
...ENTRY_COMPONENTS
],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class IIIFEntitiesModule {
static withEntryComponents() {
return {
ngModule: IIIFEntitiesModule,
providers: ENTRY_COMPONENTS.map((component) => ({provide: component}))
};
}
}

View File

@@ -0,0 +1 @@
<ds-iiif-searchable-result-grid-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-iiif-searchable-result-grid-element>

View File

@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@listableObjectComponent('IIIFSearchable', ViewMode.GridElement)
@Component({
selector: 'ds-iiif-searchable-grid-element',
styleUrls: ['./iiif-searchable-grid-element.component.scss'],
templateUrl: './iiif-searchable-grid-element.component.html'
})
/**
* The component for displaying a list element for an item of the type IIIFSearchable.
*/
export class IIIFSearchableGridElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -0,0 +1 @@
<ds-iiif-result-grid-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-iiif-result-grid-element>

View File

@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@listableObjectComponent('IIIF', ViewMode.GridElement)
@Component({
selector: 'ds-iiif-grid-element',
styleUrls: ['./iiif-grid-element.component.scss'],
templateUrl: './iiif-grid-element.component.html'
})
/**
* The component for displaying a list element for an item of the type IIIF.
*/
export class IIIFGridElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -0,0 +1,19 @@
<ds-item-type-badge *ngIf="showLabel" [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span *ngIf="dso.allMetadata(['creativeworkseries.issn']).length > 0"
class="item-list-journals">
<span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('IIIFSearchableSearchResult', ViewMode.GridElement)
@Component({
selector: 'ds-iiif-searchable-result-grid-element',
styleUrls: ['./iiif-searchable-search-result-grid-element.component.scss'],
templateUrl: './iiif-searchable-search-result-grid-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type IIIFSearchable.
*/
export class IIIFSearchableSearchResultGridElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -0,0 +1,19 @@
<ds-item-type-badge *ngIf="showLabel" [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span *ngIf="dso.allMetadata(['creativeworkseries.issn']).length > 0"
class="item-list-journals">
<span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('IIIFSearchResult', ViewMode.GridElement)
@Component({
selector: 'ds-iiif-result-grid-element',
styleUrls: ['./iiif-search-result-grid-element.component.scss'],
templateUrl: './iiif-search-result-grid-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type IIIF
*/
export class IIIFSearchResultGridElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -0,0 +1 @@
<ds-iiif-searchable-search-result-list-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-iiif-searchable-search-result-list-element>

View File

@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@listableObjectComponent('IIIFSearchable', ViewMode.ListElement)
@Component({
selector: 'ds-iiif-searchable-list-element',
styleUrls: ['./iiif-searchable-list-element.component.scss'],
templateUrl: './iiif-searchable-list-element.component.html'
})
/**
* The component for displaying a list element for an item of the type IIIFSearchable
*/
export class IIIFSearchableListElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -0,0 +1 @@
<ds-iiif-search-result-list-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-iiif-search-result-list-element>

View File

@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@listableObjectComponent('IIIF', ViewMode.ListElement)
@Component({
selector: 'ds-iiif-list-element',
styleUrls: ['./iiif-list-element.component.scss'],
templateUrl: './iiif-list-element.component.html'
})
/**
* The component for displaying a list element for an item of the type IIIF
*/
export class IIIFListElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -0,0 +1,19 @@
<ds-item-type-badge *ngIf="showLabel" [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<!-- <ds-truncatable-part [id]="dso.id" [minLines]="1">-->
<!-- <span *ngIf="dso.allMetadata(['creativeworkseries.issn']).length > 0"-->
<!-- class="item-list-journals">-->
<!-- <span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">-->
<!-- <span [innerHTML]="value"><span [innerHTML]="value"></span></span>-->
<!-- </span>-->
<!-- </span>-->
<!-- </ds-truncatable-part>-->
</span>
</ds-truncatable>

View File

@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('IIIFSearchableSearchResult', ViewMode.ListElement)
@Component({
selector: 'ds-iiif-searchable-search-result-list-element',
styleUrls: ['./iiif-searchable-search-result-list-element.component.scss'],
templateUrl: './iiif-searchable-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type IIIFSearchable
*/
export class IIIFSearchableSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -0,0 +1,19 @@
<ds-item-type-badge *ngIf="showLabel" [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<!-- <ds-truncatable-part [id]="dso.id" [minLines]="1">-->
<!-- <span *ngIf="dso.allMetadata(['creativeworkseries.issn']).length > 0"-->
<!-- class="item-list-journals">-->
<!-- <span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">-->
<!-- <span [innerHTML]="value"><span [innerHTML]="value"></span></span>-->
<!-- </span>-->
<!-- </span>-->
<!-- </ds-truncatable-part>-->
</span>
</ds-truncatable>

View File

@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('IIIFSearchResult', ViewMode.ListElement)
@Component({
selector: 'ds-iiif-search-result-list-element',
styleUrls: ['./iiif-search-result-list-element.component.scss'],
templateUrl: './iiif-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type IIIF
*/
export class IIIFSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -0,0 +1,40 @@
<ds-mirador-viewer id="iiif-viewer" [item]="object" [searchable]="searchable" [query]="query | async"></ds-mirador-viewer>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
</div>
<div class="col-xs-12 col-md-6">
<div class="item-page-title-field">
<h5>{{'iiifsearchable.page.titleprefix' | translate}}</h5><h3><ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values></h3>
</div>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.citation']"
[label]="'iiifsearchable.page.issue'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.uri']"
[label]="'item.page.uri'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.other']"
[label]="'iiifsearchable.page.doi'">
</ds-generic-item-page-field>
<ds-metadata-field-wrapper>
<ds-metadata-representation-list
[parentItem]="object"
[itemType]="'Person'"
[metadataField]="'dc.contributor.author'"
[label]="'relationships.isAuthorOf' | translate">
</ds-metadata-representation-list>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</ds-metadata-field-wrapper>
</div>
</div>

View File

@@ -0,0 +1,3 @@
#iiif-viewer {
margin-bottom: 12px;
}

View File

@@ -0,0 +1,199 @@
import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { Store } from '@ngrx/store';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { of as observableOf } from 'rxjs';
import { GenericItemPageFieldComponent } from '../../../../+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component';
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { CommunityDataService } from '../../../../core/data/community-data.service';
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { buildPaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
import { Item } from '../../../../core/shared/item.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { UUIDService } from '../../../../core/shared/uuid.service';
import { isNotEmpty } from '../../../../shared/empty.util';
import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { IIIFSearchableComponent } from './iiif-searchable.component';
import { By } from '@angular/platform-browser';
import { RelationshipService } from '../../../../core/data/relationship.service';
import { RouteService } from '../../../../core/services/route.service';
let comp: IIIFSearchableComponent;
let fixture: ComponentFixture<IIIFSearchableComponent>;
const mockItem: Item = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
metadata: {
'dc.identifier.citation': [
{
language: 'en_US',
value: 'issue'
}
],
'dc.identifier.uri': [
{
language: 'en_US',
value: 'uri'
}
]
,
'dc.identifier.other': [
{
language: 'en_US',
value: 'other'
},
]
}
});
describe('IIIFSearchableComponent', () => {
const routeServiceStub = jasmine.createSpyObj('routeService', {
getHistory: observableOf(['/browse',''])
});
const mockBitstreamDataService = {
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
return createSuccessfulRemoteDataObject$(new Bitstream());
}
};
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [IIIFSearchableComponent, GenericItemPageFieldComponent, TruncatePipe],
providers: [
{ provide: ItemDataService, useValue: {} },
{ provide: TruncatableService, useValue: {} },
{ provide: RelationshipService, useValue: {} },
{ provide: ObjectCacheService, useValue: {} },
{ provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} },
{ provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} },
{ provide: HttpClient, useValue: {} },
{ provide: DSOChangeAnalyzer, useValue: {} },
{ provide: NotificationsService, useValue: {} },
{ provide: DefaultChangeAnalyzer, useValue: {} },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
{ provide: RouteService, useValue: routeServiceStub }
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(IIIFSearchableComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IIIFSearchableComponent);
comp = fixture.componentInstance;
comp.object = mockItem;
fixture.detectChanges();
}));
// TODO: fix test
// it(`should set searchable attribute to true`, () => {
// comp.ngOnInit();
// fixture.detectChanges();
// const miradorEl = fixture.debugElement.query(By.css('ds-mirador-viewer'));
// expect(miradorEl.nativeElement.getAttribute('searchable')).toBeTruthy();
// });
for (const key of Object.keys(mockItem.metadata)) {
it(`should be calling a component with metadata field ${key}`, () => {
const fields = fixture.debugElement.queryAll(By.css('.item-page-fields'));
expect(containsFieldInput(fields, key)).toBeTruthy();
});
}
});
describe('IIIFSearchableComponent with query', () => {
const routeServiceStub = jasmine.createSpyObj('routeService', {
getHistory: observableOf(['/search?page=1&query=test',''])
});
const mockBitstreamDataService = {
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
return createSuccessfulRemoteDataObject$(new Bitstream());
}
};
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [IIIFSearchableComponent, GenericItemPageFieldComponent, TruncatePipe],
providers: [
{ provide: ItemDataService, useValue: {} },
{ provide: TruncatableService, useValue: {} },
{ provide: RelationshipService, useValue: {} },
{ provide: ObjectCacheService, useValue: {} },
{ provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} },
{ provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} },
{ provide: HttpClient, useValue: {} },
{ provide: DSOChangeAnalyzer, useValue: {} },
{ provide: NotificationsService, useValue: {} },
{ provide: DefaultChangeAnalyzer, useValue: {} },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
{ provide: RouteService, useValue: routeServiceStub }
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(IIIFSearchableComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IIIFSearchableComponent);
comp = fixture.componentInstance;
comp.object = mockItem;
fixture.detectChanges();
}));
// TODO: fix test
// it(`should set query search value`, () => {
// comp.ngOnInit();
// fixture.detectChanges();
// const miradorEl = fixture.debugElement.query(By.css('#iiif-viewer'));
// expect(miradorEl.nativeElement.getAttribute('query')).toMatch('test');
// });
});
function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean {
for (const field of fields) {
const fieldComp = field.componentInstance;
if (isNotEmpty(fieldComp.fields)) {
if (fieldComp.fields.indexOf(metadataKey) > -1) {
return true;
}
}
}
return false;
}

View File

@@ -0,0 +1,50 @@
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { Component, OnInit } from '@angular/core';
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
import { RouteService } from '../../../../core/services/route.service';
import { Observable } from 'rxjs/internal/Observable';
import { filter, map, take } from 'rxjs/operators';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
@listableObjectComponent('IIIFSearchable', ViewMode.StandalonePage)
@Component({
selector: 'ds-iiif-searchable',
styleUrls: ['./iiif-searchable.component.scss'],
templateUrl: './iiif-searchable.component.html'
})
export class IIIFSearchableComponent extends ItemComponent implements OnInit {
searchable: boolean;
query: Observable<string>;
constructor(protected routeService: RouteService,
protected bitstreamService: BitstreamDataService) {
super(bitstreamService);
}
ngOnInit(): void {
// Load iiif viewer in searchable configuration.
this.searchable = true;
// Use the route history to get the query from a
// previous search and use this value to initialize the
// viewer with search results.
// TODO: is there is a better way to look the previous search
this.query = this.routeService.getHistory().pipe(
take(1),
map(routes => routes[routes.length - 2 ]),
filter(r => {
return r.includes('/search');
}),
map(r => {
const arr = r.split('&');
const q = arr[1];
const v = q.split('=');
return v[1];
})
);
}
}

View File

@@ -0,0 +1,40 @@
<ds-mirador-viewer id="iiif-viewer" [item]="object" [searchable]="searchable"></ds-mirador-viewer>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
</div>
<div class="col-xs-12 col-md-6">
<div class="item-page-title-field">
<h5>{{'iiifsearchable.page.titleprefix' | translate}}</h5><h3><ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values></h3>
</div>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.citation']"
[label]="'iiifsearchable.page.issue'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.uri']"
[label]="'item.page.uri'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.other']"
[label]="'iiifsearchable.page.doi'">
</ds-generic-item-page-field>
<ds-metadata-field-wrapper>
<ds-metadata-representation-list
[parentItem]="object"
[itemType]="'Person'"
[metadataField]="'dc.contributor.author'"
[label]="'relationships.isAuthorOf' | translate">
</ds-metadata-representation-list>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</ds-metadata-field-wrapper>
</div>
</div>

View File

@@ -0,0 +1,127 @@
import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { Store } from '@ngrx/store';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { GenericItemPageFieldComponent } from '../../../../+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component';
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { CommunityDataService } from '../../../../core/data/community-data.service';
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { buildPaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
import { Item } from '../../../../core/shared/item.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { UUIDService } from '../../../../core/shared/uuid.service';
import { isNotEmpty } from '../../../../shared/empty.util';
import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { IIIFComponent } from './iiif.component';
import { By } from '@angular/platform-browser';
import { RelationshipService } from '../../../../core/data/relationship.service';
let comp: IIIFComponent;
let fixture: ComponentFixture<IIIFComponent>;
const mockItem: Item = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
metadata: {
'dc.identifier.citation': [
{
language: 'en_US',
value: 'issue'
}
],
'dc.identifier.uri': [
{
language: 'en_US',
value: 'uri'
}
]
,
'dc.identifier.other': [
{
language: 'en_US',
value: 'other'
}
]
}
});
describe('IIIFComponent', () => {
const mockBitstreamDataService = {
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
return createSuccessfulRemoteDataObject$(new Bitstream());
}
};
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [IIIFComponent, GenericItemPageFieldComponent, TruncatePipe],
providers: [
{ provide: ItemDataService, useValue: {} },
{ provide: TruncatableService, useValue: {} },
{ provide: RelationshipService, useValue: {} },
{ provide: ObjectCacheService, useValue: {} },
{ provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} },
{ provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} },
{ provide: HttpClient, useValue: {} },
{ provide: DSOChangeAnalyzer, useValue: {} },
{ provide: NotificationsService, useValue: {} },
{ provide: DefaultChangeAnalyzer, useValue: {} },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(IIIFComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IIIFComponent);
comp = fixture.componentInstance;
comp.object = mockItem;
fixture.detectChanges();
}));
it(`should set searchable attribute to false`, () => {
const miradorEl = fixture.debugElement.query(By.css('ds-mirador-viewer'));
expect(miradorEl.nativeElement.getAttribute('searchable')).toBeFalsy();
});
for (const key of Object.keys(mockItem.metadata)) {
it(`should be calling a component with metadata field ${key}`, () => {
const fields = fixture.debugElement.queryAll(By.css('.item-page-fields'));
expect(containsFieldInput(fields, key)).toBeTruthy();
});
}
});
function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean {
for (const field of fields) {
const fieldComp = field.componentInstance;
if (isNotEmpty(fieldComp.fields)) {
if (fieldComp.fields.indexOf(metadataKey) > -1) {
return true;
}
}
}
return false;
}

View File

@@ -0,0 +1,24 @@
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { Component, OnInit } from '@angular/core';
import {ItemComponent} from '../../../../+item-page/simple/item-types/shared/item.component';
@listableObjectComponent('IIIF', ViewMode.StandalonePage)
@Component({
selector: 'ds-iiif',
styleUrls: ['./iiif.component.scss'],
templateUrl: './iiif.component.html'
})
export class IIIFComponent extends ItemComponent implements OnInit {
searchable;
/**
* Load iiif viewer in no search configuration.
*/
ngOnInit(): void {
this.searchable = false;
}
}

View File

@@ -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>

View File

@@ -0,0 +1,13 @@
#mirador-viewer {
border: 1px solid #cccccc;
height: 660px;
width: 100%
}
.full-text-op {
text-align: right;
color: #999999;
font-size: 0.8em;
}
p.full-text-op {
margin-bottom: 0;
}

View File

@@ -0,0 +1,94 @@
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 } 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 { hasValue } from '../../../shared/empty.util';
import { Observable } from 'rxjs/internal/Observable';
import { map } from 'rxjs/operators';
import {isPlatformBrowser} from '@angular/common';
@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;
@Input() query: string;
@Input() searchable: boolean;
iframeViewerUrl: Observable<SafeResourceUrl>;
multi = false;
notMobile = false;
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 + '/api/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 += '&notMobile=true';
}
// TODO: review whether the item.id should be sanitized. The query term should be (check mirador viewer).
return this.sanitizer.bypassSecurityTrustResourceUrl(viewerPath);
}
ngOnInit(): void {
/**
* Initializes the iframe url observable.
*/
if (isPlatformBrowser(this.platformId)) {
if (window.innerWidth > 768) {
this.notMobile = true;
}
this.iframeViewerUrl = this.bitstreamDataService
.findAllByItemAndBundleName(this.item, 'IIIF', {})
.pipe(
getFirstCompletedRemoteData(),
map((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => {
if (hasValue(bitstreamsRD.payload)) {
if (bitstreamsRD.payload.totalElements > 2) {
/* IIIF bundle contains multiple images. The IIIF bundle also contains
* a single json file so multi is true only when count is 3 or more . */
this.multi = true;
}
}
return this.setURL();
})
);
}
}
}

View File

@@ -188,6 +188,7 @@ import { MissingTranslationHelper } from './translate/missing-translation.helper
import { ItemVersionsNoticeComponent } from './item/item-versions/notice/item-versions-notice.component'; import { ItemVersionsNoticeComponent } from './item/item-versions/notice/item-versions-notice.component';
import { FileValidator } from './utils/require-file.validator'; import { FileValidator } from './utils/require-file.validator';
import { FileValueAccessorDirective } from './utils/file-value-accessor.directive'; import { FileValueAccessorDirective } from './utils/file-value-accessor.directive';
import { FileSectionComponent } from '../+item-page/simple/field-components/file-section/file-section.component';
import { ExistingRelationListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component'; import { ExistingRelationListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component';
import { ModifyItemOverviewComponent } from '../+item-page/edit-item-page/modify-item-overview/modify-item-overview.component'; import { ModifyItemOverviewComponent } from '../+item-page/edit-item-page/modify-item-overview/modify-item-overview.component';
import { ClaimedTaskActionsLoaderComponent } from './mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component'; import { ClaimedTaskActionsLoaderComponent } from './mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component';
@@ -322,6 +323,7 @@ const COMPONENTS = [
DsDatePickerInlineComponent, DsDatePickerInlineComponent,
DsSelectComponent, DsSelectComponent,
ErrorComponent, ErrorComponent,
FileSectionComponent,
FormComponent, FormComponent,
LangSwitchComponent, LangSwitchComponent,
LoadingComponent, LoadingComponent,

View File

@@ -1998,6 +1998,28 @@
"journalvolume.page.volume": "Volume", "journalvolume.page.volume": "Volume",
"iiifsearchable.listelement.badge": "Document Media",
"iiifsearchable.page.titleprefix": "Document: ",
"iiifsearchable.page.doi": "Permanent Link: ",
"iiifsearchable.page.issue": "Issue: ",
"iiifsearchable.page.description": "Description: ",
"iiifviewer.fullscreen.notice": "Use full screen for better viewing.",
"iiif.listelement.badge": "Image Media",
"iiif.page.titleprefix": "Image: ",
"iiif.page.doi": "Permanent Link: ",
"iiif.page.issue": "Issue: ",
"iiif.page.description": "Description: ",
"loading.bitstream": "Loading bitstream...", "loading.bitstream": "Loading bitstream...",

166
src/mirador-viewer/index.js Normal file
View File

@@ -0,0 +1,166 @@
import Mirador from 'mirador/dist/es/src/index';
import miradorShareDialogPlugin from 'mirador-share-plugin/es/MiradorShareDialog';
import miradorSharePlugin from 'mirador-share-plugin/es/miradorSharePlugin';
import miradorDownloadPlugin from 'mirador-dl-plugin/es/miradorDownloadPlugin';
import miradorDownloadDialog from 'mirador-dl-plugin/es/MiradorDownloadDialog';
const params = new URLSearchParams(location.search);
const manifest = params.get('manifest');
const searchOption = params.get('searchable');
const query = params.get('query');
const multi = params.get('multi');
const notMobile = params.get('notMobile');
let windowSettings = {};
let sidbarPanel = 'info';
let defaultView = 'single';
let multipleItems = false;
let thumbNavigation = 'off';
windowSettings.manifestId = manifest;
(() => {
if (searchOption) {
defaultView = 'book';
sidbarPanel = 'search';
multipleItems = true;
if (notMobile) {
thumbNavigation = 'far-right';
}
if (query !== 'null') {
windowSettings.defaultSearchQuery = query;
}
} else {
if(multi) {
multipleItems = multi;
if (notMobile) {
thumbNavigation = 'far-right';
}
}
}
})();
(Mirador.viewer(
{
id: 'mirador',
mainMenuSettings: {
show: true
},
thumbnailNavigation: {
defaultPosition: thumbNavigation, // Which position for the thumbnail navigation to be be displayed. Other possible values are "far-bottom" or "far-right"
displaySettings: true, // Display the settings for this in WindowTopMenu
height: 120, // height of entire ThumbnailNavigation area when position is "far-bottom"
width: 100, // width of one canvas (doubled for book view) in ThumbnailNavigation area when position is "far-right"
},
themes: {
light: {
palette: {
type: 'light',
primary: {
main: '#b03727',
},
secondary: {
main: '#b03727',
},
shades: { // Shades that can be used to offset color areas of the Workspace / Window
dark: '#eeeeee',
main: '#ffffff',
light: '#ffffff',
},
highlights: {
primary: '#ffff00',
secondary: '#00BFFF',
},
search: {
default: { fillStyle: '#00BFFF', globalAlpha: 0.3 },
hovered: { fillStyle: '#00FFFF', globalAlpha: 0.3 },
selected: { fillStyle: '#ff0900', globalAlpha: 0.3 },
},
},
},
dark: {
palette: {
type: 'dark',
primary: {
main: '#2790b0',
},
secondary: {
main: '#eeeeee',
},
highlights: {
primary: '#ffff00',
secondary: '#00BFFF',
},
},
},
},
selectedTheme: 'light',
data: [manifest],
windows: [
windowSettings
],
miradorSharePlugin: {
dragAndDropInfoLink: 'https://iiif.io',
embedOption: {
enabled: true,
embedUrlReplacePattern: [
/.*\.edu\/(\w+)\/iiif\/manifest/,
manifest
],
syncIframeDimensions: {
height: {param: 'maxheight'},
},
},
shareLink: {
enabled: true,
manifestIdReplacePattern: [
/\/iiif\/manifest/,
'',
],
},
},
miradorDownloadPlugin: {
restrictDownloadOnSizeDefinition: false
},
window: {
allowClose: false,
// sideBarOpenByDefault: false,
allowFullscreen: true,
allowMaximize: false,
defaultView: defaultView,
sideBarOpen: notMobile,
allowTopMenuButton: true,
defaultSidebarPanelWidth: 230,
switchCanvasOnSearch: true,
views: [
{ key: 'single', behaviors: ['individuals'] },
{ key: 'book', behaviors: ['paged'] },
{ key: 'scroll', behaviors: ['continuous'] },
{ key: 'gallery' },
],
panels: {
info: true,
attribution: false,
canvas: true,
search: searchOption,
layers: false,
},
sideBarPanel: sidbarPanel
},
workspace: {
allowNewWindows: false,
showZoomControls: true,
type: 'mosaic'
},
workspaceControlPanel: {
enabled: false
}
},
[
miradorShareDialogPlugin,
miradorSharePlugin,
miradorDownloadDialog,
miradorDownloadPlugin
]
)
)(manifest);

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mirador</title>
</head>
<body>
<div id="mirador"></div>
</body>
</html>

View File

@@ -0,0 +1,28 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'production',
entry: {
mirador: './src/mirador-viewer/index.js'
},
output: {
path: path.resolve(__dirname, '..' , 'dist/iiif/mirador'),
filename: '[name].js'
},
module: {
rules: [
{
test: /\.html$/i,
loader: 'html-loader',
},
],
},
devServer: {
contentBase: '../dist/iiif/mirador',
},
plugins: [new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/mirador-viewer/mirador.html'
})]
};

947
yarn.lock

File diff suppressed because it is too large Load Diff