mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
BIN
lib/core/viewer/assets/fake-test-file.pdf
Normal file
BIN
lib/core/viewer/assets/fake-test-file.pdf
Normal file
Binary file not shown.
1
lib/core/viewer/assets/fake-test-file.txt
Normal file
1
lib/core/viewer/assets/fake-test-file.txt
Normal file
@@ -0,0 +1 @@
|
||||
Text example
|
3
lib/core/viewer/components/imgViewer.component.html
Normal file
3
lib/core/viewer/components/imgViewer.component.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="image-container">
|
||||
<img id="viewer-image" [src]="urlFile" [alt]="nameFile" />
|
||||
</div>
|
14
lib/core/viewer/components/imgViewer.component.scss
Normal file
14
lib/core/viewer/components/imgViewer.component.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
.adf-img-viewer {
|
||||
.image-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
height: 90vh;
|
||||
img {
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
}
|
93
lib/core/viewer/components/imgViewer.component.spec.ts
Normal file
93
lib/core/viewer/components/imgViewer.component.spec.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DebugElement, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {
|
||||
AlfrescoApiService,
|
||||
AuthenticationService,
|
||||
ContentService,
|
||||
SettingsService
|
||||
} from '../../services';
|
||||
import { ImgViewerComponent } from './imgViewer.component';
|
||||
|
||||
describe('Test ng2-alfresco-viewer Img viewer component ', () => {
|
||||
|
||||
let component: ImgViewerComponent;
|
||||
let service: ContentService;
|
||||
let fixture: ComponentFixture<ImgViewerComponent>;
|
||||
let debug: DebugElement;
|
||||
let element: HTMLElement;
|
||||
|
||||
function createFakeBlob() {
|
||||
let data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==');
|
||||
return new Blob([data], {type: 'image/png'});
|
||||
}
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
|
||||
declarations: [ImgViewerComponent],
|
||||
providers: [
|
||||
SettingsService,
|
||||
AuthenticationService,
|
||||
AlfrescoApiService
|
||||
]
|
||||
}).compileComponents();
|
||||
service = TestBed.get(ContentService);
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ImgViewerComponent);
|
||||
|
||||
debug = fixture.debugElement;
|
||||
element = fixture.nativeElement;
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('If no url or blob are passed should thrown an error', () => {
|
||||
let change = new SimpleChange(null, null, true);
|
||||
expect(() => {
|
||||
component.ngOnChanges({ 'blobFile': change });
|
||||
}).toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||
});
|
||||
|
||||
it('If url is passed should not thrown an error', () => {
|
||||
component.urlFile = 'fake-url';
|
||||
expect(() => {
|
||||
component.ngOnChanges(null);
|
||||
}).not.toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||
});
|
||||
|
||||
it('The file Name should be present in the alt attribute', () => {
|
||||
component.nameFile = 'fake-name';
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#viewer-image').getAttribute('alt')).toEqual('fake-name');
|
||||
});
|
||||
|
||||
it('If blob is passed should not thrown an error', () => {
|
||||
let blob = createFakeBlob();
|
||||
|
||||
spyOn(service, 'createTrustedUrl').and.returnValue('fake-blob-url');
|
||||
let change = new SimpleChange(null, blob, true);
|
||||
expect(() => {
|
||||
component.ngOnChanges({ 'blobFile': change });
|
||||
}).not.toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||
expect(component.urlFile).toEqual('fake-blob-url');
|
||||
});
|
||||
});
|
51
lib/core/viewer/components/imgViewer.component.ts
Normal file
51
lib/core/viewer/components/imgViewer.component.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
||||
import { ContentService } from '../../services';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-img-viewer',
|
||||
templateUrl: './imgViewer.component.html',
|
||||
styleUrls: ['./imgViewer.component.scss'],
|
||||
host: { 'class': 'adf-img-viewer' },
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class ImgViewerComponent implements OnChanges {
|
||||
|
||||
@Input()
|
||||
urlFile: string;
|
||||
|
||||
@Input()
|
||||
blobFile: Blob;
|
||||
|
||||
@Input()
|
||||
nameFile: string;
|
||||
|
||||
constructor(private contentService: ContentService) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
let blobFile = changes['blobFile'];
|
||||
if (blobFile && blobFile.currentValue) {
|
||||
this.urlFile = this.contentService.createTrustedUrl(this.blobFile);
|
||||
return;
|
||||
}
|
||||
if (!this.urlFile && !this.blobFile) {
|
||||
throw new Error('Attribute urlFile or blobFile is required');
|
||||
}
|
||||
}
|
||||
}
|
3
lib/core/viewer/components/mediaPlayer.component.html
Normal file
3
lib/core/viewer/components/mediaPlayer.component.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<video controls>
|
||||
<source [src]="urlFile" [type]="mimeType" />
|
||||
</video>
|
7
lib/core/viewer/components/mediaPlayer.component.scss
Normal file
7
lib/core/viewer/components/mediaPlayer.component.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
.adf-media-player {
|
||||
video {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
max-height: 90vh;
|
||||
}
|
||||
}
|
95
lib/core/viewer/components/mediaPlayer.component.spec.ts
Normal file
95
lib/core/viewer/components/mediaPlayer.component.spec.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DebugElement, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MediaPlayerComponent } from './mediaPlayer.component';
|
||||
|
||||
import {
|
||||
AlfrescoApiService,
|
||||
AuthenticationService,
|
||||
ContentService,
|
||||
SettingsService
|
||||
} from '../../services';
|
||||
|
||||
describe('Test ng2-alfresco-viewer Media player component ', () => {
|
||||
|
||||
let component: MediaPlayerComponent;
|
||||
let service: ContentService;
|
||||
let fixture: ComponentFixture<MediaPlayerComponent>;
|
||||
let debug: DebugElement;
|
||||
let element: HTMLElement;
|
||||
|
||||
function createFakeBlob() {
|
||||
let data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==');
|
||||
return new Blob([data], {type: 'image/png'});
|
||||
}
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
|
||||
declarations: [MediaPlayerComponent],
|
||||
providers: [
|
||||
SettingsService,
|
||||
AuthenticationService,
|
||||
AlfrescoApiService
|
||||
]
|
||||
}).compileComponents();
|
||||
service = TestBed.get(ContentService);
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MediaPlayerComponent);
|
||||
|
||||
debug = fixture.debugElement;
|
||||
element = fixture.nativeElement;
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should thrown an error If no url or no blob are passed', () => {
|
||||
let change = new SimpleChange(null, null, true);
|
||||
expect(() => {
|
||||
component.ngOnChanges({ 'blobFile': change });
|
||||
}).toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||
});
|
||||
|
||||
it('should not thrown an error If url is passed', () => {
|
||||
component.urlFile = 'fake-url';
|
||||
expect(() => {
|
||||
component.ngOnChanges(null);
|
||||
}).not.toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||
});
|
||||
|
||||
it('should not thrown an error If url is passed', () => {
|
||||
component.urlFile = 'fake-url';
|
||||
expect(() => {
|
||||
component.ngOnChanges(null);
|
||||
}).not.toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||
});
|
||||
|
||||
it('should not thrown an error If blob is passed', () => {
|
||||
let blob = createFakeBlob();
|
||||
|
||||
spyOn(service, 'createTrustedUrl').and.returnValue('fake-blob-url');
|
||||
let change = new SimpleChange(null, blob, true);
|
||||
expect(() => {
|
||||
component.ngOnChanges({ 'blobFile': change });
|
||||
}).not.toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||
expect(component.urlFile).toEqual('fake-blob-url');
|
||||
});
|
||||
});
|
55
lib/core/viewer/components/mediaPlayer.component.ts
Normal file
55
lib/core/viewer/components/mediaPlayer.component.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
||||
import { ContentService } from '../../services';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-media-player',
|
||||
templateUrl: './mediaPlayer.component.html',
|
||||
styleUrls: ['./mediaPlayer.component.scss'],
|
||||
host: { 'class': 'adf-media-player' },
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class MediaPlayerComponent implements OnChanges {
|
||||
|
||||
@Input()
|
||||
urlFile: string;
|
||||
|
||||
@Input()
|
||||
blobFile: Blob;
|
||||
|
||||
@Input()
|
||||
mimeType: string;
|
||||
|
||||
@Input()
|
||||
nameFile: string;
|
||||
|
||||
constructor(private contentService: ContentService ) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
let blobFile = changes['blobFile'];
|
||||
if (blobFile && blobFile.currentValue) {
|
||||
this.urlFile = this.contentService.createTrustedUrl(this.blobFile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.urlFile && !this.blobFile) {
|
||||
throw new Error('Attribute urlFile or blobFile is required');
|
||||
}
|
||||
}
|
||||
}
|
75
lib/core/viewer/components/pdfViewer.component.html
Normal file
75
lib/core/viewer/components/pdfViewer.component.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<div id="viewer-pdf-container" class="viewer-pdf-container" (window:resize)="onResize()">
|
||||
<div id="viewer-viewerPdf" class="pdfViewer">
|
||||
<div id="loader-container" class="loader-container">
|
||||
<div class="loader-item">
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</div >
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="adf-pdf-viewer__toolbar" *ngIf="showToolbar">
|
||||
<adf-toolbar>
|
||||
|
||||
<ng-container *ngIf="allowThumbnails">
|
||||
<button mat-icon-button>
|
||||
<mat-icon>dashboard</mat-icon>
|
||||
</button>
|
||||
<adf-toolbar-divider></adf-toolbar-divider>
|
||||
</ng-container>
|
||||
|
||||
<button
|
||||
id="viewer-previous-page-button"
|
||||
attr.aria-label="{{ 'ADF_VIEWER.ARIA.PREVIOUS_PAGE' | translate }}"
|
||||
mat-icon-button
|
||||
(click)="previousPage()">
|
||||
<mat-icon>keyboard_arrow_up</mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="viewer-next-page-button"
|
||||
attr.aria-label="{{ 'ADF_VIEWER.ARIA.NEXT_PAGE' | translate }}"
|
||||
mat-icon-button
|
||||
(click)="nextPage()">
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
</button>
|
||||
|
||||
<div>
|
||||
{{ 'ADF_VIEWER.PAGE_LABEL.SHOWING' | translate }}
|
||||
<input #page
|
||||
class="adf-pdf-viewer__toolbar-page-selector"
|
||||
type="text"
|
||||
pattern="-?[0-9]*(\.[0-9]+)?"
|
||||
value="{{ displayPage }}"
|
||||
(keyup.enter)="inputPage(page.value)">
|
||||
{{ 'ADF_VIEWER.PAGE_LABEL.OF' | translate }} {{ totalPages }}
|
||||
</div>
|
||||
|
||||
<adf-toolbar-divider></adf-toolbar-divider>
|
||||
|
||||
<button
|
||||
id="viewer-zoom-in-button"
|
||||
attr.aria-label="{{ 'ADF_VIEWER.ARIA.ZOOM_IN' | translate }}"
|
||||
mat-icon-button
|
||||
(click)="zoomIn()">
|
||||
<mat-icon>zoom_in</mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="viewer-zoom-out-button"
|
||||
attr.aria-label="{{ 'ADF_VIEWER.ARIA.ZOOM_OUT' | translate }}"
|
||||
mat-icon-button
|
||||
(click)="zoomOut()">
|
||||
<mat-icon>zoom_out</mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="viewer-scale-page-button"
|
||||
attr.aria-label="{{ 'ADF_VIEWER.ARIA.FIT_PAGE' | translate }}"
|
||||
mat-icon-button
|
||||
(click)="pageFit()">
|
||||
<mat-icon>zoom_out_map</mat-icon>
|
||||
</button>
|
||||
|
||||
</adf-toolbar>
|
||||
</div>
|
38
lib/core/viewer/components/pdfViewer.component.scss
Normal file
38
lib/core/viewer/components/pdfViewer.component.scss
Normal file
@@ -0,0 +1,38 @@
|
||||
.adf-pdf-viewer {
|
||||
.loader-container {
|
||||
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
|
||||
display: -webkit-flex; /* NEW - Chrome */
|
||||
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
-webkit-box-flex-direction: row;
|
||||
-moz-box-flex-direction: row;
|
||||
-webkit-flex-direction: row;
|
||||
flex-direction: row;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.loader-item {
|
||||
margin: auto;
|
||||
max-height:100px;
|
||||
max-width:300px;
|
||||
}
|
||||
|
||||
&__toolbar {
|
||||
position: absolute;
|
||||
bottom: 60px;
|
||||
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
&-page-selector {
|
||||
font-size: 16px;
|
||||
padding: 4px 0;
|
||||
text-align: right;
|
||||
width: 33px;
|
||||
margin-right: 4px;
|
||||
height: 20px;
|
||||
outline-width: 1px;
|
||||
outline-color: gray;
|
||||
}
|
||||
}
|
||||
}
|
387
lib/core/viewer/components/pdfViewer.component.spec.ts
Normal file
387
lib/core/viewer/components/pdfViewer.component.spec.ts
Normal file
@@ -0,0 +1,387 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {
|
||||
AlfrescoApiService,
|
||||
AuthenticationService,
|
||||
SettingsService
|
||||
} from '../../services';
|
||||
import { MaterialModule } from '../../material.module';
|
||||
import { ToolbarModule } from '../../toolbar';
|
||||
import { EventMock } from '../../mock/event.mock';
|
||||
import { RenderingQueueServices } from '../services/rendering-queue.services';
|
||||
import { PdfViewerComponent } from './pdfViewer.component';
|
||||
|
||||
declare var require: any;
|
||||
|
||||
describe('Test ng2-alfresco-viewer PdfViewer component', () => {
|
||||
|
||||
let component: PdfViewerComponent;
|
||||
let fixture: ComponentFixture<PdfViewerComponent>;
|
||||
let debug: DebugElement;
|
||||
let element: HTMLElement;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ToolbarModule,
|
||||
MaterialModule
|
||||
],
|
||||
declarations: [PdfViewerComponent],
|
||||
providers: [
|
||||
SettingsService,
|
||||
AuthenticationService,
|
||||
AlfrescoApiService,
|
||||
RenderingQueueServices
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
function createFakeBlob(): Blob {
|
||||
let pdfData = atob(
|
||||
'JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwog' +
|
||||
'IC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAv' +
|
||||
'TWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0K' +
|
||||
'Pj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAg' +
|
||||
'L1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+' +
|
||||
'PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9u' +
|
||||
'dAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2Jq' +
|
||||
'Cgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJU' +
|
||||
'CjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVu' +
|
||||
'ZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4g' +
|
||||
'CjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAw' +
|
||||
'MDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9v' +
|
||||
'dCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G');
|
||||
return new Blob([pdfData], {type: 'application/pdf'});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PdfViewerComponent);
|
||||
|
||||
debug = fixture.debugElement;
|
||||
element = fixture.nativeElement;
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.showToolbar = true;
|
||||
|
||||
});
|
||||
|
||||
describe('View with url file', () => {
|
||||
beforeEach(() => {
|
||||
component.urlFile = require('../assets/fake-test-file.pdf');
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should thrown an error If urlfile is not present', () => {
|
||||
component.urlFile = undefined;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(() => {
|
||||
component.ngOnChanges(null);
|
||||
}).toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||
});
|
||||
|
||||
it('should Canvas be present', () => {
|
||||
expect(element.querySelector('.pdfViewer')).not.toBeNull();
|
||||
expect(element.querySelector('.viewer-pdf-container')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Loader be present', () => {
|
||||
expect(element.querySelector('.loader-container')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Next an Previous Buttons be present', () => {
|
||||
expect(element.querySelector('#viewer-previous-page-button')).not.toBeNull();
|
||||
expect(element.querySelector('#viewer-next-page-button')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Input Page elements be present', () => {
|
||||
expect(element.querySelector('.viewer-pagenumber-input')).toBeDefined();
|
||||
expect(element.querySelector('.viewer-total-pages')).toBeDefined();
|
||||
|
||||
expect(element.querySelector('#viewer-previous-page-button')).not.toBeNull();
|
||||
expect(element.querySelector('#viewer-next-page-button')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Toolbar be hide if showToolbar is false', () => {
|
||||
component.showToolbar = false;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('.viewer-toolbar-command')).toBeNull();
|
||||
expect(element.querySelector('.viewer-toolbar-pagination')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('View with blob file', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.urlFile = undefined;
|
||||
component.blobFile = createFakeBlob();
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should If blobFile is not present thrown an error ', () => {
|
||||
component.blobFile = undefined;
|
||||
expect(() => {
|
||||
component.ngOnChanges(null);
|
||||
}).toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||
});
|
||||
|
||||
it('should Canvas be present', () => {
|
||||
expect(element.querySelector('.pdfViewer')).not.toBeNull();
|
||||
expect(element.querySelector('.viewer-pdf-container')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Loader be present', () => {
|
||||
expect(element.querySelector('.loader-container')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Next an Previous Buttons be present', () => {
|
||||
expect(element.querySelector('#viewer-previous-page-button')).not.toBeNull();
|
||||
expect(element.querySelector('#viewer-next-page-button')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Input Page elements be present', () => {
|
||||
expect(element.querySelector('.viewer-pagenumber-input')).toBeDefined();
|
||||
expect(element.querySelector('.viewer-total-pages')).toBeDefined();
|
||||
|
||||
expect(element.querySelector('#viewer-previous-page-button')).not.toBeNull();
|
||||
expect(element.querySelector('#viewer-next-page-button')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Toolbar be hide if showToolbar is false', () => {
|
||||
component.showToolbar = false;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('.viewer-toolbar-command')).toBeNull();
|
||||
expect(element.querySelector('.viewer-toolbar-pagination')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('User interaction', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.urlFile = require('../assets/fake-test-file.pdf');
|
||||
fixture.detectChanges();
|
||||
component.inputPage('1');
|
||||
});
|
||||
|
||||
it('should Total number of pages be loaded', (done) => {
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
expect(component.totalPages).toEqual(6);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
it('should right arrow move to the next page', (done) => {
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.displayPage).toBe(1);
|
||||
EventMock.keyDown(39);
|
||||
fixture.detectChanges();
|
||||
expect(component.displayPage).toBe(2);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
it('should nextPage move to the next page', (done) => {
|
||||
let nextPageButton: any = element.querySelector('#viewer-next-page-button');
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
expect(component.displayPage).toBe(1);
|
||||
nextPageButton.click();
|
||||
fixture.detectChanges();
|
||||
expect(component.displayPage).toBe(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should left arrow move to the previous page', (done) => {
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
expect(component.displayPage).toBe(1);
|
||||
EventMock.keyDown(39);
|
||||
EventMock.keyDown(39);
|
||||
EventMock.keyDown(37);
|
||||
fixture.detectChanges();
|
||||
expect(component.displayPage).toBe(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should previous page move to the previous page', (done) => {
|
||||
let previousPageButton: any = element.querySelector('#viewer-previous-page-button');
|
||||
let nextPageButton: any = element.querySelector('#viewer-next-page-button');
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
expect(component.displayPage).toBe(1);
|
||||
nextPageButton.click();
|
||||
nextPageButton.click();
|
||||
previousPageButton.click();
|
||||
fixture.detectChanges();
|
||||
expect(component.displayPage).toBe(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should previous page not move to the previous page if is page 1', (done) => {
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
expect(component.displayPage).toBe(1);
|
||||
component.previousPage();
|
||||
fixture.detectChanges();
|
||||
expect(component.displayPage).toBe(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should Input page move to the inserted page', (done) => {
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
expect(component.displayPage).toBe(1);
|
||||
component.inputPage('2');
|
||||
fixture.detectChanges();
|
||||
expect(component.displayPage).toBe(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Zoom', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.currentScale = 1;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should zoom in increment the scale value', (done) => {
|
||||
let zoomInButton: any = element.querySelector('#viewer-zoom-in-button');
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
let zoomBefore = component.currentScale;
|
||||
zoomInButton.click();
|
||||
expect(component.currentScaleMode).toBe('auto');
|
||||
let currentZoom = component.currentScale;
|
||||
expect(zoomBefore < currentZoom).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should zoom out decrement the scale value', (done) => {
|
||||
let zoomOutButton: any = element.querySelector('#viewer-zoom-out-button');
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
let zoomBefore = component.currentScale;
|
||||
zoomOutButton.click();
|
||||
expect(component.currentScaleMode).toBe('auto');
|
||||
let currentZoom = component.currentScale;
|
||||
expect(zoomBefore > currentZoom).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fit-in button toggle page-fit and auto scale mode', (done) => {
|
||||
let fitPage: any = element.querySelector('#viewer-scale-page-button');
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
expect(component.currentScaleMode).toBe('auto');
|
||||
fitPage.click();
|
||||
expect(component.currentScaleMode).toBe('page-fit');
|
||||
fitPage.click();
|
||||
expect(component.currentScaleMode).toBe('auto');
|
||||
done();
|
||||
});
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Resize interaction', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.urlFile = require('../assets/fake-test-file.pdf');
|
||||
fixture.detectChanges();
|
||||
component.inputPage('1');
|
||||
});
|
||||
it('should resize event trigger setScaleUpdatePages', (done) => {
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
spyOn(component, 'onResize');
|
||||
EventMock.resizeMobileView();
|
||||
expect(component.onResize).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('scroll interaction', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.urlFile = require('../assets/fake-test-file.pdf');
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should scroll page return the current page', (done) => {
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
expect(component.displayPage).toBe(1);
|
||||
component.inputPage('2');
|
||||
fixture.detectChanges();
|
||||
expect(component.displayPage).toBe(2);
|
||||
let documentContainer = element.querySelector('.viewer-pdf-container');
|
||||
documentContainer.scrollTop = 100000;
|
||||
component.watchScroll(documentContainer);
|
||||
fixture.detectChanges();
|
||||
expect(component.displayPage).toBe(6);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
413
lib/core/viewer/components/pdfViewer.component.ts
Normal file
413
lib/core/viewer/components/pdfViewer.component.ts
Normal file
@@ -0,0 +1,413 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, HostListener, Input, OnChanges, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||
import { LogService } from '../../services';
|
||||
import { RenderingQueueServices } from '../services/rendering-queue.services';
|
||||
|
||||
declare let PDFJS: any;
|
||||
|
||||
@Component({
|
||||
selector: 'adf-pdf-viewer',
|
||||
templateUrl: './pdfViewer.component.html',
|
||||
styleUrls: [
|
||||
'./pdfViewer.component.scss',
|
||||
'./pdfViewerHost.component.scss'
|
||||
],
|
||||
providers: [ RenderingQueueServices ],
|
||||
host: { 'class': 'adf-pdf-viewer' },
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
|
||||
@Input()
|
||||
urlFile: string;
|
||||
|
||||
@Input()
|
||||
blobFile: Blob;
|
||||
|
||||
@Input()
|
||||
nameFile: string;
|
||||
|
||||
@Input()
|
||||
showToolbar: boolean = true;
|
||||
|
||||
@Input()
|
||||
allowThumbnails = false;
|
||||
|
||||
currentPdfDocument: any;
|
||||
page: number;
|
||||
displayPage: number;
|
||||
totalPages: number;
|
||||
loadingPercent: number;
|
||||
pdfViewer: any;
|
||||
currentScaleMode: string = 'auto';
|
||||
currentScale: number;
|
||||
|
||||
MAX_AUTO_SCALE: number = 1.25;
|
||||
DEFAULT_SCALE_DELTA: number = 1.1;
|
||||
MIN_SCALE: number = 0.25;
|
||||
MAX_SCALE: number = 10.0;
|
||||
|
||||
constructor(private renderingQueueServices: RenderingQueueServices,
|
||||
private logService: LogService) {
|
||||
// needed to preserve "this" context when setting as a global document event listener
|
||||
this.onDocumentScroll = this.onDocumentScroll.bind(this);
|
||||
}
|
||||
|
||||
ngOnChanges(changes) {
|
||||
if (!this.urlFile && !this.blobFile) {
|
||||
throw new Error('Attribute urlFile or blobFile is required');
|
||||
}
|
||||
|
||||
if (this.urlFile) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.executePdf(this.urlFile, resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
this.executePdf(reader.result, resolve, reject);
|
||||
};
|
||||
reader.readAsArrayBuffer(this.blobFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
executePdf(src, resolve, reject) {
|
||||
let loadingTask = this.getPDFJS().getDocument(src);
|
||||
|
||||
loadingTask.onProgress = (progressData) => {
|
||||
let level = progressData.loaded / progressData.total;
|
||||
this.loadingPercent = Math.round(level * 100);
|
||||
};
|
||||
|
||||
loadingTask.then((pdfDocument) => {
|
||||
this.currentPdfDocument = pdfDocument;
|
||||
this.totalPages = pdfDocument.numPages;
|
||||
this.page = 1;
|
||||
this.displayPage = 1;
|
||||
this.initPDFViewer(this.currentPdfDocument);
|
||||
|
||||
this.currentPdfDocument.getPage(1).then(() => {
|
||||
this.scalePage('auto');
|
||||
resolve();
|
||||
}, (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
}, (error) => {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* return the PDFJS global object (exist to facilitate the mock of PDFJS in the test)
|
||||
*
|
||||
* @returns {PDFJS}
|
||||
*/
|
||||
getPDFJS() {
|
||||
return PDFJS;
|
||||
}
|
||||
|
||||
initPDFViewer(pdfDocument: any) {
|
||||
PDFJS.verbosity = 1;
|
||||
PDFJS.disableWorker = false;
|
||||
|
||||
let documentContainer = document.getElementById('viewer-pdf-container');
|
||||
let viewer: any = document.getElementById('viewer-viewerPdf');
|
||||
|
||||
window.document.addEventListener('scroll', this.onDocumentScroll, true);
|
||||
|
||||
this.pdfViewer = new PDFJS.PDFViewer({
|
||||
container: documentContainer,
|
||||
viewer: viewer,
|
||||
renderingQueue: this.renderingQueueServices
|
||||
});
|
||||
|
||||
this.renderingQueueServices.setViewer(this.pdfViewer);
|
||||
|
||||
this.pdfViewer.setDocument(pdfDocument);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
window.document.removeEventListener('scroll', this.onDocumentScroll, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to scale the page current support implementation
|
||||
*
|
||||
* @param {string} scaleMode - new scale mode
|
||||
*/
|
||||
scalePage(scaleMode) {
|
||||
this.currentScaleMode = scaleMode;
|
||||
|
||||
if (this.pdfViewer) {
|
||||
|
||||
let viewerContainer = document.getElementById('viewer-main-container');
|
||||
let documentContainer = document.getElementById('viewer-pdf-container');
|
||||
|
||||
let widthContainer;
|
||||
let heigthContainer;
|
||||
|
||||
if (viewerContainer && viewerContainer.clientWidth <= documentContainer.clientWidth) {
|
||||
widthContainer = viewerContainer.clientWidth;
|
||||
heigthContainer = viewerContainer.clientHeight;
|
||||
} else {
|
||||
widthContainer = documentContainer.clientWidth;
|
||||
heigthContainer = documentContainer.clientHeight;
|
||||
}
|
||||
|
||||
let currentPage = this.pdfViewer._pages[this.pdfViewer._currentPageNumber - 1];
|
||||
|
||||
let padding = 20;
|
||||
let pageWidthScale = (widthContainer - padding) / currentPage.width * currentPage.scale;
|
||||
let pageHeightScale = (heigthContainer - padding) / currentPage.width * currentPage.scale;
|
||||
|
||||
let scale;
|
||||
|
||||
switch (this.currentScaleMode) {
|
||||
case 'page-actual':
|
||||
scale = 1;
|
||||
break;
|
||||
case 'page-width':
|
||||
scale = pageWidthScale;
|
||||
break;
|
||||
case 'page-height':
|
||||
scale = pageHeightScale;
|
||||
break;
|
||||
case 'page-fit':
|
||||
scale = Math.min(pageWidthScale, pageHeightScale);
|
||||
break;
|
||||
case 'auto':
|
||||
let horizontalScale;
|
||||
if (this.isLandscape) {
|
||||
horizontalScale = Math.min(pageHeightScale, pageWidthScale);
|
||||
} else {
|
||||
horizontalScale = pageWidthScale;
|
||||
}
|
||||
scale = Math.min(this.MAX_AUTO_SCALE, horizontalScale);
|
||||
|
||||
break;
|
||||
default:
|
||||
this.logService.error('pdfViewSetScale: \'' + scaleMode + '\' is an unknown zoom value.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setScaleUpdatePages(scale);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all the pages with the newScale scale
|
||||
*
|
||||
* @param {number} newScale - new scale page
|
||||
*/
|
||||
setScaleUpdatePages(newScale: number) {
|
||||
if (!this.isSameScale(this.currentScale, newScale)) {
|
||||
this.currentScale = newScale;
|
||||
|
||||
this.pdfViewer._pages.forEach(function (currentPage) {
|
||||
currentPage.update(newScale);
|
||||
});
|
||||
|
||||
this.pdfViewer.update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* method to check if the request scale of the page is the same for avoid unuseful re-rendering
|
||||
*
|
||||
* @param {number} oldScale - old scale page
|
||||
* @param {number} newScale - new scale page
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSameScale(oldScale: number, newScale: number) {
|
||||
return (newScale === oldScale);
|
||||
}
|
||||
|
||||
/**
|
||||
* method to check if is a land scape view
|
||||
*
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLandscape(width: number, height: number) {
|
||||
return (width > height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method triggered when the page is resized
|
||||
*/
|
||||
onResize() {
|
||||
this.scalePage(this.currentScaleMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle the fit page pdf
|
||||
*/
|
||||
pageFit() {
|
||||
if (this.currentScaleMode !== 'page-fit') {
|
||||
this.scalePage('page-fit');
|
||||
} else {
|
||||
this.scalePage('auto');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* zoom in page pdf
|
||||
*
|
||||
* @param {number} ticks
|
||||
*/
|
||||
zoomIn(ticks: number) {
|
||||
let newScale: any = this.currentScale;
|
||||
do {
|
||||
newScale = (newScale * this.DEFAULT_SCALE_DELTA).toFixed(2);
|
||||
newScale = Math.ceil(newScale * 10) / 10;
|
||||
newScale = Math.min(this.MAX_SCALE, newScale);
|
||||
} while (--ticks > 0 && newScale < this.MAX_SCALE);
|
||||
this.currentScaleMode = 'auto';
|
||||
this.setScaleUpdatePages(newScale);
|
||||
}
|
||||
|
||||
/**
|
||||
* zoom out page pdf
|
||||
*
|
||||
* @param {number} ticks
|
||||
*/
|
||||
zoomOut(ticks: number) {
|
||||
let newScale: any = this.currentScale;
|
||||
do {
|
||||
newScale = (newScale / this.DEFAULT_SCALE_DELTA).toFixed(2);
|
||||
newScale = Math.floor(newScale * 10) / 10;
|
||||
newScale = Math.max(this.MIN_SCALE, newScale);
|
||||
} while (--ticks > 0 && newScale > this.MIN_SCALE);
|
||||
this.currentScaleMode = 'auto';
|
||||
this.setScaleUpdatePages(newScale);
|
||||
}
|
||||
|
||||
/**
|
||||
* load the previous page
|
||||
*/
|
||||
previousPage() {
|
||||
if (this.pdfViewer && this.page > 1) {
|
||||
this.page--;
|
||||
this.displayPage = this.page;
|
||||
|
||||
this.pdfViewer.currentPageNumber = this.page;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load the next page
|
||||
*/
|
||||
nextPage() {
|
||||
if (this.pdfViewer && this.page < this.totalPages) {
|
||||
this.page++;
|
||||
this.displayPage = this.page;
|
||||
|
||||
this.pdfViewer.currentPageNumber = this.page;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load the page in input
|
||||
*
|
||||
* @param {string} page - page to load
|
||||
*/
|
||||
inputPage(page: string) {
|
||||
let pageInput = parseInt(page, 10);
|
||||
|
||||
if (!isNaN(pageInput) && pageInput > 0 && pageInput <= this.totalPages) {
|
||||
this.page = pageInput;
|
||||
this.displayPage = this.page;
|
||||
this.pdfViewer.currentPageNumber = this.page;
|
||||
} else {
|
||||
this.displayPage = this.page;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Litener Scroll Event
|
||||
*
|
||||
* @param {any} target
|
||||
*/
|
||||
watchScroll(target) {
|
||||
let outputPage = this.getVisibleElement(target);
|
||||
|
||||
if (outputPage) {
|
||||
this.page = outputPage.id;
|
||||
this.displayPage = this.page;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* find out what elements are visible within a scroll pane
|
||||
*
|
||||
* @param {any} target
|
||||
*
|
||||
* @returns {Object} page
|
||||
*/
|
||||
getVisibleElement(target) {
|
||||
return this.pdfViewer._pages.find((page) => {
|
||||
return this.isOnScreen(page, target);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a page is visible
|
||||
*
|
||||
* @param {any} page
|
||||
* @param {any} target
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isOnScreen(page: any, target: any) {
|
||||
let viewport: any = {};
|
||||
viewport.top = target.scrollTop;
|
||||
viewport.bottom = viewport.top + target.scrollHeight;
|
||||
let bounds: any = {};
|
||||
bounds.top = page.div.offsetTop;
|
||||
bounds.bottom = bounds.top + page.viewport.height;
|
||||
return ((bounds.top <= viewport.bottom) && (bounds.bottom >= viewport.top));
|
||||
}
|
||||
|
||||
/**
|
||||
* Litener Keyboard Event
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
handleKeyboardEvent(event: KeyboardEvent) {
|
||||
let key = event.keyCode;
|
||||
if (key === 39) { // right arrow
|
||||
this.nextPage();
|
||||
} else if (key === 37) {// left arrow
|
||||
this.previousPage();
|
||||
}
|
||||
}
|
||||
|
||||
onDocumentScroll(event: Event) {
|
||||
if (event && event.target) {
|
||||
this.watchScroll(event.target);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
251
lib/core/viewer/components/pdfViewerHost.component.scss
Normal file
251
lib/core/viewer/components/pdfViewerHost.component.scss
Normal file
@@ -0,0 +1,251 @@
|
||||
.adf-pdf-viewer {
|
||||
.textLayer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0.2;
|
||||
line-height: 1.0;
|
||||
border: 1px solid gray;
|
||||
|
||||
& > div {
|
||||
color: transparent;
|
||||
position: absolute;
|
||||
white-space: pre;
|
||||
cursor: text;
|
||||
-webkit-transform-origin: 0% 0%;
|
||||
-moz-transform-origin: 0% 0%;
|
||||
-o-transform-origin: 0% 0%;
|
||||
-ms-transform-origin: 0% 0%;
|
||||
transform-origin: 0% 0%;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
margin: -1px;
|
||||
padding: 1px;
|
||||
|
||||
background-color: rgb(180, 0, 170);
|
||||
border-radius: 4px;
|
||||
|
||||
&.begin {
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
}
|
||||
|
||||
&.end {
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
}
|
||||
|
||||
&.middle {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: rgb(0, 100, 0);
|
||||
}
|
||||
}
|
||||
|
||||
&::selection { background: rgb(0,0,255); }
|
||||
&::-moz-selection { background: rgb(0,0,255); }
|
||||
|
||||
.endOfContent {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 100%;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
z-index: -1;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-moz-user-select: none;
|
||||
|
||||
&.active {
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.annotationLayer {
|
||||
section {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.linkAnnotation {
|
||||
& > a {
|
||||
position: absolute;
|
||||
font-size: 1em;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.2;
|
||||
background: #ff0;
|
||||
box-shadow: 0px 2px 10px #ff0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.textAnnotation {
|
||||
img {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.popupWrapper {
|
||||
position: absolute;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
.popup {
|
||||
position: absolute;
|
||||
z-index: 200;
|
||||
max-width: 20em;
|
||||
background-color: #FFFF99;
|
||||
box-shadow: 0px 2px 5px #333;
|
||||
border-radius: 2px;
|
||||
padding: 0.6em;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
word-wrap: break-word;
|
||||
|
||||
h1 {
|
||||
font-size: 1em;
|
||||
border-bottom: 1px solid #000000;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
p {
|
||||
padding-top: 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.highlightAnnotation,
|
||||
.underlineAnnotation,
|
||||
.squigglyAnnotation,
|
||||
.strikeoutAnnotation,
|
||||
.fileAttachmentAnnotation {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.pdfViewer {
|
||||
canvasWrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page {
|
||||
direction: ltr;
|
||||
width: 816px;
|
||||
height: 1056px;
|
||||
margin: 1px auto -8px auto;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
border: 9px solid transparent;
|
||||
background-clip: content-box;
|
||||
background-color: white;
|
||||
|
||||
canvas {
|
||||
margin: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loadingIcon {
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.removePageBorders {
|
||||
.page {
|
||||
margin: 0px auto 10px auto;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.loadingIcon {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
left: 50% !important;
|
||||
top: 50% !important;
|
||||
|
||||
margin-top: -50px;
|
||||
margin-left: -50px;
|
||||
|
||||
font-size: 5px;
|
||||
text-indent: -9999em;
|
||||
border-top: 1.1em solid rgba(3,0,2, 0.2);
|
||||
border-right: 1.1em solid rgba(3,0,2, 0.2);
|
||||
border-bottom: 1.1em solid rgba(3,0,2, 0.2);
|
||||
border-left: 1.1em solid #030002;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-animation: load8 1.1s infinite linear;
|
||||
animation: load8 1.1s infinite linear;
|
||||
|
||||
border-radius: 50%;
|
||||
&:after {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hidden, [hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes load8 {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes load8 {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.viewer-pdf-container {
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
outline: none;
|
||||
}
|
||||
html[dir='ltr'] .viewer-pdf-container {
|
||||
box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05);
|
||||
}
|
||||
html[dir='rtl'] .viewer-pdf-container {
|
||||
box-shadow: inset -1px 0 0 hsla(0,0%,100%,.05);
|
||||
}
|
3
lib/core/viewer/components/txtViewer.component.html
Normal file
3
lib/core/viewer/components/txtViewer.component.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<pre class="adf-txt-viewer-content">
|
||||
{{content}}
|
||||
</pre>
|
7
lib/core/viewer/components/txtViewer.component.scss
Normal file
7
lib/core/viewer/components/txtViewer.component.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
.adf-txt-viewer {
|
||||
background-color: white;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
margin-bottom: 42px;
|
||||
}
|
86
lib/core/viewer/components/txtViewer.component.spec.ts
Normal file
86
lib/core/viewer/components/txtViewer.component.spec.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DebugElement, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {
|
||||
AlfrescoApiService,
|
||||
AuthenticationService,
|
||||
SettingsService
|
||||
} from '../../services';
|
||||
import { TxtViewerComponent } from './txtViewer.component';
|
||||
|
||||
declare var require: any;
|
||||
|
||||
describe('Test ng2-alfresco-viewer Text View component', () => {
|
||||
|
||||
let component: TxtViewerComponent;
|
||||
let fixture: ComponentFixture<TxtViewerComponent>;
|
||||
let debug: DebugElement;
|
||||
let element: HTMLElement;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TxtViewerComponent],
|
||||
providers: [
|
||||
SettingsService,
|
||||
AuthenticationService,
|
||||
AlfrescoApiService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TxtViewerComponent);
|
||||
|
||||
debug = fixture.debugElement;
|
||||
element = fixture.nativeElement;
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
describe('View', () => {
|
||||
|
||||
it('Should text container be present with urlfile', (done) => {
|
||||
fixture.detectChanges();
|
||||
let urlFile = require('../assets/fake-test-file.txt');
|
||||
let change = new SimpleChange(null, urlFile, true);
|
||||
|
||||
component.ngOnChanges({ 'urlFile': change }).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(element.querySelector('.adf-txt-viewer-content').textContent).toContain('example');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Should text container be present with Blob file', (done) => {
|
||||
let blobFile = new Blob(['text example'], {type: 'text/txt'});
|
||||
|
||||
let change = new SimpleChange(null, blobFile, true);
|
||||
|
||||
component.ngOnChanges({ 'blobFile': change }).then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(element.querySelector('.adf-txt-viewer-content').textContent).toContain('example');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
87
lib/core/viewer/components/txtViewer.component.ts
Normal file
87
lib/core/viewer/components/txtViewer.component.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, Input, OnChanges, ViewEncapsulation } from '@angular/core';
|
||||
import { SimpleChanges } from '@angular/core';
|
||||
import 'rxjs/add/operator/toPromise';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-txt-viewer',
|
||||
templateUrl: './txtViewer.component.html',
|
||||
styleUrls: ['./txtViewer.component.scss'],
|
||||
host: { 'class': 'adf-txt-viewer' },
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class TxtViewerComponent implements OnChanges {
|
||||
|
||||
@Input()
|
||||
urlFile: any;
|
||||
|
||||
@Input()
|
||||
blobFile: Blob;
|
||||
|
||||
content: string;
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): Promise<any> {
|
||||
|
||||
let blobFile = changes['blobFile'];
|
||||
if (blobFile && blobFile.currentValue) {
|
||||
return this.readBlob(blobFile.currentValue);
|
||||
}
|
||||
|
||||
let urlFile = changes['urlFile'];
|
||||
if (urlFile && urlFile.currentValue) {
|
||||
return this.getUrlContent(urlFile.currentValue);
|
||||
}
|
||||
|
||||
if (!this.urlFile && !this.blobFile) {
|
||||
throw new Error('Attribute urlFile or blobFile is required');
|
||||
}
|
||||
}
|
||||
|
||||
private getUrlContent(url: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.http.get(url, { responseType: 'text' }).subscribe(res => {
|
||||
this.content = res;
|
||||
resolve();
|
||||
}, (event) => {
|
||||
reject(event);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private readBlob(blob: Blob): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
|
||||
reader.onload = () => {
|
||||
this.content = reader.result;
|
||||
resolve();
|
||||
};
|
||||
|
||||
reader.onerror = (error: ErrorEvent) => {
|
||||
reject(error);
|
||||
};
|
||||
|
||||
reader.readAsText(blob);
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
<div class="adf-viewer__unknown-format-view">
|
||||
<div>
|
||||
<mat-icon class="icon">wifi_tethering</mat-icon>
|
||||
<div class="label">{{ 'ADF_VIEWER.UNKNOWN_FORMAT' | translate }}</div>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,8 @@
|
||||
.adf-viewer__unknown-format-view {
|
||||
height: 90vh;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer-unknown-format',
|
||||
templateUrl: 'unknown-format.component.html',
|
||||
styleUrls: ['unknown-format.component.scss']
|
||||
})
|
||||
export class UnknownFormatComponent {
|
||||
}
|
28
lib/core/viewer/components/viewer-more-actions.component.ts
Normal file
28
lib/core/viewer/components/viewer-more-actions.component.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer-more-actions',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'adf-viewer-more-actions' },
|
||||
template: `<ng-content></ng-content>`
|
||||
})
|
||||
export class ViewerMoreActionsComponent {
|
||||
}
|
28
lib/core/viewer/components/viewer-open-with.component.ts
Normal file
28
lib/core/viewer/components/viewer-open-with.component.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer-open-with',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'adf-viewer-open-with' },
|
||||
template: `<ng-content></ng-content>`
|
||||
})
|
||||
export class ViewerOpenWithComponent {
|
||||
}
|
28
lib/core/viewer/components/viewer-sidebar.component.ts
Normal file
28
lib/core/viewer/components/viewer-sidebar.component.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer-sidebar',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'adf-viewer-sidebar' },
|
||||
template: `<ng-content></ng-content>`
|
||||
})
|
||||
export class ViewerSidebarComponent {
|
||||
}
|
28
lib/core/viewer/components/viewer-toolbar.component.ts
Normal file
28
lib/core/viewer/components/viewer-toolbar.component.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer-toolbar',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'adf-viewer-toolbar' },
|
||||
template: `<ng-content></ng-content>`
|
||||
})
|
||||
export class ViewerToolbarComponent {
|
||||
}
|
161
lib/core/viewer/components/viewer.component.html
Normal file
161
lib/core/viewer/components/viewer.component.html
Normal file
@@ -0,0 +1,161 @@
|
||||
<div *ngIf="showViewer"
|
||||
class="adf-viewer-container"
|
||||
[class.adf-viewer-overlay-container]="overlayMode"
|
||||
[class.adf-viewer-inline-container]="!overlayMode">
|
||||
|
||||
<div class="adf-viewer-content">
|
||||
<ng-content select="adf-viewer-toolbar"></ng-content>
|
||||
<ng-container *ngIf="showToolbar && !toolbar">
|
||||
<adf-toolbar color="default" class="adf-viewer-toolbar">
|
||||
|
||||
<adf-toolbar-title>
|
||||
<button *ngIf="allowGoBack"
|
||||
class="adf-viewer-close-button"
|
||||
data-automation-id="toolbar-back"
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'ADF_VIEWER.ACTIONS.BACK' | translate }}"
|
||||
(click)="onBackButtonClick()">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
<img class="adf-viewer__mimeicon" [src]="mimeType | adfMimeTypeIcon">
|
||||
<span id="adf-viewer-display-name">{{ displayName }}</span>
|
||||
</adf-toolbar-title>
|
||||
|
||||
<ng-container *ngIf="mnuOpenWith">
|
||||
<button
|
||||
mat-button
|
||||
[matMenuTriggerFor]="mnuOpenWith"
|
||||
data-automation-id="toolbar-open-with">
|
||||
<span>{{ 'ADF_VIEWER.ACTIONS.OPEN_WITH' | translate }}</span>
|
||||
<mat-icon>arrow_drop_down</mat-icon>
|
||||
</button>
|
||||
<mat-menu #mnuOpenWith="matMenu" [overlapTrigger]="false">
|
||||
<ng-content select="adf-viewer-open-with"></ng-content>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
|
||||
<adf-toolbar-divider></adf-toolbar-divider>
|
||||
|
||||
<button
|
||||
*ngIf="allowDownload"
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'ADF_VIEWER.ACTIONS.DOWNLOAD' | translate }}"
|
||||
data-automation-id="toolbar-download"
|
||||
(click)="downloadContent()">
|
||||
<mat-icon>file_download</mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
*ngIf="allowPrint"
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'ADF_VIEWER.ACTIONS.PRINT' | translate }}"
|
||||
data-automation-id="toolbar-print"
|
||||
(click)="printContent()">
|
||||
<mat-icon>print</mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
*ngIf="allowShare"
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'ADF_VIEWER.ACTIONS.SHARE' | translate }}"
|
||||
data-automation-id="toolbar-share"
|
||||
(click)="shareContent()">
|
||||
<mat-icon>share</mat-icon>
|
||||
</button>
|
||||
|
||||
<ng-container *ngIf="mnuMoreActions">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="mnuMoreActions"
|
||||
matTooltip="{{ 'ADF_VIEWER.ACTIONS.MORE_ACTIONS' | translate }}"
|
||||
data-automation-id="toolbar-more-actions">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #mnuMoreActions="matMenu" [overlapTrigger]="false">
|
||||
<ng-content select="adf-viewer-more-actions"></ng-content>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="allowSidebar">
|
||||
<adf-toolbar-divider></adf-toolbar-divider>
|
||||
|
||||
<button
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'ADF_VIEWER.ACTIONS.INFO' | translate }}"
|
||||
data-automation-id="toolbar-sidebar"
|
||||
[color]="showSidebar ? 'accent' : 'default'"
|
||||
(click)="showSidebar = !showSidebar">
|
||||
<mat-icon>info_outline</mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
</adf-toolbar>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="isLoading">
|
||||
<div class="adf-viewer__loading-screen">
|
||||
<h2>{{ 'ADF_VIEWER.LOADING' | translate }}</h2>
|
||||
<div>
|
||||
<mat-spinner></mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div *ngIf="!isLoading" class="adf-viewer-layout">
|
||||
|
||||
<ng-container *ngIf="showSidebar && sidebarPosition === 'left'">
|
||||
<div class="adf-viewer__sidebar adf-viewer__sidebar-left">
|
||||
<ng-content select="adf-viewer-sidebar"></ng-content>
|
||||
<ng-container *ngIf="!sidebar">
|
||||
<!-- todo: default info drawer -->
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="adf-viewer-layout-content">
|
||||
<div class="adf-viewer-content-container" [ngSwitch]="viewerType">
|
||||
|
||||
<ng-container *ngSwitchCase="'pdf'">
|
||||
<adf-pdf-viewer [blobFile]="blobFile" [urlFile]="urlFileContent" [nameFile]="displayName"></adf-pdf-viewer>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'image'">
|
||||
<adf-img-viewer [urlFile]="urlFileContent" [nameFile]="displayName" [blobFile]="blobFile"></adf-img-viewer>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'media'">
|
||||
<adf-media-player [urlFile]="urlFileContent" [mimeType]="mimeType" [blobFile]="blobFile" [nameFile]="displayName"></adf-media-player>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'text'">
|
||||
<adf-txt-viewer [urlFile]="urlFileContent" [blobFile]="blobFile"></adf-txt-viewer>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'custom'">
|
||||
<span *ngFor="let extensionTemplate of extensionTemplates">
|
||||
<ng-template
|
||||
*ngIf="extensionTemplate.isVisible"
|
||||
[ngTemplateOutlet]="extensionTemplate.template"
|
||||
[ngTemplateOutletContext]="{ urlFileContent: urlFileContent, extension:extension }">
|
||||
</ng-template>
|
||||
</span>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchDefault>
|
||||
<adf-viewer-unknown-format></adf-viewer-unknown-format>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="showSidebar && sidebarPosition !== 'left'">
|
||||
<div class="adf-viewer__sidebar adf-viewer__sidebar-right">
|
||||
<ng-content select="adf-viewer-sidebar"></ng-content>
|
||||
<ng-container *ngIf="!sidebar">
|
||||
<!-- todo: default info drawer -->
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
95
lib/core/viewer/components/viewer.component.scss
Normal file
95
lib/core/viewer/components/viewer.component.scss
Normal file
@@ -0,0 +1,95 @@
|
||||
$adf-viewer-background-color: #f5f5f5;
|
||||
|
||||
@mixin full-screen() {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: $adf-viewer-background-color;
|
||||
}
|
||||
|
||||
.adf-viewer {
|
||||
|
||||
&__mimeicon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-container {
|
||||
.adf-viewer-layout-content {
|
||||
@include full-screen();
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
z-index: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
margin: 0 auto;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-viewer-layout {
|
||||
@include full-screen();
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.adf-viewer-content {
|
||||
@include full-screen();
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&-overlay-container {
|
||||
.adf-viewer-content {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
&-inline-container {
|
||||
@include full-screen();
|
||||
}
|
||||
|
||||
&-content-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&-unknown-content {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__loading-screen {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
height: 85vh;
|
||||
|
||||
.mat-spinner {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
width: 350px;
|
||||
display: block;
|
||||
padding: 8px 0;
|
||||
background-color: #fafafa;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.27);
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
}
|
699
lib/core/viewer/components/viewer.component.spec.ts
Normal file
699
lib/core/viewer/components/viewer.component.spec.ts
Normal file
@@ -0,0 +1,699 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Location } from '@angular/common';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
import { Component, DebugElement } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { AlfrescoApiService, RenditionsService } from '../../services';
|
||||
|
||||
import { MaterialModule } from './../../material.module';
|
||||
import { ToolbarModule } from '../../toolbar';
|
||||
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { EventMock } from '../../mock/event.mock';
|
||||
import { RenderingQueueServices } from '../services/rendering-queue.services';
|
||||
import { ImgViewerComponent } from './imgViewer.component';
|
||||
import { MediaPlayerComponent } from './mediaPlayer.component';
|
||||
import { PdfViewerComponent } from './pdfViewer.component';
|
||||
import { TxtViewerComponent } from './txtViewer.component';
|
||||
import { UnknownFormatComponent } from './unknown-format/unknown-format.component';
|
||||
import { ViewerMoreActionsComponent } from './viewer-more-actions.component';
|
||||
import { ViewerOpenWithComponent } from './viewer-open-with.component';
|
||||
import { ViewerSidebarComponent } from './viewer-sidebar.component';
|
||||
import { ViewerToolbarComponent } from './viewer-toolbar.component';
|
||||
import { ViewerComponent } from './viewer.component';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer-container-toolbar',
|
||||
template: `
|
||||
<adf-viewer>
|
||||
<adf-viewer-toolbar>
|
||||
<div class="custom-toolbar-element"></div>
|
||||
</adf-viewer-toolbar>
|
||||
</adf-viewer>
|
||||
`
|
||||
})
|
||||
class ViewerWithCustomToolbarComponent {}
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer-container-sidebar',
|
||||
template: `
|
||||
<adf-viewer>
|
||||
<adf-viewer-sidebar>
|
||||
<div class="custom-sidebar"></div>
|
||||
</adf-viewer-sidebar>
|
||||
</adf-viewer>
|
||||
`
|
||||
})
|
||||
class ViewerWithCustomSidebarComponent {}
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer-container-open-with',
|
||||
template: `
|
||||
<adf-viewer>
|
||||
<adf-viewer-open-with>
|
||||
<button mat-menu-item>
|
||||
<mat-icon>dialpad</mat-icon>
|
||||
<span>Option 1</span>
|
||||
</button>
|
||||
<button mat-menu-item disabled>
|
||||
<mat-icon>voicemail</mat-icon>
|
||||
<span>Option 2</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<mat-icon>notifications_off</mat-icon>
|
||||
<span>Option 3</span>
|
||||
</button>
|
||||
</adf-viewer-open-with>
|
||||
</adf-viewer>
|
||||
`
|
||||
})
|
||||
class ViewerWithCustomOpenWithComponent {}
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer-container-more-actions',
|
||||
template: `
|
||||
<adf-viewer>
|
||||
<adf-viewer-more-actions>
|
||||
<button mat-menu-item>
|
||||
<mat-icon>dialpad</mat-icon>
|
||||
<span>Action One</span>
|
||||
</button>
|
||||
<button mat-menu-item disabled>
|
||||
<mat-icon>voicemail</mat-icon>
|
||||
<span>Action Two</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<mat-icon>notifications_off</mat-icon>
|
||||
<span>Action Three</span>
|
||||
</button>
|
||||
</adf-viewer-more-actions>
|
||||
</adf-viewer>
|
||||
`
|
||||
})
|
||||
class ViewerWithCustomMoreActionsComponent {}
|
||||
|
||||
describe('ViewerComponent', () => {
|
||||
|
||||
let component: ViewerComponent;
|
||||
let fixture: ComponentFixture<ViewerComponent>;
|
||||
let debug: DebugElement;
|
||||
let alfrescoApiService: AlfrescoApiService;
|
||||
let element: HTMLElement;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ToolbarModule,
|
||||
MaterialModule
|
||||
],
|
||||
declarations: [
|
||||
ViewerComponent,
|
||||
PdfViewerComponent,
|
||||
TxtViewerComponent,
|
||||
MediaPlayerComponent,
|
||||
ImgViewerComponent,
|
||||
UnknownFormatComponent,
|
||||
ViewerSidebarComponent,
|
||||
ViewerToolbarComponent,
|
||||
ViewerOpenWithComponent,
|
||||
ViewerMoreActionsComponent,
|
||||
ViewerWithCustomToolbarComponent,
|
||||
ViewerWithCustomSidebarComponent,
|
||||
ViewerWithCustomOpenWithComponent,
|
||||
ViewerWithCustomMoreActionsComponent
|
||||
],
|
||||
providers: [
|
||||
{provide: RenditionsService, useValue: {
|
||||
getRendition: () => {
|
||||
return Observable.throw('throwed');
|
||||
}
|
||||
}},
|
||||
AlfrescoApiService,
|
||||
RenderingQueueServices,
|
||||
{ provide: Location, useClass: SpyLocation }
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ViewerComponent);
|
||||
|
||||
debug = fixture.debugElement;
|
||||
element = fixture.nativeElement;
|
||||
component = fixture.componentInstance;
|
||||
|
||||
jasmine.Ajax.install();
|
||||
|
||||
alfrescoApiService = TestBed.get(AlfrescoApiService);
|
||||
|
||||
component.showToolbar = true;
|
||||
component.urlFile = 'base/src/assets/fake-test-file.pdf';
|
||||
component.mimeType = 'application/pdf';
|
||||
component.ngOnChanges(null);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('should use custom toolbar', () => {
|
||||
let customFixture = TestBed.createComponent(ViewerWithCustomToolbarComponent);
|
||||
let customElement: HTMLElement = customFixture.nativeElement;
|
||||
|
||||
customFixture.detectChanges();
|
||||
expect(customElement.querySelector('.custom-toolbar-element')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should use custom info drawer', () => {
|
||||
let customFixture = TestBed.createComponent(ViewerWithCustomSidebarComponent);
|
||||
let customElement: HTMLElement = customFixture.nativeElement;
|
||||
|
||||
customFixture.detectChanges();
|
||||
expect(customElement.querySelector('.custom-info-drawer-element')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should use custom open with menu', () => {
|
||||
let customFixture = TestBed.createComponent(ViewerWithCustomOpenWithComponent);
|
||||
let customElement: HTMLElement = customFixture.nativeElement;
|
||||
|
||||
customFixture.detectChanges();
|
||||
expect(customElement.querySelector('.adf-viewer-container-open-with')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should use custom more actions menu', () => {
|
||||
let customFixture = TestBed.createComponent(ViewerWithCustomMoreActionsComponent);
|
||||
let customElement: HTMLElement = customFixture.nativeElement;
|
||||
|
||||
customFixture.detectChanges();
|
||||
expect(customElement.querySelector('.adf-viewer-container-more-actions')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should display left sidebar', () => {
|
||||
component.sidebarPosition = 'left';
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-viewer__sidebar-left')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should display right sidebar', () => {
|
||||
component.sidebarPosition = 'right';
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-viewer__sidebar-right')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should display right sidebar as fallback', () => {
|
||||
component.sidebarPosition = 'unknown-value';
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-viewer__sidebar-right')).toBeDefined();
|
||||
});
|
||||
|
||||
describe('Toolbar', () => {
|
||||
|
||||
it('should render default download button', () => {
|
||||
component.allowDownload = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('[data-automation-id="toolbar-download"]')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not render default download button', () => {
|
||||
component.allowDownload = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('[data-automation-id="toolbar-download"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('should invoke download action with the toolbar button', () => {
|
||||
component.allowDownload = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
spyOn(component, 'downloadContent').and.stub();
|
||||
|
||||
const button: HTMLButtonElement = element.querySelector('[data-automation-id="toolbar-download"]') as HTMLButtonElement;
|
||||
button.click();
|
||||
|
||||
expect(component.downloadContent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should raise download event with the toolbar button', (done) => {
|
||||
component.allowDownload = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
component.download.subscribe(e => {
|
||||
e.preventDefault();
|
||||
done();
|
||||
});
|
||||
|
||||
const button: HTMLButtonElement = element.querySelector('[data-automation-id="toolbar-download"]') as HTMLButtonElement;
|
||||
button.click();
|
||||
});
|
||||
|
||||
it('should render default print button', () => {
|
||||
component.allowPrint = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('[data-automation-id="toolbar-print"]')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not render default print button', () => {
|
||||
component.allowPrint = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('[data-automation-id="toolbar-print"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('should invoke print action with the toolbar button', () => {
|
||||
component.allowPrint = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
spyOn(component, 'printContent').and.stub();
|
||||
|
||||
const button: HTMLButtonElement = element.querySelector('[data-automation-id="toolbar-print"]') as HTMLButtonElement;
|
||||
button.click();
|
||||
|
||||
expect(component.printContent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should raise the print event with the toolbar button', (done) => {
|
||||
component.allowPrint = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
component.print.subscribe(e => {
|
||||
e.preventDefault();
|
||||
done();
|
||||
});
|
||||
|
||||
const button: HTMLButtonElement = element.querySelector('[data-automation-id="toolbar-print"]') as HTMLButtonElement;
|
||||
button.click();
|
||||
});
|
||||
|
||||
it('should render default share button', () => {
|
||||
component.allowShare = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('[data-automation-id="toolbar-share"]')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not render default share button', () => {
|
||||
component.allowShare = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('[data-automation-id="toolbar-share"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('should invoke share action with the toolbar button', () => {
|
||||
component.allowShare = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
spyOn(component, 'shareContent').and.stub();
|
||||
|
||||
const button: HTMLButtonElement = element.querySelector('[data-automation-id="toolbar-share"]') as HTMLButtonElement;
|
||||
button.click();
|
||||
|
||||
expect(component.shareContent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should raise share event iwth the toolbar button', (done) => {
|
||||
component.allowShare = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
component.share.subscribe(e => {
|
||||
e.preventDefault();
|
||||
done();
|
||||
});
|
||||
|
||||
const button: HTMLButtonElement = element.querySelector('[data-automation-id="toolbar-share"]') as HTMLButtonElement;
|
||||
button.click();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('View', () => {
|
||||
|
||||
describe('Overlay mode true', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.overlayMode = true;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should header be present if is overlay mode', () => {
|
||||
expect(element.querySelector('.adf-viewer-toolbar')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Name File be present if is overlay mode ', () => {
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-viewer-filename').innerHTML).toEqual('fake-test-file.pdf');
|
||||
});
|
||||
});
|
||||
|
||||
it('should Close button be present if overlay mode', () => {
|
||||
expect(element.querySelector('.adf-viewer-close-button')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should Click on close button hide the viewer', () => {
|
||||
let closebutton: any = element.querySelector('.adf-viewer-close-button');
|
||||
closebutton.click();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-viewer-content')).toBeNull();
|
||||
});
|
||||
|
||||
it('should Esc button hide the viewer', () => {
|
||||
EventMock.keyDown(27);
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-viewer-content')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Overlay mode false', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.overlayMode = false;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should header be NOT be present if is not overlay mode', () => {
|
||||
expect(element.querySelector('header')).toBeNull();
|
||||
});
|
||||
|
||||
it('should Esc button not hide the viewer if is not overlay mode', () => {
|
||||
EventMock.keyDown(27);
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-viewer-content')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Attribute', () => {
|
||||
it('should Url or fileNodeId be mandatory', () => {
|
||||
component.showViewer = true;
|
||||
component.fileNodeId = undefined;
|
||||
component.urlFile = undefined;
|
||||
|
||||
expect(() => {
|
||||
component.ngOnChanges(null);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('should FileNodeId present not thrown any error ', () => {
|
||||
component.showViewer = true;
|
||||
component.fileNodeId = 'file-node-id';
|
||||
component.urlFile = undefined;
|
||||
|
||||
expect(() => {
|
||||
component.ngOnChanges(null);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should urlFile present not thrown any error ', () => {
|
||||
component.showViewer = true;
|
||||
component.fileNodeId = undefined;
|
||||
|
||||
expect(() => {
|
||||
component.ngOnChanges(null);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should showViewer default value be true', () => {
|
||||
expect(component.showViewer).toBe(true);
|
||||
});
|
||||
|
||||
it('should viewer be hide if showViewer value is false', () => {
|
||||
component.showViewer = false;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-viewer-content')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Extension Type Test', () => {
|
||||
it('should extension file pdf be loaded', (done) => {
|
||||
component.urlFile = 'base/src/assets/fake-test-file.pdf';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-pdf-viewer')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should extension file png be loaded', (done) => {
|
||||
component.urlFile = 'fake-url-file.png';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#viewer-image')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should extension file mp4 be loaded', (done) => {
|
||||
component.urlFile = 'fake-url-file.mp4';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-media-player')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should extension file mp3 be loaded', (done) => {
|
||||
component.urlFile = 'fake-url-file.mp3';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-media-player')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should extension file wav be loaded', (done) => {
|
||||
component.urlFile = 'fake-url-file.wav';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-media-player')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should extension file txt be loaded', (done) => {
|
||||
component.urlFile = 'fake-url-file.txt';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-txt-viewer')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display [unknown format] for unsupported extensions', (done) => {
|
||||
component.urlFile = 'fake-url-file.unsupported';
|
||||
component.mimeType = '';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-viewer-unknown-format')).toBeDefined();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MimeType handling', () => {
|
||||
it('should display a PDF file identified by mimetype when the filename has no extension', (done) => {
|
||||
component.urlFile = 'content';
|
||||
component.mimeType = 'application/pdf';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-pdf-viewer')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should display a PDF file identified by mimetype when the file extension is wrong', (done) => {
|
||||
component.urlFile = 'content.bin';
|
||||
component.mimeType = 'application/pdf';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-pdf-viewer')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display an image file identified by mimetype when the filename has no extension', (done) => {
|
||||
component.urlFile = 'content';
|
||||
component.mimeType = 'image/png';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#viewer-image')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display a image file identified by mimetype when the file extension is wrong', (done) => {
|
||||
component.urlFile = 'content.bin';
|
||||
component.mimeType = 'image/png';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#viewer-image')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display the media player if the file identified by mimetype is a media when the filename has wrong extension', (done) => {
|
||||
component.urlFile = 'content.bin';
|
||||
component.mimeType = 'video/mp4';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-media-player')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display the txt viewer if the file identified by mimetype is a txt when the filename has wrong extension', (done) => {
|
||||
component.urlFile = 'content.bin';
|
||||
component.mimeType = 'text/txt';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-txt-viewer')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display the media player if the file identified by mimetype is a media when the filename has no extension', (done) => {
|
||||
component.urlFile = 'content';
|
||||
component.mimeType = 'video/mp4';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('adf-media-player')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Events', () => {
|
||||
it('should if the extension change extension Change event be fired ', (done) => {
|
||||
|
||||
component.extensionChange.subscribe((fileExtension) => {
|
||||
expect(fileExtension).toEqual('png');
|
||||
done();
|
||||
});
|
||||
|
||||
component.urlFile = 'fake-url-file.png';
|
||||
|
||||
component.ngOnChanges(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('display name property override by urlFile', () => {
|
||||
it('should displayName override the default name if is present and urlFile is set' , (done) => {
|
||||
component.urlFile = 'base/src/assets/fake-test-file.pdf';
|
||||
component.displayName = 'test name';
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-viewer-display-name').textContent).toEqual('test name');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the urlFile name if displayName is NOT set and urlFile is set' , (done) => {
|
||||
component.urlFile = 'base/src/assets/fake-test-file.pdf';
|
||||
component.displayName = null;
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-viewer-display-name').textContent).toEqual('fake-test-file.pdf');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('display name property override by blobFile', () => {
|
||||
it('should displayName override the name if is present and blobFile is set' , (done) => {
|
||||
component.displayName = 'blob file display name';
|
||||
component.blobFile = new Blob(['This is my blob content'], {type : 'text/plain'});
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-viewer-display-name').textContent).toEqual('blob file display name');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show uknownn name if displayName is NOT set and blobFile is set' , (done) => {
|
||||
component.displayName = null;
|
||||
component.blobFile = new Blob(['This is my blob content'], {type : 'text/plain'});
|
||||
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-viewer-display-name').textContent).toEqual('Unknown');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('display name property override by nodeId', () => {
|
||||
const displayName = 'the-name';
|
||||
const nodeDetails = { name: displayName, id: '12', content: { mimeType: 'txt' }};
|
||||
const contentUrl = '/content/url/path';
|
||||
const alfrescoApiInstanceMock = {
|
||||
nodes: { getNodeInfo: () => Promise.resolve(nodeDetails) },
|
||||
content: { getContentUrl: () => contentUrl }
|
||||
};
|
||||
|
||||
it('should use the displayName if displayName is set and fileNodeId is set' , (done) => {
|
||||
const userDefinedDisplayName = 'user defined display name';
|
||||
component.fileNodeId = '12';
|
||||
component.urlFile = null;
|
||||
component.displayName = userDefinedDisplayName;
|
||||
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(alfrescoApiInstanceMock);
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-viewer-display-name').textContent).toEqual(userDefinedDisplayName);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the node name if displayName is NOT set and fileNodeId is set' , (done) => {
|
||||
component.fileNodeId = '12';
|
||||
component.urlFile = null;
|
||||
component.displayName = null;
|
||||
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(alfrescoApiInstanceMock);
|
||||
component.ngOnChanges(null).then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-viewer-display-name').textContent).toEqual(displayName);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
441
lib/core/viewer/components/viewer.component.ts
Normal file
441
lib/core/viewer/components/viewer.component.ts
Normal file
@@ -0,0 +1,441 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Location } from '@angular/common';
|
||||
import {
|
||||
Component, ContentChild, EventEmitter, HostListener,
|
||||
Input, OnChanges, OnDestroy, Output, TemplateRef, ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { BaseEvent } from '../../events';
|
||||
import { AlfrescoApiService, LogService, RenditionsService } from '../../services';
|
||||
|
||||
import { ViewerMoreActionsComponent } from './viewer-more-actions.component';
|
||||
import { ViewerOpenWithComponent } from './viewer-open-with.component';
|
||||
import { ViewerSidebarComponent } from './viewer-sidebar.component';
|
||||
import { ViewerToolbarComponent } from './viewer-toolbar.component';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-viewer',
|
||||
templateUrl: './viewer.component.html',
|
||||
styleUrls: ['./viewer.component.scss'],
|
||||
host: { 'class': 'adf-viewer' },
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class ViewerComponent implements OnDestroy, OnChanges {
|
||||
|
||||
@ContentChild(ViewerToolbarComponent)
|
||||
toolbar: ViewerToolbarComponent;
|
||||
|
||||
@ContentChild(ViewerSidebarComponent)
|
||||
sidebar: ViewerSidebarComponent;
|
||||
|
||||
@ContentChild(ViewerOpenWithComponent)
|
||||
mnuOpenWith: ViewerOpenWithComponent;
|
||||
|
||||
@ContentChild(ViewerMoreActionsComponent)
|
||||
mnuMoreActions: ViewerMoreActionsComponent;
|
||||
|
||||
@Input()
|
||||
urlFile = '';
|
||||
|
||||
@Input()
|
||||
blobFile: Blob;
|
||||
|
||||
@Input()
|
||||
fileNodeId: string = null;
|
||||
|
||||
@Input()
|
||||
overlayMode = false;
|
||||
|
||||
@Input()
|
||||
showViewer = true;
|
||||
|
||||
@Input()
|
||||
showToolbar = true;
|
||||
|
||||
@Input()
|
||||
displayName: string;
|
||||
|
||||
@Input()
|
||||
allowGoBack = true;
|
||||
|
||||
@Input()
|
||||
allowDownload = true;
|
||||
|
||||
@Input()
|
||||
allowPrint = false;
|
||||
|
||||
@Input()
|
||||
allowShare = false;
|
||||
|
||||
@Input()
|
||||
allowSidebar = false;
|
||||
|
||||
@Input()
|
||||
showSidebar = false;
|
||||
|
||||
@Input()
|
||||
sidebarPosition = 'right';
|
||||
|
||||
@Output()
|
||||
goBack = new EventEmitter<BaseEvent<any>>();
|
||||
|
||||
@Output()
|
||||
download = new EventEmitter<BaseEvent<any>>();
|
||||
|
||||
@Output()
|
||||
print = new EventEmitter<BaseEvent<any>>();
|
||||
|
||||
@Output()
|
||||
share = new EventEmitter<BaseEvent<any>>();
|
||||
|
||||
@Output()
|
||||
showViewerChange = new EventEmitter<boolean>();
|
||||
|
||||
@Output()
|
||||
extensionChange = new EventEmitter<string>();
|
||||
|
||||
viewerType = 'unknown';
|
||||
downloadUrl: string = null;
|
||||
fileName = 'document';
|
||||
isLoading = false;
|
||||
|
||||
extensionTemplates: { template: TemplateRef<any>, isVisible: boolean }[] = [];
|
||||
externalExtensions: string[] = [];
|
||||
urlFileContent: string;
|
||||
otherMenu: any;
|
||||
extension: string;
|
||||
mimeType: string;
|
||||
|
||||
private extensions = {
|
||||
image: ['png', 'jpg', 'jpeg', 'gif', 'bpm'],
|
||||
media: ['wav', 'mp4', 'mp3', 'webm', 'ogg'],
|
||||
text: ['txt', 'xml', 'js', 'html', 'json'],
|
||||
pdf: ['pdf']
|
||||
};
|
||||
|
||||
private mimeTypes = [
|
||||
{ mimeType: 'application/x-javascript', type: 'text' },
|
||||
{ mimeType: 'application/pdf', type: 'pdf' }
|
||||
];
|
||||
|
||||
constructor(private apiService: AlfrescoApiService,
|
||||
private logService: LogService,
|
||||
private location: Location,
|
||||
private renditionService: RenditionsService) {
|
||||
}
|
||||
|
||||
ngOnChanges(changes) {
|
||||
if (this.showViewer) {
|
||||
if (!this.urlFile && !this.blobFile && !this.fileNodeId) {
|
||||
throw new Error('Attribute urlFile or fileNodeId or blobFile is required');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.blobFile) {
|
||||
this.displayName = this.getDisplayName('Unknown');
|
||||
this.isLoading = true;
|
||||
this.mimeType = this.blobFile.type;
|
||||
this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
|
||||
|
||||
this.allowDownload = false;
|
||||
// TODO: wrap blob into the data url and allow downloading
|
||||
|
||||
this.extensionChange.emit(this.mimeType);
|
||||
this.isLoading = false;
|
||||
this.scrollTop();
|
||||
resolve();
|
||||
} else if (this.urlFile) {
|
||||
this.isLoading = true;
|
||||
let filenameFromUrl = this.getFilenameFromUrl(this.urlFile);
|
||||
this.displayName = this.getDisplayName(filenameFromUrl);
|
||||
this.extension = this.getFileExtension(filenameFromUrl);
|
||||
this.urlFileContent = this.urlFile;
|
||||
|
||||
this.downloadUrl = this.urlFile;
|
||||
this.fileName = this.displayName;
|
||||
|
||||
this.viewerType = this.getViewerTypeByExtension(this.extension);
|
||||
if (this.viewerType === 'unknown') {
|
||||
this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
|
||||
}
|
||||
|
||||
this.extensionChange.emit(this.extension);
|
||||
this.isLoading = false;
|
||||
this.scrollTop();
|
||||
resolve();
|
||||
} else if (this.fileNodeId) {
|
||||
this.isLoading = true;
|
||||
this.apiService.getInstance().nodes.getNodeInfo(this.fileNodeId).then(
|
||||
(data: MinimalNodeEntryEntity) => {
|
||||
this.mimeType = data.content.mimeType;
|
||||
this.displayName = this.getDisplayName(data.name);
|
||||
this.urlFileContent = this.apiService.getInstance().content.getContentUrl(data.id);
|
||||
this.extension = this.getFileExtension(data.name);
|
||||
|
||||
this.fileName = data.name;
|
||||
this.downloadUrl = this.apiService.getInstance().content.getContentUrl(data.id, true);
|
||||
|
||||
this.viewerType = this.getViewerTypeByExtension(this.extension);
|
||||
if (this.viewerType === 'unknown') {
|
||||
this.viewerType = this.getViewerTypeByMimeType(this.mimeType);
|
||||
}
|
||||
|
||||
if (this.viewerType === 'unknown') {
|
||||
this.displayAsPdf(data.id);
|
||||
} else {
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
this.extensionChange.emit(this.extension);
|
||||
this.scrollTop();
|
||||
resolve();
|
||||
},
|
||||
(error) => {
|
||||
this.isLoading = false;
|
||||
reject(error);
|
||||
this.logService.error('This node does not exist');
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getDisplayName(name) {
|
||||
return this.displayName || name;
|
||||
}
|
||||
|
||||
scrollTop() {
|
||||
window.scrollTo(0, 1);
|
||||
}
|
||||
|
||||
getViewerTypeByMimeType(mimeType: string) {
|
||||
if (mimeType) {
|
||||
mimeType = mimeType.toLowerCase();
|
||||
|
||||
if (mimeType.startsWith('image/')) {
|
||||
return 'image';
|
||||
}
|
||||
|
||||
if (mimeType.startsWith('text/')) {
|
||||
return 'text';
|
||||
}
|
||||
|
||||
if (mimeType.startsWith('video/')) {
|
||||
return 'media';
|
||||
}
|
||||
|
||||
if (mimeType.startsWith('audio/')) {
|
||||
return 'media';
|
||||
}
|
||||
|
||||
const registered = this.mimeTypes.find(t => t.mimeType === mimeType);
|
||||
if (registered) {
|
||||
return registered.type;
|
||||
}
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
getViewerTypeByExtension(extension: string) {
|
||||
if (extension) {
|
||||
extension = extension.toLowerCase();
|
||||
}
|
||||
|
||||
if (this.isCustomViewerExtension(extension)) {
|
||||
return 'custom';
|
||||
}
|
||||
|
||||
if (this.extensions.image.indexOf(extension) >= 0) {
|
||||
return 'image';
|
||||
}
|
||||
|
||||
if (this.extensions.media.indexOf(extension) >= 0) {
|
||||
return 'media';
|
||||
}
|
||||
|
||||
if (this.extensions.text.indexOf(extension) >= 0) {
|
||||
return 'text';
|
||||
}
|
||||
|
||||
if (this.extensions.pdf.indexOf(extension) >= 0) {
|
||||
return 'pdf';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
onBackButtonClick() {
|
||||
if (this.overlayMode) {
|
||||
this.close();
|
||||
} else {
|
||||
const event = new BaseEvent<any>();
|
||||
this.goBack.next(event);
|
||||
|
||||
if (!event.defaultPrevented) {
|
||||
this.location.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* close the viewer
|
||||
*/
|
||||
close() {
|
||||
if (this.otherMenu) {
|
||||
this.otherMenu.hidden = false;
|
||||
}
|
||||
this.cleanup();
|
||||
this.showViewer = false;
|
||||
this.showViewerChange.emit(this.showViewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* cleanup before the close
|
||||
*/
|
||||
cleanup() {
|
||||
this.urlFileContent = '';
|
||||
this.displayName = '';
|
||||
this.fileNodeId = null;
|
||||
this.extension = null;
|
||||
this.mimeType = null;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* get File name from url
|
||||
*
|
||||
* @param {string} url - url file
|
||||
* @returns {string} name file
|
||||
*/
|
||||
getFilenameFromUrl(url: string): string {
|
||||
let anchor = url.indexOf('#');
|
||||
let query = url.indexOf('?');
|
||||
let end = Math.min(
|
||||
anchor > 0 ? anchor : url.length,
|
||||
query > 0 ? query : url.length);
|
||||
return url.substring(url.lastIndexOf('/', end) + 1, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token from the local storage
|
||||
*
|
||||
* @param {string} fileName - file name
|
||||
* @returns {string} file name extension
|
||||
*/
|
||||
getFileExtension(fileName: string): string {
|
||||
return fileName.split('.').pop().toLowerCase();
|
||||
}
|
||||
|
||||
isCustomViewerExtension(extension: string): boolean {
|
||||
const extensions = this.externalExtensions || [];
|
||||
|
||||
if (extension && extensions.length > 0) {
|
||||
extension = extension.toLowerCase();
|
||||
return extensions.indexOf(extension) >= 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Litener Keyboard Event
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
handleKeyboardEvent(event: KeyboardEvent) {
|
||||
let key = event.keyCode;
|
||||
if (key === 27 && this.overlayMode) { // esc
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
downloadContent() {
|
||||
if (this.allowDownload && this.downloadUrl && this.fileName) {
|
||||
const args = new BaseEvent();
|
||||
this.download.next(args);
|
||||
|
||||
if (!args.defaultPrevented) {
|
||||
const link = document.createElement('a');
|
||||
|
||||
link.style.display = 'none';
|
||||
link.download = this.fileName;
|
||||
link.href = this.downloadUrl;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printContent() {
|
||||
if (this.allowPrint) {
|
||||
const args = new BaseEvent();
|
||||
this.print.next(args);
|
||||
}
|
||||
}
|
||||
|
||||
shareContent() {
|
||||
if (this.allowShare) {
|
||||
const args = new BaseEvent();
|
||||
this.share.next(args);
|
||||
}
|
||||
}
|
||||
|
||||
private displayAsPdf(nodeId: string) {
|
||||
this.isLoading = true;
|
||||
|
||||
this.renditionService.getRendition(nodeId, 'pdf').subscribe(
|
||||
(response) => {
|
||||
const status = response.entry.status.toString();
|
||||
|
||||
if (status === 'CREATED') {
|
||||
this.isLoading = false;
|
||||
this.showPdfRendition(nodeId);
|
||||
} else if (status === 'NOT_CREATED') {
|
||||
this.renditionService.convert(nodeId, 'pdf').subscribe({
|
||||
complete: () => {
|
||||
this.isLoading = false;
|
||||
this.showPdfRendition(nodeId);
|
||||
},
|
||||
error: (error) => {
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.isLoading = false;
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
this.isLoading = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private showPdfRendition(nodeId: string) {
|
||||
if (nodeId) {
|
||||
this.viewerType = 'pdf';
|
||||
this.urlFileContent = this.renditionService.getRenditionUrl(nodeId, 'pdf');
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Location } from '@angular/common';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
import { ElementRef } from '@angular/core';
|
||||
import { Injector } from '@angular/core';
|
||||
import { async, getTestBed, TestBed } from '@angular/core/testing';
|
||||
import { ViewerComponent } from '../components/viewer.component';
|
||||
import { ViewerExtensionDirective } from './viewer-extension.directive';
|
||||
|
||||
export class MockElementRef extends ElementRef {
|
||||
constructor() {
|
||||
super(null);
|
||||
}
|
||||
}
|
||||
|
||||
describe('ExtensionViewerDirective', () => {
|
||||
let injector: Injector;
|
||||
let extensionViewerDirective: ViewerExtensionDirective;
|
||||
let viewerComponent: ViewerComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: Location, useClass: SpyLocation },
|
||||
ViewerExtensionDirective,
|
||||
{provide: ElementRef, useClass: MockElementRef},
|
||||
ViewerComponent
|
||||
]
|
||||
});
|
||||
injector = getTestBed();
|
||||
extensionViewerDirective = injector.get(ViewerExtensionDirective);
|
||||
viewerComponent = injector.get(ViewerComponent);
|
||||
extensionViewerDirective.templateModel = {template: '', isVisible: false};
|
||||
}));
|
||||
|
||||
it('is defined', () => {
|
||||
expect(extensionViewerDirective).toBeDefined();
|
||||
});
|
||||
|
||||
it('if the file in the viewer has an extension handled by this extension isVisible should be true', () => {
|
||||
extensionViewerDirective.supportedExtensions = ['xls', 'sts'];
|
||||
expect(extensionViewerDirective.isVisible('xls')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('if the file in the viewer not has an extension handled by this extension isVisible should be false', () => {
|
||||
extensionViewerDirective.supportedExtensions = ['xls', 'sts'];
|
||||
expect(extensionViewerDirective.isVisible('png')).not.toBeTruthy();
|
||||
});
|
||||
});
|
77
lib/core/viewer/directives/viewer-extension.directive.ts
Normal file
77
lib/core/viewer/directives/viewer-extension.directive.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AfterContentInit, ContentChild, Directive, Input, TemplateRef } from '@angular/core';
|
||||
import { ViewerComponent } from '../components/viewer.component';
|
||||
|
||||
@Directive({
|
||||
selector: 'adf-viewer-extension'
|
||||
})
|
||||
export class ViewerExtensionDirective implements AfterContentInit {
|
||||
|
||||
@ContentChild(TemplateRef)
|
||||
template: any;
|
||||
|
||||
@Input()
|
||||
urlFileContent: string;
|
||||
|
||||
@Input()
|
||||
extension: string;
|
||||
|
||||
@Input()
|
||||
supportedExtensions: string[];
|
||||
|
||||
templateModel: any;
|
||||
|
||||
constructor(private viewerComponent: ViewerComponent) {
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.templateModel = {template: this.template, isVisible: false};
|
||||
|
||||
this.viewerComponent.extensionTemplates.push(this.templateModel);
|
||||
|
||||
this.viewerComponent.extensionChange.subscribe((fileExtension) => {
|
||||
this.templateModel.isVisible = this.isVisible(fileExtension);
|
||||
});
|
||||
|
||||
if (this.supportedExtensions instanceof Array) {
|
||||
this.supportedExtensions.forEach((extension) => {
|
||||
this.viewerComponent.externalExtensions.push(extension);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the current extension in the viewer is compatible with this extension checking against supportedExtensions
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isVisible(fileExtension) {
|
||||
let supportedExtension: string;
|
||||
|
||||
if (this.supportedExtensions && (this.supportedExtensions instanceof Array)) {
|
||||
supportedExtension = this.supportedExtensions.find((extension) => {
|
||||
return extension.toLowerCase() === fileExtension;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
return !!supportedExtension;
|
||||
}
|
||||
|
||||
}
|
18
lib/core/viewer/index.ts
Normal file
18
lib/core/viewer/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
31
lib/core/viewer/public-api.ts
Normal file
31
lib/core/viewer/public-api.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './components/viewer.component';
|
||||
export * from './components/imgViewer.component';
|
||||
export * from './components/mediaPlayer.component';
|
||||
export * from './components/pdfViewer.component';
|
||||
export * from './components/txtViewer.component';
|
||||
export * from './components/unknown-format/unknown-format.component';
|
||||
export * from './components/viewer-more-actions.component';
|
||||
export * from './components/viewer-open-with.component';
|
||||
export * from './components/viewer-sidebar.component';
|
||||
export * from './components/viewer-toolbar.component';
|
||||
export * from './components/viewer.component';
|
||||
export * from './directives/viewer-extension.directive';
|
||||
|
||||
export * from './viewer.module';
|
38
lib/core/viewer/services/rendering-queue.services.spec.ts
Normal file
38
lib/core/viewer/services/rendering-queue.services.spec.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ReflectiveInjector } from '@angular/core';
|
||||
import { RenderingQueueServices } from './rendering-queue.services';
|
||||
|
||||
describe('RenderingQueueServices', () => {
|
||||
|
||||
let service, injector;
|
||||
|
||||
beforeEach(() => {
|
||||
injector = ReflectiveInjector.resolveAndCreate([
|
||||
RenderingQueueServices
|
||||
]);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = injector.get(RenderingQueueServices);
|
||||
});
|
||||
|
||||
it('Simple import example', () => {
|
||||
expect(service.CLEANUP_TIMEOUT).toEqual(30000);
|
||||
});
|
||||
});
|
169
lib/core/viewer/services/rendering-queue.services.ts
Normal file
169
lib/core/viewer/services/rendering-queue.services.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
/**
|
||||
*
|
||||
* RenderingQueueServices rendering of the views for pages and thumbnails.
|
||||
*
|
||||
* @returns {RenderingQueueServices} .
|
||||
*/
|
||||
@Injectable()
|
||||
export class RenderingQueueServices {
|
||||
|
||||
renderingStates = {
|
||||
INITIAL: 0,
|
||||
RUNNING: 1,
|
||||
PAUSED: 2,
|
||||
FINISHED: 3
|
||||
};
|
||||
|
||||
CLEANUP_TIMEOUT: number = 30000;
|
||||
|
||||
pdfViewer: any = null;
|
||||
pdfThumbnailViewer: any = null;
|
||||
onIdle: any = null;
|
||||
|
||||
highestPriorityPage: any = null;
|
||||
idleTimeout: any = null;
|
||||
printing: any = false;
|
||||
isThumbnailViewEnabled: any = false;
|
||||
|
||||
/**
|
||||
* @param {PDFViewer} pdfViewer
|
||||
*/
|
||||
setViewer(pdfViewer) {
|
||||
this.pdfViewer = pdfViewer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PDFThumbnailViewer} pdfThumbnailViewer
|
||||
*/
|
||||
setThumbnailViewer(pdfThumbnailViewer) {
|
||||
this.pdfThumbnailViewer = pdfThumbnailViewer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {IRenderableView} view
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isHighestPriority(view: any) {
|
||||
return this.highestPriorityPage === view.renderingId;
|
||||
}
|
||||
|
||||
renderHighestPriority(currentlyVisiblePages) {
|
||||
if (this.idleTimeout) {
|
||||
clearTimeout(this.idleTimeout);
|
||||
this.idleTimeout = null;
|
||||
}
|
||||
|
||||
// Pages have a higher priority than thumbnails, so check them first.
|
||||
if (this.pdfViewer.forceRendering(currentlyVisiblePages)) {
|
||||
return;
|
||||
}
|
||||
// No pages needed rendering so check thumbnails.
|
||||
if (this.pdfThumbnailViewer && this.isThumbnailViewEnabled) {
|
||||
if (this.pdfThumbnailViewer.forceRendering()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.printing) {
|
||||
// If printing is currently ongoing do not reschedule cleanup.
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.onIdle) {
|
||||
this.idleTimeout = setTimeout(this.onIdle.bind(this), this.CLEANUP_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
getHighestPriority(visible, views, scrolledDown) {
|
||||
// The state has changed figure out which page has the highest priority to
|
||||
// render next (if any).
|
||||
// Priority:
|
||||
// 1 visible pages
|
||||
// 2 if last scrolled down page after the visible pages
|
||||
// 2 if last scrolled up page before the visible pages
|
||||
let visibleViews = visible.views;
|
||||
|
||||
let numVisible = visibleViews.length;
|
||||
if (numVisible === 0) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < numVisible; ++i) {
|
||||
let view = visibleViews[i].view;
|
||||
if (!this.isViewFinished(view)) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
// All the visible views have rendered, try to render next/previous pages.
|
||||
if (scrolledDown) {
|
||||
let nextPageIndex = visible.last.id;
|
||||
// ID's start at 1 so no need to add 1.
|
||||
if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) {
|
||||
return views[nextPageIndex];
|
||||
}
|
||||
} else {
|
||||
let previousPageIndex = visible.first.id - 2;
|
||||
if (views[previousPageIndex] && !this.isViewFinished(views[previousPageIndex])) {
|
||||
return views[previousPageIndex];
|
||||
}
|
||||
}
|
||||
// Everything that needs to be rendered has been.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {IRenderableView} view
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isViewFinished(view) {
|
||||
return view.renderingState === this.renderingStates.FINISHED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a page or thumbnail view. This calls the appropriate function
|
||||
* based on the views state. If the view is already rendered it will return
|
||||
* false.
|
||||
* @param {IRenderableView} view
|
||||
*/
|
||||
renderView(view: any) {
|
||||
let state = view.renderingState;
|
||||
switch (state) {
|
||||
case this.renderingStates.FINISHED:
|
||||
return false;
|
||||
case this.renderingStates.PAUSED:
|
||||
this.highestPriorityPage = view.renderingId;
|
||||
view.resume();
|
||||
break;
|
||||
case this.renderingStates.RUNNING:
|
||||
this.highestPriorityPage = view.renderingId;
|
||||
break;
|
||||
case this.renderingStates.INITIAL:
|
||||
this.highestPriorityPage = view.renderingId;
|
||||
let continueRendering = function () {
|
||||
this.renderHighestPriority();
|
||||
}.bind(this);
|
||||
view.draw().then(continueRendering, continueRendering);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
73
lib/core/viewer/viewer.module.ts
Normal file
73
lib/core/viewer/viewer.module.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { ToolbarModule } from '../toolbar';
|
||||
import { PipeModule } from '../pipes';
|
||||
import { ImgViewerComponent } from './components/imgViewer.component';
|
||||
import { MediaPlayerComponent } from './components/mediaPlayer.component';
|
||||
import { PdfViewerComponent } from './components/pdfViewer.component';
|
||||
import { TxtViewerComponent } from './components/txtViewer.component';
|
||||
import { UnknownFormatComponent } from './components/unknown-format/unknown-format.component';
|
||||
import { ViewerMoreActionsComponent } from './components/viewer-more-actions.component';
|
||||
import { ViewerOpenWithComponent } from './components/viewer-open-with.component';
|
||||
import { ViewerSidebarComponent } from './components/viewer-sidebar.component';
|
||||
import { ViewerToolbarComponent } from './components/viewer-toolbar.component';
|
||||
import { ViewerComponent } from './components/viewer.component';
|
||||
import { ViewerExtensionDirective } from './directives/viewer-extension.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MaterialModule,
|
||||
TranslateModule,
|
||||
ToolbarModule,
|
||||
PipeModule
|
||||
],
|
||||
declarations: [
|
||||
ViewerComponent,
|
||||
ImgViewerComponent,
|
||||
TxtViewerComponent,
|
||||
MediaPlayerComponent,
|
||||
PdfViewerComponent,
|
||||
ViewerExtensionDirective,
|
||||
UnknownFormatComponent,
|
||||
ViewerToolbarComponent,
|
||||
ViewerSidebarComponent,
|
||||
ViewerOpenWithComponent,
|
||||
ViewerMoreActionsComponent
|
||||
],
|
||||
exports: [
|
||||
ViewerComponent,
|
||||
ImgViewerComponent,
|
||||
TxtViewerComponent,
|
||||
MediaPlayerComponent,
|
||||
PdfViewerComponent,
|
||||
ViewerExtensionDirective,
|
||||
UnknownFormatComponent,
|
||||
ViewerToolbarComponent,
|
||||
ViewerSidebarComponent,
|
||||
ViewerOpenWithComponent,
|
||||
ViewerMoreActionsComponent
|
||||
]
|
||||
})
|
||||
export class ViewerModule {
|
||||
}
|
Reference in New Issue
Block a user