mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ADF-1534] Automatic PDF rendering for unsupported types. (#2313)
* move viewer dialog to a separate folder * make dialog take full screen size * automatic PDF conversion * mime type icon in the title * [ADF-1530] correct plain text viewer resolving * separate lightweigh pdf view for the dialog
This commit is contained in:
parent
3ba93a6840
commit
1cf2db48ac
@ -122,6 +122,7 @@ import { NodePermissionDirective } from './src/directives/node-permission.direct
|
|||||||
import { UploadDirective } from './src/directives/upload.directive';
|
import { UploadDirective } from './src/directives/upload.directive';
|
||||||
|
|
||||||
import { FileSizePipe } from './src/pipes/file-size.pipe';
|
import { FileSizePipe } from './src/pipes/file-size.pipe';
|
||||||
|
import { MimeTypeIconPipe } from './src/pipes/mime-type-icon.pipe';
|
||||||
import { HighlightPipe } from './src/pipes/text-highlight.pipe';
|
import { HighlightPipe } from './src/pipes/text-highlight.pipe';
|
||||||
import { TimeAgoPipe } from './src/pipes/time-ago.pipe';
|
import { TimeAgoPipe } from './src/pipes/time-ago.pipe';
|
||||||
|
|
||||||
@ -193,7 +194,8 @@ export function providers() {
|
|||||||
SharedLinksApiService,
|
SharedLinksApiService,
|
||||||
SitesApiService,
|
SitesApiService,
|
||||||
DiscoveryApiService,
|
DiscoveryApiService,
|
||||||
HighlightTransformService
|
HighlightTransformService,
|
||||||
|
MomentDateAdapter
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +214,15 @@ export function obsoleteMdlDirectives() {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function pipes() {
|
||||||
|
return [
|
||||||
|
FileSizePipe,
|
||||||
|
HighlightPipe,
|
||||||
|
TimeAgoPipe,
|
||||||
|
MimeTypeIconPipe
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export function createTranslateLoader(http: Http, logService: LogService) {
|
export function createTranslateLoader(http: Http, logService: LogService) {
|
||||||
return new AlfrescoTranslateLoader(http, logService);
|
return new AlfrescoTranslateLoader(http, logService);
|
||||||
}
|
}
|
||||||
@ -240,6 +251,7 @@ export function createTranslateLoader(http: Http, logService: LogService) {
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
...obsoleteMdlDirectives(),
|
...obsoleteMdlDirectives(),
|
||||||
|
...pipes(),
|
||||||
UploadDirective,
|
UploadDirective,
|
||||||
NodePermissionDirective,
|
NodePermissionDirective,
|
||||||
HighlightDirective,
|
HighlightDirective,
|
||||||
@ -251,16 +263,12 @@ export function createTranslateLoader(http: Http, logService: LogService) {
|
|||||||
InfoDrawerTitleDirective,
|
InfoDrawerTitleDirective,
|
||||||
InfoDrawerButtonsDirective,
|
InfoDrawerButtonsDirective,
|
||||||
InfoDrawerContentDirective,
|
InfoDrawerContentDirective,
|
||||||
FileSizePipe,
|
|
||||||
HighlightPipe,
|
|
||||||
TimeAgoPipe,
|
|
||||||
CreateFolderDialogComponent,
|
CreateFolderDialogComponent,
|
||||||
DownloadZipDialogComponent
|
DownloadZipDialogComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
...providers(),
|
...providers(),
|
||||||
...deprecatedProviders(),
|
...deprecatedProviders(),
|
||||||
MomentDateAdapter,
|
|
||||||
{
|
{
|
||||||
provide: TRANSLATION_PROVIDER,
|
provide: TRANSLATION_PROVIDER,
|
||||||
multi: true,
|
multi: true,
|
||||||
@ -284,14 +292,12 @@ export function createTranslateLoader(http: Http, logService: LogService) {
|
|||||||
PaginationModule,
|
PaginationModule,
|
||||||
ToolbarModule,
|
ToolbarModule,
|
||||||
...obsoleteMdlDirectives(),
|
...obsoleteMdlDirectives(),
|
||||||
|
...pipes(),
|
||||||
UploadDirective,
|
UploadDirective,
|
||||||
NodePermissionDirective,
|
NodePermissionDirective,
|
||||||
HighlightDirective,
|
HighlightDirective,
|
||||||
DataColumnComponent,
|
DataColumnComponent,
|
||||||
DataColumnListComponent,
|
DataColumnListComponent,
|
||||||
FileSizePipe,
|
|
||||||
HighlightPipe,
|
|
||||||
TimeAgoPipe,
|
|
||||||
CreateFolderDialogComponent,
|
CreateFolderDialogComponent,
|
||||||
DownloadZipDialogComponent,
|
DownloadZipDialogComponent,
|
||||||
InfoDrawerComponent,
|
InfoDrawerComponent,
|
||||||
|
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { ThumbnailService } from '../services/thumbnail.service';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'adfMimeTypeIcon'
|
||||||
|
})
|
||||||
|
export class MimeTypeIconPipe implements PipeTransform {
|
||||||
|
|
||||||
|
constructor(private thumbnailService: ThumbnailService) { }
|
||||||
|
|
||||||
|
transform(text: string): string {
|
||||||
|
return this.thumbnailService.getMimeTypeIcon(text);
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { AlfrescoApi } from 'alfresco-js-api';
|
import { AlfrescoApi, ContentApi, NodesApi, RenditionsApi } from 'alfresco-js-api';
|
||||||
import * as alfrescoApi from 'alfresco-js-api';
|
import * as alfrescoApi from 'alfresco-js-api';
|
||||||
import { AppConfigService } from './app-config.service';
|
import { AppConfigService } from './app-config.service';
|
||||||
import { StorageService } from './storage.service';
|
import { StorageService } from './storage.service';
|
||||||
@ -26,10 +26,22 @@ export class AlfrescoApiService {
|
|||||||
|
|
||||||
private alfrescoApi: AlfrescoApi;
|
private alfrescoApi: AlfrescoApi;
|
||||||
|
|
||||||
public getInstance(): AlfrescoApi {
|
getInstance(): AlfrescoApi {
|
||||||
return this.alfrescoApi;
|
return this.alfrescoApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get contentApi(): ContentApi {
|
||||||
|
return this.getInstance().content;
|
||||||
|
}
|
||||||
|
|
||||||
|
get nodesApi(): NodesApi {
|
||||||
|
return this.getInstance().nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
get renditionsApi(): RenditionsApi {
|
||||||
|
return this.getInstance().core.renditionsApi;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private appConfig: AppConfigService,
|
constructor(private appConfig: AppConfigService,
|
||||||
private storage: StorageService) {
|
private storage: StorageService) {
|
||||||
|
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { RenditionEntry, RenditionPaging } from 'alfresco-js-api';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { Observable } from 'rxjs/Rx';
|
||||||
import { AlfrescoApiService } from './alfresco-api.service';
|
import { AlfrescoApiService } from './alfresco-api.service';
|
||||||
import { LogService } from './log.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RenditionsService
|
* RenditionsService
|
||||||
@ -28,52 +28,57 @@ import { LogService } from './log.service';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class RenditionsService {
|
export class RenditionsService {
|
||||||
|
|
||||||
constructor(private apiService: AlfrescoApiService,
|
constructor(private apiService: AlfrescoApiService) {
|
||||||
private logService: LogService) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isRenditionAvailable(nodeId: string, encoding: string) {
|
isRenditionAvailable(nodeId: string, encoding: string): Observable<boolean> {
|
||||||
return Observable.create((observer) => {
|
return Observable.create((observer) => {
|
||||||
this.getRendition(nodeId, encoding).subscribe((res) => {
|
this.getRendition(nodeId, encoding).subscribe(
|
||||||
|
(res) => {
|
||||||
let isAvailable = true;
|
let isAvailable = true;
|
||||||
if (res.entry.status === 'NOT_CREATED') {
|
if (res.entry.status.toString() === 'NOT_CREATED') {
|
||||||
isAvailable = false;
|
isAvailable = false;
|
||||||
}
|
}
|
||||||
observer.next(isAvailable);
|
observer.next(isAvailable);
|
||||||
observer.complete();
|
observer.complete();
|
||||||
}, () => {
|
},
|
||||||
|
() => {
|
||||||
observer.next(false);
|
observer.next(false);
|
||||||
observer.complete();
|
observer.complete();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isConversionPossible(nodeId: string, encoding: string) {
|
isConversionPossible(nodeId: string, encoding: string): Observable<boolean> {
|
||||||
return Observable.create((observer) => {
|
return Observable.create((observer) => {
|
||||||
this.getRendition(nodeId, encoding).subscribe(() => {
|
this.getRendition(nodeId, encoding).subscribe(
|
||||||
|
() => {
|
||||||
observer.next(true);
|
observer.next(true);
|
||||||
observer.complete();
|
observer.complete();
|
||||||
}, () => {
|
},
|
||||||
|
() => {
|
||||||
observer.next(false);
|
observer.next(false);
|
||||||
observer.complete();
|
observer.complete();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getRendition(nodeId: string, encoding: string) {
|
getRenditionUrl(nodeId: string, encoding: string): string {
|
||||||
return Observable.fromPromise(this.apiService.getInstance().core.renditionsApi.getRendition(nodeId, encoding))
|
return this.apiService.contentApi.getRenditionUrl(nodeId, 'pdf');
|
||||||
.catch(err => this.handleError(err));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getRenditionsListByNodeId(nodeId: string) {
|
getRendition(nodeId: string, encoding: string): Observable<RenditionEntry> {
|
||||||
return Observable.fromPromise(this.apiService.getInstance().core.renditionsApi.getRenditions(nodeId))
|
return Observable.fromPromise(this.apiService.renditionsApi.getRendition(nodeId, encoding));
|
||||||
.catch(err => this.handleError(err));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createRendition(nodeId: string, encoding: string) {
|
getRenditionsListByNodeId(nodeId: string): Observable<RenditionPaging> {
|
||||||
return Observable.fromPromise(this.apiService.getInstance().core.renditionsApi.createRendition(nodeId, {id: encoding}))
|
return Observable.fromPromise(this.apiService.renditionsApi.getRenditions(nodeId));
|
||||||
.catch(err => this.handleError(err));
|
}
|
||||||
|
|
||||||
|
createRendition(nodeId: string, encoding: string): Observable<{}> {
|
||||||
|
return Observable.fromPromise(this.apiService.renditionsApi.createRendition(nodeId, {id: encoding}));
|
||||||
}
|
}
|
||||||
|
|
||||||
convert(nodeId: string, encoding: string, pollingInterval: number = 1000) {
|
convert(nodeId: string, encoding: string, pollingInterval: number = 1000) {
|
||||||
@ -85,12 +90,7 @@ export class RenditionsService {
|
|||||||
return Observable.interval(interval)
|
return Observable.interval(interval)
|
||||||
.switchMap(() => this.getRendition(nodeId, encoding))
|
.switchMap(() => this.getRendition(nodeId, encoding))
|
||||||
.takeWhile((data) => {
|
.takeWhile((data) => {
|
||||||
return (data.entry.status !== 'CREATED');
|
return (data.entry.status.toString() !== 'CREATED');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleError(error: any): Observable<any> {
|
|
||||||
this.logService.error(error);
|
|
||||||
return Observable.throw(error || 'Server error');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,15 +26,16 @@ import { MediaPlayerComponent } from './src/components/mediaPlayer.component';
|
|||||||
import { NotSupportedFormatComponent } from './src/components/notSupportedFormat.component';
|
import { NotSupportedFormatComponent } from './src/components/notSupportedFormat.component';
|
||||||
import { PdfViewerComponent } from './src/components/pdfViewer.component';
|
import { PdfViewerComponent } from './src/components/pdfViewer.component';
|
||||||
import { TxtViewerComponent } from './src/components/txtViewer.component';
|
import { TxtViewerComponent } from './src/components/txtViewer.component';
|
||||||
import { ViewerDialogComponent } from './src/components/viewer-dialog.component';
|
import { PdfViewComponent } from './src/components/viewer-dialog/pdf-view/pdf-view.component';
|
||||||
|
import { ViewerDialogComponent } from './src/components/viewer-dialog/viewer-dialog.component';
|
||||||
import { ViewerComponent } from './src/components/viewer.component';
|
import { ViewerComponent } from './src/components/viewer.component';
|
||||||
import { ExtensionViewerDirective } from './src/directives/extension-viewer.directive';
|
import { ExtensionViewerDirective } from './src/directives/extension-viewer.directive';
|
||||||
|
|
||||||
import { RenderingQueueServices } from './src/services/rendering-queue.services';
|
import { RenderingQueueServices } from './src/services/rendering-queue.services';
|
||||||
import { ViewerService } from './src/services/viewer.service';
|
import { ViewerService } from './src/services/viewer.service';
|
||||||
|
|
||||||
export { ViewerDialogComponent } from './src/components/viewer-dialog.component';
|
export { ViewerDialogComponent } from './src/components/viewer-dialog/viewer-dialog.component';
|
||||||
export { ViewerDialogSettings } from './src/components/viewer-dialog.settings';
|
export { ViewerDialogSettings } from './src/components/viewer-dialog/viewer-dialog.settings';
|
||||||
export { ViewerService } from './src/services/viewer.service';
|
export { ViewerService } from './src/services/viewer.service';
|
||||||
|
|
||||||
export const VIEWER_DIRECTIVES: any[] = [
|
export const VIEWER_DIRECTIVES: any[] = [
|
||||||
@ -45,7 +46,8 @@ export const VIEWER_DIRECTIVES: any[] = [
|
|||||||
NotSupportedFormatComponent,
|
NotSupportedFormatComponent,
|
||||||
PdfViewerComponent,
|
PdfViewerComponent,
|
||||||
ExtensionViewerDirective,
|
ExtensionViewerDirective,
|
||||||
ViewerDialogComponent
|
ViewerDialogComponent,
|
||||||
|
PdfViewComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
import { ContentService, RenditionsService } from 'ng2-alfresco-core';
|
import { ContentService, RenditionsService } from 'ng2-alfresco-core';
|
||||||
import { AlfrescoApiService } from 'ng2-alfresco-core';
|
|
||||||
|
|
||||||
const DEFAULT_CONVERSION_ENCODING = 'pdf';
|
const DEFAULT_CONVERSION_ENCODING = 'pdf';
|
||||||
|
|
||||||
@ -54,9 +53,7 @@ export class NotSupportedFormatComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private contentService: ContentService,
|
private contentService: ContentService,
|
||||||
private renditionsService: RenditionsService,
|
private renditionsService: RenditionsService) {}
|
||||||
private apiService: AlfrescoApiService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for available renditions if the nodeId is present
|
* Checks for available renditions if the nodeId is present
|
||||||
@ -86,11 +83,12 @@ export class NotSupportedFormatComponent implements OnInit, OnDestroy {
|
|||||||
checkRendition(encoding: string = DEFAULT_CONVERSION_ENCODING): void {
|
checkRendition(encoding: string = DEFAULT_CONVERSION_ENCODING): void {
|
||||||
this.renditionsService.getRendition(this.nodeId, encoding)
|
this.renditionsService.getRendition(this.nodeId, encoding)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(response: any) => {
|
(response) => {
|
||||||
if (response.entry.status === 'NOT_CREATED') {
|
const status = response.entry.status.toString();
|
||||||
|
if (status === 'NOT_CREATED') {
|
||||||
this.convertible = true;
|
this.convertible = true;
|
||||||
this.displayable = false;
|
this.displayable = false;
|
||||||
} else if (response.entry.status === 'CREATED') {
|
} else if (status === 'CREATED') {
|
||||||
this.convertible = false;
|
this.convertible = false;
|
||||||
this.displayable = true;
|
this.displayable = true;
|
||||||
}
|
}
|
||||||
@ -119,7 +117,7 @@ export class NotSupportedFormatComponent implements OnInit, OnDestroy {
|
|||||||
* Show the PDF rendition of the node
|
* Show the PDF rendition of the node
|
||||||
*/
|
*/
|
||||||
showPDF(): void {
|
showPDF(): void {
|
||||||
this.renditionUrl = this.apiService.getInstance().content.getRenditionUrl(this.nodeId, DEFAULT_CONVERSION_ENCODING);
|
this.renditionUrl = this.renditionsService.getRenditionUrl(this.nodeId, DEFAULT_CONVERSION_ENCODING);
|
||||||
this.isConversionStarted = false;
|
this.isConversionStarted = false;
|
||||||
this.isConversionFinished = true;
|
this.isConversionFinished = true;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
<div id="viewer-pdf-container" class="viewer-pdf-container" (window:resize)="onResize()">
|
||||||
|
<div id="viewer-viewerPdf" class="pdfViewer">
|
||||||
|
<div *ngIf="isLoading" class="adf-pdf-view__loading-screen">
|
||||||
|
<h2>Loading</h2>
|
||||||
|
<div>
|
||||||
|
<md-spinner></md-spinner>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="showToolbar">
|
||||||
|
<div class="viewer-toolbar-pagination">
|
||||||
|
<div
|
||||||
|
id="viewer-previous-page-button"
|
||||||
|
aria-label="arrow left"
|
||||||
|
class="button-page left"
|
||||||
|
tabindex="0"
|
||||||
|
(click)="previousPage()">
|
||||||
|
<md-icon>keyboard_arrow_left</md-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="viewer-page-counter left" >
|
||||||
|
<input #page
|
||||||
|
class="viewer-pagenumber-input left"
|
||||||
|
type="text"
|
||||||
|
pattern="-?[0-9]*(\.[0-9]+)?"
|
||||||
|
value="{{ displayPage }}"
|
||||||
|
(keyup.enter)="inputPage(page.value)">
|
||||||
|
<div class="left viewer-total-pages">/ {{ totalPages }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="viewer-next-page-button"
|
||||||
|
aria-label="arrow right"
|
||||||
|
class="button-page left"
|
||||||
|
tabindex="0"
|
||||||
|
(click)="nextPage()">
|
||||||
|
<md-icon>keyboard_arrow_right</md-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="viewer-toolbar-command">
|
||||||
|
<div
|
||||||
|
id="viewer-scale-page-button"
|
||||||
|
aria-label="zoom out map"
|
||||||
|
class="button-page left"
|
||||||
|
tabindex="0"
|
||||||
|
(click)="pageFit()">
|
||||||
|
<md-icon>zoom_out_map</md-icon>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="viewer-zoom-in-button"
|
||||||
|
aria-label="zoom in"
|
||||||
|
class="button-page left"
|
||||||
|
tabindex="0"
|
||||||
|
(click)="zoomIn()">
|
||||||
|
<md-icon>zoom_in</md-icon>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="viewer-zoom-out-button"
|
||||||
|
aria-label="zoom out"
|
||||||
|
class="button-page left"
|
||||||
|
tabindex="0"
|
||||||
|
(click)="zoomOut()">
|
||||||
|
<md-icon>zoom_out</md-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
@ -0,0 +1,339 @@
|
|||||||
|
.adf-pdf-view {
|
||||||
|
|
||||||
|
&__loading-screen {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 85vh;
|
||||||
|
|
||||||
|
.md-spinner {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-toolbar-pagination{
|
||||||
|
padding-top: 4px;
|
||||||
|
top: 80px;
|
||||||
|
right:35px;
|
||||||
|
width:auto;
|
||||||
|
position:absolute;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #3E3E3E;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-toolbar-command{
|
||||||
|
height: 30px;
|
||||||
|
padding-top: 4px;
|
||||||
|
top: 80px;
|
||||||
|
left:35px;
|
||||||
|
width:auto;
|
||||||
|
position:absolute;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #3E3E3E;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-pagenumber-input {
|
||||||
|
border: none;
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 4px 0;
|
||||||
|
background: 0 0;
|
||||||
|
text-align: right;
|
||||||
|
color: inherit;
|
||||||
|
width: 33px;
|
||||||
|
margin-right: 4px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-total-pages {
|
||||||
|
border: medium none;
|
||||||
|
display: flex;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 4px 0px;
|
||||||
|
background: transparent none repeat scroll 0px 0px;
|
||||||
|
text-align: right;
|
||||||
|
color: inherit;
|
||||||
|
margin-right: 4px;
|
||||||
|
height: 20px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-page-counter {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-page {
|
||||||
|
margin-right: 4px;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
margin-left: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-page:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: grey;
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.adf-pdf-view {
|
||||||
|
.textLayer {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0.2;
|
||||||
|
line-height: 1.0;
|
||||||
|
|
||||||
|
& > 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);
|
||||||
|
}
|
@ -0,0 +1,386 @@
|
|||||||
|
/*!
|
||||||
|
* @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, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { LogService } from 'ng2-alfresco-core';
|
||||||
|
import { RenderingQueueServices } from '../../../services/rendering-queue.services';
|
||||||
|
|
||||||
|
declare let PDFJS: any;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-pdf-view',
|
||||||
|
templateUrl: 'pdf-view.component.html',
|
||||||
|
styleUrls: [ 'pdf-view.component.scss' ],
|
||||||
|
providers: [ RenderingQueueServices ],
|
||||||
|
host: { 'class': 'adf-pdf-view' },
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class PdfViewComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fileUrl: string;
|
||||||
|
|
||||||
|
page: number;
|
||||||
|
displayPage: number;
|
||||||
|
totalPages: number;
|
||||||
|
loadingPercent: number;
|
||||||
|
showToolbar = false;
|
||||||
|
isLoading = false;
|
||||||
|
|
||||||
|
private currentPdfDocument: any;
|
||||||
|
private pdfViewer: any;
|
||||||
|
private currentScaleMode: string = 'auto';
|
||||||
|
private currentScale: number;
|
||||||
|
|
||||||
|
private MAX_AUTO_SCALE: number = 1.25;
|
||||||
|
private DEFAULT_SCALE_DELTA: number = 1.1;
|
||||||
|
private MIN_SCALE: number = 0.25;
|
||||||
|
private MAX_SCALE: number = 10.0;
|
||||||
|
|
||||||
|
constructor(private renderingQueueServices: RenderingQueueServices,
|
||||||
|
private logService: LogService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (this.fileUrl) {
|
||||||
|
this.render(this.fileUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private render(src) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.isLoading = true;
|
||||||
|
const 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');
|
||||||
|
this.showToolbar = true;
|
||||||
|
this.isLoading = false;
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
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) {
|
||||||
|
let documentContainer = document.getElementById('viewer-pdf-container');
|
||||||
|
let viewer: any = document.getElementById('viewer-viewerPdf');
|
||||||
|
|
||||||
|
window.document.addEventListener('scroll', (event) => {
|
||||||
|
this.watchScroll(event.target);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
this.pdfViewer = new PDFJS.PDFViewer({
|
||||||
|
container: documentContainer,
|
||||||
|
viewer: viewer,
|
||||||
|
renderingQueue: this.renderingQueueServices
|
||||||
|
});
|
||||||
|
|
||||||
|
this.renderingQueueServices.setViewer(this.pdfViewer);
|
||||||
|
|
||||||
|
this.pdfViewer.setDocument(pdfDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,7 +3,10 @@
|
|||||||
<button md-icon-button [md-dialog-close]="true" mdTooltip="Close and go back">
|
<button md-icon-button [md-dialog-close]="true" mdTooltip="Close and go back">
|
||||||
<md-icon>arrow_back</md-icon>
|
<md-icon>arrow_back</md-icon>
|
||||||
</button>
|
</button>
|
||||||
|
<span>
|
||||||
|
<img class="adf-viewer-dialog__mimetype-icon" [src]="fileMimeType | adfMimeTypeIcon">
|
||||||
<span>{{ fileName }}</span>
|
<span>{{ fileName }}</span>
|
||||||
|
</span>
|
||||||
</adf-toolbar-title>
|
</adf-toolbar-title>
|
||||||
|
|
||||||
<button md-button [mdMenuTriggerFor]="mnuOpenWith">
|
<button md-button [mdMenuTriggerFor]="mnuOpenWith">
|
||||||
@ -74,7 +77,18 @@
|
|||||||
</adf-toolbar>
|
</adf-toolbar>
|
||||||
|
|
||||||
<md-dialog-content>
|
<md-dialog-content>
|
||||||
<ng-container [ngSwitch]="viewerType">
|
|
||||||
|
<ng-container *ngIf="isLoading">
|
||||||
|
<div class="adf-viewer-dialog__loading-screen">
|
||||||
|
<h2>Loading</h2>
|
||||||
|
<div>
|
||||||
|
<md-spinner></md-spinner>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="!isLoading" [ngSwitch]="viewerType">
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'image'">
|
<ng-container *ngSwitchCase="'image'">
|
||||||
<div class="adf-viewer-dialog__image-view">
|
<div class="adf-viewer-dialog__image-view">
|
||||||
@ -89,10 +103,10 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'pdf'">
|
<ng-container *ngSwitchCase="'pdf'">
|
||||||
<adf-pdf-viewer class="adf-viewer-dialog__pdf-view"
|
<adf-pdf-view
|
||||||
[showToolbar]="true"
|
class="adf-viewer-dialog__pdf-view"
|
||||||
[urlFile]="fileUrl">
|
[fileUrl]="fileUrl">
|
||||||
</adf-pdf-viewer>
|
</adf-pdf-view>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'video'">
|
<ng-container *ngSwitchCase="'video'">
|
@ -2,8 +2,8 @@
|
|||||||
.mat-dialog-container {
|
.mat-dialog-container {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
width: 99vw;
|
width: 100vw;
|
||||||
height: 99vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,10 +11,28 @@
|
|||||||
|
|
||||||
.mat-dialog-content {
|
.mat-dialog-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
max-height: 90vh;
|
max-height: 100vh;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__mimetype-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__loading-screen {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 85vh;
|
||||||
|
|
||||||
|
.md-spinner {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__info-drawer {
|
&__info-drawer {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
display: block;
|
display: block;
|
||||||
@ -46,7 +64,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 90vh;
|
height: 95vh;
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
@ -83,9 +101,7 @@
|
|||||||
|
|
||||||
&__pdf-view {
|
&__pdf-view {
|
||||||
.viewer-pdf-container {
|
.viewer-pdf-container {
|
||||||
top: 52px;
|
top: 50px;
|
||||||
left: 7px;
|
|
||||||
right: 7px;
|
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
background-color: lightgray;
|
background-color: lightgray;
|
||||||
}
|
}
|
@ -18,6 +18,7 @@
|
|||||||
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
|
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
import { Http, Response } from '@angular/http';
|
import { Http, Response } from '@angular/http';
|
||||||
import { MD_DIALOG_DATA, MdDialogRef } from '@angular/material';
|
import { MD_DIALOG_DATA, MdDialogRef } from '@angular/material';
|
||||||
|
import { RenditionsService } from 'ng2-alfresco-core';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
|
||||||
import { ViewerDialogSettings } from './viewer-dialog.settings';
|
import { ViewerDialogSettings } from './viewer-dialog.settings';
|
||||||
@ -42,9 +43,13 @@ export class ViewerDialogComponent implements OnInit {
|
|||||||
unknownFormatIcon = 'wifi_tethering';
|
unknownFormatIcon = 'wifi_tethering';
|
||||||
unknownFormatText = 'Document preview could not be loaded.';
|
unknownFormatText = 'Document preview could not be loaded.';
|
||||||
|
|
||||||
|
isLoading: boolean = false;
|
||||||
|
|
||||||
viewerType: string = null;
|
viewerType: string = null;
|
||||||
asText: Observable<string>;
|
asText: Observable<string>;
|
||||||
|
|
||||||
|
private nodeId: string;
|
||||||
|
|
||||||
private types = [
|
private types = [
|
||||||
{ mimeType: 'application/x-javascript', type: 'text' },
|
{ mimeType: 'application/x-javascript', type: 'text' },
|
||||||
{ mimeType: 'application/pdf', type: 'pdf' }
|
{ mimeType: 'application/pdf', type: 'pdf' }
|
||||||
@ -52,11 +57,13 @@ export class ViewerDialogComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(private dialogRef: MdDialogRef<ViewerDialogComponent>,
|
constructor(private dialogRef: MdDialogRef<ViewerDialogComponent>,
|
||||||
@Inject(MD_DIALOG_DATA) settings: ViewerDialogSettings,
|
@Inject(MD_DIALOG_DATA) settings: ViewerDialogSettings,
|
||||||
private http: Http) {
|
private http: Http,
|
||||||
|
private renditionService: RenditionsService) {
|
||||||
this.fileUrl = settings.fileUrl;
|
this.fileUrl = settings.fileUrl;
|
||||||
this.fileName = settings.fileName;
|
this.fileName = settings.fileName;
|
||||||
this.fileMimeType = settings.fileMimeType;
|
this.fileMimeType = settings.fileMimeType;
|
||||||
this.downloadUrl = settings.downloadUrl;
|
this.downloadUrl = settings.downloadUrl;
|
||||||
|
this.nodeId = settings.nodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -65,6 +72,10 @@ export class ViewerDialogComponent implements OnInit {
|
|||||||
|
|
||||||
if (this.viewerType !== 'unknown') {
|
if (this.viewerType !== 'unknown') {
|
||||||
this.allowInfoDrawer = true;
|
this.allowInfoDrawer = true;
|
||||||
|
} else {
|
||||||
|
if (this.nodeId) {
|
||||||
|
this.displayAsPdf(this.nodeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,4 +128,43 @@ export class ViewerDialogComponent implements OnInit {
|
|||||||
close() {
|
close() {
|
||||||
this.dialogRef.close(true);
|
this.dialogRef.close(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.showRenditionPdf(nodeId);
|
||||||
|
} else if (status === 'NOT_CREATED') {
|
||||||
|
this.renditionService.convert(nodeId, 'pdf').subscribe({
|
||||||
|
complete: () => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.showRenditionPdf(nodeId);
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private showRenditionPdf(nodeId: string) {
|
||||||
|
if (nodeId) {
|
||||||
|
this.viewerType = 'pdf';
|
||||||
|
this.fileUrl = this.renditionService.getRenditionUrl(nodeId, 'pdf');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -20,4 +20,6 @@ export interface ViewerDialogSettings {
|
|||||||
fileMimeType?: string;
|
fileMimeType?: string;
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
downloadUrl?: string;
|
downloadUrl?: string;
|
||||||
|
|
||||||
|
nodeId?: string;
|
||||||
}
|
}
|
@ -52,7 +52,7 @@
|
|||||||
</adf-media-player>
|
</adf-media-player>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="isText()">
|
<ng-container *ngIf="isText() && !isExternalSupportedExtension()">
|
||||||
<adf-txt-viewer [urlFile]="urlFileContent" [blobFile]="blobFile"></adf-txt-viewer>
|
<adf-txt-viewer [urlFile]="urlFileContent" [blobFile]="blobFile"></adf-txt-viewer>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ import { MdDialog } from '@angular/material';
|
|||||||
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
|
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||||
import { AlfrescoApiService } from 'ng2-alfresco-core';
|
import { AlfrescoApiService } from 'ng2-alfresco-core';
|
||||||
|
|
||||||
import { ViewerDialogComponent } from './../components/viewer-dialog.component';
|
import { ViewerDialogComponent } from './../components/viewer-dialog/viewer-dialog.component';
|
||||||
import { ViewerDialogSettings } from './../components/viewer-dialog.settings';
|
import { ViewerDialogSettings } from './../components/viewer-dialog/viewer-dialog.settings';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ViewerService {
|
export class ViewerService {
|
||||||
@ -30,21 +30,14 @@ export class ViewerService {
|
|||||||
private apiService: AlfrescoApiService) {
|
private apiService: AlfrescoApiService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get contentApi() {
|
|
||||||
return this.apiService.getInstance().content;
|
|
||||||
}
|
|
||||||
|
|
||||||
private get nodesApi() {
|
|
||||||
return this.apiService.getInstance().nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
showViewerForNode(node: MinimalNodeEntryEntity): Promise<boolean> {
|
showViewerForNode(node: MinimalNodeEntryEntity): Promise<boolean> {
|
||||||
return new Promise<boolean>((resolve, reject) => {
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
const settings: ViewerDialogSettings = {
|
const settings: ViewerDialogSettings = {
|
||||||
fileName: node.name,
|
fileName: node.name,
|
||||||
fileMimeType: node.content.mimeType,
|
fileMimeType: node.content.mimeType,
|
||||||
fileUrl: this.contentApi.getContentUrl(node.id, false),
|
fileUrl: this.apiService.contentApi.getContentUrl(node.id, false),
|
||||||
downloadUrl: this.contentApi.getContentUrl(node.id, true)
|
downloadUrl: this.apiService.contentApi.getContentUrl(node.id, true),
|
||||||
|
nodeId: node.id
|
||||||
};
|
};
|
||||||
|
|
||||||
const dialogRef = this.dialog.open(ViewerDialogComponent, {
|
const dialogRef = this.dialog.open(ViewerDialogComponent, {
|
||||||
@ -60,7 +53,7 @@ export class ViewerService {
|
|||||||
|
|
||||||
showViewerForNodeId(nodeId: string): Promise<boolean> {
|
showViewerForNodeId(nodeId: string): Promise<boolean> {
|
||||||
return new Promise<boolean>((resolve, reject) => {
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
this.nodesApi.getNode(nodeId).then(
|
this.apiService.nodesApi.getNode(nodeId).then(
|
||||||
(node: MinimalNodeEntity) => {
|
(node: MinimalNodeEntity) => {
|
||||||
if (node && node.entry && node.entry.isFile) {
|
if (node && node.entry && node.entry.isFile) {
|
||||||
return this.showViewerForNode(node.entry);
|
return this.showViewerForNode(node.entry);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user